Skip to content
This repository was archived by the owner on Sep 2, 2021. It is now read-only.

Commit 87f9a15

Browse files
committed
Merge pull request #190 from adobe/glenn/file-association
Initial implementation of drag and drop to open files
2 parents f254eca + a30f0fc commit 87f9a15

11 files changed

Lines changed: 243 additions & 7 deletions

appshell/appshell_extensions.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,18 @@ class ProcessMessageDelegate : public ClientHandler::ProcessMessageDelegate {
334334
ExtensionString path = argList->GetString(1);
335335
error = ShowFolderInOSWindow(path);
336336
}
337+
} else if (message_name == "GetPendingFilesToOpen") {
338+
// Parameters:
339+
// 0: int32 - callback id
340+
if (argList->GetSize() != 1) {
341+
error = ERR_INVALID_PARAMS;
342+
}
343+
344+
if (error == NO_ERROR) {
345+
ExtensionString files;
346+
error = GetPendingFilesToOpen(files);
347+
responseArgs->SetString(2, files.c_str());
348+
}
337349
} else if (message_name == "AddMenu") {
338350
// Parameters:
339351
// 0: int32 - callback id

appshell/appshell_extensions.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,23 @@ if (!appshell.app) {
375375
OpenURLInDefaultBrowser(callback, url);
376376
};
377377

378+
/**
379+
* Get files passed to app at startup.
380+
*
381+
* @param {function(err, files)} callback Asynchronous callback function with two arguments:
382+
* err - error code
383+
* files - Array of file paths to open
384+
*
385+
* @return None. This is an asynchronous call that sends all return information to the callback.
386+
*/
387+
native function GetPendingFilesToOpen();
388+
appshell.app.getPendingFilesToOpen = function (callback) {
389+
GetPendingFilesToOpen(function (err, files) {
390+
// "files" is a string, convert to Array
391+
callback(err, err ? [] : JSON.parse(files));
392+
});
393+
};
394+
378395
/**
379396
* Set menu enabled/checked state.
380397
* @param {string} command ID of the menu item.

appshell/appshell_extensions_mac.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include <Cocoa/Cocoa.h>
2929

30+
extern ExtensionString gPendingFilesToOpen;
3031

3132
@interface ChromeWindowsTerminatedObserver : NSObject
3233
- (void)appTerminated:(NSNotification *)note;
@@ -586,6 +587,13 @@ int32 ShowFolderInOSWindow(ExtensionString pathname)
586587
return NO_ERROR;
587588
}
588589

590+
int32 GetPendingFilesToOpen(ExtensionString& files)
591+
{
592+
files = gPendingFilesToOpen;
593+
gPendingFilesToOpen = "[]";
594+
return NO_ERROR;
595+
}
596+
589597
int32 GetMenuPosition(CefRefPtr<CefBrowser> browser, const ExtensionString& commandId, ExtensionString& parentId, int& index)
590598
{
591599
index = -1;

appshell/appshell_extensions_platform.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ void BringBrowserWindowToFront(CefRefPtr<CefBrowser> browser);
9898

9999
int32 ShowFolderInOSWindow(ExtensionString pathname);
100100

101+
int32 GetPendingFilesToOpen(ExtensionString& files);
102+
101103
int32 AddMenu(CefRefPtr<CefBrowser> browser, ExtensionString title, ExtensionString command,
102104
ExtensionString position, ExtensionString relativeId);
103105

appshell/appshell_extensions_win.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ time_t FiletimeToTime(FILETIME const& ft);
5151

5252
extern HINSTANCE hInst;
5353
extern HACCEL hAccelTable;
54+
extern std::wstring gFilesToOpen;
5455

5556
// constants
5657
#define MAX_LOADSTRING 100
@@ -798,6 +799,12 @@ int32 ShowFolderInOSWindow(ExtensionString pathname) {
798799
return NO_ERROR;
799800
}
800801

802+
int32 GetPendingFilesToOpen(ExtensionString& files) {
803+
files = gFilesToOpen;
804+
ConvertToUnixPath(files);
805+
gFilesToOpen = L"";
806+
return NO_ERROR;
807+
}
801808

802809
// Return index where menu or menu item should be placed.
803810
// -1 indicates append. -2 indicates 'before' - WINAPI supports

appshell/cefclient_mac.mm

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
// Memory AutoRelease pool.
4747
static NSAutoreleasePool* g_autopool = nil;
4848

49+
// Files passed to the app at startup
50+
static NSMutableArray* pendingOpenFiles;
51+
ExtensionString gPendingFilesToOpen;
52+
4953
// Provide the CefAppProtocol implementation required by CEF.
5054
@interface ClientApplication : NSApplication<CefAppProtocol> {
5155
@private
@@ -286,6 +290,8 @@ - (void)cleanup:(id)window {
286290
// Receives notifications from the application. Will delete itself when done.
287291
@interface ClientAppDelegate : NSObject
288292
- (void)createApp:(id)object;
293+
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
294+
- (BOOL)application:(NSApplication *)theApplication openFiles:(NSArray *)filenames;
289295
@end
290296

291297
@implementation ClientAppDelegate
@@ -420,8 +426,23 @@ - (void)createApp:(id)object {
420426
window_info.SetAsChild(contentView, 0, 0, content_rect.size.width, content_rect.size.height);
421427

422428
NSString* str = [[startupUrl absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
423-
CefBrowserHost::CreateBrowser(window_info, g_handler.get(),
429+
CefBrowserHost::CreateBrowserSync(window_info, g_handler.get(),
424430
[str UTF8String], settings);
431+
432+
if (pendingOpenFiles) {
433+
NSUInteger count = [pendingOpenFiles count];
434+
gPendingFilesToOpen = "[";
435+
for (NSUInteger i = 0; i < count; i++) {
436+
NSString* filename = [pendingOpenFiles objectAtIndex:i];
437+
438+
gPendingFilesToOpen += ("\"" + std::string([filename UTF8String]) + "\"");
439+
if (i < count - 1)
440+
gPendingFilesToOpen += ",";
441+
}
442+
gPendingFilesToOpen += "]";
443+
} else {
444+
gPendingFilesToOpen = "[]";
445+
}
425446

426447
// Show the window.
427448
[mainWnd display];
@@ -450,21 +471,52 @@ -(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theAppli
450471
}
451472

452473
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)theApplication {
453-
if (!g_isTerminating && g_handler.get() && !g_handler->AppIsQuitting() && g_handler->HasWindows()) {
474+
if (!g_isTerminating && g_handler.get() && !g_handler->AppIsQuitting() && g_handler->HasWindows() && [NSApp keyWindow]) {
454475
g_handler->DispatchCloseToNextBrowser();
455476
return NSTerminateCancel;
456477
}
457478
g_isTerminating = true;
458479
return NSTerminateNow;
459480
}
460481

482+
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
483+
if (g_handler) {
484+
CefRefPtr<CefBrowser> browser = ClientHandler::GetBrowserForNativeWindow([NSApp keyWindow]);
485+
g_handler->SendOpenFileCommand(browser, CefString([filename UTF8String]));
486+
} else {
487+
// App is just starting up. Save the filename so we can open it later.
488+
if (!pendingOpenFiles) {
489+
pendingOpenFiles = [[NSMutableArray alloc] init];
490+
[pendingOpenFiles addObject:filename];
491+
}
492+
}
493+
return YES;
494+
}
495+
496+
- (BOOL)application:(NSApplication *)theApplication openFiles:(NSArray *)filenames {
497+
if (g_handler) {
498+
CefRefPtr<CefBrowser> browser = ClientHandler::GetBrowserForNativeWindow([NSApp keyWindow]);
499+
for (NSUInteger i = 0; i < [filenames count]; i++) {
500+
g_handler->SendOpenFileCommand(browser, CefString([[filenames objectAtIndex:i] UTF8String]));
501+
}
502+
} else {
503+
// App is just starting up. Save the filenames so we can open them later.
504+
pendingOpenFiles = [[NSMutableArray alloc] init];
505+
for (NSUInteger i = 0; i < [filenames count]; i++) {
506+
[pendingOpenFiles addObject:[filenames objectAtIndex:i]];
507+
}
508+
}
509+
return YES;
510+
}
461511
@end
462512

463513

464514
int main(int argc, char* argv[]) {
465515
// Initialize the AutoRelease pool.
466516
g_autopool = [[NSAutoreleasePool alloc] init];
467517

518+
pendingOpenFiles = nil;
519+
468520
CefMainArgs main_args(argc, argv);
469521

470522
// Delete Special Characters Palette from Edit menu.
@@ -485,6 +537,8 @@ int main(int argc, char* argv[]) {
485537

486538
// Initialize the ClientApplication instance.
487539
[ClientApplication sharedApplication];
540+
NSObject* delegate = [[ClientAppDelegate alloc] init];
541+
[NSApp setDelegate:delegate];
488542

489543
// Parse command line arguments.
490544
AppInitCommandLine(argc, argv);
@@ -552,7 +606,6 @@ int main(int argc, char* argv[]) {
552606
}
553607

554608
// Create the application delegate and window.
555-
NSObject* delegate = [[ClientAppDelegate alloc] init];
556609
[delegate performSelectorOnMainThread:@selector(createApp:) withObject:nil
557610
waitUntilDone:NO];
558611

appshell/cefclient_win.cpp

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "client_switches.h"
2121
#include "native_menu_model.h"
2222

23+
#include <algorithm>
24+
#include <ShellAPI.h>
2325
#include <ShlObj.h>
2426

2527
#define MAX_LOADSTRING 100
@@ -37,6 +39,7 @@ DWORD g_appStartupTime;
3739
HINSTANCE hInst; // current instance
3840
HACCEL hAccelTable;
3941
HWND hWndMain;
42+
std::wstring gFilesToOpen; // Filenames passed as arguments to app
4043
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
4144
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
4245
char szWorkingDir[MAX_PATH]; // The current working directory
@@ -82,6 +85,37 @@ void SaveWindowRect(HWND hWnd);
8285
void RestoreWindowRect(int& left, int& top, int& width, int& height, int& showCmd);
8386
void RestoreWindowPlacement(HWND hWnd, int showCmd);
8487

88+
bool IsFilename(const std::wstring& str) {
89+
// See if we can access the passed in value
90+
return (GetFileAttributes(str.c_str()) != INVALID_FILE_ATTRIBUTES);
91+
}
92+
93+
std::wstring GetFilenamesFromCommandLine() {
94+
std::wstring result = L"[]";
95+
96+
if (AppGetCommandLine()->HasArguments()) {
97+
bool firstEntry = true;
98+
std::vector<CefString> args;
99+
AppGetCommandLine()->GetArguments(args);
100+
std::vector<CefString>::iterator iterator;
101+
102+
result = L"[";
103+
for (iterator = args.begin(); iterator != args.end(); iterator++) {
104+
std::wstring argument = (*iterator).ToWString();
105+
if (IsFilename(argument)) {
106+
if (!firstEntry) {
107+
result += L",";
108+
}
109+
firstEntry = false;
110+
result += L"\"" + argument + L"\"";
111+
}
112+
}
113+
result += L"]";
114+
}
115+
116+
return result;
117+
}
118+
85119
// Program entry point function.
86120
int APIENTRY wWinMain(HINSTANCE hInstance,
87121
HINSTANCE hPrevInstance,
@@ -182,6 +216,8 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
182216
if (!InitInstance (hInstance, nCmdShow))
183217
return FALSE;
184218

219+
gFilesToOpen = GetFilenamesFromCommandLine();
220+
185221
int result = 0;
186222

187223
if (!settings.multi_threaded_message_loop) {
@@ -520,12 +556,25 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
520556
if (!hWndMain)
521557
return FALSE;
522558

559+
DragAcceptFiles(hWndMain, TRUE);
523560
RestoreWindowPlacement(hWndMain, showCmd);
524561
UpdateWindow(hWndMain);
525-
562+
\
526563
return TRUE;
527564
}
528565

566+
LRESULT HandleDropFiles(HDROP hDrop, CefRefPtr<ClientHandler> handler, CefRefPtr<CefBrowser> browser) {
567+
UINT fileCount = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
568+
for (UINT i = 0; i < fileCount; i++) {
569+
wchar_t filename[MAX_PATH];
570+
DragQueryFile(hDrop, i, filename, MAX_PATH);
571+
std::wstring pathStr(filename);
572+
replace(pathStr.begin(), pathStr.end(), '\\', '/');
573+
handler->SendOpenFileCommand(browser, CefString(pathStr));
574+
}
575+
return 0;
576+
}
577+
529578
//
530579
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
531580
//
@@ -806,6 +855,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
806855
PostQuitMessage(0);
807856
return 0;
808857

858+
case WM_DROPFILES:
859+
if (g_handler.get()) {
860+
return HandleDropFiles((HDROP)wParam, g_handler, g_handler->GetBrowser());
861+
}
862+
return 0;
863+
809864
case WM_INITMENUPOPUP:
810865
HMENU menu = (HMENU)wParam;
811866
int count = GetMenuItemCount(menu);
@@ -912,4 +967,4 @@ CefString AppGetProductVersionString() {
912967
s.append(L"/");
913968
s.append(version);
914969
return CefString(s);
915-
}
970+
}

appshell/client_handler.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,14 @@ bool ClientHandler::SendJSCommand(CefRefPtr<CefBrowser> browser, const CefString
340340
return browser->SendProcessMessage(PID_RENDERER, message);
341341
}
342342

343+
void ClientHandler::SendOpenFileCommand(CefRefPtr<CefBrowser> browser, const CefString &filename) {
344+
std::string filenameStr(filename);
345+
// FIXME: Use SendJSCommand once it supports parameters
346+
std::string cmd = "require('command/CommandManager').execute('file.open',{fullPath:'" + filenameStr + "'})";
347+
browser->GetMainFrame()->ExecuteJavaScript(CefString(cmd.c_str()),
348+
browser->GetMainFrame()->GetURL(), 0);
349+
}
350+
343351
void ClientHandler::DispatchCloseToNextBrowser()
344352
{
345353
// If the inner loop iterates thru all browsers and there's still at least one

appshell/client_handler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ class ClientHandler : public CefClient,
200200
// If callback is specified, it will be called with the result from the command.
201201
bool SendJSCommand(CefRefPtr<CefBrowser> browser, const CefString& command, CefRefPtr<CommandCallback> callback = NULL);
202202

203+
void SendOpenFileCommand(CefRefPtr<CefBrowser> browser, const CefString& filename);
203204
void DispatchCloseToNextBrowser();
204205
void AbortQuit();
205206
static CefRefPtr<CefBrowser> GetBrowserForNativeWindow(void* window);

appshell/client_handler_win.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@
1010
#include "resource.h"
1111
#include "native_menu_model.h"
1212

13+
#include <ShellAPI.h>
14+
1315
#define CLOSING_PROP L"CLOSING"
1416

15-
// The global ClientHandler reference.
1617
extern CefRefPtr<ClientHandler> g_handler;
1718

19+
// WM_DROPFILES handler, defined in cefclient_win.cpp
20+
extern LRESULT HandleDropFiles(HDROP hDrop, CefRefPtr<ClientHandler> handler, CefRefPtr<CefBrowser> browser);
21+
1822
// Additional globals
1923
extern HACCEL hAccelTable;
2024

@@ -151,6 +155,12 @@ LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa
151155
}
152156
break;
153157

158+
case WM_DROPFILES:
159+
if (g_handler.get() && browser.get()) {
160+
return HandleDropFiles((HDROP)wParam, g_handler, browser);
161+
}
162+
break;
163+
154164
case WM_INITMENUPOPUP:
155165
HMENU menu = (HMENU)wParam;
156166
int count = GetMenuItemCount(menu);
@@ -216,6 +226,7 @@ void ClientHandler::PopupCreated(CefRefPtr<CefBrowser> browser)
216226
HWND hWnd = browser->GetHost()->GetWindowHandle();
217227
AttachWindProcToPopup(hWnd);
218228
LoadWindowsIcons(hWnd);
229+
DragAcceptFiles(hWnd, true);
219230
browser->GetHost()->SetFocus(true);
220231
}
221232

@@ -261,4 +272,4 @@ bool ClientHandler::OnKeyEvent(CefRefPtr<CefBrowser> browser,
261272
const CefKeyEvent& event,
262273
CefEventHandle os_event) {
263274
return false;
264-
}
275+
}

0 commit comments

Comments
 (0)