1#include "UserInteractions.h"
2#include <CoreFoundation/CoreFoundation.h>
3#include <CoreServices/CoreServices.h>
4#include <ApplicationServices/ApplicationServices.h>
5#include <AppKit/AppKit.h>
6
7#include <iostream>
8#include <sstream>
9#include <unistd.h>
10
11namespace Surge
12{
13
14namespace UserInteractions
15{
16
17void promptError(const std::string &message, const std::string &title, SurgeGUIEditor *guiEditor)
18{
19    CFStringRef cfT =
20        CFStringCreateWithCString(kCFAllocatorDefault, title.c_str(), kCFStringEncodingUTF8);
21    CFStringRef cfM =
22        CFStringCreateWithCString(kCFAllocatorDefault, message.c_str(), kCFStringEncodingUTF8);
23
24    SInt32 nRes = 0;
25    CFUserNotificationRef pDlg = NULL;
26    const void *keys[] = {kCFUserNotificationAlertHeaderKey, kCFUserNotificationAlertMessageKey};
27    const void *vals[] = {cfT, cfM};
28
29    CFDictionaryRef dict =
30        CFDictionaryCreate(0, keys, vals, sizeof(keys) / sizeof(*keys),
31                           &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
32
33    pDlg = CFUserNotificationCreate(kCFAllocatorDefault, 0, kCFUserNotificationStopAlertLevel,
34                                    &nRes, dict);
35
36    CFRelease(cfT);
37    CFRelease(cfM);
38}
39
40void promptInfo(const std::string &message, const std::string &title, SurgeGUIEditor *guiEditor)
41{
42    CFStringRef cfT =
43        CFStringCreateWithCString(kCFAllocatorDefault, title.c_str(), kCFStringEncodingUTF8);
44    CFStringRef cfM =
45        CFStringCreateWithCString(kCFAllocatorDefault, message.c_str(), kCFStringEncodingUTF8);
46
47    SInt32 nRes = 0;
48    CFUserNotificationRef pDlg = NULL;
49    const void *keys[] = {kCFUserNotificationAlertHeaderKey, kCFUserNotificationAlertMessageKey};
50    const void *vals[] = {cfT, cfM};
51
52    CFDictionaryRef dict =
53        CFDictionaryCreate(0, keys, vals, sizeof(keys) / sizeof(*keys),
54                           &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
55
56    pDlg = CFUserNotificationCreate(kCFAllocatorDefault, 0, kCFUserNotificationNoteAlertLevel,
57                                    &nRes, dict);
58
59    CFRelease(cfT);
60    CFRelease(cfM);
61}
62
63MessageResult promptOKCancel(const std::string &message, const std::string &title,
64                             SurgeGUIEditor *guiEditor)
65{
66    CFStringRef cfT =
67        CFStringCreateWithCString(kCFAllocatorDefault, title.c_str(), kCFStringEncodingUTF8);
68    CFStringRef cfM =
69        CFStringCreateWithCString(kCFAllocatorDefault, message.c_str(), kCFStringEncodingUTF8);
70
71    CFOptionFlags responseFlags;
72    CFUserNotificationDisplayAlert(0, kCFUserNotificationPlainAlertLevel, 0, 0, 0, cfT, cfM,
73                                   CFSTR("OK"), CFSTR("Cancel"), 0, &responseFlags);
74
75    CFRelease(cfT);
76    CFRelease(cfM);
77
78    if ((responseFlags & 0x3) != kCFUserNotificationDefaultResponse)
79        return UserInteractions::CANCEL;
80    return UserInteractions::OK;
81}
82
83void openURL(const std::string &url_str)
84{
85    CFURLRef url = CFURLCreateWithBytes(NULL,                     // allocator
86                                        (UInt8 *)url_str.c_str(), // URLBytes
87                                        url_str.length(),         // length
88                                        kCFStringEncodingASCII,   // encoding
89                                        NULL                      // baseURL
90    );
91    LSOpenCFURLRef(url, 0);
92    CFRelease(url);
93}
94
95void showHTML(const std::string &html)
96{
97    // Why does mktemp crash on macos I wonder?
98    std::ostringstream fns;
99    fns << "/var/tmp/surge-data." << rand() << ".html";
100
101    FILE *f = fopen(fns.str().c_str(), "w");
102    if (f)
103    {
104        fprintf(f, "%s", html.c_str());
105        fclose(f);
106        std::string url = std::string("file://") + fns.str();
107        openURL(url);
108    }
109}
110
111void openFolderInFileBrowser(const std::string &folder)
112{
113    std::string url = "file://" + folder;
114    UserInteractions::openURL(url);
115}
116
117void promptFileOpenDialog(const std::string &initialDirectory, const std::string &filterSuffix,
118                          const std::string &filterDescription,
119                          std::function<void(std::string)> callbackOnOpen,
120                          bool canSelectDirectories, bool canCreateDirectories,
121                          SurgeGUIEditor *guiEditor)
122{
123    // FIXME TODO - support the filterSuffix and initialDirectory
124    NSOpenPanel *panel = [NSOpenPanel openPanel];
125    [panel setCanChooseDirectories:canSelectDirectories];
126    [panel setCanCreateDirectories:canCreateDirectories];
127    [panel
128        setDirectoryURL:[NSURL
129                            fileURLWithPath:[NSString
130                                                stringWithUTF8String:(initialDirectory.c_str())]]];
131
132    // Logic and others have wierd window ordering which can mean this somethimes pops behind.
133    // See #1001.
134    [panel makeKeyAndOrderFront:panel];
135    [panel setLevel:CGShieldingWindowLevel()];
136
137    [panel beginWithCompletionHandler:^(NSInteger result) {
138      if (result == NSFileHandlingPanelOKButton)
139      {
140          NSURL *theDoc = [[panel URLs] objectAtIndex:0];
141          NSString *path = [theDoc path];
142          std::string pstring([path UTF8String]);
143          callbackOnOpen(pstring);
144      }
145    }];
146}
147
148};
149
150};
151