Vuo 2.4.4
Loading...
Searching...
No Matches
VuoEditorUtilities.cc
Go to the documentation of this file.
1
10#include "VuoEditorUtilities.hh"
11
12#include "VuoCodeWindow.hh"
14#include "VuoEditorWindow.hh"
15#include "VuoFileUtilities.hh"
16#include "VuoNodeClass.hh"
17
18#ifdef __APPLE__
19#include <objc/objc-runtime.h>
20#include <ApplicationServices/ApplicationServices.h> // for kCGEventSourceStateCombinedSessionState
21#include <Security/Authorization.h>
22#include <Security/AuthorizationTags.h>
23#endif
24
29{
30 return QIcon(":/Icons/vuo.svg").pixmap(64,64);
31}
32
37QString VuoEditorUtilities::getHTMLForSVG(QString svgPath, int pointsWide, int pointsHigh)
38{
39 // QIcon::pixmap might not return the exact requested size
40 // (e.g., if the icon's aspect ratio doesn't match the requested aspect ratio),
41 // so create an exact pixmap and draw the icon on it.
42 qreal dpr = QApplication::desktop()->devicePixelRatio();
43 QPixmap pixmap(pointsWide * dpr, pointsHigh * dpr);
44 pixmap.fill(Qt::transparent);
45 QPainter painter(&pixmap);
46 QIcon(svgPath).paint(&painter, 0, 0, pointsWide * dpr, pointsHigh * dpr);
47
48 QByteArray bytes;
49 QBuffer buffer(&bytes);
50 pixmap.save(&buffer, "PNG");
51 return QString("<img src='data:image/png;base64,")
52 + bytes.toBase64()
53 + QString("' width=%1 height=%2 />")
54 .arg(pointsWide)
55 .arg(pointsHigh);
56}
57
62{
63 QString path = QString::fromUtf8(VuoFileUtilities::getUserModulesPath().c_str());
64 QDir dir(path);
65 if (!dir.exists())
66 QDir().mkpath(path);
67
68 if (dir.exists())
69 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
70 else
71 QMessageBox::information(NULL, QObject::tr("Vuo User Library folder"), QObject::tr("Please create this folder if you'd like to install nodes for this user account:\n\n%1").arg(path));
72}
73
78{
79 QString path = QString::fromUtf8(VuoFileUtilities::getSystemModulesPath().c_str());
80 QDir dir(path);
81
82 // Request authorization to create the folder.
83 if (!dir.exists())
84 {
85 AuthorizationRef auth = NULL;
86 if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth) == errAuthorizationSuccess)
87 {
88 AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0};
89 AuthorizationRights rights = {1, &right};
90
91 const char *promptString = QObject::tr("Vuo wants to create its System Library folder.").toUtf8().constData();
92 AuthorizationItem prompt = {kAuthorizationEnvironmentPrompt, strlen(promptString), (void *)promptString, 0};
93 AuthorizationEnvironment environment = {1, &prompt};
94
95 if (AuthorizationCopyRights(auth, &rights, &environment,
96 kAuthorizationFlagDefaults
97 | kAuthorizationFlagInteractionAllowed
98 | kAuthorizationFlagPreAuthorize
99 | kAuthorizationFlagExtendRights,
100 NULL) == errAuthorizationSuccess)
101 {
102 const char *args[] = { "-p", strdup(path.toUtf8().data()), NULL };
103 FILE *pipe = NULL;
104#pragma clang diagnostic push
105#pragma clang diagnostic ignored "-Wdeprecated-declarations"
106 // See http://www.stevestreeting.com/2011/11/25/escalating-privileges-on-mac-os-x-securely-and-without-using-deprecated-methods/ for info on a non-deprecated path.
107 if (AuthorizationExecuteWithPrivileges(auth, "/bin/mkdir", kAuthorizationFlagDefaults, (char * const *)args, &pipe) == errAuthorizationSuccess)
108#pragma clang diagnostic pop
109 {
110 // Wait for `mkdir` to complete.
111 int bytesRead = 0;
112 do
113 {
114 char buffer[128];
115 bytesRead = read(fileno(pipe), buffer, sizeof(buffer));
116 } while (bytesRead);
117 fclose(pipe);
118 }
119 free((char *)args[1]);
120 }
121
122 AuthorizationFree(auth, kAuthorizationFlagDefaults);
123 }
124 }
125
126 if (dir.exists())
127 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
128 else
129 QMessageBox::information(NULL, QObject::tr("Vuo System Library folder"), QObject::tr("Please create this folder if you'd like to install nodes for all users on this computer:\n\n%1").arg(path));
130}
131
140{
141 // Technically we want to know whether the "Option" key was pressed at the time of the drop, not right at this moment,
142 // but QGraphicsSceneDragDropEvent::modifiers() isn't providing that information reliably on OS X at Qt 5.3.
143#ifdef __APPLE__
144 bool optionKeyPressed = (CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState) & kCGEventFlagMaskAlternate);
145 #else
146 bool optionKeyPressed = (event->modifiers() & Qt::AltModifier);
147 #endif
148
149 return optionKeyPressed;
150}
151
160bool VuoEditorUtilities::isNodeClassEditable(VuoNodeClass *nodeClass, QString &editLabel, QString &sourcePath)
161{
162 if (nodeClass->hasCompiler())
163 {
164 string expectedSourcePath = nodeClass->getCompiler()->getSourcePath();
165
166 if (VuoFileUtilities::fileExists(expectedSourcePath))
167 {
168 string dir, file, ext;
169 VuoFileUtilities::splitPath(expectedSourcePath, dir, file, ext);
170 string sourceKind = (VuoFileUtilities::isCompositionExtension(ext) ?
171 "Composition" :
173 "Node"));
174
175 editLabel = QObject::tr(("Edit " + sourceKind + "…").c_str());
176 sourcePath = QString::fromStdString(expectedSourcePath);
177
178 return true;
179 }
180 }
181
182 return false;
183}
184
192bool VuoEditorUtilities::isNodeClassSuccessorTo(QString oldNodeClass, QString newNodeClass)
193{
194 map<QString, QString> successorForNodeClass;
195 successorForNodeClass["vuo.mesh.make.lines"] = "vuo.scene.make.lines";
196 successorForNodeClass["vuo.mesh.make.lineStrips"] = "vuo.scene.make.lineStrips";
197 successorForNodeClass["vuo.mesh.make.parametric"] = "vuo.scene.make.parametric";
198 successorForNodeClass["vuo.mesh.make.points"] = "vuo.scene.make.points";
199 successorForNodeClass["vuo.mesh.make.sphere"] = "vuo.scene.make.sphere";
200 successorForNodeClass["vuo.mesh.make.square"] = "vuo.scene.make.square";
201 successorForNodeClass["vuo.mesh.make.triangle"] = "vuo.scene.make.triangle";
202
203 // Case: newNodeClass is a designated successor to oldNodeClass
204 if (successorForNodeClass[oldNodeClass] == newNodeClass)
205 return true;
206
207 // Case: oldNodeClass and newNodeClass have identical root names with increasing numerical suffixes, or
208 // newNodeClass is identical to oldNodeClass plus a numerical suffix.
209 QString root = "";
210 QString oldSuffix = "";
211 QString newSuffix = "";
212 int minLength = qMin(oldNodeClass.length(), newNodeClass.length());
213 for (int i = 0; (i < minLength); ++i)
214 {
215 if (oldNodeClass.at(i) == newNodeClass.at(i))
216 root += oldNodeClass.at(i);
217 else
218 break;
219 }
220
221 oldSuffix = oldNodeClass.right(oldNodeClass.length()-root.length());
222 newSuffix = newNodeClass.right(newNodeClass.length()-root.length());
223
224 bool oldSuffixIsNumeric = false;
225 int oldSuffixVal = oldSuffix.toInt(&oldSuffixIsNumeric);
226
227 bool newSuffixIsNumeric = false;
228 int newSuffixVal = newSuffix.toInt(&newSuffixIsNumeric);
229
230 if (newSuffixIsNumeric && (oldSuffix.isEmpty() || (oldSuffixIsNumeric && (newSuffixVal > oldSuffixVal))))
231 {
232 // Exclude false positives where the numerical endings are meaningful parts of the root,
233 // such as "vuo.osc.message.make.2" -> "vuo.osc.message.make.3" and
234 // "vuo.osc.message.get.1" -> "vuo.osc.message.get.11"
235 bool rootEndsInNumberSegment = false;
236 for (int i = root.length()-1; i >= 0; --i)
237 {
238 if (root.at(i).isDigit())
239 continue;
240 else
241 {
242 if (root.at(i) == '.')
243 rootEndsInNumberSegment = true;
244 break;
245 }
246 }
247
248 return !rootEndsInNumberSegment;
249 }
250
251 return false;
252}
253
258{
259 QList<QMainWindow *> openWindows;
260 for (QWidget *openWidget : QApplication::topLevelWidgets())
261 if ((dynamic_cast<VuoEditorWindow *>(openWidget) || dynamic_cast<VuoCodeWindow *>(openWidget)) && ! openWidget->isHidden())
262 openWindows.append(static_cast<QMainWindow *>(openWidget));
263
264 std::sort(openWindows.begin(), openWindows.end(), [](const QMainWindow *window1, const QMainWindow *window2) {
265 return window1->windowTitle().remove("[*]").compare(window2->windowTitle().remove("[*]"), Qt::CaseInsensitive) < 0;
266 });
267
268 return openWindows;
269}
270
275{
276 QList<VuoEditorWindow *> openCompositionWindows;
277 for (QMainWindow *openWindow : getOpenEditingWindows())
278 if (dynamic_cast<VuoEditorWindow *>(openWindow))
279 openCompositionWindows.append(static_cast<VuoEditorWindow *>(openWindow));
280
281 return openCompositionWindows;
282}
283
288{
289 QList<QMainWindow *> openWindows = getOpenEditingWindows();
290
291 std::sort(openWindows.begin(), openWindows.end(), [](const QMainWindow *window1, const QMainWindow *window2) {
292 id nsView1 = (id)window1->winId();
293 id nsWindow1 = ((id (*)(id, SEL))objc_msgSend)(nsView1, sel_getUid("window"));
294 long orderedIndex1 = ((long (*)(id, SEL))objc_msgSend)(nsWindow1, sel_getUid("orderedIndex"));
295
296 id nsView2 = (id)window2->winId();
297 id nsWindow2 = ((id (*)(id, SEL))objc_msgSend)(nsView2, sel_getUid("window"));
298 long orderedIndex2 = ((long (*)(id, SEL))objc_msgSend)(nsWindow2, sel_getUid("orderedIndex"));
299
300 return orderedIndex1 < orderedIndex2;
301 });
302
303 return openWindows;
304}
305
309QMainWindow * VuoEditorUtilities::existingWindowWithFile(const QString &filename)
310{
311 QString canonicalFilePath = QFileInfo(filename).canonicalFilePath();
312
313 for (QMainWindow *openWindow : getOpenEditingWindows())
314 if (openWindow->windowFilePath() == canonicalFilePath)
315 return openWindow;
316
317 return NULL;
318}
319
325{
326 VuoEditorWindow *editorWindow = dynamic_cast<VuoEditorWindow *>(window);
327 if (editorWindow)
328 {
329 return editorWindow->getRaiseDocumentAction();
330 }
331 else
332 {
333 VuoCodeWindow *codeWindow = dynamic_cast<VuoCodeWindow *>(window);
334 if (codeWindow)
335 return codeWindow->getRaiseDocumentAction();
336 }
337
338 return nullptr;
339}
340
345{
346 VuoEditorWindow *editorWindow = dynamic_cast<VuoEditorWindow *>(window);
347 if (editorWindow)
348 {
349 return editorWindow->getRecentFileMenu();
350 }
351 else
352 {
353 VuoCodeWindow *codeWindow = dynamic_cast<VuoCodeWindow *>(window);
354 if (codeWindow)
355 return codeWindow->getRecentFileMenu();
356 }
357
358 return nullptr;
359}
360
365{
366 VuoEditorWindow *editorWindow = dynamic_cast<VuoEditorWindow *>(window);
367 if (editorWindow)
368 {
369 return editorWindow->getFileMenu();
370 }
371 else
372 {
373 VuoCodeWindow *codeWindow = dynamic_cast<VuoCodeWindow *>(window);
374 if (codeWindow)
375 return codeWindow->getFileMenu();
376 }
377
378 return nullptr;
379}
380
385{
386 VuoEditorWindow *editorWindow = dynamic_cast<VuoEditorWindow *>(window);
387 if (editorWindow)
388 {
389 return editorWindow->setAsActiveWindow();
390 }
391 else
392 {
393 VuoCodeWindow *codeWindow = dynamic_cast<VuoCodeWindow *>(window);
394 if (codeWindow)
395 return codeWindow->setAsActiveWindow();
396 }
397}
398
404void VuoEditorUtilities::setWindowOpacity(QMainWindow *window, int opacity)
405{
406#ifdef __APPLE__
407 id nsView = (id)window->winId();
408 id nsWindow = ((id (*)(id, SEL))objc_msgSend)(nsView, sel_getUid("window"));
409 ((void (*)(id, SEL, CGFloat))objc_msgSend)(nsWindow, sel_getUid("setAlphaValue:"), opacity/255.);
410 #endif
411}