mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f9ac88c31 | ||
|
|
424a00cf29 | ||
|
|
3c078b4dab | ||
|
|
8cc49ffa80 | ||
|
|
58a09f6495 | ||
|
|
c2885f77c9 | ||
|
|
5d5a3138bc | ||
|
|
09f9d0729c | ||
|
|
3d989b6736 | ||
|
|
daa00e6539 | ||
|
|
382dbb500c | ||
|
|
92d6fd641f | ||
|
|
f219e7f0dd | ||
|
|
8a9e1824c3 | ||
|
|
b139d97f3d | ||
|
|
d29efb7f81 | ||
|
|
53cedc6e5d | ||
|
|
4f4aabef7a | ||
|
|
a888e4b960 | ||
|
|
15c31ad1ba | ||
|
|
196be5291d | ||
|
|
fe9f94555b | ||
|
|
47e0a61dd8 | ||
|
|
5ba324ca9a | ||
|
|
0721b34847 | ||
|
|
dc257f1f86 | ||
|
|
dfe111b95a | ||
|
|
a76ae8cd35 | ||
|
|
866e20b4be | ||
|
|
d25645ba67 | ||
|
|
ad1efa67bc | ||
|
|
cbb14f5ca2 | ||
|
|
a8cd101ff5 | ||
|
|
009e0790fe | ||
|
|
d31ebb71db | ||
|
|
2125a0be82 | ||
|
|
6dc01945af | ||
|
|
28b9df24a6 | ||
|
|
94b4ceb8ce | ||
|
|
e3eaf909a5 | ||
|
|
33580f66df | ||
|
|
139316b975 | ||
|
|
8fe8cd46b9 | ||
|
|
e3118359ad | ||
|
|
681de1b048 | ||
|
|
4880096f3d | ||
|
|
9c038a2402 | ||
|
|
c4d9dc91a6 | ||
|
|
16428baea2 | ||
|
|
4cd3119125 | ||
|
|
191b1aa719 | ||
|
|
035679057e | ||
|
|
fb4ec66b37 | ||
|
|
89f565906b | ||
|
|
6a5f732bba | ||
|
|
fba1772000 | ||
|
|
e62986b97d | ||
|
|
2e38bafdb1 | ||
|
|
f3e49b0696 | ||
|
|
70aad83b07 | ||
|
|
10c862f0bb | ||
|
|
d993c92cea | ||
|
|
546e4e431d | ||
|
|
4a7e98e398 | ||
|
|
ab4558ae32 | ||
|
|
978f73756b | ||
|
|
5086873f78 | ||
|
|
b2217474c1 | ||
|
|
9342d59a7c | ||
|
|
ad827eee90 | ||
|
|
a8034364ff | ||
|
|
c2c5111d75 | ||
|
|
4b3bd9c3cc | ||
|
|
476f545a67 | ||
|
|
16a1edb422 | ||
|
|
474445fb7d | ||
|
|
5db31517cb | ||
|
|
740e7fbf1a | ||
|
|
be5789b483 | ||
|
|
1c415b0666 | ||
|
|
8dd7f81175 | ||
|
|
87019a1a70 | ||
|
|
10c52bd6a6 | ||
|
|
c23c667c2d | ||
|
|
ec4275ca13 | ||
|
|
143bde007a | ||
|
|
a6ede12cd7 | ||
|
|
7a89a08534 | ||
|
|
5ad203ad99 | ||
|
|
82dcdc6314 | ||
|
|
371e38198f | ||
|
|
472a95e433 | ||
|
|
b84226244d | ||
|
|
e4290393af | ||
|
|
cab00a1450 | ||
|
|
ce50b38a75 | ||
|
|
e8d59c4326 | ||
|
|
b9d64784bb | ||
|
|
ad3eac2e38 | ||
|
|
e11c8a07ea | ||
|
|
a04cb08715 | ||
|
|
b9fc5474c5 | ||
|
|
709670be8e | ||
|
|
2afd3a85a9 | ||
|
|
92a0a4cf6c | ||
|
|
32c6f4cfe8 | ||
|
|
dbf19a5734 | ||
|
|
f7a9b56e93 | ||
|
|
53c73c0631 | ||
|
|
6c866ea909 |
20
atom.gyp
20
atom.gyp
@@ -18,7 +18,9 @@
|
||||
'atom/browser/api/lib/atom-delegate.coffee',
|
||||
'atom/browser/api/lib/auto-updater.coffee',
|
||||
'atom/browser/api/lib/browser-window.coffee',
|
||||
'atom/browser/api/lib/content-tracing.coffee',
|
||||
'atom/browser/api/lib/dialog.coffee',
|
||||
'atom/browser/api/lib/global-shortcut.coffee',
|
||||
'atom/browser/api/lib/ipc.coffee',
|
||||
'atom/browser/api/lib/menu.coffee',
|
||||
'atom/browser/api/lib/menu-item.coffee',
|
||||
@@ -51,7 +53,10 @@
|
||||
'atom/browser/api/atom_api_app.h',
|
||||
'atom/browser/api/atom_api_auto_updater.cc',
|
||||
'atom/browser/api/atom_api_auto_updater.h',
|
||||
'atom/browser/api/atom_api_content_tracing.cc',
|
||||
'atom/browser/api/atom_api_dialog.cc',
|
||||
'atom/browser/api/atom_api_global_shortcut.cc',
|
||||
'atom/browser/api/atom_api_global_shortcut.h',
|
||||
'atom/browser/api/atom_api_menu.cc',
|
||||
'atom/browser/api/atom_api_menu.h',
|
||||
'atom/browser/api/atom_api_menu_views.cc',
|
||||
@@ -93,6 +98,8 @@
|
||||
'atom/browser/browser_mac.mm',
|
||||
'atom/browser/browser_win.cc',
|
||||
'atom/browser/browser_observer.h',
|
||||
'atom/browser/javascript_environment.cc',
|
||||
'atom/browser/javascript_environment.h',
|
||||
'atom/browser/mac/atom_application.h',
|
||||
'atom/browser/mac/atom_application.mm',
|
||||
'atom/browser/mac/atom_application_delegate.h',
|
||||
@@ -187,6 +194,8 @@
|
||||
'atom/common/draggable_region.cc',
|
||||
'atom/common/draggable_region.h',
|
||||
'atom/common/linux/application_info.cc',
|
||||
'atom/common/native_mate_converters/accelerator_converter.cc',
|
||||
'atom/common/native_mate_converters/accelerator_converter.h',
|
||||
'atom/common/native_mate_converters/file_path_converter.h',
|
||||
'atom/common/native_mate_converters/function_converter.h',
|
||||
'atom/common/native_mate_converters/gurl_converter.h',
|
||||
@@ -221,6 +230,14 @@
|
||||
'atom/renderer/atom_render_view_observer.h',
|
||||
'atom/renderer/atom_renderer_client.cc',
|
||||
'atom/renderer/atom_renderer_client.h',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener.cc',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener.h',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.mm',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.h',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.cc',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h',
|
||||
'chromium_src/chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.cc',
|
||||
'chromium_src/chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h',
|
||||
'chromium_src/chrome/browser/ui/libgtk2ui/gtk2_status_icon.cc',
|
||||
@@ -377,6 +394,9 @@
|
||||
'<(libchromiumcontent_resources_dir)/ui_resources_200_percent.pak',
|
||||
'<(libchromiumcontent_resources_dir)/webkit_resources_200_percent.pak',
|
||||
'external_binaries/d3dcompiler_43.dll',
|
||||
'external_binaries/msvcp120.dll',
|
||||
'external_binaries/msvcr120.dll',
|
||||
'external_binaries/vccorlib120.dll',
|
||||
'external_binaries/xinput1_3.dll',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -74,6 +74,13 @@ void AtomMainDelegate::PreSandboxStartup() {
|
||||
// Add a flag to mark the start of switches added by atom-shell.
|
||||
command_line->AppendSwitch("atom-shell-switches-start");
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Disable the LegacyRenderWidgetHostHWND, it made frameless windows unable
|
||||
// to move and resize. We may consider enabling it again after upgraded to
|
||||
// Chrome 38, which should have fixed the problem.
|
||||
command_line->AppendSwitch(switches::kDisableLegacyIntermediateWindow);
|
||||
#endif
|
||||
|
||||
// Disable renderer sandbox for most of node's functions.
|
||||
command_line->AppendSwitch(switches::kNoSandbox);
|
||||
|
||||
|
||||
77
atom/browser/api/atom_api_content_tracing.cc
Normal file
77
atom/browser/api/atom_api_content_tracing.cc
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
#include "atom/common/native_mate_converters/function_converter.h"
|
||||
#include "base/bind.h"
|
||||
#include "content/public/browser/tracing_controller.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
using content::TracingController;
|
||||
|
||||
namespace mate {
|
||||
|
||||
template<typename T>
|
||||
struct Converter<std::set<T> > {
|
||||
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const std::set<T>& val) {
|
||||
v8::Handle<v8::Array> result = v8::Array::New(
|
||||
isolate, static_cast<int>(val.size()));
|
||||
typename std::set<T>::const_iterator it;
|
||||
int i;
|
||||
for (i = 0, it = val.begin(); it != val.end(); ++it, ++i)
|
||||
result->Set(i, Converter<T>::ToV8(isolate, *it));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<TracingController::Options> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Handle<v8::Value> val,
|
||||
TracingController::Options* out) {
|
||||
if (!val->IsNumber())
|
||||
return false;
|
||||
*out = static_cast<TracingController::Options>(val->IntegerValue());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
namespace {
|
||||
|
||||
void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused,
|
||||
v8::Handle<v8::Context> context, void* priv) {
|
||||
TracingController* controller = TracingController::GetInstance();
|
||||
mate::Dictionary dict(context->GetIsolate(), exports);
|
||||
dict.SetMethod("getCategories", base::Bind(
|
||||
&TracingController::GetCategories, base::Unretained(controller)));
|
||||
dict.SetMethod("startRecording", base::Bind(
|
||||
&TracingController::EnableRecording, base::Unretained(controller)));
|
||||
dict.SetMethod("stopRecording", base::Bind(
|
||||
&TracingController::DisableRecording, base::Unretained(controller)));
|
||||
dict.SetMethod("startMonitoring", base::Bind(
|
||||
&TracingController::EnableMonitoring, base::Unretained(controller)));
|
||||
dict.SetMethod("stopMonitoring", base::Bind(
|
||||
&TracingController::DisableMonitoring, base::Unretained(controller)));
|
||||
dict.SetMethod("captureMonitoringSnapshot", base::Bind(
|
||||
&TracingController::CaptureMonitoringSnapshot,
|
||||
base::Unretained(controller)));
|
||||
dict.SetMethod("getTraceBufferPercentFull", base::Bind(
|
||||
&TracingController::GetTraceBufferPercentFull,
|
||||
base::Unretained(controller)));
|
||||
dict.SetMethod("setWatchEvent", base::Bind(
|
||||
&TracingController::SetWatchEvent, base::Unretained(controller)));
|
||||
dict.SetMethod("cancelWatchEvent", base::Bind(
|
||||
&TracingController::CancelWatchEvent, base::Unretained(controller)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_content_tracing, Initialize)
|
||||
@@ -3,19 +3,40 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "atom/browser/api/atom_api_window.h"
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "atom/browser/ui/file_dialog.h"
|
||||
#include "atom/browser/ui/message_box.h"
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
#include "atom/common/native_mate_converters/function_converter.h"
|
||||
#include "base/bind.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
template<>
|
||||
struct Converter<file_dialog::Filter> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Handle<v8::Value> val,
|
||||
file_dialog::Filter* out) {
|
||||
mate::Dictionary dict(isolate);
|
||||
if (!ConvertFromV8(isolate, val, &dict))
|
||||
return false;
|
||||
if (!dict.Get("name", &(out->first)))
|
||||
return false;
|
||||
if (!dict.Get("extensions", &(out->second)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
namespace {
|
||||
|
||||
void ShowMessageBox(int type,
|
||||
@@ -41,6 +62,7 @@ void ShowMessageBox(int type,
|
||||
|
||||
void ShowOpenDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const file_dialog::Filters& filters,
|
||||
int properties,
|
||||
atom::NativeWindow* window,
|
||||
mate::Arguments* args) {
|
||||
@@ -49,18 +71,19 @@ void ShowOpenDialog(const std::string& title,
|
||||
if (mate::Converter<file_dialog::OpenDialogCallback>::FromV8(args->isolate(),
|
||||
peek,
|
||||
&callback)) {
|
||||
file_dialog::ShowOpenDialog(window, title, default_path, properties,
|
||||
callback);
|
||||
file_dialog::ShowOpenDialog(window, title, default_path, filters,
|
||||
properties, callback);
|
||||
} else {
|
||||
std::vector<base::FilePath> paths;
|
||||
if (file_dialog::ShowOpenDialog(window, title, default_path, properties,
|
||||
&paths))
|
||||
if (file_dialog::ShowOpenDialog(window, title, default_path, filters,
|
||||
properties, &paths))
|
||||
args->Return(paths);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowSaveDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const file_dialog::Filters& filters,
|
||||
atom::NativeWindow* window,
|
||||
mate::Arguments* args) {
|
||||
v8::Handle<v8::Value> peek = args->PeekNext();
|
||||
@@ -68,10 +91,11 @@ void ShowSaveDialog(const std::string& title,
|
||||
if (mate::Converter<file_dialog::SaveDialogCallback>::FromV8(args->isolate(),
|
||||
peek,
|
||||
&callback)) {
|
||||
file_dialog::ShowSaveDialog(window, title, default_path, callback);
|
||||
file_dialog::ShowSaveDialog(window, title, default_path, filters, callback);
|
||||
} else {
|
||||
base::FilePath path;
|
||||
if (file_dialog::ShowSaveDialog(window, title, default_path, &path))
|
||||
if (file_dialog::ShowSaveDialog(window, title, default_path, filters,
|
||||
&path))
|
||||
args->Return(path);
|
||||
}
|
||||
}
|
||||
|
||||
107
atom/browser/api/atom_api_global_shortcut.cc
Normal file
107
atom/browser/api/atom_api_global_shortcut.cc
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/api/atom_api_global_shortcut.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/native_mate_converters/accelerator_converter.h"
|
||||
#include "atom/common/native_mate_converters/function_converter.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
using extensions::GlobalShortcutListener;
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
GlobalShortcut::GlobalShortcut() {
|
||||
}
|
||||
|
||||
GlobalShortcut::~GlobalShortcut() {
|
||||
UnregisterAll();
|
||||
}
|
||||
|
||||
void GlobalShortcut::OnKeyPressed(const ui::Accelerator& accelerator) {
|
||||
if (accelerator_callback_map_.find(accelerator) ==
|
||||
accelerator_callback_map_.end()) {
|
||||
// This should never occur, because if it does, GlobalGlobalShortcutListener
|
||||
// notifes us with wrong accelerator.
|
||||
NOTREACHED();
|
||||
return;
|
||||
}
|
||||
accelerator_callback_map_[accelerator].Run();
|
||||
}
|
||||
|
||||
bool GlobalShortcut::Register(const ui::Accelerator& accelerator,
|
||||
const base::Closure& callback) {
|
||||
if (!GlobalShortcutListener::GetInstance()->RegisterAccelerator(
|
||||
accelerator, this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
accelerator_callback_map_[accelerator] = callback;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlobalShortcut::Unregister(const ui::Accelerator& accelerator) {
|
||||
if (!ContainsKey(accelerator_callback_map_, accelerator))
|
||||
return;
|
||||
|
||||
accelerator_callback_map_.erase(accelerator);
|
||||
GlobalShortcutListener::GetInstance()->UnregisterAccelerator(
|
||||
accelerator, this);
|
||||
}
|
||||
|
||||
bool GlobalShortcut::IsRegistered(const ui::Accelerator& accelerator) {
|
||||
return ContainsKey(accelerator_callback_map_, accelerator);
|
||||
}
|
||||
|
||||
void GlobalShortcut::UnregisterAll() {
|
||||
accelerator_callback_map_.clear();
|
||||
GlobalShortcutListener::GetInstance()->UnregisterAccelerators(this);
|
||||
}
|
||||
|
||||
// static
|
||||
mate::ObjectTemplateBuilder GlobalShortcut::GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) {
|
||||
return mate::ObjectTemplateBuilder(isolate)
|
||||
.SetMethod("register",
|
||||
base::Bind(&GlobalShortcut::Register,
|
||||
base::Unretained(this)))
|
||||
.SetMethod("isRegistered",
|
||||
base::Bind(&GlobalShortcut::IsRegistered,
|
||||
base::Unretained(this)))
|
||||
.SetMethod("unregister",
|
||||
base::Bind(&GlobalShortcut::Unregister,
|
||||
base::Unretained(this)))
|
||||
.SetMethod("unregisterAll",
|
||||
base::Bind(&GlobalShortcut::UnregisterAll,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
// static
|
||||
mate::Handle<GlobalShortcut> GlobalShortcut::Create(v8::Isolate* isolate) {
|
||||
return CreateHandle(isolate, new GlobalShortcut);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
namespace {
|
||||
|
||||
void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused,
|
||||
v8::Handle<v8::Context> context, void* priv) {
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
mate::Dictionary dict(isolate, exports);
|
||||
dict.Set("globalShortcut", atom::api::GlobalShortcut::Create(isolate));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_global_shortcut, Initialize)
|
||||
55
atom/browser/api/atom_api_global_shortcut.h
Normal file
55
atom/browser/api/atom_api_global_shortcut.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_API_ATOM_API_GLOBAL_SHORTCUT_H_
|
||||
#define ATOM_BROWSER_API_ATOM_API_GLOBAL_SHORTCUT_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "chrome/browser/extensions/global_shortcut_listener.h"
|
||||
#include "native_mate/wrappable.h"
|
||||
#include "native_mate/handle.h"
|
||||
#include "ui/base/accelerators/accelerator.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
class GlobalShortcut : public extensions::GlobalShortcutListener::Observer,
|
||||
public mate::Wrappable {
|
||||
public:
|
||||
static mate::Handle<GlobalShortcut> Create(v8::Isolate* isolate);
|
||||
|
||||
protected:
|
||||
GlobalShortcut();
|
||||
virtual ~GlobalShortcut();
|
||||
|
||||
// mate::Wrappable implementations:
|
||||
virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) OVERRIDE;
|
||||
|
||||
private:
|
||||
typedef std::map<ui::Accelerator, base::Closure> AcceleratorCallbackMap;
|
||||
|
||||
bool Register(const ui::Accelerator& accelerator,
|
||||
const base::Closure& callback);
|
||||
bool IsRegistered(const ui::Accelerator& accelerator);
|
||||
void Unregister(const ui::Accelerator& accelerator);
|
||||
void UnregisterAll();
|
||||
|
||||
// GlobalShortcutListener::Observer implementation.
|
||||
virtual void OnKeyPressed(const ui::Accelerator& accelerator) OVERRIDE;
|
||||
|
||||
AcceleratorCallbackMap accelerator_callback_map_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GlobalShortcut);
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_API_ATOM_API_GLOBAL_SHORTCUT_H_
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "atom/browser/api/atom_api_menu.h"
|
||||
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "atom/browser/ui/accelerator_util.h"
|
||||
#include "atom/common/native_mate_converters/accelerator_converter.h"
|
||||
#include "atom/common/native_mate_converters/string16_converter.h"
|
||||
#include "native_mate/constructor.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
@@ -92,12 +92,7 @@ bool Menu::GetAcceleratorForCommandId(int command_id,
|
||||
GetWrapper(isolate),
|
||||
"getAcceleratorForCommandId",
|
||||
command_id);
|
||||
if (shortcut->IsString()) {
|
||||
std::string shortcut_str = mate::V8ToString(shortcut);
|
||||
return accelerator_util::StringToAccelerator(shortcut_str, accelerator);
|
||||
}
|
||||
|
||||
return false;
|
||||
return mate::ConvertFromV8(isolate, shortcut, accelerator);
|
||||
}
|
||||
|
||||
bool Menu::IsItemForCommandIdDynamic(int command_id) const {
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_view.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
@@ -44,7 +43,7 @@ void MenuMac::Popup(Window* window) {
|
||||
// Show the menu.
|
||||
[NSMenu popUpContextMenu:[menu_controller menu]
|
||||
withEvent:clickEvent
|
||||
forView:web_contents->GetView()->GetContentNativeView()];
|
||||
forView:web_contents->GetContentNativeView()];
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
#include "ui/gfx/screen.h"
|
||||
#include "ui/views/controls/menu/menu_runner.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "ui/gfx/win/dpi.h"
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
@@ -21,16 +17,12 @@ MenuViews::MenuViews() {
|
||||
|
||||
void MenuViews::Popup(Window* window) {
|
||||
gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
|
||||
#if defined(OS_WIN)
|
||||
cursor = gfx::win::ScreenToDIPPoint(cursor);
|
||||
#endif
|
||||
|
||||
views::MenuRunner menu_runner(model());
|
||||
ignore_result(menu_runner.RunMenuAt(
|
||||
static_cast<NativeWindowViews*>(window->window())->widget(),
|
||||
NULL,
|
||||
gfx::Rect(cursor, gfx::Size()),
|
||||
views::MenuItemView::TOPLEFT,
|
||||
views::MENU_ANCHOR_TOPLEFT,
|
||||
ui::MENU_SOURCE_MOUSE,
|
||||
views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "atom/common/native_mate_converters/string16_converter.h"
|
||||
#include "atom/common/native_mate_converters/value_converter.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
@@ -66,7 +67,7 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
|
||||
return handled;
|
||||
}
|
||||
|
||||
void WebContents::WebContentsDestroyed(content::WebContents*) {
|
||||
void WebContents::WebContentsDestroyed() {
|
||||
// The RenderViewDeleted was not called when the WebContents is destroyed.
|
||||
RenderViewDeleted(web_contents_->GetRenderViewHost());
|
||||
Emit("destroyed");
|
||||
@@ -155,8 +156,7 @@ bool WebContents::IsCrashed() const {
|
||||
}
|
||||
|
||||
void WebContents::ExecuteJavaScript(const base::string16& code) {
|
||||
web_contents()->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
|
||||
base::string16(), code);
|
||||
web_contents()->GetMainFrame()->ExecuteJavaScript(code);
|
||||
}
|
||||
|
||||
bool WebContents::SendIPCMessage(const base::string16& channel,
|
||||
|
||||
@@ -62,7 +62,7 @@ class WebContents : public mate::EventEmitter,
|
||||
virtual void DidStopLoading(
|
||||
content::RenderViewHost* render_view_host) OVERRIDE;
|
||||
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
|
||||
virtual void WebContentsDestroyed(content::WebContents*) OVERRIDE;
|
||||
virtual void WebContentsDestroyed() OVERRIDE;
|
||||
|
||||
private:
|
||||
// Called when received a message from renderer.
|
||||
|
||||
@@ -161,6 +161,10 @@ void Window::Restore() {
|
||||
window_->Restore();
|
||||
}
|
||||
|
||||
bool Window::IsMinimized() {
|
||||
return window_->IsMinimized();
|
||||
}
|
||||
|
||||
void Window::SetFullscreen(bool fullscreen) {
|
||||
window_->SetFullscreen(fullscreen);
|
||||
}
|
||||
@@ -357,6 +361,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("isMaximized", &Window::IsMaximized)
|
||||
.SetMethod("minimize", &Window::Minimize)
|
||||
.SetMethod("restore", &Window::Restore)
|
||||
.SetMethod("isMinimized", &Window::IsMinimized)
|
||||
.SetMethod("setFullScreen", &Window::SetFullscreen)
|
||||
.SetMethod("isFullScreen", &Window::IsFullscreen)
|
||||
.SetMethod("getSize", &Window::GetSize)
|
||||
|
||||
@@ -66,6 +66,7 @@ class Window : public mate::EventEmitter,
|
||||
bool IsMaximized();
|
||||
void Minimize();
|
||||
void Restore();
|
||||
bool IsMinimized();
|
||||
void SetFullscreen(bool fullscreen);
|
||||
bool IsFullscreen();
|
||||
void SetSize(int width, int height);
|
||||
|
||||
@@ -36,7 +36,7 @@ void Event::SetSenderAndMessage(content::WebContents* sender,
|
||||
Observe(sender);
|
||||
}
|
||||
|
||||
void Event::WebContentsDestroyed(content::WebContents* web_contents) {
|
||||
void Event::WebContentsDestroyed() {
|
||||
sender_ = NULL;
|
||||
message_ = NULL;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class Event : public Wrappable,
|
||||
virtual ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate);
|
||||
|
||||
// content::WebContentsObserver implementations:
|
||||
virtual void WebContentsDestroyed(content::WebContents*) OVERRIDE;
|
||||
virtual void WebContentsDestroyed() OVERRIDE;
|
||||
|
||||
private:
|
||||
// Replyer for the synchronous messages.
|
||||
|
||||
@@ -24,8 +24,8 @@ if process.platform is 'darwin'
|
||||
cancelBounce: bindings.dockCancelBounce
|
||||
setBadge: bindings.dockSetBadgeText
|
||||
getBadge: bindings.dockGetBadgeText
|
||||
hide: bindings.hide
|
||||
show: bindings.show
|
||||
hide: bindings.dockHide
|
||||
show: bindings.dockShow
|
||||
|
||||
# Be compatible with old API.
|
||||
app.once 'ready', -> app.emit 'finish-launching'
|
||||
|
||||
7
atom/browser/api/lib/content-tracing.coffee
Normal file
7
atom/browser/api/lib/content-tracing.coffee
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = process.atomBinding 'content_tracing'
|
||||
|
||||
# Mirrored from content::TracingController::Options
|
||||
module.exports.DEFAULT_OPTIONS = 0
|
||||
module.exports.ENABLE_SYSTRACE = 1 << 0
|
||||
module.exports.ENABLE_SAMPLING = 1 << 1
|
||||
module.exports.RECORD_CONTINUOUSLY = 1 << 2
|
||||
@@ -3,7 +3,10 @@ v8Util = process.atomBinding 'v8_util'
|
||||
BrowserWindow = require 'browser-window'
|
||||
|
||||
fileDialogProperties =
|
||||
openFile: 1, openDirectory: 2, multiSelections: 4, createDirectory: 8
|
||||
openFile: 1 << 0
|
||||
openDirectory: 1 << 1
|
||||
multiSelections: 1 << 2
|
||||
createDirectory: 1 << 3
|
||||
|
||||
messageBoxTypes = ['none', 'info', 'warning']
|
||||
|
||||
@@ -25,6 +28,7 @@ module.exports =
|
||||
|
||||
options.title ?= ''
|
||||
options.defaultPath ?= ''
|
||||
options.filters ?= []
|
||||
|
||||
wrappedCallback =
|
||||
if typeof callback is 'function'
|
||||
@@ -34,6 +38,7 @@ module.exports =
|
||||
|
||||
binding.showOpenDialog String(options.title),
|
||||
String(options.defaultPath),
|
||||
options.filters
|
||||
properties,
|
||||
window,
|
||||
wrappedCallback
|
||||
@@ -48,6 +53,7 @@ module.exports =
|
||||
options ?= title: 'Save'
|
||||
options.title ?= ''
|
||||
options.defaultPath ?= ''
|
||||
options.filters ?= []
|
||||
|
||||
wrappedCallback =
|
||||
if typeof callback is 'function'
|
||||
@@ -57,6 +63,7 @@ module.exports =
|
||||
|
||||
binding.showSaveDialog String(options.title),
|
||||
String(options.defaultPath),
|
||||
options.filters
|
||||
window,
|
||||
wrappedCallback
|
||||
|
||||
|
||||
5
atom/browser/api/lib/global-shortcut.coffee
Normal file
5
atom/browser/api/lib/global-shortcut.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
bindings = process.atomBinding 'global_shortcut'
|
||||
|
||||
globalShortcut = bindings.globalShortcut
|
||||
|
||||
module.exports = globalShortcut
|
||||
@@ -17,6 +17,7 @@ class AtomBrowserClient : public brightray::BrowserClient {
|
||||
virtual ~AtomBrowserClient();
|
||||
|
||||
protected:
|
||||
// content::ContentBrowserClient:
|
||||
net::URLRequestContextGetter* CreateRequestContext(
|
||||
content::BrowserContext* browser_context,
|
||||
content::ProtocolHandlerMap* protocol_handlers,
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/javascript_environment.h"
|
||||
#include "atom/common/api/atom_bindings.h"
|
||||
#include "atom/common/node_bindings.h"
|
||||
#include "base/command_line.h"
|
||||
#include "net/proxy/proxy_resolver_v8.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "ui/gfx/win/dpi.h"
|
||||
@@ -28,14 +28,9 @@ namespace atom {
|
||||
AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL;
|
||||
|
||||
AtomBrowserMainParts::AtomBrowserMainParts()
|
||||
: atom_bindings_(new AtomBindings),
|
||||
browser_(new Browser),
|
||||
: browser_(new Browser),
|
||||
node_bindings_(NodeBindings::Create(true)),
|
||||
isolate_(v8::Isolate::GetCurrent()),
|
||||
locker_(isolate_),
|
||||
handle_scope_(isolate_),
|
||||
context_(isolate_, v8::Context::New(isolate_)),
|
||||
context_scope_(v8::Local<v8::Context>::New(isolate_, context_)) {
|
||||
atom_bindings_(new AtomBindings) {
|
||||
DCHECK(!self_) << "Cannot have two AtomBrowserMainParts";
|
||||
self_ = this;
|
||||
}
|
||||
@@ -53,39 +48,35 @@ brightray::BrowserContext* AtomBrowserMainParts::CreateBrowserContext() {
|
||||
return new AtomBrowserContext();
|
||||
}
|
||||
|
||||
void AtomBrowserMainParts::InitProxyResolverV8() {
|
||||
// Since we are integrating node in browser, we can just be sure that an
|
||||
// V8 instance would be prepared, while the ProxyResolverV8::CreateIsolate()
|
||||
// would try to create a V8 isolate, which messed everything on Windows, so
|
||||
// we have to override and call RememberDefaultIsolate on Windows instead.
|
||||
net::ProxyResolverV8::RememberDefaultIsolate();
|
||||
}
|
||||
|
||||
void AtomBrowserMainParts::PostEarlyInitialization() {
|
||||
brightray::BrowserMainParts::PostEarlyInitialization();
|
||||
|
||||
// The ProxyResolverV8 has setup a complete V8 environment, in order to avoid
|
||||
// conflicts we only initialize our V8 environment after that.
|
||||
js_env_.reset(new JavascriptEnvironment);
|
||||
|
||||
node_bindings_->Initialize();
|
||||
|
||||
// Create the global environment.
|
||||
global_env = node_bindings_->CreateEnvironment(
|
||||
v8::Local<v8::Context>::New(isolate_, context_));
|
||||
global_env = node_bindings_->CreateEnvironment(js_env_->context());
|
||||
|
||||
// Add atom-shell extended APIs.
|
||||
atom_bindings_->BindTo(isolate_, global_env->process_object());
|
||||
atom_bindings_->BindTo(js_env_->isolate(), global_env->process_object());
|
||||
}
|
||||
|
||||
void AtomBrowserMainParts::PreMainMessageLoopRun() {
|
||||
// Run user's main script before most things get initialized, so we can have
|
||||
// a chance to setup everything.
|
||||
node_bindings_->PrepareMessageLoop();
|
||||
node_bindings_->RunMessageLoop();
|
||||
|
||||
brightray::BrowserMainParts::PreMainMessageLoopRun();
|
||||
|
||||
#if defined(USE_X11)
|
||||
libgtk2ui::GtkInitFromCommandLine(*CommandLine::ForCurrentProcess());
|
||||
#endif
|
||||
|
||||
node_bindings_->PrepareMessageLoop();
|
||||
node_bindings_->RunMessageLoop();
|
||||
|
||||
// Make sure the url request job factory is created before the
|
||||
// will-finish-launching event.
|
||||
// Make sure the url request job factory is created before the ready event.
|
||||
static_cast<content::BrowserContext*>(AtomBrowserContext::Get())->
|
||||
GetRequestContext();
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
#define ATOM_BROWSER_ATOM_BROWSER_MAIN_PARTS_H_
|
||||
|
||||
#include "brightray/browser/browser_main_parts.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomBindings;
|
||||
class Browser;
|
||||
class JavascriptEnvironment;
|
||||
class NodeBindings;
|
||||
|
||||
class AtomBrowserMainParts : public brightray::BrowserMainParts {
|
||||
@@ -26,7 +26,6 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
|
||||
protected:
|
||||
// Implementations of brightray::BrowserMainParts.
|
||||
virtual brightray::BrowserContext* CreateBrowserContext() OVERRIDE;
|
||||
virtual void InitProxyResolverV8() OVERRIDE;
|
||||
|
||||
// Implementations of content::BrowserMainParts.
|
||||
virtual void PostEarlyInitialization() OVERRIDE;
|
||||
@@ -37,16 +36,10 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
|
||||
#endif
|
||||
|
||||
private:
|
||||
scoped_ptr<AtomBindings> atom_bindings_;
|
||||
scoped_ptr<Browser> browser_;
|
||||
scoped_ptr<JavascriptEnvironment> js_env_;
|
||||
scoped_ptr<NodeBindings> node_bindings_;
|
||||
|
||||
// The V8 environment of browser process.
|
||||
v8::Isolate* isolate_;
|
||||
v8::Locker locker_;
|
||||
v8::HandleScope handle_scope_;
|
||||
v8::UniquePersistent<v8::Context> context_;
|
||||
v8::Context::Scope context_scope_;
|
||||
scoped_ptr<AtomBindings> atom_bindings_;
|
||||
|
||||
static AtomBrowserMainParts* self_;
|
||||
|
||||
|
||||
17
atom/browser/javascript_environment.cc
Normal file
17
atom/browser/javascript_environment.cc
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/javascript_environment.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
JavascriptEnvironment::JavascriptEnvironment()
|
||||
: isolate_(v8::Isolate::GetCurrent()),
|
||||
locker_(isolate_),
|
||||
handle_scope_(isolate_),
|
||||
context_(isolate_, v8::Context::New(isolate_)),
|
||||
context_scope_(v8::Local<v8::Context>::New(isolate_, context_)) {
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
34
atom/browser/javascript_environment.h
Normal file
34
atom/browser/javascript_environment.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_JAVASCRIPT_ENVIRONMENT_H_
|
||||
#define ATOM_BROWSER_JAVASCRIPT_ENVIRONMENT_H_
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class JavascriptEnvironment {
|
||||
public:
|
||||
JavascriptEnvironment();
|
||||
|
||||
v8::Isolate* isolate() const { return isolate_; }
|
||||
v8::Local<v8::Context> context() const {
|
||||
return v8::Local<v8::Context>::New(isolate_, context_);
|
||||
}
|
||||
|
||||
private:
|
||||
v8::Isolate* isolate_;
|
||||
v8::Locker locker_;
|
||||
v8::HandleScope handle_scope_;
|
||||
v8::UniquePersistent<v8::Context> context_;
|
||||
v8::Context::Scope context_scope_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(JavascriptEnvironment);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_JAVASCRIPT_ENVIRONMENT_H_
|
||||
@@ -14,7 +14,7 @@ process.argv.splice 1, 1
|
||||
# Pick out switches appended by atom-shell.
|
||||
startMark = process.argv.indexOf '--atom-shell-switches-start'
|
||||
endMark = process.argv.indexOf '--atom-shell-switches-end'
|
||||
process.execArgv = process.argv.splice startMark, endMark - startMark + 1
|
||||
process.argv.splice startMark, endMark - startMark + 1
|
||||
|
||||
# Add browser/api/lib to require's search paths,
|
||||
# which contains javascript part of Atom's built-in libraries.
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/render_widget_host_view.h"
|
||||
#include "content/public/browser/web_contents_view.h"
|
||||
#include "content/public/common/renderer_preferences.h"
|
||||
#include "content/public/common/user_agent.h"
|
||||
#include "ipc/ipc_message_macros.h"
|
||||
@@ -105,10 +104,6 @@ NativeWindow::NativeWindow(content::WebContents* web_contents,
|
||||
}
|
||||
|
||||
NativeWindow::~NativeWindow() {
|
||||
// Make sure we have the OnRenderViewDeleted message sent even when the window
|
||||
// is destroyed directly.
|
||||
DestroyWebContents();
|
||||
|
||||
// It's possible that the windows gets destroyed before it's closed, in that
|
||||
// case we need to ensure the OnWindowClosed message is still notified.
|
||||
NotifyWindowClosed();
|
||||
@@ -305,7 +300,7 @@ void NativeWindow::CloseWebContents() {
|
||||
ScheduleUnresponsiveEvent(5000);
|
||||
|
||||
if (web_contents->NeedToFireBeforeUnload())
|
||||
web_contents->GetMainFrame()->DispatchBeforeUnload(false);
|
||||
web_contents->DispatchBeforeUnload(false);
|
||||
else
|
||||
web_contents->Close();
|
||||
}
|
||||
@@ -356,8 +351,6 @@ void NativeWindow::OverrideWebkitPrefs(const GURL& url, WebPreferences* prefs) {
|
||||
prefs->experimental_webgl_enabled = b;
|
||||
if (web_preferences.Get("webaudio", &b))
|
||||
prefs->webaudio_enabled = b;
|
||||
if (web_preferences.Get("accelerated-compositing", &b))
|
||||
prefs->accelerated_compositing_enabled = b;
|
||||
if (web_preferences.Get("plugins", &b))
|
||||
prefs->plugins_enabled = b;
|
||||
if (web_preferences.Get("extra-plugin-dirs", &list))
|
||||
@@ -535,8 +528,9 @@ void NativeWindow::DevToolsSaveToFile(const std::string& url,
|
||||
if (it != saved_files_.end() && !save_as) {
|
||||
path = it->second;
|
||||
} else {
|
||||
file_dialog::Filters filters;
|
||||
base::FilePath default_path(base::FilePath::FromUTF8Unsafe(url));
|
||||
if (!file_dialog::ShowSaveDialog(this, url, default_path, &path)) {
|
||||
if (!file_dialog::ShowSaveDialog(this, url, default_path, filters, &path)) {
|
||||
base::StringValue url_value(url);
|
||||
CallDevToolsFunction("InspectorFrontendAPI.canceledSaveURL", &url_value);
|
||||
return;
|
||||
|
||||
@@ -111,6 +111,7 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
virtual bool IsMaximized() = 0;
|
||||
virtual void Minimize() = 0;
|
||||
virtual void Restore() = 0;
|
||||
virtual bool IsMinimized() = 0;
|
||||
virtual void SetFullscreen(bool fullscreen) = 0;
|
||||
virtual bool IsFullscreen() = 0;
|
||||
virtual void SetSize(const gfx::Size& size) = 0;
|
||||
|
||||
@@ -35,6 +35,7 @@ class NativeWindowMac : public NativeWindow {
|
||||
virtual bool IsMaximized() OVERRIDE;
|
||||
virtual void Minimize() OVERRIDE;
|
||||
virtual void Restore() OVERRIDE;
|
||||
virtual bool IsMinimized() OVERRIDE;
|
||||
virtual void SetFullscreen(bool fullscreen) OVERRIDE;
|
||||
virtual bool IsFullscreen() OVERRIDE;
|
||||
virtual void SetSize(const gfx::Size& size) OVERRIDE;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "content/public/browser/native_web_keyboard_event.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_view.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/render_widget_host_view.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
@@ -50,27 +49,31 @@ static const CGFloat kAtomWindowCornerRadius = 4.0;
|
||||
}
|
||||
|
||||
- (void)windowDidBecomeMain:(NSNotification*)notification {
|
||||
shell_->NotifyWindowFocus();
|
||||
content::WebContents* web_contents = shell_->GetWebContents();
|
||||
if (!web_contents)
|
||||
return;
|
||||
|
||||
if (shell_->GetWebContents())
|
||||
shell_->GetWebContents()->GetView()->StoreFocus();
|
||||
web_contents->RestoreFocus();
|
||||
|
||||
content::RenderWidgetHostView* rwhv =
|
||||
shell_->GetWebContents()->GetRenderWidgetHostView();
|
||||
content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
|
||||
if (rwhv)
|
||||
rwhv->SetActive(true);
|
||||
|
||||
shell_->NotifyWindowFocus();
|
||||
}
|
||||
|
||||
- (void)windowDidResignMain:(NSNotification*)notification {
|
||||
shell_->NotifyWindowBlur();
|
||||
content::WebContents* web_contents = shell_->GetWebContents();
|
||||
if (!web_contents)
|
||||
return;
|
||||
|
||||
if (shell_->GetWebContents())
|
||||
shell_->GetWebContents()->GetView()->StoreFocus();
|
||||
web_contents->StoreFocus();
|
||||
|
||||
content::RenderWidgetHostView* rwhv =
|
||||
shell_->GetWebContents()->GetRenderWidgetHostView();
|
||||
content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
|
||||
if (rwhv)
|
||||
rwhv->SetActive(false);
|
||||
|
||||
shell_->NotifyWindowBlur();
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification*)otification {
|
||||
@@ -300,6 +303,10 @@ void NativeWindowMac::Restore() {
|
||||
[window_ deminiaturize:nil];
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsMinimized() {
|
||||
return [window_ isMiniaturized];
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetFullscreen(bool fullscreen) {
|
||||
if (fullscreen == IsFullscreen())
|
||||
return;
|
||||
@@ -484,7 +491,7 @@ gfx::NativeWindow NativeWindowMac::GetNativeWindow() {
|
||||
bool NativeWindowMac::IsWithinDraggableRegion(NSPoint point) const {
|
||||
if (!draggable_region_)
|
||||
return false;
|
||||
NSView* webView = GetWebContents()->GetView()->GetNativeView();
|
||||
NSView* webView = GetWebContents()->GetNativeView();
|
||||
NSInteger webViewHeight = NSHeight([webView bounds]);
|
||||
// |draggable_region_| is stored in local platform-indepdent coordiate system
|
||||
// while |point| is in local Cocoa coordinate system. Do the conversion
|
||||
@@ -534,7 +541,11 @@ void NativeWindowMac::HandleKeyboardEvent(
|
||||
// The event comes from detached devtools view, and it has already been
|
||||
// handled by the devtools itself, we now send it to application menu to
|
||||
// make menu acclerators work.
|
||||
[[NSApp mainMenu] performKeyEquivalent:event.os_event];
|
||||
BOOL handled = [[NSApp mainMenu] performKeyEquivalent:event.os_event];
|
||||
// Handle the cmd+~ shortcut.
|
||||
if (!handled && (event.os_event.modifierFlags & NSCommandKeyMask) &&
|
||||
(event.os_event.keyCode == 50 /* ~ key */))
|
||||
Focus(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,8 +556,6 @@ void NativeWindowMac::InstallView() {
|
||||
base::scoped_nsobject<CALayer> layer([[CALayer alloc] init]);
|
||||
[layer setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
|
||||
[view setLayer:layer];
|
||||
[view setWantsLayer:YES];
|
||||
|
||||
[view setFrame:[[window_ contentView] bounds]];
|
||||
[[window_ contentView] addSubview:view];
|
||||
} else {
|
||||
@@ -569,9 +578,7 @@ void NativeWindowMac::UninstallView() {
|
||||
}
|
||||
|
||||
void NativeWindowMac::ClipWebView() {
|
||||
NSView* view = GetWebContents()->GetView()->GetNativeView();
|
||||
|
||||
view.wantsLayer = YES;
|
||||
NSView* view = GetWebContents()->GetNativeView();
|
||||
view.layer.masksToBounds = YES;
|
||||
view.layer.cornerRadius = kAtomWindowCornerRadius;
|
||||
}
|
||||
@@ -582,7 +589,7 @@ void NativeWindowMac::InstallDraggableRegionViews() {
|
||||
// All ControlRegionViews should be added as children of the WebContentsView,
|
||||
// because WebContentsView will be removed and re-added when entering and
|
||||
// leaving fullscreen mode.
|
||||
NSView* webView = GetWebContents()->GetView()->GetNativeView();
|
||||
NSView* webView = GetWebContents()->GetNativeView();
|
||||
NSInteger webViewHeight = NSHeight([webView bounds]);
|
||||
|
||||
// Remove all ControlRegionViews that are added last time.
|
||||
@@ -614,7 +621,7 @@ void NativeWindowMac::UpdateDraggableRegionsForCustomDrag(
|
||||
const std::vector<DraggableRegion>& regions) {
|
||||
// We still need one ControlRegionView to cover the whole window such that
|
||||
// mouse events could be captured.
|
||||
NSView* web_view = GetWebContents()->GetView()->GetNativeView();
|
||||
NSView* web_view = GetWebContents()->GetNativeView();
|
||||
gfx::Rect window_bounds(
|
||||
0, 0, NSWidth([web_view bounds]), NSHeight([web_view bounds]));
|
||||
system_drag_exclude_areas_.clear();
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "browser/inspectable_web_contents_view.h"
|
||||
#include "content/public/browser/native_web_keyboard_event.h"
|
||||
#include "content/public/browser/web_contents_view.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "ui/aura/window.h"
|
||||
#include "ui/aura/window_tree_host.h"
|
||||
@@ -40,11 +39,15 @@
|
||||
#if defined(USE_X11)
|
||||
#include "atom/browser/ui/views/global_menu_bar_x11.h"
|
||||
#include "atom/browser/ui/views/frameless_view.h"
|
||||
#include "base/environment.h"
|
||||
#include "base/nix/xdg_util.h"
|
||||
#include "chrome/browser/ui/libgtk2ui/unity_service.h"
|
||||
#include "ui/gfx/x/x11_types.h"
|
||||
#include "ui/views/window/native_frame_view.h"
|
||||
#elif defined(OS_WIN)
|
||||
#include "atom/browser/ui/views/win_frame_view.h"
|
||||
#include "base/win/scoped_comptr.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
@@ -52,7 +55,37 @@ namespace atom {
|
||||
namespace {
|
||||
|
||||
// The menu bar height in pixels.
|
||||
#if defined(OS_WIN)
|
||||
const int kMenuBarHeight = 20;
|
||||
#else
|
||||
const int kMenuBarHeight = 25;
|
||||
#endif
|
||||
|
||||
#if defined(USE_X11)
|
||||
bool ShouldUseGlobalMenuBar() {
|
||||
// Some DE would pretend to be Unity but don't have global application menu,
|
||||
// so we can not trust unity::IsRunning().
|
||||
scoped_ptr<base::Environment> env(base::Environment::Create());
|
||||
return unity::IsRunning() && (base::nix::GetDesktopEnvironment(env.get()) ==
|
||||
base::nix::DESKTOP_ENVIRONMENT_UNITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool IsAltKey(const content::NativeWebKeyboardEvent& event) {
|
||||
#if defined(USE_X11)
|
||||
// 164 and 165 represent VK_LALT and VK_RALT.
|
||||
return event.windowsKeyCode == 164 || event.windowsKeyCode == 165;
|
||||
#else
|
||||
return event.windowsKeyCode == ui::VKEY_MENU;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
|
||||
typedef content::NativeWebKeyboardEvent::Modifiers Modifiers;
|
||||
return (event.modifiers == Modifiers::AltKey) ||
|
||||
(event.modifiers == (Modifiers::AltKey | Modifiers::IsLeft)) ||
|
||||
(event.modifiers == (Modifiers::AltKey | Modifiers::IsRight));
|
||||
}
|
||||
|
||||
class NativeWindowClientView : public views::ClientView {
|
||||
public:
|
||||
@@ -77,13 +110,16 @@ NativeWindowViews::NativeWindowViews(content::WebContents* web_contents,
|
||||
const mate::Dictionary& options)
|
||||
: NativeWindow(web_contents, options),
|
||||
window_(new views::Widget),
|
||||
menu_bar_(NULL),
|
||||
web_view_(inspectable_web_contents()->GetView()->GetView()),
|
||||
menu_bar_autohide_(false),
|
||||
menu_bar_visible_(false),
|
||||
menu_bar_alt_pressed_(false),
|
||||
keyboard_event_handler_(new views::UnhandledKeyboardEventHandler),
|
||||
use_content_size_(false),
|
||||
resizable_(true) {
|
||||
options.Get(switches::kResizable, &resizable_);
|
||||
options.Get(switches::kTitle, &title_);
|
||||
options.Get(switches::kAutoHideMenuBar, &menu_bar_autohide_);
|
||||
|
||||
int width = 800, height = 600;
|
||||
options.Get(switches::kWidth, &width);
|
||||
@@ -119,6 +155,7 @@ NativeWindowViews::NativeWindowViews(content::WebContents* web_contents,
|
||||
use_content_size_)
|
||||
bounds = ContentBoundsToWindowBounds(bounds);
|
||||
|
||||
window_->UpdateWindowIcon();
|
||||
window_->CenterWindow(bounds.size());
|
||||
Layout();
|
||||
}
|
||||
@@ -182,6 +219,10 @@ void NativeWindowViews::Restore() {
|
||||
window_->Restore();
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsMinimized() {
|
||||
return window_->IsMinimized();
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetFullscreen(bool fullscreen) {
|
||||
window_->SetFullscreen(fullscreen);
|
||||
}
|
||||
@@ -195,6 +236,11 @@ void NativeWindowViews::SetSize(const gfx::Size& size) {
|
||||
}
|
||||
|
||||
gfx::Size NativeWindowViews::GetSize() {
|
||||
#if defined(OS_WIN)
|
||||
if (IsMinimized())
|
||||
return window_->GetRestoredBounds().size();
|
||||
#endif
|
||||
|
||||
return window_->GetWindowBoundsInScreen().size();
|
||||
}
|
||||
|
||||
@@ -215,7 +261,7 @@ gfx::Size NativeWindowViews::GetContentSize() {
|
||||
|
||||
gfx::Size content_size =
|
||||
window_->non_client_view()->frame_view()->GetBoundsForClientView().size();
|
||||
if (menu_bar_)
|
||||
if (menu_bar_ && menu_bar_visible_)
|
||||
content_size.set_height(content_size.height() - kMenuBarHeight);
|
||||
return content_size;
|
||||
}
|
||||
@@ -254,6 +300,21 @@ gfx::Size NativeWindowViews::GetMaximumSize() {
|
||||
|
||||
void NativeWindowViews::SetResizable(bool resizable) {
|
||||
resizable_ = resizable;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
if (has_frame_) {
|
||||
// WS_MAXIMIZEBOX => Maximize button
|
||||
// WS_MINIMIZEBOX => Minimize button
|
||||
// WS_THICKFRAME => Resize handle
|
||||
DWORD style = ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE);
|
||||
if (resizable)
|
||||
style |= WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME;
|
||||
else
|
||||
style &= ~(WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME);
|
||||
::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, style);
|
||||
}
|
||||
#endif
|
||||
|
||||
// FIXME Implement me for X11.
|
||||
}
|
||||
|
||||
@@ -278,6 +339,11 @@ void NativeWindowViews::SetPosition(const gfx::Point& position) {
|
||||
}
|
||||
|
||||
gfx::Point NativeWindowViews::GetPosition() {
|
||||
#if defined(OS_WIN)
|
||||
if (IsMinimized())
|
||||
return window_->GetRestoredBounds().origin();
|
||||
#endif
|
||||
|
||||
return window_->GetWindowBoundsInScreen().origin();
|
||||
}
|
||||
|
||||
@@ -320,11 +386,11 @@ void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) {
|
||||
RegisterAccelerators(menu_model);
|
||||
|
||||
#if defined(USE_X11)
|
||||
if (!global_menu_bar_)
|
||||
if (!global_menu_bar_ && ShouldUseGlobalMenuBar())
|
||||
global_menu_bar_.reset(new GlobalMenuBarX11(this));
|
||||
|
||||
// Use global application menu bar when possible.
|
||||
if (global_menu_bar_->IsServerStarted()) {
|
||||
if (global_menu_bar_ && global_menu_bar_->IsServerStarted()) {
|
||||
global_menu_bar_->SetMenu(menu_model);
|
||||
return;
|
||||
}
|
||||
@@ -336,11 +402,14 @@ void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) {
|
||||
|
||||
if (!menu_bar_) {
|
||||
gfx::Size content_size = GetContentSize();
|
||||
menu_bar_ = new MenuBar;
|
||||
AddChildViewAt(menu_bar_, 0);
|
||||
menu_bar_.reset(new MenuBar);
|
||||
menu_bar_->set_owned_by_client();
|
||||
|
||||
if (use_content_size_)
|
||||
SetContentSize(content_size);
|
||||
if (!menu_bar_autohide_) {
|
||||
SetMenuBarVisibility(true);
|
||||
if (use_content_size_)
|
||||
SetContentSize(content_size);
|
||||
}
|
||||
}
|
||||
|
||||
menu_bar_->SetMenu(menu_model);
|
||||
@@ -387,6 +456,15 @@ void NativeWindowViews::OnWidgetActivationChanged(
|
||||
NotifyWindowFocus();
|
||||
else
|
||||
NotifyWindowBlur();
|
||||
|
||||
if (active && GetWebContents() && !IsDevToolsOpened())
|
||||
GetWebContents()->Focus();
|
||||
|
||||
// Hide menu bar when window is blured.
|
||||
if (!active && menu_bar_autohide_ && menu_bar_visible_) {
|
||||
SetMenuBarVisibility(false);
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowViews::DeleteDelegate() {
|
||||
@@ -461,9 +539,11 @@ views::ClientView* NativeWindowViews::CreateClientView(views::Widget* widget) {
|
||||
views::NonClientFrameView* NativeWindowViews::CreateNonClientFrameView(
|
||||
views::Widget* widget) {
|
||||
#if defined(OS_WIN)
|
||||
WinFrameView* frame_view = new WinFrameView;
|
||||
frame_view->Init(this, widget);
|
||||
return frame_view;
|
||||
if (ui::win::IsAeroGlassEnabled()) {
|
||||
WinFrameView* frame_view = new WinFrameView;
|
||||
frame_view->Init(this, widget);
|
||||
return frame_view;
|
||||
}
|
||||
#elif defined(OS_LINUX)
|
||||
if (has_frame_) {
|
||||
return new views::NativeFrameView(widget);
|
||||
@@ -472,14 +552,38 @@ views::NonClientFrameView* NativeWindowViews::CreateNonClientFrameView(
|
||||
frame_view->Init(this, widget);
|
||||
return frame_view;
|
||||
}
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void NativeWindowViews::HandleMouseDown() {
|
||||
// Hide menu bar when web view is clicked.
|
||||
if (menu_bar_autohide_ && menu_bar_visible_) {
|
||||
SetMenuBarVisibility(false);
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowViews::HandleKeyboardEvent(
|
||||
content::WebContents*,
|
||||
const content::NativeWebKeyboardEvent& event) {
|
||||
// Toggle the menu bar only when a single Alt is released.
|
||||
if (event.type == blink::WebInputEvent::RawKeyDown && IsAltKey(event) &&
|
||||
IsAltModifier(event)) {
|
||||
// When a single Alt is pressed:
|
||||
menu_bar_alt_pressed_ = true;
|
||||
} else if (event.type == blink::WebInputEvent::KeyUp && IsAltKey(event) &&
|
||||
event.modifiers == 0 && menu_bar_alt_pressed_) {
|
||||
// When a single Alt is released right after a Alt is pressed:
|
||||
menu_bar_alt_pressed_ = false;
|
||||
SetMenuBarVisibility(!menu_bar_visible_);
|
||||
Layout();
|
||||
} else {
|
||||
// When any other keys except single Alt have been pressed/released:
|
||||
menu_bar_alt_pressed_ = false;
|
||||
}
|
||||
|
||||
keyboard_event_handler_->HandleKeyboardEvent(event, GetFocusManager());
|
||||
}
|
||||
|
||||
@@ -509,11 +613,25 @@ gfx::Rect NativeWindowViews::ContentBoundsToWindowBounds(
|
||||
const gfx::Rect& bounds) {
|
||||
gfx::Rect window_bounds =
|
||||
window_->non_client_view()->GetWindowBoundsForClientBounds(bounds);
|
||||
if (menu_bar_)
|
||||
if (menu_bar_ && menu_bar_visible_)
|
||||
window_bounds.set_height(window_bounds.height() + kMenuBarHeight);
|
||||
return window_bounds;
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetMenuBarVisibility(bool visible) {
|
||||
if (!menu_bar_)
|
||||
return;
|
||||
|
||||
menu_bar_visible_ = visible;
|
||||
if (visible) {
|
||||
DCHECK_EQ(child_count(), 1);
|
||||
AddChildView(menu_bar_.get());
|
||||
} else {
|
||||
DCHECK_EQ(child_count(), 2);
|
||||
RemoveChildView(menu_bar_.get());
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
NativeWindow* NativeWindow::Create(content::WebContents* web_contents,
|
||||
const mate::Dictionary& options) {
|
||||
|
||||
@@ -46,6 +46,7 @@ class NativeWindowViews : public NativeWindow,
|
||||
virtual bool IsMaximized() OVERRIDE;
|
||||
virtual void Minimize() OVERRIDE;
|
||||
virtual void Restore() OVERRIDE;
|
||||
virtual bool IsMinimized() OVERRIDE;
|
||||
virtual void SetFullscreen(bool fullscreen) OVERRIDE;
|
||||
virtual bool IsFullscreen() OVERRIDE;
|
||||
virtual void SetSize(const gfx::Size& size) OVERRIDE;
|
||||
@@ -106,6 +107,7 @@ class NativeWindowViews : public NativeWindow,
|
||||
views::Widget* widget) OVERRIDE;
|
||||
|
||||
// content::WebContentsDelegate:
|
||||
virtual void HandleMouseDown() OVERRIDE;
|
||||
virtual void HandleKeyboardEvent(
|
||||
content::WebContents*,
|
||||
const content::NativeWebKeyboardEvent& event) OVERRIDE;
|
||||
@@ -120,10 +122,17 @@ class NativeWindowViews : public NativeWindow,
|
||||
// in client area we need to substract/add menu bar's height in convertions.
|
||||
gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& content_bounds);
|
||||
|
||||
// Show/Hide the menu bar.
|
||||
void SetMenuBarVisibility(bool visible);
|
||||
|
||||
scoped_ptr<views::Widget> window_;
|
||||
MenuBar* menu_bar_;
|
||||
views::View* web_view_; // Managed by inspectable_web_contents_.
|
||||
|
||||
scoped_ptr<MenuBar> menu_bar_;
|
||||
bool menu_bar_autohide_;
|
||||
bool menu_bar_visible_;
|
||||
bool menu_bar_alt_pressed_;
|
||||
|
||||
#if defined(USE_X11)
|
||||
scoped_ptr<GlobalMenuBarX11> global_menu_bar_;
|
||||
#endif
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>atom.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.14.1</string>
|
||||
<string>0.15.4</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.8.0</string>
|
||||
<key>NSMainNibFile</key>
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,14,1,0
|
||||
PRODUCTVERSION 0,14,1,0
|
||||
FILEVERSION 0,15,4,0
|
||||
PRODUCTVERSION 0,15,4,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +68,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Atom-Shell"
|
||||
VALUE "FileVersion", "0.14.1"
|
||||
VALUE "FileVersion", "0.15.4"
|
||||
VALUE "InternalName", "atom.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2013 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "atom.exe"
|
||||
VALUE "ProductName", "Atom-Shell"
|
||||
VALUE "ProductVersion", "0.14.1"
|
||||
VALUE "ProductVersion", "0.15.4"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -87,7 +87,7 @@ ui::KeyboardCode KeyboardCodeFromCharCode(char c, bool* shifted) {
|
||||
|
||||
bool StringToAccelerator(const std::string& description,
|
||||
ui::Accelerator* accelerator) {
|
||||
if (!IsStringASCII(description)) {
|
||||
if (!base::IsStringASCII(description)) {
|
||||
LOG(ERROR) << "The accelerator string can only contain ASCII characters";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define ATOM_BROWSER_UI_FILE_DIALOG_H_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
@@ -17,11 +18,15 @@ class NativeWindow;
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
// <description, extensions>
|
||||
typedef std::pair<std::string, std::vector<std::string> > Filter;
|
||||
typedef std::vector<Filter> Filters;
|
||||
|
||||
enum FileDialogProperty {
|
||||
FILE_DIALOG_OPEN_FILE = 1,
|
||||
FILE_DIALOG_OPEN_DIRECTORY = 2,
|
||||
FILE_DIALOG_MULTI_SELECTIONS = 4,
|
||||
FILE_DIALOG_CREATE_DIRECTORY = 8,
|
||||
FILE_DIALOG_OPEN_FILE = 1 << 0,
|
||||
FILE_DIALOG_OPEN_DIRECTORY = 1 << 1,
|
||||
FILE_DIALOG_MULTI_SELECTIONS = 1 << 2,
|
||||
FILE_DIALOG_CREATE_DIRECTORY = 1 << 3,
|
||||
};
|
||||
|
||||
typedef base::Callback<void(
|
||||
@@ -33,23 +38,27 @@ typedef base::Callback<void(
|
||||
bool ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
int properties,
|
||||
std::vector<base::FilePath>* paths);
|
||||
|
||||
void ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
int properties,
|
||||
const OpenDialogCallback& callback);
|
||||
|
||||
bool ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
base::FilePath* path);
|
||||
|
||||
void ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
const SaveDialogCallback& callback);
|
||||
|
||||
} // namespace file_dialog
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/file_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "chrome/browser/ui/libgtk2ui/gtk2_signal.h"
|
||||
#include "ui/aura/window.h"
|
||||
#include "ui/aura/window_tree_host.h"
|
||||
@@ -45,12 +46,24 @@ void SetGtkTransientForAura(GtkWidget* dialog, aura::Window* parent) {
|
||||
g_object_set_data(G_OBJECT(dialog), kAuraTransientParent, parent);
|
||||
}
|
||||
|
||||
// Makes sure that .jpg also shows .JPG.
|
||||
gboolean FileFilterCaseInsensitive(const GtkFileFilterInfo* file_info,
|
||||
std::string* file_extension) {
|
||||
return EndsWith(file_info->filename, *file_extension, false);
|
||||
}
|
||||
|
||||
// Deletes |data| when gtk_file_filter_add_custom() is done with it.
|
||||
void OnFileFilterDataDestroyed(std::string* file_extension) {
|
||||
delete file_extension;
|
||||
}
|
||||
|
||||
class FileChooserDialog {
|
||||
public:
|
||||
FileChooserDialog(GtkFileChooserAction action,
|
||||
atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path)
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters)
|
||||
: dialog_scope_(new atom::NativeWindow::DialogScope(parent_window)) {
|
||||
const char* confirm_text = GTK_STOCK_OK;
|
||||
if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
|
||||
@@ -86,6 +99,9 @@ class FileChooserDialog {
|
||||
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog_),
|
||||
default_path.value().c_str());
|
||||
}
|
||||
|
||||
if (!filters.empty())
|
||||
AddFilters(filters);
|
||||
}
|
||||
|
||||
virtual ~FileChooserDialog() {
|
||||
@@ -135,6 +151,8 @@ class FileChooserDialog {
|
||||
GtkWidget* dialog() const { return dialog_; }
|
||||
|
||||
private:
|
||||
void AddFilters(const Filters& filters);
|
||||
|
||||
GtkWidget* dialog_;
|
||||
|
||||
SaveDialogCallback save_callback_;
|
||||
@@ -162,17 +180,40 @@ void FileChooserDialog::OnFileDialogResponse(GtkWidget* widget, int response) {
|
||||
delete this;
|
||||
}
|
||||
|
||||
void FileChooserDialog::AddFilters(const Filters& filters) {
|
||||
for (size_t i = 0; i < filters.size(); ++i) {
|
||||
const Filter& filter = filters[i];
|
||||
GtkFileFilter* gtk_filter = gtk_file_filter_new();
|
||||
|
||||
for (size_t j = 0; j < filter.second.size(); ++j) {
|
||||
scoped_ptr<std::string> file_extension(
|
||||
new std::string("." + filter.second[j]));
|
||||
gtk_file_filter_add_custom(
|
||||
gtk_filter,
|
||||
GTK_FILE_FILTER_FILENAME,
|
||||
reinterpret_cast<GtkFileFilterFunc>(FileFilterCaseInsensitive),
|
||||
file_extension.release(),
|
||||
reinterpret_cast<GDestroyNotify>(OnFileFilterDataDestroyed));
|
||||
}
|
||||
|
||||
gtk_file_filter_set_name(gtk_filter, filter.first.c_str());
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_), gtk_filter);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
int properties,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
if (properties & FILE_DIALOG_OPEN_DIRECTORY)
|
||||
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||
FileChooserDialog open_dialog(action, parent_window, title, default_path);
|
||||
FileChooserDialog open_dialog(action, parent_window, title, default_path,
|
||||
filters);
|
||||
if (properties & FILE_DIALOG_MULTI_SELECTIONS)
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(open_dialog.dialog()),
|
||||
TRUE);
|
||||
@@ -190,13 +231,14 @@ bool ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
void ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
int properties,
|
||||
const OpenDialogCallback& callback) {
|
||||
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
if (properties & FILE_DIALOG_OPEN_DIRECTORY)
|
||||
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||
FileChooserDialog* open_dialog = new FileChooserDialog(
|
||||
action, parent_window, title, default_path);
|
||||
action, parent_window, title, default_path, filters);
|
||||
if (properties & FILE_DIALOG_MULTI_SELECTIONS)
|
||||
gtk_file_chooser_set_select_multiple(
|
||||
GTK_FILE_CHOOSER(open_dialog->dialog()), TRUE);
|
||||
@@ -207,9 +249,10 @@ void ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
bool ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
base::FilePath* path) {
|
||||
FileChooserDialog save_dialog(
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE, parent_window, title, default_path);
|
||||
FileChooserDialog save_dialog(GTK_FILE_CHOOSER_ACTION_SAVE, parent_window,
|
||||
title, default_path, filters);
|
||||
gtk_widget_show_all(save_dialog.dialog());
|
||||
int response = gtk_dialog_run(GTK_DIALOG(save_dialog.dialog()));
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
@@ -223,9 +266,11 @@ bool ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
void ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
const SaveDialogCallback& callback) {
|
||||
FileChooserDialog* save_dialog = new FileChooserDialog(
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE, parent_window, title, default_path);
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE, parent_window, title, default_path,
|
||||
filters);
|
||||
save_dialog->RunSaveAsynchronous(callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,15 +9,45 @@
|
||||
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "base/file_util.h"
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
namespace {
|
||||
|
||||
CFStringRef CreateUTIFromExtension(const std::string& ext) {
|
||||
base::ScopedCFTypeRef<CFStringRef> ext_cf(base::SysUTF8ToCFStringRef(ext));
|
||||
return UTTypeCreatePreferredIdentifierForTag(
|
||||
kUTTagClassFilenameExtension, ext_cf.get(), NULL);
|
||||
}
|
||||
|
||||
void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
|
||||
NSMutableSet* file_type_set = [NSMutableSet set];
|
||||
for (size_t i = 0; i < filters.size(); ++i) {
|
||||
const Filter& filter = filters[i];
|
||||
for (size_t j = 0; j < filter.second.size(); ++j) {
|
||||
base::ScopedCFTypeRef<CFStringRef> uti(
|
||||
CreateUTIFromExtension(filter.second[j]));
|
||||
[file_type_set addObject:base::mac::CFToNSCast(uti.get())];
|
||||
|
||||
// Always allow the extension itself, in case the UTI doesn't map
|
||||
// back to the original extension correctly. This occurs with dynamic
|
||||
// UTIs on 10.7 and 10.8.
|
||||
// See http://crbug.com/148840, http://openradar.me/12316273
|
||||
base::ScopedCFTypeRef<CFStringRef> ext_cf(
|
||||
base::SysUTF8ToCFStringRef(filter.second[j]));
|
||||
[file_type_set addObject:base::mac::CFToNSCast(ext_cf.get())];
|
||||
}
|
||||
}
|
||||
[dialog setAllowedFileTypes:[file_type_set allObjects]];
|
||||
}
|
||||
|
||||
void SetupDialog(NSSavePanel* dialog,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path) {
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters) {
|
||||
if (!title.empty())
|
||||
[dialog setTitle:base::SysUTF8ToNSString(title)];
|
||||
|
||||
@@ -39,7 +69,10 @@ void SetupDialog(NSSavePanel* dialog,
|
||||
[dialog setNameFieldStringValue:default_filename];
|
||||
|
||||
[dialog setCanSelectHiddenExtension:YES];
|
||||
[dialog setAllowsOtherFileTypes:YES];
|
||||
if (filters.empty())
|
||||
[dialog setAllowsOtherFileTypes:YES];
|
||||
else
|
||||
SetAllowedFileTypes(dialog, filters);
|
||||
}
|
||||
|
||||
void SetupDialogForProperties(NSOpenPanel* dialog, int properties) {
|
||||
@@ -55,7 +88,7 @@ void SetupDialogForProperties(NSOpenPanel* dialog, int properties) {
|
||||
// Run modal dialog with parent window and return user's choice.
|
||||
int RunModalDialog(NSSavePanel* dialog, atom::NativeWindow* parent_window) {
|
||||
__block int chosen = NSFileHandlingPanelCancelButton;
|
||||
if (parent_window == NULL) {
|
||||
if (!parent_window || !parent_window->GetNativeWindow()) {
|
||||
chosen = [dialog runModal];
|
||||
} else {
|
||||
NSWindow* window = parent_window->GetNativeWindow();
|
||||
@@ -83,12 +116,13 @@ void ReadDialogPaths(NSOpenPanel* dialog, std::vector<base::FilePath>* paths) {
|
||||
bool ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
int properties,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
DCHECK(paths);
|
||||
NSOpenPanel* dialog = [NSOpenPanel openPanel];
|
||||
|
||||
SetupDialog(dialog, title, default_path);
|
||||
SetupDialog(dialog, title, default_path, filters);
|
||||
SetupDialogForProperties(dialog, properties);
|
||||
|
||||
int chosen = RunModalDialog(dialog, parent_window);
|
||||
@@ -102,11 +136,12 @@ bool ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
void ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
int properties,
|
||||
const OpenDialogCallback& c) {
|
||||
NSOpenPanel* dialog = [NSOpenPanel openPanel];
|
||||
|
||||
SetupDialog(dialog, title, default_path);
|
||||
SetupDialog(dialog, title, default_path, filters);
|
||||
SetupDialogForProperties(dialog, properties);
|
||||
|
||||
// Duplicate the callback object here since c is a reference and gcd would
|
||||
@@ -129,11 +164,12 @@ void ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
bool ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
base::FilePath* path) {
|
||||
DCHECK(path);
|
||||
NSSavePanel* dialog = [NSSavePanel savePanel];
|
||||
|
||||
SetupDialog(dialog, title, default_path);
|
||||
SetupDialog(dialog, title, default_path, filters);
|
||||
|
||||
int chosen = RunModalDialog(dialog, parent_window);
|
||||
if (chosen == NSFileHandlingPanelCancelButton || ![[dialog URL] isFileURL])
|
||||
@@ -146,10 +182,11 @@ bool ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
void ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
const SaveDialogCallback& c) {
|
||||
NSSavePanel* dialog = [NSSavePanel savePanel];
|
||||
|
||||
SetupDialog(dialog, title, default_path);
|
||||
SetupDialog(dialog, title, default_path, filters);
|
||||
|
||||
__block SaveDialogCallback callback = c;
|
||||
|
||||
|
||||
@@ -30,103 +30,30 @@ bool IsDirectory(const base::FilePath& path) {
|
||||
file_info.is_directory : path.EndsWithSeparator();
|
||||
}
|
||||
|
||||
// Get the file type description from the registry. This will be "Text Document"
|
||||
// for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't
|
||||
// have an entry for the file type, we return false, true if the description was
|
||||
// found. 'file_ext' must be in form ".txt".
|
||||
static bool GetRegistryDescriptionFromExtension(const std::wstring& file_ext,
|
||||
std::wstring* reg_description) {
|
||||
DCHECK(reg_description);
|
||||
base::win::RegKey reg_ext(HKEY_CLASSES_ROOT, file_ext.c_str(), KEY_READ);
|
||||
std::wstring reg_app;
|
||||
if (reg_ext.ReadValue(NULL, ®_app) == ERROR_SUCCESS && !reg_app.empty()) {
|
||||
base::win::RegKey reg_link(HKEY_CLASSES_ROOT, reg_app.c_str(), KEY_READ);
|
||||
if (reg_link.ReadValue(NULL, reg_description) == ERROR_SUCCESS)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up a filter for a Save/Open dialog, which will consist of |file_ext| file
|
||||
// extensions (internally separated by semicolons), |ext_desc| as the text
|
||||
// descriptions of the |file_ext| types (optional), and (optionally) the default
|
||||
// 'All Files' view. The purpose of the filter is to show only files of a
|
||||
// particular type in a Windows Save/Open dialog box. The resulting filter is
|
||||
// returned. The filters created here are:
|
||||
// 1. only files that have 'file_ext' as their extension
|
||||
// 2. all files (only added if 'include_all_files' is true)
|
||||
// Example:
|
||||
// file_ext: { "*.txt", "*.htm;*.html" }
|
||||
// ext_desc: { "Text Document" }
|
||||
// returned: "Text Document\0*.txt\0HTML Document\0*.htm;*.html\0"
|
||||
// "All Files\0*.*\0\0" (in one big string)
|
||||
// If a description is not provided for a file extension, it will be retrieved
|
||||
// from the registry. If the file extension does not exist in the registry, it
|
||||
// will be omitted from the filter, as it is likely a bogus extension.
|
||||
void FormatFilterForExtensions(
|
||||
std::vector<std::wstring>* file_ext,
|
||||
std::vector<std::wstring>* ext_desc,
|
||||
bool include_all_files,
|
||||
std::vector<COMDLG_FILTERSPEC>* file_types) {
|
||||
DCHECK(file_ext->size() >= ext_desc->size());
|
||||
|
||||
if (file_ext->empty())
|
||||
include_all_files = true;
|
||||
|
||||
for (size_t i = 0; i < file_ext->size(); ++i) {
|
||||
std::wstring ext = (*file_ext)[i];
|
||||
std::wstring desc;
|
||||
if (i < ext_desc->size())
|
||||
desc = (*ext_desc)[i];
|
||||
|
||||
if (ext.empty()) {
|
||||
// Force something reasonable to appear in the dialog box if there is no
|
||||
// extension provided.
|
||||
include_all_files = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (desc.empty()) {
|
||||
DCHECK(ext.find(L'.') != std::wstring::npos);
|
||||
std::wstring first_extension = ext.substr(ext.find(L'.'));
|
||||
size_t first_separator_index = first_extension.find(L';');
|
||||
if (first_separator_index != std::wstring::npos)
|
||||
first_extension = first_extension.substr(0, first_separator_index);
|
||||
|
||||
// Find the extension name without the preceeding '.' character.
|
||||
std::wstring ext_name = first_extension;
|
||||
size_t ext_index = ext_name.find_first_not_of(L'.');
|
||||
if (ext_index != std::wstring::npos)
|
||||
ext_name = ext_name.substr(ext_index);
|
||||
|
||||
if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) {
|
||||
// The extension doesn't exist in the registry. Create a description
|
||||
// based on the unknown extension type (i.e. if the extension is .qqq,
|
||||
// the we create a description "QQQ File (.qqq)").
|
||||
include_all_files = true;
|
||||
// TODO(zcbenz): should be localized.
|
||||
desc = base::i18n::ToUpper(base::WideToUTF16(ext_name)) + L" File";
|
||||
}
|
||||
desc += L" (*." + ext_name + L")";
|
||||
|
||||
// Store the description.
|
||||
ext_desc->push_back(desc);
|
||||
}
|
||||
|
||||
COMDLG_FILTERSPEC spec = { (*ext_desc)[i].c_str(), (*file_ext)[i].c_str() };
|
||||
file_types->push_back(spec);
|
||||
void ConvertFilters(const Filters& filters,
|
||||
std::vector<std::wstring>* buffer,
|
||||
std::vector<COMDLG_FILTERSPEC>* filterspec) {
|
||||
if (filters.empty()) {
|
||||
COMDLG_FILTERSPEC spec = { L"All Files (*.*)", L"*.*" };
|
||||
filterspec->push_back(spec);
|
||||
return;
|
||||
}
|
||||
|
||||
if (include_all_files) {
|
||||
// TODO(zcbenz): Should be localized.
|
||||
ext_desc->push_back(L"All Files (*.*)");
|
||||
file_ext->push_back(L"*.*");
|
||||
buffer->reserve(filters.size() * 2);
|
||||
for (size_t i = 0; i < filters.size(); ++i) {
|
||||
const Filter& filter = filters[i];
|
||||
|
||||
COMDLG_FILTERSPEC spec = {
|
||||
(*ext_desc)[ext_desc->size() - 1].c_str(),
|
||||
(*file_ext)[file_ext->size() - 1].c_str(),
|
||||
};
|
||||
file_types->push_back(spec);
|
||||
COMDLG_FILTERSPEC spec;
|
||||
buffer->push_back(base::UTF8ToWide(filter.first));
|
||||
spec.pszName = buffer->back().c_str();
|
||||
|
||||
std::vector<std::string> extensions(filter.second);
|
||||
for (size_t j = 0; j < extensions.size(); ++j)
|
||||
extensions[j].insert(0, "*.");
|
||||
buffer->push_back(base::UTF8ToWide(JoinString(extensions, ";")));
|
||||
spec.pszSpec = buffer->back().c_str();
|
||||
|
||||
filterspec->push_back(spec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,26 +62,18 @@ void FormatFilterForExtensions(
|
||||
template <typename T>
|
||||
class FileDialog {
|
||||
public:
|
||||
FileDialog(const base::FilePath& default_path,
|
||||
const std::string title,
|
||||
int options,
|
||||
const std::vector<std::wstring>& file_ext,
|
||||
const std::vector<std::wstring>& desc_ext)
|
||||
: file_ext_(file_ext),
|
||||
desc_ext_(desc_ext) {
|
||||
std::vector<COMDLG_FILTERSPEC> filters;
|
||||
FormatFilterForExtensions(&file_ext_, &desc_ext_, true, &filters);
|
||||
|
||||
FileDialog(const base::FilePath& default_path, const std::string& title,
|
||||
const Filters& filters, int options) {
|
||||
std::wstring file_part;
|
||||
if (!IsDirectory(default_path))
|
||||
file_part = default_path.BaseName().value();
|
||||
|
||||
dialog_.reset(new T(
|
||||
file_part.c_str(),
|
||||
options,
|
||||
NULL,
|
||||
filters.data(),
|
||||
filters.size()));
|
||||
std::vector<std::wstring> buffer;
|
||||
std::vector<COMDLG_FILTERSPEC> filterspec;
|
||||
ConvertFilters(filters, &buffer, &filterspec);
|
||||
|
||||
dialog_.reset(new T(file_part.c_str(), options, NULL,
|
||||
filterspec.data(), filterspec.size()));
|
||||
|
||||
if (!title.empty())
|
||||
GetPtr()->SetTitle(base::UTF8ToUTF16(title).c_str());
|
||||
@@ -174,8 +93,6 @@ class FileDialog {
|
||||
|
||||
IFileDialog* GetPtr() const { return dialog_->GetPtr(); }
|
||||
|
||||
const std::vector<std::wstring> file_ext() const { return file_ext_; }
|
||||
|
||||
private:
|
||||
// Set up the initial directory for the dialog.
|
||||
void SetDefaultFolder(const base::FilePath file_path) {
|
||||
@@ -193,10 +110,6 @@ class FileDialog {
|
||||
|
||||
scoped_ptr<T> dialog_;
|
||||
|
||||
std::vector<std::wstring> file_ext_;
|
||||
std::vector<std::wstring> desc_ext_;
|
||||
std::vector<COMDLG_FILTERSPEC> filters_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FileDialog);
|
||||
};
|
||||
|
||||
@@ -205,6 +118,7 @@ class FileDialog {
|
||||
bool ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
int properties,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
int options = FOS_FORCEFILESYSTEM | FOS_FILEMUSTEXIST;
|
||||
@@ -214,11 +128,7 @@ bool ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
options |= FOS_ALLOWMULTISELECT;
|
||||
|
||||
FileDialog<CShellFileOpenDialog> open_dialog(
|
||||
default_path,
|
||||
title,
|
||||
options,
|
||||
std::vector<std::wstring>(),
|
||||
std::vector<std::wstring>());
|
||||
default_path, title, filters, options);
|
||||
if (!open_dialog.Show(parent_window))
|
||||
return false;
|
||||
|
||||
@@ -255,12 +165,14 @@ bool ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
void ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
int properties,
|
||||
const OpenDialogCallback& callback) {
|
||||
std::vector<base::FilePath> paths;
|
||||
bool result = ShowOpenDialog(parent_window,
|
||||
title,
|
||||
default_path,
|
||||
filters,
|
||||
properties,
|
||||
&paths);
|
||||
callback.Run(result, paths);
|
||||
@@ -269,52 +181,51 @@ void ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
bool ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
base::FilePath* path) {
|
||||
// TODO(zcbenz): Accept custom filters from caller.
|
||||
std::vector<std::wstring> file_ext;
|
||||
std::wstring extension = default_path.Extension();
|
||||
if (!extension.empty())
|
||||
file_ext.push_back(extension.insert(0, L"*"));
|
||||
|
||||
FileDialog<CShellFileSaveDialog> save_dialog(
|
||||
default_path,
|
||||
title,
|
||||
FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT,
|
||||
file_ext,
|
||||
std::vector<std::wstring>());
|
||||
default_path, title, filters,
|
||||
FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT);
|
||||
if (!save_dialog.Show(parent_window))
|
||||
return false;
|
||||
|
||||
wchar_t file_name[MAX_PATH];
|
||||
HRESULT hr = save_dialog.GetDialog()->GetFilePath(file_name, MAX_PATH);
|
||||
wchar_t buffer[MAX_PATH];
|
||||
HRESULT hr = save_dialog.GetDialog()->GetFilePath(buffer, MAX_PATH);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
std::string file_name = base::WideToUTF8(std::wstring(buffer));
|
||||
|
||||
// Append extension according to selected filter.
|
||||
UINT filter_index = 1;
|
||||
save_dialog.GetPtr()->GetFileTypeIndex(&filter_index);
|
||||
std::wstring selected_filter = save_dialog.file_ext()[filter_index - 1];
|
||||
if (selected_filter != L"*.*") {
|
||||
std::wstring result = file_name;
|
||||
if (!EndsWith(result, selected_filter.substr(1), false)) {
|
||||
if (result[result.length() - 1] != L'.')
|
||||
result.push_back(L'.');
|
||||
result.append(selected_filter.substr(2));
|
||||
*path = base::FilePath(result);
|
||||
return true;
|
||||
if (!filters.empty()) {
|
||||
UINT filter_index = 1;
|
||||
save_dialog.GetPtr()->GetFileTypeIndex(&filter_index);
|
||||
const Filter& filter = filters[filter_index - 1];
|
||||
|
||||
bool matched = false;
|
||||
for (size_t i = 0; i < filter.second.size(); ++i) {
|
||||
if (EndsWith(file_name, filter.second[i], false)) {
|
||||
matched = true;
|
||||
break;;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched && !filter.second.empty())
|
||||
file_name += ("." + filter.second[0]);
|
||||
}
|
||||
|
||||
*path = base::FilePath(file_name);
|
||||
*path = base::FilePath(base::UTF8ToUTF16(file_name));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const Filters& filters,
|
||||
const SaveDialogCallback& callback) {
|
||||
base::FilePath path;
|
||||
bool result = ShowSaveDialog(parent_window, title, default_path, &path);
|
||||
bool result = ShowSaveDialog(parent_window, title, default_path, filters,
|
||||
&path);
|
||||
callback.Run(result, path);
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ int ShowMessageBox(NativeWindow* parent_window,
|
||||
|
||||
// Use runModal for synchronous alert without parent, since we don't have a
|
||||
// window to wait for.
|
||||
if (!parent_window)
|
||||
if (!parent_window || !parent_window->GetNativeWindow())
|
||||
return [[alert autorelease] runModal];
|
||||
|
||||
int ret_code = -1;
|
||||
|
||||
@@ -4,12 +4,25 @@
|
||||
|
||||
#include "atom/browser/ui/views/menu_bar.h"
|
||||
|
||||
#if defined(USE_X11)
|
||||
#include "gtk/gtk.h"
|
||||
#endif
|
||||
|
||||
#include "atom/browser/ui/views/menu_delegate.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "ui/base/models/menu_model.h"
|
||||
#include "ui/views/background.h"
|
||||
#include "ui/views/controls/button/menu_button.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "ui/gfx/color_utils.h"
|
||||
#elif defined(USE_X11)
|
||||
#include "chrome/browser/ui/libgtk2ui/owned_widget_gtk2.h"
|
||||
#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
@@ -19,11 +32,41 @@ const char kViewClassName[] = "AtomMenuBar";
|
||||
// Default color of the menu bar.
|
||||
const SkColor kDefaultColor = SkColorSetARGB(255, 233, 233, 233);
|
||||
|
||||
// Filter out the "&" in menu label.
|
||||
base::string16 FilterMenuButtonLabel(const base::string16& label) {
|
||||
base::string16 out;
|
||||
base::RemoveChars(label, base::ASCIIToUTF16("&").c_str(), &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
#if defined(USE_X11)
|
||||
void GetMenuBarColor(SkColor* enabled, SkColor* disabled, SkColor* highlight,
|
||||
SkColor* hover, SkColor* background) {
|
||||
libgtk2ui::OwnedWidgetGtk fake_menu_bar;
|
||||
fake_menu_bar.Own(gtk_menu_bar_new());
|
||||
|
||||
GtkStyle* style = gtk_rc_get_style(fake_menu_bar.get());
|
||||
*enabled = libgtk2ui::GdkColorToSkColor(style->text[GTK_STATE_NORMAL]);
|
||||
*disabled = libgtk2ui::GdkColorToSkColor(style->text[GTK_STATE_INSENSITIVE]);
|
||||
*highlight = libgtk2ui::GdkColorToSkColor(style->text[GTK_STATE_SELECTED]);
|
||||
*hover = libgtk2ui::GdkColorToSkColor(style->text[GTK_STATE_PRELIGHT]);
|
||||
*background = libgtk2ui::GdkColorToSkColor(style->bg[GTK_STATE_NORMAL]);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
MenuBar::MenuBar()
|
||||
: menu_model_(NULL) {
|
||||
set_background(views::Background::CreateSolidBackground(kDefaultColor));
|
||||
: background_color_(kDefaultColor),
|
||||
menu_model_(NULL) {
|
||||
#if defined(OS_WIN)
|
||||
background_color_ = color_utils::GetSysSkColor(COLOR_MENUBAR);
|
||||
#elif defined(USE_X11)
|
||||
GetMenuBarColor(&enabled_color_, &disabled_color_, &highlight_color_,
|
||||
&hover_color_, &background_color_);
|
||||
#endif
|
||||
|
||||
set_background(views::Background::CreateSolidBackground(background_color_));
|
||||
SetLayoutManager(new views::BoxLayout(
|
||||
views::BoxLayout::kHorizontal, 0, 0, 0));
|
||||
}
|
||||
@@ -36,9 +79,17 @@ void MenuBar::SetMenu(ui::MenuModel* model) {
|
||||
RemoveAllChildViews(true);
|
||||
|
||||
for (int i = 0; i < model->GetItemCount(); ++i) {
|
||||
views::MenuButton* button =
|
||||
new views::MenuButton(this, model->GetLabelAt(i), this, false);
|
||||
views::MenuButton* button = new views::MenuButton(
|
||||
this, FilterMenuButtonLabel(model->GetLabelAt(i)), this, false);
|
||||
button->set_tag(i);
|
||||
|
||||
#if defined(USE_X11)
|
||||
button->SetEnabledColor(enabled_color_);
|
||||
button->SetDisabledColor(disabled_color_);
|
||||
button->SetHighlightColor(highlight_color_);
|
||||
button->SetHoverColor(hover_color_);
|
||||
#endif
|
||||
|
||||
AddChildView(button);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,15 @@ class MenuBar : public views::View,
|
||||
const gfx::Point& point) OVERRIDE;
|
||||
|
||||
private:
|
||||
SkColor background_color_;
|
||||
|
||||
#if defined(USE_X11)
|
||||
SkColor enabled_color_;
|
||||
SkColor disabled_color_;
|
||||
SkColor highlight_color_;
|
||||
SkColor hover_color_;
|
||||
#endif
|
||||
|
||||
ui::MenuModel* menu_model_;
|
||||
scoped_ptr<MenuDelegate> menu_delegate_;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "atom/browser/ui/views/menu_bar.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "ui/views/controls/button/menu_button.h"
|
||||
#include "ui/views/controls/menu/menu_item_view.h"
|
||||
#include "ui/views/controls/menu/menu_model_adapter.h"
|
||||
#include "ui/views/controls/menu/menu_runner.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
@@ -39,7 +40,7 @@ void MenuDelegate::RunMenu(ui::MenuModel* model, views::MenuButton* button) {
|
||||
button->GetWidget()->GetTopLevelWidget(),
|
||||
button,
|
||||
bounds,
|
||||
views::MenuItemView::TOPRIGHT,
|
||||
views::MENU_ANCHOR_TOPRIGHT,
|
||||
ui::MENU_SOURCE_MOUSE,
|
||||
views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
|
||||
}
|
||||
@@ -107,14 +108,14 @@ void MenuDelegate::WillHideMenu(views::MenuItemView* menu) {
|
||||
views::MenuItemView* MenuDelegate::GetSiblingMenu(
|
||||
views::MenuItemView* menu,
|
||||
const gfx::Point& screen_point,
|
||||
views::MenuItemView::AnchorPosition* anchor,
|
||||
views::MenuAnchorPosition* anchor,
|
||||
bool* has_mnemonics,
|
||||
views::MenuButton** button) {
|
||||
ui::MenuModel* model;
|
||||
if (!menu_bar_->GetMenuButtonFromScreenPoint(screen_point, &model, button))
|
||||
return NULL;
|
||||
|
||||
*anchor = views::MenuItemView::TOPLEFT;
|
||||
*anchor = views::MENU_ANCHOR_TOPLEFT;
|
||||
*has_mnemonics = true;
|
||||
|
||||
id_ = (*button)->tag();
|
||||
|
||||
@@ -46,7 +46,7 @@ class MenuDelegate : public views::MenuDelegate {
|
||||
virtual views::MenuItemView* GetSiblingMenu(
|
||||
views::MenuItemView* menu,
|
||||
const gfx::Point& screen_point,
|
||||
views::MenuItemView::AnchorPosition* anchor,
|
||||
views::MenuAnchorPosition* anchor,
|
||||
bool* has_mnemonics,
|
||||
views::MenuButton** button);
|
||||
|
||||
|
||||
@@ -24,10 +24,10 @@ void MenuLayout::Layout(views::View* host) {
|
||||
gfx::Rect web_view_bounds = gfx::Rect(
|
||||
0, menu_height_, size.width(), size.height() - menu_height_);
|
||||
|
||||
views::View* menu_bar = host->child_at(0);
|
||||
views::View* web_view = host->child_at(1);
|
||||
menu_bar->SetBoundsRect(menu_Bar_bounds);
|
||||
views::View* web_view = host->child_at(0);
|
||||
views::View* menu_bar = host->child_at(1);
|
||||
web_view->SetBoundsRect(web_view_bounds);
|
||||
menu_bar->SetBoundsRect(menu_Bar_bounds);
|
||||
}
|
||||
|
||||
gfx::Size MenuLayout::GetPreferredSize(views::View* host) {
|
||||
|
||||
@@ -60,16 +60,14 @@ void NotifyIcon::HandleClickEvent(const gfx::Point& cursor_pos,
|
||||
if (!SetForegroundWindow(window_))
|
||||
return;
|
||||
|
||||
menu_runner_.reset(new views::MenuRunner(menu_model_));
|
||||
views::MenuRunner::RunResult result = menu_runner_->RunMenuAt(
|
||||
views::MenuRunner menu_runner(menu_model_);
|
||||
ignore_result(menu_runner.RunMenuAt(
|
||||
NULL,
|
||||
NULL,
|
||||
gfx::Rect(cursor_pos, gfx::Size()),
|
||||
views::MenuItemView::TOPLEFT,
|
||||
views::MENU_ANCHOR_TOPLEFT,
|
||||
ui::MENU_SOURCE_MOUSE,
|
||||
views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU);
|
||||
if (result == views::MenuRunner::MENU_DELETED)
|
||||
LOG(ERROR) << "Menu deleted when running";
|
||||
views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
|
||||
}
|
||||
|
||||
void NotifyIcon::ResetIcon() {
|
||||
|
||||
@@ -20,10 +20,6 @@ namespace gfx {
|
||||
class Point;
|
||||
}
|
||||
|
||||
namespace views {
|
||||
class MenuRunner;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NotifyIconHost;
|
||||
@@ -72,7 +68,6 @@ class NotifyIcon : public TrayIcon {
|
||||
|
||||
// The context menu.
|
||||
ui::SimpleMenuModel* menu_model_;
|
||||
scoped_ptr<views::MenuRunner> menu_runner_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NotifyIcon);
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#define ATOM_VERSION_H
|
||||
|
||||
#define ATOM_MAJOR_VERSION 0
|
||||
#define ATOM_MINOR_VERSION 14
|
||||
#define ATOM_PATCH_VERSION 1
|
||||
#define ATOM_MINOR_VERSION 15
|
||||
#define ATOM_PATCH_VERSION 4
|
||||
|
||||
#define ATOM_VERSION_IS_RELEASE 1
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#ifndef ATOM_COMMON_CHROME_VERSION_H_
|
||||
#define ATOM_COMMON_CHROME_VERSION_H_
|
||||
|
||||
#define CHROME_VERSION_STRING "35.0.1916.153"
|
||||
#define CHROME_VERSION_STRING "36.0.1985.125"
|
||||
#define CHROME_VERSION "v" CHROME_VERSION_STRING
|
||||
|
||||
#endif // ATOM_COMMON_CHROME_VERSION_H_
|
||||
|
||||
@@ -54,12 +54,14 @@ void CrashReporterWin::InitBreakpad(const std::string& product_name,
|
||||
if (waiting_event != INVALID_HANDLE_VALUE)
|
||||
WaitForSingleObject(waiting_event, 1000);
|
||||
|
||||
int handler_types = google_breakpad::ExceptionHandler::HANDLER_EXCEPTION |
|
||||
google_breakpad::ExceptionHandler::HANDLER_PURECALL;
|
||||
breakpad_.reset(new google_breakpad::ExceptionHandler(
|
||||
temp_dir.value(),
|
||||
FilterCallback,
|
||||
MinidumpCallback,
|
||||
this,
|
||||
google_breakpad::ExceptionHandler::HANDLER_ALL,
|
||||
handler_types,
|
||||
kSmallDumpType,
|
||||
pipe_name.c_str(),
|
||||
GetCustomInfo(product_name, version, company_name)));
|
||||
|
||||
@@ -350,6 +350,8 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
|
||||
"--timeout=60", // Set a timeout so we don't hang forever.
|
||||
"--tries=1", // Don't retry if the upload fails.
|
||||
"--quiet", // Be silent.
|
||||
"-O", // output reply to /dev/null.
|
||||
"/dev/null",
|
||||
NULL,
|
||||
};
|
||||
static const char msg[] = "Cannot upload crash dump: cannot exec "
|
||||
|
||||
@@ -25,3 +25,11 @@ wrapWithActivateUvLoop = (func) ->
|
||||
process.nextTick = wrapWithActivateUvLoop process.nextTick
|
||||
global.setImmediate = wrapWithActivateUvLoop timers.setImmediate
|
||||
global.clearImmediate = timers.clearImmediate
|
||||
|
||||
# setTimeout needs to update the polling timeout of the event loop, when called
|
||||
# under Chromium's event loop the node's event loop won't get a chance to update
|
||||
# the timeout, so we have to force the node's event loop to recalculate the
|
||||
# timeout in browser process.
|
||||
if process.type is 'browser'
|
||||
global.setTimeout = wrapWithActivateUvLoop timers.setTimeout
|
||||
global.setInterval = wrapWithActivateUvLoop timers.setInterval
|
||||
|
||||
22
atom/common/native_mate_converters/accelerator_converter.cc
Normal file
22
atom/common/native_mate_converters/accelerator_converter.cc
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/common/native_mate_converters/accelerator_converter.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/ui/accelerator_util.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
// static
|
||||
bool Converter<ui::Accelerator>::FromV8(
|
||||
v8::Isolate* isolate, v8::Handle<v8::Value> val, ui::Accelerator* out) {
|
||||
std::string keycode;
|
||||
if (!ConvertFromV8(isolate, val, &keycode))
|
||||
return false;
|
||||
return accelerator_util::StringToAccelerator(keycode, out);
|
||||
}
|
||||
|
||||
} // namespace mate
|
||||
24
atom/common/native_mate_converters/accelerator_converter.h
Normal file
24
atom/common/native_mate_converters/accelerator_converter.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_ACCELERATOR_CONVERTER_H_
|
||||
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_ACCELERATOR_CONVERTER_H_
|
||||
|
||||
#include "native_mate/converter.h"
|
||||
|
||||
namespace ui {
|
||||
class Accelerator;
|
||||
}
|
||||
|
||||
namespace mate {
|
||||
|
||||
template<>
|
||||
struct Converter<ui::Accelerator> {
|
||||
static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
|
||||
ui::Accelerator* out);
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_ACCELERATOR_CONVERTER_H_
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "atom/common/native_mate_converters/image_converter.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
#include "base/file_util.h"
|
||||
@@ -18,12 +19,83 @@ namespace mate {
|
||||
|
||||
namespace {
|
||||
|
||||
ui::ScaleFactor GetScaleFactorFromFileName(const base::FilePath& path) {
|
||||
struct ScaleFactorPair {
|
||||
const char* name;
|
||||
float scale;
|
||||
};
|
||||
|
||||
ScaleFactorPair kScaleFactorPairs[] = {
|
||||
// The "@2x" is put as first one to make scale matching faster.
|
||||
{ "@2x" , 2.0f },
|
||||
{ "@3x" , 3.0f },
|
||||
{ "@1x" , 1.0f },
|
||||
{ "@1.25x" , 1.25f },
|
||||
{ "@1.33x" , 1.33f },
|
||||
{ "@1.4x" , 1.4f },
|
||||
{ "@1.5x" , 1.5f },
|
||||
{ "@1.8x" , 1.8f },
|
||||
{ "@2.5x" , 2.5f },
|
||||
};
|
||||
|
||||
float GetScaleFactorFromPath(const base::FilePath& path) {
|
||||
std::string filename(path.BaseName().RemoveExtension().AsUTF8Unsafe());
|
||||
if (EndsWith(filename, "@2x", true))
|
||||
return ui::SCALE_FACTOR_200P;
|
||||
else
|
||||
return ui::SCALE_FACTOR_100P;
|
||||
|
||||
// There is no scale info in the file path.
|
||||
if (!EndsWith(filename, "x", true))
|
||||
return 1.0f;
|
||||
|
||||
// We don't try to convert string to float here because it is very very
|
||||
// expensive.
|
||||
for (unsigned i = 0; i < arraysize(kScaleFactorPairs); ++i) {
|
||||
if (EndsWith(filename, kScaleFactorPairs[i].name, true))
|
||||
return kScaleFactorPairs[i].scale;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
void AppendIfExists(std::vector<base::FilePath>* paths,
|
||||
const base::FilePath& path) {
|
||||
if (base::PathExists(path))
|
||||
paths->push_back(path);
|
||||
}
|
||||
|
||||
void PopulatePossibleFilePaths(std::vector<base::FilePath>* paths,
|
||||
const base::FilePath& path) {
|
||||
AppendIfExists(paths, path);
|
||||
|
||||
std::string filename(path.BaseName().RemoveExtension().AsUTF8Unsafe());
|
||||
if (MatchPattern(filename, "*@*x"))
|
||||
return;
|
||||
|
||||
for (unsigned i = 0; i < arraysize(kScaleFactorPairs); ++i)
|
||||
AppendIfExists(paths,
|
||||
path.InsertBeforeExtensionASCII(kScaleFactorPairs[i].name));
|
||||
}
|
||||
|
||||
bool AddImageSkiaRepFromPath(gfx::ImageSkia* image,
|
||||
const base::FilePath& path) {
|
||||
std::string file_contents;
|
||||
if (!base::ReadFileToString(path, &file_contents))
|
||||
return false;
|
||||
|
||||
const unsigned char* data =
|
||||
reinterpret_cast<const unsigned char*>(file_contents.data());
|
||||
size_t size = file_contents.size();
|
||||
scoped_ptr<SkBitmap> decoded(new SkBitmap());
|
||||
|
||||
// Try PNG first.
|
||||
if (!gfx::PNGCodec::Decode(data, size, decoded.get()))
|
||||
// Try JPEG.
|
||||
decoded.reset(gfx::JPEGCodec::Decode(data, size));
|
||||
|
||||
if (decoded) {
|
||||
image->AddRepresentation(gfx::ImageSkiaRep(
|
||||
*decoded.release(), GetScaleFactorFromPath(path)));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -33,25 +105,16 @@ bool Converter<gfx::ImageSkia>::FromV8(v8::Isolate* isolate,
|
||||
gfx::ImageSkia* out) {
|
||||
base::FilePath path;
|
||||
if (Converter<base::FilePath>::FromV8(isolate, val, &path)) {
|
||||
std::string file_contents;
|
||||
if (!base::ReadFileToString(path, &file_contents))
|
||||
std::vector<base::FilePath> paths;
|
||||
PopulatePossibleFilePaths(&paths, path);
|
||||
if (paths.empty())
|
||||
return false;
|
||||
|
||||
const unsigned char* data =
|
||||
reinterpret_cast<const unsigned char*>(file_contents.data());
|
||||
size_t size = file_contents.size();
|
||||
scoped_ptr<SkBitmap> decoded(new SkBitmap());
|
||||
|
||||
// Try PNG first.
|
||||
if (!gfx::PNGCodec::Decode(data, size, decoded.get()))
|
||||
// Try JPEG.
|
||||
decoded.reset(gfx::JPEGCodec::Decode(data, size));
|
||||
|
||||
if (decoded) {
|
||||
*out = gfx::ImageSkia(gfx::ImageSkiaRep(*decoded.release(),
|
||||
GetScaleFactorFromFileName(path)));
|
||||
return true;
|
||||
for (size_t i = 0; i < paths.size(); ++i) {
|
||||
if (!AddImageSkiaRepFromPath(out, paths[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -61,10 +61,12 @@ REFERENCE_MODULE(uv);
|
||||
// Atom Shell's builtin modules.
|
||||
REFERENCE_MODULE(atom_browser_app);
|
||||
REFERENCE_MODULE(atom_browser_auto_updater);
|
||||
REFERENCE_MODULE(atom_browser_content_tracing);
|
||||
REFERENCE_MODULE(atom_browser_dialog);
|
||||
REFERENCE_MODULE(atom_browser_menu);
|
||||
REFERENCE_MODULE(atom_browser_power_monitor);
|
||||
REFERENCE_MODULE(atom_browser_protocol);
|
||||
REFERENCE_MODULE(atom_browser_global_shortcut);
|
||||
REFERENCE_MODULE(atom_browser_tray);
|
||||
REFERENCE_MODULE(atom_browser_window);
|
||||
REFERENCE_MODULE(atom_common_clipboard);
|
||||
|
||||
@@ -48,6 +48,9 @@ const char kWebPreferences[] = "web-preferences";
|
||||
// The factor of which page should be zoomed.
|
||||
const char kZoomFactor[] = "zoom-factor";
|
||||
|
||||
// The menu bar is hidden unless "Alt" is pressed.
|
||||
const char kAutoHideMenuBar[] = "auto-hide-menu-bar";
|
||||
|
||||
} // namespace switches
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -32,6 +32,7 @@ extern const char kAcceptFirstMouse[];
|
||||
extern const char kUseContentSize[];
|
||||
extern const char kWebPreferences[];
|
||||
extern const char kZoomFactor[];
|
||||
extern const char kAutoHideMenuBar[];
|
||||
|
||||
} // namespace switches
|
||||
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
#include "atom/common/native_mate_converters/value_converter.h"
|
||||
#include "content/public/renderer/render_view.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebLocalFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebView.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
using content::RenderView;
|
||||
using blink::WebFrame;
|
||||
using blink::WebLocalFrame;
|
||||
using blink::WebView;
|
||||
|
||||
namespace {
|
||||
|
||||
RenderView* GetCurrentRenderView() {
|
||||
WebFrame* frame = WebFrame::frameForCurrentContext();
|
||||
WebLocalFrame* frame = WebLocalFrame::frameForCurrentContext();
|
||||
if (!frame)
|
||||
return NULL;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebLocalFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebView.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
@@ -18,7 +18,7 @@ namespace api {
|
||||
namespace {
|
||||
|
||||
blink::WebView* GetCurrentWebView() {
|
||||
blink::WebFrame* frame = blink::WebFrame::frameForCurrentContext();
|
||||
blink::WebLocalFrame* frame = blink::WebLocalFrame::frameForCurrentContext();
|
||||
if (!frame)
|
||||
return NULL;
|
||||
return frame->view();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "third_party/WebKit/public/web/WebDraggableRegion.h"
|
||||
#include "third_party/WebKit/public/web/WebDocument.h"
|
||||
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebLocalFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebView.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
@@ -36,7 +37,8 @@ AtomRenderViewObserver::AtomRenderViewObserver(
|
||||
AtomRenderViewObserver::~AtomRenderViewObserver() {
|
||||
}
|
||||
|
||||
void AtomRenderViewObserver::DidCreateDocumentElement(blink::WebFrame* frame) {
|
||||
void AtomRenderViewObserver::DidCreateDocumentElement(
|
||||
blink::WebLocalFrame* frame) {
|
||||
// Read --zoom-factor from command line.
|
||||
std::string zoom_factor_str = CommandLine::ForCurrentProcess()->
|
||||
GetSwitchValueASCII(switches::kZoomFactor);;
|
||||
|
||||
@@ -25,7 +25,7 @@ class AtomRenderViewObserver : public content::RenderViewObserver {
|
||||
|
||||
private:
|
||||
// content::RenderViewObserver implementation.
|
||||
virtual void DidCreateDocumentElement(blink::WebFrame* frame) OVERRIDE;
|
||||
virtual void DidCreateDocumentElement(blink::WebLocalFrame* frame) OVERRIDE;
|
||||
virtual void DraggableRegionsChanged(blink::WebFrame* frame) OVERRIDE;
|
||||
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
|
||||
|
||||
|
||||
@@ -70,6 +70,10 @@ AtomRendererClient::AtomRendererClient()
|
||||
node_integration_ = ALL;
|
||||
|
||||
if (IsNodeBindingEnabled()) {
|
||||
// Always enable harmony when node binding is on.
|
||||
std::string flags("--harmony");
|
||||
v8::V8::SetFlagsFromString(flags.c_str(), static_cast<int>(flags.size()));
|
||||
|
||||
node_bindings_.reset(NodeBindings::Create(false));
|
||||
atom_bindings_.reset(new AtomRendererBindings);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/extensions/global_shortcut_listener.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "ui/base/accelerators/accelerator.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace extensions {
|
||||
|
||||
GlobalShortcutListener::GlobalShortcutListener()
|
||||
: shortcut_handling_suspended_(false) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
}
|
||||
|
||||
GlobalShortcutListener::~GlobalShortcutListener() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
DCHECK(accelerator_map_.empty()); // Make sure we've cleaned up.
|
||||
}
|
||||
|
||||
bool GlobalShortcutListener::RegisterAccelerator(
|
||||
const ui::Accelerator& accelerator, Observer* observer) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
if (IsShortcutHandlingSuspended())
|
||||
return false;
|
||||
|
||||
AcceleratorMap::const_iterator it = accelerator_map_.find(accelerator);
|
||||
if (it != accelerator_map_.end()) {
|
||||
// The accelerator has been registered.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RegisterAcceleratorImpl(accelerator)) {
|
||||
// If the platform-specific registration fails, mostly likely the shortcut
|
||||
// has been registered by other native applications.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (accelerator_map_.empty())
|
||||
StartListening();
|
||||
|
||||
accelerator_map_[accelerator] = observer;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlobalShortcutListener::UnregisterAccelerator(
|
||||
const ui::Accelerator& accelerator, Observer* observer) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
if (IsShortcutHandlingSuspended())
|
||||
return;
|
||||
|
||||
AcceleratorMap::iterator it = accelerator_map_.find(accelerator);
|
||||
// We should never get asked to unregister something that we didn't register.
|
||||
DCHECK(it != accelerator_map_.end());
|
||||
// The caller should call this function with the right observer.
|
||||
DCHECK(it->second == observer);
|
||||
|
||||
UnregisterAcceleratorImpl(accelerator);
|
||||
accelerator_map_.erase(it);
|
||||
if (accelerator_map_.empty())
|
||||
StopListening();
|
||||
}
|
||||
|
||||
void GlobalShortcutListener::UnregisterAccelerators(Observer* observer) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
if (IsShortcutHandlingSuspended())
|
||||
return;
|
||||
|
||||
AcceleratorMap::iterator it = accelerator_map_.begin();
|
||||
while (it != accelerator_map_.end()) {
|
||||
if (it->second == observer) {
|
||||
AcceleratorMap::iterator to_remove = it++;
|
||||
UnregisterAccelerator(to_remove->first, observer);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalShortcutListener::SetShortcutHandlingSuspended(bool suspended) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
if (shortcut_handling_suspended_ == suspended)
|
||||
return;
|
||||
|
||||
shortcut_handling_suspended_ = suspended;
|
||||
for (AcceleratorMap::iterator it = accelerator_map_.begin();
|
||||
it != accelerator_map_.end();
|
||||
++it) {
|
||||
// On Linux, when shortcut handling is suspended we cannot simply early
|
||||
// return in NotifyKeyPressed (similar to what we do for non-global
|
||||
// shortcuts) because we'd eat the keyboard event thereby preventing the
|
||||
// user from setting the shortcut. Therefore we must unregister while
|
||||
// handling is suspended and register when handling resumes.
|
||||
if (shortcut_handling_suspended_)
|
||||
UnregisterAcceleratorImpl(it->first);
|
||||
else
|
||||
RegisterAcceleratorImpl(it->first);
|
||||
}
|
||||
}
|
||||
|
||||
bool GlobalShortcutListener::IsShortcutHandlingSuspended() const {
|
||||
return shortcut_handling_suspended_;
|
||||
}
|
||||
|
||||
void GlobalShortcutListener::NotifyKeyPressed(
|
||||
const ui::Accelerator& accelerator) {
|
||||
AcceleratorMap::iterator iter = accelerator_map_.find(accelerator);
|
||||
if (iter == accelerator_map_.end()) {
|
||||
// This should never occur, because if it does, we have failed to unregister
|
||||
// or failed to clean up the map after unregistering the shortcut.
|
||||
NOTREACHED();
|
||||
return; // No-one is listening to this key.
|
||||
}
|
||||
|
||||
iter->second->OnKeyPressed(accelerator);
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
|
||||
#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "ui/events/keycodes/keyboard_codes.h"
|
||||
|
||||
namespace ui {
|
||||
class Accelerator;
|
||||
}
|
||||
|
||||
namespace extensions {
|
||||
|
||||
// Platform-neutral implementation of a class that keeps track of observers and
|
||||
// monitors keystrokes. It relays messages to the appropriate observer when a
|
||||
// global shortcut has been struck by the user.
|
||||
class GlobalShortcutListener {
|
||||
public:
|
||||
class Observer {
|
||||
public:
|
||||
// Called when your global shortcut (|accelerator|) is struck.
|
||||
virtual void OnKeyPressed(const ui::Accelerator& accelerator) = 0;
|
||||
};
|
||||
|
||||
virtual ~GlobalShortcutListener();
|
||||
|
||||
static GlobalShortcutListener* GetInstance();
|
||||
|
||||
// Register an observer for when a certain |accelerator| is struck. Returns
|
||||
// true if register successfully, or false if 1) the specificied |accelerator|
|
||||
// has been registered by another caller or other native applications, or
|
||||
// 2) shortcut handling is suspended.
|
||||
//
|
||||
// Note that we do not support recognizing that an accelerator has been
|
||||
// registered by another application on all platforms. This is a per-platform
|
||||
// consideration.
|
||||
bool RegisterAccelerator(const ui::Accelerator& accelerator,
|
||||
Observer* observer);
|
||||
|
||||
// Stop listening for the given |accelerator|, does nothing if shortcut
|
||||
// handling is suspended.
|
||||
void UnregisterAccelerator(const ui::Accelerator& accelerator,
|
||||
Observer* observer);
|
||||
|
||||
// Stop listening for all accelerators of the given |observer|, does nothing
|
||||
// if shortcut handling is suspended.
|
||||
void UnregisterAccelerators(Observer* observer);
|
||||
|
||||
// Suspend/Resume global shortcut handling. Note that when suspending,
|
||||
// RegisterAccelerator/UnregisterAccelerator/UnregisterAccelerators are not
|
||||
// allowed to be called until shortcut handling has been resumed.
|
||||
void SetShortcutHandlingSuspended(bool suspended);
|
||||
|
||||
// Returns whether shortcut handling is currently suspended.
|
||||
bool IsShortcutHandlingSuspended() const;
|
||||
|
||||
protected:
|
||||
GlobalShortcutListener();
|
||||
|
||||
// Called by platform specific implementations of this class whenever a key
|
||||
// is struck. Only called for keys that have an observer registered.
|
||||
void NotifyKeyPressed(const ui::Accelerator& accelerator);
|
||||
|
||||
private:
|
||||
// The following methods are implemented by platform-specific implementations
|
||||
// of this class.
|
||||
//
|
||||
// Start/StopListening are called when transitioning between zero and nonzero
|
||||
// registered accelerators. StartListening will be called after
|
||||
// RegisterAcceleratorImpl and StopListening will be called after
|
||||
// UnregisterAcceleratorImpl.
|
||||
//
|
||||
// For RegisterAcceleratorImpl, implementations return false if registration
|
||||
// did not complete successfully.
|
||||
virtual void StartListening() = 0;
|
||||
virtual void StopListening() = 0;
|
||||
virtual bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) = 0;
|
||||
virtual void UnregisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) = 0;
|
||||
|
||||
// The map of accelerators that have been successfully registered as global
|
||||
// shortcuts and their observer.
|
||||
typedef std::map<ui::Accelerator, Observer*> AcceleratorMap;
|
||||
AcceleratorMap accelerator_map_;
|
||||
|
||||
// Keeps track of whether shortcut handling is currently suspended.
|
||||
bool shortcut_handling_suspended_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListener);
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_
|
||||
#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_
|
||||
|
||||
#include "chrome/browser/extensions/global_shortcut_listener.h"
|
||||
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
// Mac-specific implementation of the GlobalShortcutListener class that
|
||||
// listens for global shortcuts. Handles basic keyboard intercepting and
|
||||
// forwards its output to the base class for processing.
|
||||
//
|
||||
// This class does two things:
|
||||
// 1. Intercepts media keys. Uses an event tap for intercepting media keys
|
||||
// (PlayPause, NextTrack, PreviousTrack).
|
||||
// 2. Binds keyboard shortcuts (hot keys). Carbon RegisterEventHotKey API for
|
||||
// binding to non-media key global hot keys (eg. Command-Shift-1).
|
||||
class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||
public:
|
||||
GlobalShortcutListenerMac();
|
||||
virtual ~GlobalShortcutListenerMac();
|
||||
|
||||
private:
|
||||
typedef int KeyId;
|
||||
typedef std::map<ui::Accelerator, KeyId> AcceleratorIdMap;
|
||||
typedef std::map<KeyId, ui::Accelerator> IdAcceleratorMap;
|
||||
typedef std::map<KeyId, EventHotKeyRef> IdHotKeyRefMap;
|
||||
|
||||
// Keyboard event callbacks.
|
||||
void OnHotKeyEvent(EventHotKeyID hot_key_id);
|
||||
bool OnMediaKeyEvent(int key_code);
|
||||
|
||||
// GlobalShortcutListener implementation.
|
||||
virtual void StartListening() OVERRIDE;
|
||||
virtual void StopListening() OVERRIDE;
|
||||
virtual bool RegisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) OVERRIDE;
|
||||
virtual void UnregisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) OVERRIDE;
|
||||
|
||||
// Mac-specific functions for registering hot keys with modifiers.
|
||||
bool RegisterHotKey(const ui::Accelerator& accelerator, KeyId hot_key_id);
|
||||
void UnregisterHotKey(const ui::Accelerator& accelerator);
|
||||
|
||||
// Enable and disable the media key event tap.
|
||||
void StartWatchingMediaKeys();
|
||||
void StopWatchingMediaKeys();
|
||||
|
||||
// Enable and disable the hot key event handler.
|
||||
void StartWatchingHotKeys();
|
||||
void StopWatchingHotKeys();
|
||||
|
||||
// Whether or not any media keys are currently registered.
|
||||
bool IsAnyMediaKeyRegistered();
|
||||
|
||||
// Whether or not any hot keys are currently registered.
|
||||
bool IsAnyHotKeyRegistered();
|
||||
|
||||
// The callback for when an event tap happens.
|
||||
static CGEventRef EventTapCallback(
|
||||
CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon);
|
||||
|
||||
// The callback for when a hot key event happens.
|
||||
static OSStatus HotKeyHandler(
|
||||
EventHandlerCallRef next_handler, EventRef event, void* user_data);
|
||||
|
||||
// Whether this object is listening for global shortcuts.
|
||||
bool is_listening_;
|
||||
|
||||
// The hotkey identifier for the next global shortcut that is added.
|
||||
KeyId hot_key_id_;
|
||||
|
||||
// A map of all hotkeys (media keys and shortcuts) mapping to their
|
||||
// corresponding hotkey IDs. For quickly finding if an accelerator is
|
||||
// registered.
|
||||
AcceleratorIdMap accelerator_ids_;
|
||||
|
||||
// The inverse map for quickly looking up accelerators by hotkey id.
|
||||
IdAcceleratorMap id_accelerators_;
|
||||
|
||||
// Keyboard shortcut IDs to hotkeys map for unregistration.
|
||||
IdHotKeyRefMap id_hot_key_refs_;
|
||||
|
||||
// Event tap for intercepting mac media keys.
|
||||
CFMachPortRef event_tap_;
|
||||
CFRunLoopSourceRef event_tap_source_;
|
||||
|
||||
// Event handler for keyboard shortcut hot keys.
|
||||
EventHandlerRef event_handler_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerMac);
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_
|
||||
@@ -0,0 +1,383 @@
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/extensions/global_shortcut_listener_mac.h"
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <IOKit/hidsystem/ev_keymap.h>
|
||||
|
||||
#import "base/mac/foundation_util.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "ui/base/accelerators/accelerator.h"
|
||||
#include "ui/events/event.h"
|
||||
#import "ui/events/keycodes/keyboard_code_conversion_mac.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
using extensions::GlobalShortcutListenerMac;
|
||||
|
||||
namespace {
|
||||
|
||||
// The media keys subtype. No official docs found, but widely known.
|
||||
// http://lists.apple.com/archives/cocoa-dev/2007/Aug/msg00499.html
|
||||
const int kSystemDefinedEventMediaKeysSubtype = 8;
|
||||
|
||||
ui::KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) {
|
||||
switch (key_code) {
|
||||
case NX_KEYTYPE_PLAY:
|
||||
return ui::VKEY_MEDIA_PLAY_PAUSE;
|
||||
case NX_KEYTYPE_PREVIOUS:
|
||||
case NX_KEYTYPE_REWIND:
|
||||
return ui::VKEY_MEDIA_PREV_TRACK;
|
||||
case NX_KEYTYPE_NEXT:
|
||||
case NX_KEYTYPE_FAST:
|
||||
return ui::VKEY_MEDIA_NEXT_TRACK;
|
||||
}
|
||||
return ui::VKEY_UNKNOWN;
|
||||
}
|
||||
|
||||
bool IsMediaKey(const ui::Accelerator& accelerator) {
|
||||
if (accelerator.modifiers() != 0)
|
||||
return false;
|
||||
return (accelerator.key_code() == ui::VKEY_MEDIA_NEXT_TRACK ||
|
||||
accelerator.key_code() == ui::VKEY_MEDIA_PREV_TRACK ||
|
||||
accelerator.key_code() == ui::VKEY_MEDIA_PLAY_PAUSE ||
|
||||
accelerator.key_code() == ui::VKEY_MEDIA_STOP);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace extensions {
|
||||
|
||||
// static
|
||||
GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
static GlobalShortcutListenerMac* instance =
|
||||
new GlobalShortcutListenerMac();
|
||||
return instance;
|
||||
}
|
||||
|
||||
GlobalShortcutListenerMac::GlobalShortcutListenerMac()
|
||||
: is_listening_(false),
|
||||
hot_key_id_(0),
|
||||
event_tap_(NULL),
|
||||
event_tap_source_(NULL),
|
||||
event_handler_(NULL) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
}
|
||||
|
||||
GlobalShortcutListenerMac::~GlobalShortcutListenerMac() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
|
||||
// By this point, UnregisterAccelerator should have been called for all
|
||||
// keyboard shortcuts. Still we should clean up.
|
||||
if (is_listening_)
|
||||
StopListening();
|
||||
|
||||
// If keys are still registered, make sure we stop the tap. Again, this
|
||||
// should never happen.
|
||||
if (IsAnyMediaKeyRegistered())
|
||||
StopWatchingMediaKeys();
|
||||
|
||||
if (IsAnyHotKeyRegistered())
|
||||
StopWatchingHotKeys();
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::StartListening() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
|
||||
DCHECK(!accelerator_ids_.empty());
|
||||
DCHECK(!id_accelerators_.empty());
|
||||
DCHECK(!is_listening_);
|
||||
|
||||
is_listening_ = true;
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::StopListening() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
|
||||
DCHECK(accelerator_ids_.empty()); // Make sure the set is clean.
|
||||
DCHECK(id_accelerators_.empty());
|
||||
DCHECK(is_listening_);
|
||||
|
||||
is_listening_ = false;
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::OnHotKeyEvent(EventHotKeyID hot_key_id) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
|
||||
// This hot key should be registered.
|
||||
DCHECK(id_accelerators_.find(hot_key_id.id) != id_accelerators_.end());
|
||||
// Look up the accelerator based on this hot key ID.
|
||||
const ui::Accelerator& accelerator = id_accelerators_[hot_key_id.id];
|
||||
NotifyKeyPressed(accelerator);
|
||||
}
|
||||
|
||||
bool GlobalShortcutListenerMac::OnMediaKeyEvent(int media_key_code) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
ui::KeyboardCode key_code = MediaKeyCodeToKeyboardCode(media_key_code);
|
||||
// Create an accelerator corresponding to the keyCode.
|
||||
ui::Accelerator accelerator(key_code, 0);
|
||||
// Look for a match with a bound hot_key.
|
||||
if (accelerator_ids_.find(accelerator) != accelerator_ids_.end()) {
|
||||
// If matched, callback to the event handling system.
|
||||
NotifyKeyPressed(accelerator);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GlobalShortcutListenerMac::RegisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
DCHECK(accelerator_ids_.find(accelerator) == accelerator_ids_.end());
|
||||
|
||||
if (IsMediaKey(accelerator)) {
|
||||
if (!IsAnyMediaKeyRegistered()) {
|
||||
// If this is the first media key registered, start the event tap.
|
||||
StartWatchingMediaKeys();
|
||||
}
|
||||
} else {
|
||||
// Register hot_key if they are non-media keyboard shortcuts.
|
||||
if (!RegisterHotKey(accelerator, hot_key_id_))
|
||||
return false;
|
||||
|
||||
if (!IsAnyHotKeyRegistered()) {
|
||||
StartWatchingHotKeys();
|
||||
}
|
||||
}
|
||||
|
||||
// Store the hotkey-ID mappings we will need for lookup later.
|
||||
id_accelerators_[hot_key_id_] = accelerator;
|
||||
accelerator_ids_[accelerator] = hot_key_id_;
|
||||
++hot_key_id_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::UnregisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end());
|
||||
|
||||
// Unregister the hot_key if it's a keyboard shortcut.
|
||||
if (!IsMediaKey(accelerator))
|
||||
UnregisterHotKey(accelerator);
|
||||
|
||||
// Remove hot_key from the mappings.
|
||||
KeyId key_id = accelerator_ids_[accelerator];
|
||||
id_accelerators_.erase(key_id);
|
||||
accelerator_ids_.erase(accelerator);
|
||||
|
||||
if (IsMediaKey(accelerator)) {
|
||||
// If we unregistered a media key, and now no media keys are registered,
|
||||
// stop the media key tap.
|
||||
if (!IsAnyMediaKeyRegistered())
|
||||
StopWatchingMediaKeys();
|
||||
} else {
|
||||
// If we unregistered a hot key, and no more hot keys are registered, remove
|
||||
// the hot key handler.
|
||||
if (!IsAnyHotKeyRegistered()) {
|
||||
StopWatchingHotKeys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GlobalShortcutListenerMac::RegisterHotKey(
|
||||
const ui::Accelerator& accelerator, KeyId hot_key_id) {
|
||||
EventHotKeyID event_hot_key_id;
|
||||
|
||||
// Signature uniquely identifies the application that owns this hot_key.
|
||||
event_hot_key_id.signature = base::mac::CreatorCodeForApplication();
|
||||
event_hot_key_id.id = hot_key_id;
|
||||
|
||||
// Translate ui::Accelerator modifiers to cmdKey, altKey, etc.
|
||||
int modifiers = 0;
|
||||
modifiers |= (accelerator.IsShiftDown() ? shiftKey : 0);
|
||||
modifiers |= (accelerator.IsCtrlDown() ? controlKey : 0);
|
||||
modifiers |= (accelerator.IsAltDown() ? optionKey : 0);
|
||||
modifiers |= (accelerator.IsCmdDown() ? cmdKey : 0);
|
||||
|
||||
int key_code = ui::MacKeyCodeForWindowsKeyCode(accelerator.key_code(), 0,
|
||||
NULL, NULL);
|
||||
|
||||
// Register the event hot key.
|
||||
EventHotKeyRef hot_key_ref;
|
||||
OSStatus status = RegisterEventHotKey(key_code, modifiers, event_hot_key_id,
|
||||
GetApplicationEventTarget(), 0, &hot_key_ref);
|
||||
if (status != noErr)
|
||||
return false;
|
||||
|
||||
id_hot_key_refs_[hot_key_id] = hot_key_ref;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::UnregisterHotKey(
|
||||
const ui::Accelerator& accelerator) {
|
||||
// Ensure this accelerator is already registered.
|
||||
DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end());
|
||||
// Get the ref corresponding to this accelerator.
|
||||
KeyId key_id = accelerator_ids_[accelerator];
|
||||
EventHotKeyRef ref = id_hot_key_refs_[key_id];
|
||||
// Unregister the event hot key.
|
||||
UnregisterEventHotKey(ref);
|
||||
|
||||
// Remove the event from the mapping.
|
||||
id_hot_key_refs_.erase(key_id);
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::StartWatchingMediaKeys() {
|
||||
// Make sure there's no existing event tap.
|
||||
DCHECK(event_tap_ == NULL);
|
||||
DCHECK(event_tap_source_ == NULL);
|
||||
|
||||
// Add an event tap to intercept the system defined media key events.
|
||||
event_tap_ = CGEventTapCreate(kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionDefault,
|
||||
CGEventMaskBit(NX_SYSDEFINED),
|
||||
EventTapCallback,
|
||||
this);
|
||||
if (event_tap_ == NULL) {
|
||||
LOG(ERROR) << "Error: failed to create event tap.";
|
||||
return;
|
||||
}
|
||||
|
||||
event_tap_source_ = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault,
|
||||
event_tap_, 0);
|
||||
if (event_tap_source_ == NULL) {
|
||||
LOG(ERROR) << "Error: failed to create new run loop source.";
|
||||
return;
|
||||
}
|
||||
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), event_tap_source_,
|
||||
kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::StopWatchingMediaKeys() {
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), event_tap_source_,
|
||||
kCFRunLoopCommonModes);
|
||||
// Ensure both event tap and source are initialized.
|
||||
DCHECK(event_tap_ != NULL);
|
||||
DCHECK(event_tap_source_ != NULL);
|
||||
|
||||
// Invalidate the event tap.
|
||||
CFMachPortInvalidate(event_tap_);
|
||||
CFRelease(event_tap_);
|
||||
event_tap_ = NULL;
|
||||
|
||||
// Release the event tap source.
|
||||
CFRelease(event_tap_source_);
|
||||
event_tap_source_ = NULL;
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::StartWatchingHotKeys() {
|
||||
DCHECK(!event_handler_);
|
||||
EventHandlerUPP hot_key_function = NewEventHandlerUPP(HotKeyHandler);
|
||||
EventTypeSpec event_type;
|
||||
event_type.eventClass = kEventClassKeyboard;
|
||||
event_type.eventKind = kEventHotKeyPressed;
|
||||
InstallApplicationEventHandler(
|
||||
hot_key_function, 1, &event_type, this, &event_handler_);
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::StopWatchingHotKeys() {
|
||||
DCHECK(event_handler_);
|
||||
RemoveEventHandler(event_handler_);
|
||||
event_handler_ = NULL;
|
||||
}
|
||||
|
||||
bool GlobalShortcutListenerMac::IsAnyMediaKeyRegistered() {
|
||||
// Iterate through registered accelerators, looking for media keys.
|
||||
AcceleratorIdMap::iterator it;
|
||||
for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
|
||||
if (IsMediaKey(it->first))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GlobalShortcutListenerMac::IsAnyHotKeyRegistered() {
|
||||
AcceleratorIdMap::iterator it;
|
||||
for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
|
||||
if (!IsMediaKey(it->first))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Processed events should propagate if they aren't handled by any listeners.
|
||||
// For events that don't matter, this handler should return as quickly as
|
||||
// possible.
|
||||
// Returning event causes the event to propagate to other applications.
|
||||
// Returning NULL prevents the event from propagating.
|
||||
// static
|
||||
CGEventRef GlobalShortcutListenerMac::EventTapCallback(
|
||||
CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon) {
|
||||
GlobalShortcutListenerMac* shortcut_listener =
|
||||
static_cast<GlobalShortcutListenerMac*>(refcon);
|
||||
|
||||
// Handle the timeout case by re-enabling the tap.
|
||||
if (type == kCGEventTapDisabledByTimeout) {
|
||||
CGEventTapEnable(shortcut_listener->event_tap_, TRUE);
|
||||
return event;
|
||||
}
|
||||
|
||||
// Convert the CGEvent to an NSEvent for access to the data1 field.
|
||||
NSEvent* ns_event = [NSEvent eventWithCGEvent:event];
|
||||
if (ns_event == nil) {
|
||||
return event;
|
||||
}
|
||||
|
||||
// Ignore events that are not system defined media keys.
|
||||
if (type != NX_SYSDEFINED ||
|
||||
[ns_event type] != NSSystemDefined ||
|
||||
[ns_event subtype] != kSystemDefinedEventMediaKeysSubtype) {
|
||||
return event;
|
||||
}
|
||||
|
||||
NSInteger data1 = [ns_event data1];
|
||||
// Ignore media keys that aren't previous, next and play/pause.
|
||||
// Magical constants are from http://weblog.rogueamoeba.com/2007/09/29/
|
||||
int key_code = (data1 & 0xFFFF0000) >> 16;
|
||||
if (key_code != NX_KEYTYPE_PLAY && key_code != NX_KEYTYPE_NEXT &&
|
||||
key_code != NX_KEYTYPE_PREVIOUS && key_code != NX_KEYTYPE_FAST &&
|
||||
key_code != NX_KEYTYPE_REWIND) {
|
||||
return event;
|
||||
}
|
||||
|
||||
int key_flags = data1 & 0x0000FFFF;
|
||||
bool is_key_pressed = ((key_flags & 0xFF00) >> 8) == 0xA;
|
||||
|
||||
// If the key wasn't pressed (eg. was released), ignore this event.
|
||||
if (!is_key_pressed)
|
||||
return event;
|
||||
|
||||
// Now we have a media key that we care about. Send it to the caller.
|
||||
bool was_handled = shortcut_listener->OnMediaKeyEvent(key_code);
|
||||
|
||||
// Prevent event from proagating to other apps if handled by Chrome.
|
||||
if (was_handled)
|
||||
return NULL;
|
||||
|
||||
// By default, pass the event through.
|
||||
return event;
|
||||
}
|
||||
|
||||
// static
|
||||
OSStatus GlobalShortcutListenerMac::HotKeyHandler(
|
||||
EventHandlerCallRef next_handler, EventRef event, void* user_data) {
|
||||
// Extract the hotkey from the event.
|
||||
EventHotKeyID hot_key_id;
|
||||
OSStatus result = GetEventParameter(event, kEventParamDirectObject,
|
||||
typeEventHotKeyID, NULL, sizeof(hot_key_id), NULL, &hot_key_id);
|
||||
if (result != noErr)
|
||||
return result;
|
||||
|
||||
GlobalShortcutListenerMac* shortcut_listener =
|
||||
static_cast<GlobalShortcutListenerMac*>(user_data);
|
||||
shortcut_listener->OnHotKeyEvent(hot_key_id);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/extensions/global_shortcut_listener_win.h"
|
||||
|
||||
#include "base/win/win_util.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "ui/base/accelerators/accelerator.h"
|
||||
#include "ui/events/event_constants.h"
|
||||
#include "ui/events/keycodes/keyboard_code_conversion_win.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace extensions {
|
||||
|
||||
// static
|
||||
GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
static GlobalShortcutListenerWin* instance =
|
||||
new GlobalShortcutListenerWin();
|
||||
return instance;
|
||||
}
|
||||
|
||||
GlobalShortcutListenerWin::GlobalShortcutListenerWin()
|
||||
: is_listening_(false) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
}
|
||||
|
||||
GlobalShortcutListenerWin::~GlobalShortcutListenerWin() {
|
||||
if (is_listening_)
|
||||
StopListening();
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerWin::StartListening() {
|
||||
DCHECK(!is_listening_); // Don't start twice.
|
||||
DCHECK(!hotkey_ids_.empty()); // Also don't start if no hotkey is registered.
|
||||
gfx::SingletonHwnd::GetInstance()->AddObserver(this);
|
||||
is_listening_ = true;
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerWin::StopListening() {
|
||||
DCHECK(is_listening_); // No point if we are not already listening.
|
||||
DCHECK(hotkey_ids_.empty()); // Make sure the map is clean before ending.
|
||||
gfx::SingletonHwnd::GetInstance()->RemoveObserver(this);
|
||||
is_listening_ = false;
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerWin::OnWndProc(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam) {
|
||||
if (message != WM_HOTKEY)
|
||||
return;
|
||||
|
||||
int key_code = HIWORD(lparam);
|
||||
int modifiers = 0;
|
||||
modifiers |= (LOWORD(lparam) & MOD_SHIFT) ? ui::EF_SHIFT_DOWN : 0;
|
||||
modifiers |= (LOWORD(lparam) & MOD_ALT) ? ui::EF_ALT_DOWN : 0;
|
||||
modifiers |= (LOWORD(lparam) & MOD_CONTROL) ? ui::EF_CONTROL_DOWN : 0;
|
||||
ui::Accelerator accelerator(
|
||||
ui::KeyboardCodeForWindowsKeyCode(key_code), modifiers);
|
||||
|
||||
NotifyKeyPressed(accelerator);
|
||||
}
|
||||
|
||||
bool GlobalShortcutListenerWin::RegisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) {
|
||||
DCHECK(hotkey_ids_.find(accelerator) == hotkey_ids_.end());
|
||||
|
||||
int modifiers = 0;
|
||||
modifiers |= accelerator.IsShiftDown() ? MOD_SHIFT : 0;
|
||||
modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0;
|
||||
modifiers |= accelerator.IsAltDown() ? MOD_ALT : 0;
|
||||
static int hotkey_id = 0;
|
||||
bool success = !!RegisterHotKey(
|
||||
gfx::SingletonHwnd::GetInstance()->hwnd(),
|
||||
hotkey_id,
|
||||
modifiers,
|
||||
accelerator.key_code());
|
||||
|
||||
if (!success) {
|
||||
// Most likely error: 1409 (Hotkey already registered).
|
||||
return false;
|
||||
}
|
||||
|
||||
hotkey_ids_[accelerator] = hotkey_id++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerWin::UnregisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) {
|
||||
HotkeyIdMap::iterator it = hotkey_ids_.find(accelerator);
|
||||
DCHECK(it != hotkey_ids_.end());
|
||||
|
||||
bool success = !!UnregisterHotKey(
|
||||
gfx::SingletonHwnd::GetInstance()->hwnd(), it->second);
|
||||
// This call should always succeed, as long as we pass in the right HWND and
|
||||
// an id we've used to register before.
|
||||
DCHECK(success);
|
||||
|
||||
hotkey_ids_.erase(it);
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
|
||||
#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "chrome/browser/extensions/global_shortcut_listener.h"
|
||||
#include "ui/gfx/win/singleton_hwnd.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
// Windows-specific implementation of the GlobalShortcutListener class that
|
||||
// listens for global shortcuts. Handles setting up a keyboard hook and
|
||||
// forwarding its output to the base class for processing.
|
||||
class GlobalShortcutListenerWin : public GlobalShortcutListener,
|
||||
public gfx::SingletonHwnd::Observer {
|
||||
public:
|
||||
GlobalShortcutListenerWin();
|
||||
virtual ~GlobalShortcutListenerWin();
|
||||
|
||||
private:
|
||||
// The implementation of our Window Proc, called by SingletonHwnd.
|
||||
virtual void OnWndProc(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam) OVERRIDE;
|
||||
|
||||
// GlobalShortcutListener implementation.
|
||||
virtual void StartListening() OVERRIDE;
|
||||
virtual void StopListening() OVERRIDE;
|
||||
virtual bool RegisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) OVERRIDE;
|
||||
virtual void UnregisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) OVERRIDE;
|
||||
|
||||
// Whether this object is listening for global shortcuts.
|
||||
bool is_listening_;
|
||||
|
||||
// A map of registered accelerators and their registration ids.
|
||||
typedef std::map<ui::Accelerator, int> HotkeyIdMap;
|
||||
HotkeyIdMap hotkey_ids_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerWin);
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
|
||||
@@ -0,0 +1,158 @@
|
||||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/extensions/global_shortcut_listener_x11.h"
|
||||
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "ui/base/accelerators/accelerator.h"
|
||||
#include "ui/events/keycodes/keyboard_code_conversion_x.h"
|
||||
#include "ui/events/platform/x11/x11_event_source.h"
|
||||
#include "ui/gfx/x/x11_error_tracker.h"
|
||||
#include "ui/gfx/x/x11_types.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace {
|
||||
|
||||
// The modifiers masks used for grabing keys. Due to XGrabKey only working on
|
||||
// exact modifiers, we need to grab all key combination including zero or more
|
||||
// of the following: Num lock, Caps lock and Scroll lock. So that we can make
|
||||
// sure the behavior of global shortcuts is consistent on all platforms.
|
||||
const unsigned int kModifiersMasks[] = {
|
||||
0, // No additional modifier.
|
||||
Mod2Mask, // Num lock
|
||||
LockMask, // Caps lock
|
||||
Mod5Mask, // Scroll lock
|
||||
Mod2Mask | LockMask,
|
||||
Mod2Mask | Mod5Mask,
|
||||
LockMask | Mod5Mask,
|
||||
Mod2Mask | LockMask | Mod5Mask
|
||||
};
|
||||
|
||||
int GetNativeModifiers(const ui::Accelerator& accelerator) {
|
||||
int modifiers = 0;
|
||||
modifiers |= accelerator.IsShiftDown() ? ShiftMask : 0;
|
||||
modifiers |= accelerator.IsCtrlDown() ? ControlMask : 0;
|
||||
modifiers |= accelerator.IsAltDown() ? Mod1Mask : 0;
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace extensions {
|
||||
|
||||
// static
|
||||
GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
static GlobalShortcutListenerX11* instance =
|
||||
new GlobalShortcutListenerX11();
|
||||
return instance;
|
||||
}
|
||||
|
||||
GlobalShortcutListenerX11::GlobalShortcutListenerX11()
|
||||
: is_listening_(false),
|
||||
x_display_(gfx::GetXDisplay()),
|
||||
x_root_window_(DefaultRootWindow(x_display_)) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
}
|
||||
|
||||
GlobalShortcutListenerX11::~GlobalShortcutListenerX11() {
|
||||
if (is_listening_)
|
||||
StopListening();
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerX11::StartListening() {
|
||||
DCHECK(!is_listening_); // Don't start twice.
|
||||
DCHECK(!registered_hot_keys_.empty()); // Also don't start if no hotkey is
|
||||
// registered.
|
||||
|
||||
ui::X11EventSource::GetInstance()->AddPlatformEventDispatcher(this);
|
||||
|
||||
is_listening_ = true;
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerX11::StopListening() {
|
||||
DCHECK(is_listening_); // No point if we are not already listening.
|
||||
DCHECK(registered_hot_keys_.empty()); // Make sure the set is clean before
|
||||
// ending.
|
||||
|
||||
ui::X11EventSource::GetInstance()->RemovePlatformEventDispatcher(this);
|
||||
|
||||
is_listening_ = false;
|
||||
}
|
||||
|
||||
bool GlobalShortcutListenerX11::CanDispatchEvent(
|
||||
const ui::PlatformEvent& event) {
|
||||
return event->type == KeyPress;
|
||||
}
|
||||
|
||||
uint32_t GlobalShortcutListenerX11::DispatchEvent(
|
||||
const ui::PlatformEvent& event) {
|
||||
CHECK_EQ(KeyPress, event->type);
|
||||
OnXKeyPressEvent(event);
|
||||
|
||||
return ui::POST_DISPATCH_NONE;
|
||||
}
|
||||
|
||||
bool GlobalShortcutListenerX11::RegisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) {
|
||||
DCHECK(registered_hot_keys_.find(accelerator) == registered_hot_keys_.end());
|
||||
|
||||
int modifiers = GetNativeModifiers(accelerator);
|
||||
KeyCode keycode = XKeysymToKeycode(x_display_,
|
||||
XKeysymForWindowsKeyCode(accelerator.key_code(), false));
|
||||
gfx::X11ErrorTracker err_tracker;
|
||||
|
||||
// Because XGrabKey only works on the exact modifiers mask, we should register
|
||||
// our hot keys with modifiers that we want to ignore, including Num lock,
|
||||
// Caps lock, Scroll lock. See comment about |kModifiersMasks|.
|
||||
for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
|
||||
XGrabKey(x_display_, keycode, modifiers | kModifiersMasks[i],
|
||||
x_root_window_, False, GrabModeAsync, GrabModeAsync);
|
||||
}
|
||||
|
||||
if (err_tracker.FoundNewError()) {
|
||||
// We may have part of the hotkeys registered, clean up.
|
||||
for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
|
||||
XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i],
|
||||
x_root_window_);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
registered_hot_keys_.insert(accelerator);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerX11::UnregisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) {
|
||||
DCHECK(registered_hot_keys_.find(accelerator) != registered_hot_keys_.end());
|
||||
|
||||
int modifiers = GetNativeModifiers(accelerator);
|
||||
KeyCode keycode = XKeysymToKeycode(x_display_,
|
||||
XKeysymForWindowsKeyCode(accelerator.key_code(), false));
|
||||
|
||||
for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
|
||||
XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i],
|
||||
x_root_window_);
|
||||
}
|
||||
registered_hot_keys_.erase(accelerator);
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerX11::OnXKeyPressEvent(::XEvent* x_event) {
|
||||
DCHECK(x_event->type == KeyPress);
|
||||
int modifiers = 0;
|
||||
modifiers |= (x_event->xkey.state & ShiftMask) ? ui::EF_SHIFT_DOWN : 0;
|
||||
modifiers |= (x_event->xkey.state & ControlMask) ? ui::EF_CONTROL_DOWN : 0;
|
||||
modifiers |= (x_event->xkey.state & Mod1Mask) ? ui::EF_ALT_DOWN : 0;
|
||||
|
||||
ui::Accelerator accelerator(
|
||||
ui::KeyboardCodeFromXKeyEvent(x_event), modifiers);
|
||||
if (registered_hot_keys_.find(accelerator) != registered_hot_keys_.end())
|
||||
NotifyKeyPressed(accelerator);
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_X11_H_
|
||||
#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_X11_H_
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <set>
|
||||
|
||||
#include "chrome/browser/extensions/global_shortcut_listener.h"
|
||||
#include "ui/events/platform/platform_event_dispatcher.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
// X11-specific implementation of the GlobalShortcutListener class that
|
||||
// listens for global shortcuts. Handles basic keyboard intercepting and
|
||||
// forwards its output to the base class for processing.
|
||||
class GlobalShortcutListenerX11 : public GlobalShortcutListener,
|
||||
public ui::PlatformEventDispatcher {
|
||||
public:
|
||||
GlobalShortcutListenerX11();
|
||||
virtual ~GlobalShortcutListenerX11();
|
||||
|
||||
// ui::PlatformEventDispatcher implementation.
|
||||
virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE;
|
||||
virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE;
|
||||
|
||||
private:
|
||||
// GlobalShortcutListener implementation.
|
||||
virtual void StartListening() OVERRIDE;
|
||||
virtual void StopListening() OVERRIDE;
|
||||
virtual bool RegisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) OVERRIDE;
|
||||
virtual void UnregisterAcceleratorImpl(
|
||||
const ui::Accelerator& accelerator) OVERRIDE;
|
||||
|
||||
// Invoked when a global shortcut is pressed.
|
||||
void OnXKeyPressEvent(::XEvent* x_event);
|
||||
|
||||
// Whether this object is listening for global shortcuts.
|
||||
bool is_listening_;
|
||||
|
||||
// The x11 default display and the native root window.
|
||||
::Display* x_display_;
|
||||
::Window x_root_window_;
|
||||
|
||||
// A set of registered accelerators.
|
||||
typedef std::set<ui::Accelerator> RegisteredHotKeys;
|
||||
RegisteredHotKeys registered_hot_keys_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerX11);
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_X11_H_
|
||||
@@ -14,7 +14,9 @@ Modules for browser side:
|
||||
* [app](api/app.md)
|
||||
* [auto-updater](api/auto-updater.md)
|
||||
* [browser-window](api/browser-window.md)
|
||||
* [content-tracing](api/content-tracing.md)
|
||||
* [dialog](api/dialog.md)
|
||||
* [global-shortcut](api/global-shortcut.md)
|
||||
* [ipc (browser)](api/ipc-browser.md)
|
||||
* [menu](api/menu.md)
|
||||
* [menu-item](api/menu-item.md)
|
||||
|
||||
41
docs/api/accelerator.md
Normal file
41
docs/api/accelerator.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Accelerator
|
||||
|
||||
An accelerator is string that represents a keyboard shortcut, it can contain
|
||||
multiple modifiers and key codes, combined by the `+` character.
|
||||
|
||||
Examples:
|
||||
|
||||
* `Command+A`
|
||||
* `Ctrl+Shift+Z`
|
||||
|
||||
## Platform notice
|
||||
|
||||
On Linux and Windows, the `Command` key would not have any effect, you can
|
||||
use `CommandOrControl` which represents `Command` on OS X and `Control` on
|
||||
Linux and Windows to define some accelerators.
|
||||
|
||||
## Available modifiers
|
||||
|
||||
* `Command` (or `Cmd` for short)
|
||||
* `Control` (or `Ctrl` for short)
|
||||
* `CommandOrControl` (or `CmdOrCtrl` for short)
|
||||
* `Alt`
|
||||
* `Shift`
|
||||
|
||||
## Available key codes
|
||||
|
||||
* `0` to `9`
|
||||
* `A` to `Z`
|
||||
* `F1` to `F24`
|
||||
* Punctuations like `~`, `!`, `@`, `#`, `$`, etc.
|
||||
* `Space`
|
||||
* `Backspace`
|
||||
* `Delete`
|
||||
* `Insert`
|
||||
* `Return` (or `Enter` as alias)
|
||||
* `Up`, `Down`, `Left` and `Right`
|
||||
* `Home` and `End`
|
||||
* `PageUp` and `PageDown`
|
||||
* `Escape` (or `Esc` for short)
|
||||
* `VolumeUp`, `VolumeDown` and `VolumeMute`
|
||||
* `MediaNextTrack`, `MediaPreviousTrack`, `MediaStop` and `MediaPlayPause`
|
||||
@@ -5,6 +5,10 @@
|
||||
The `auto-updater` module is a simple wrap around the
|
||||
[Squirrel.Mac](https://github.com/Squirrel/Squirrel.Mac) framework.
|
||||
|
||||
Squirrel.Mac requires that your `.app` folder is signed using the
|
||||
[codesign](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/codesign.1.html)
|
||||
utility for updates to be installed.
|
||||
|
||||
## Squirrel
|
||||
|
||||
Squirrel is an OS X framework focused on making application updates **as safe
|
||||
|
||||
@@ -55,6 +55,8 @@ You can also create a window without chrome by using
|
||||
`manual-enable-iframe` or `disable`.
|
||||
* `accept-first-mouse` Boolean - Whether the web view accepts a single
|
||||
mouse-down event that simultaneously activates the window
|
||||
* `auto-hide-menu-bar` Boolean - Auto hide the menu bar unless the `Alt`
|
||||
key is pressed.
|
||||
* `web-preferences` Object - Settings of web page's features
|
||||
* `javascript` Boolean
|
||||
* `web-security` Boolean
|
||||
@@ -63,7 +65,6 @@ You can also create a window without chrome by using
|
||||
* `text-areas-are-resizable` Boolean
|
||||
* `webgl` Boolean
|
||||
* `webaudio` Boolean
|
||||
* `accelerated-compositing` Boolean
|
||||
* `plugins` Boolean - Whether plugins should be enabled, currently only
|
||||
`NPAPI` plugins are supported.
|
||||
* `extra-plugin-dirs` Array - Array of paths that would be searched for
|
||||
@@ -252,6 +253,10 @@ the Dock.
|
||||
|
||||
Restores the window from minimized state to its previous state.
|
||||
|
||||
### BrowserWindow.isMinimized()
|
||||
|
||||
Returns whether the window is minimized.
|
||||
|
||||
### BrowserWindow.setFullScreen(flag)
|
||||
|
||||
* `flag` Boolean
|
||||
|
||||
136
docs/api/content-tracing.md
Normal file
136
docs/api/content-tracing.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# content-tracing
|
||||
|
||||
The `content-trace` module is used to collect tracing data generated by the
|
||||
underlying Chromium content module. This module does not include a web interface
|
||||
so you need to open `chrome://tracing/` in Chrome browser and load the generated
|
||||
file to view the result.
|
||||
|
||||
```javascript
|
||||
var tracing = require('content-tracing');
|
||||
tracing.startRecording('*', tracing.DEFAULT_OPTIONS, function() {
|
||||
console.log('Tracing started');
|
||||
|
||||
setTimeout(function() {
|
||||
tracing.stopRecording('', function(path) {
|
||||
console.log('Tracing data recorded to ' + path);
|
||||
});
|
||||
}, 5000);
|
||||
});
|
||||
```
|
||||
|
||||
## tracing.getCategories(callback)
|
||||
|
||||
* `callback` Function
|
||||
|
||||
Get a set of category groups. The category groups can change as new code paths
|
||||
are reached.
|
||||
|
||||
Once all child processes have acked to the `getCategories` request, `callback`
|
||||
is called back with an array of category groups.
|
||||
|
||||
## tracing.startRecording(categoryFilter, options, callback)
|
||||
|
||||
* `categoryFilter` String
|
||||
* `options` Integer
|
||||
* `callback` Function
|
||||
|
||||
Start recording on all processes.
|
||||
|
||||
Recording begins immediately locally, and asynchronously on child processes
|
||||
as soon as they receive the EnableRecording request. Once all child processes
|
||||
have acked to the `startRecording` request, `callback` will be called back.
|
||||
|
||||
`categoryFilter` is a filter to control what category groups should be
|
||||
traced. A filter can have an optional `-` prefix to exclude category groups
|
||||
that contain a matching category. Having both included and excluded
|
||||
category patterns in the same list would not be supported.
|
||||
|
||||
Examples:
|
||||
|
||||
* `test_MyTest*`,
|
||||
* `test_MyTest*,test_OtherStuff`,
|
||||
* `"-excluded_category1,-excluded_category2`
|
||||
|
||||
`options` controls what kind of tracing is enabled, it could be a OR-ed
|
||||
combination of `tracing.DEFAULT_OPTIONS`, `tracing.ENABLE_SYSTRACE`,
|
||||
`tracing.ENABLE_SAMPLING` and `tracing.RECORD_CONTINUOUSLY`.
|
||||
|
||||
## tracing.stopRecording(resultFilePath, callback)
|
||||
|
||||
* `resultFilePath` String
|
||||
* `callback` Function
|
||||
|
||||
Stop recording on all processes.
|
||||
|
||||
Child processes typically are caching trace data and only rarely flush and send
|
||||
trace data back to the browser process. That is because it may be an expensive
|
||||
operation to send the trace data over IPC, and we would like to avoid much
|
||||
runtime overhead of tracing. So, to end tracing, we must asynchronously ask all
|
||||
child processes to flush any pending trace data.
|
||||
|
||||
Once all child processes have acked to the `stopRecording` request, `callback`
|
||||
will be called back with a file that contains the traced data.
|
||||
|
||||
Trace data will be written into `resultFilePath` if it is not empty, or into a
|
||||
temporary file. The actual file path will be passed to `callback` if it's not
|
||||
null.
|
||||
|
||||
## tracing.startMonitoring(categoryFilter, options, callback)
|
||||
|
||||
* `categoryFilter` String
|
||||
* `options` Integer
|
||||
* `callback` Function
|
||||
|
||||
Start monitoring on all processes.
|
||||
|
||||
Monitoring begins immediately locally, and asynchronously on child processes as
|
||||
soon as they receive the `startMonitoring` request.
|
||||
|
||||
Once all child processes have acked to the `startMonitoring` request, `callback`
|
||||
will be called back.
|
||||
|
||||
## tracing.stopMonitoring(callback);
|
||||
|
||||
* `callback` Function
|
||||
|
||||
Stop monitoring on all processes.
|
||||
|
||||
Once all child processes have acked to the `stopMonitoring` request, `callback`
|
||||
is called back.
|
||||
|
||||
## tracing.captureMonitoringSnapshot(resultFilePath, callback)
|
||||
|
||||
* `resultFilePath` String
|
||||
* `callback` Function
|
||||
|
||||
Get the current monitoring traced data.
|
||||
|
||||
Child processes typically are caching trace data and only rarely flush and send
|
||||
trace data back to the browser process. That is because it may be an expensive
|
||||
operation to send the trace data over IPC, and we would like to avoid much
|
||||
runtime overhead of tracing. So, to end tracing, we must asynchronously ask all
|
||||
child processes to flush any pending trace data.
|
||||
|
||||
Once all child processes have acked to the `captureMonitoringSnapshot` request,
|
||||
`callback` will be called back with a file that contains the traced data.
|
||||
|
||||
|
||||
## tracing.getTraceBufferPercentFull(callback)
|
||||
|
||||
* `callback` Function
|
||||
|
||||
Get the maximum across processes of trace buffer percent full state. When the
|
||||
TraceBufferPercentFull value is determined, the `callback` is called.
|
||||
|
||||
## tracing.setWatchEvent(categoryName, eventName, callback)
|
||||
|
||||
* `categoryName` String
|
||||
* `eventName` String
|
||||
* `callback` Function
|
||||
|
||||
`callback` will will be called every time the given event occurs on any process.
|
||||
|
||||
## tracing.cancelWatchEvent()
|
||||
|
||||
Cancel the watch event. If tracing is enabled, this may race with the watch
|
||||
event callback.
|
||||
@@ -19,6 +19,7 @@ console.log(dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory', '
|
||||
* `options` Object
|
||||
* `title` String
|
||||
* `defaultPath` String
|
||||
* `filters` Array
|
||||
* `properties` Array - Contains which features the dialog should use, can
|
||||
contain `openFile`, `openDirectory`, `multiSelections` and
|
||||
`createDirectory`
|
||||
@@ -27,6 +28,19 @@ console.log(dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory', '
|
||||
On success, returns an array of file paths chosen by the user, otherwise
|
||||
returns `undefined`.
|
||||
|
||||
The `filters` specifies an array of file types that can be displayed or
|
||||
selected, an example is:
|
||||
|
||||
```javascript
|
||||
{
|
||||
filters: [
|
||||
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
|
||||
{ name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
|
||||
{ name: 'Custom File Type', extensions: ['as'] },
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
If a `callback` is passed, the API call would be asynchronous and the result
|
||||
would be passed via `callback(filenames)`
|
||||
|
||||
@@ -41,11 +55,15 @@ be showed.
|
||||
* `options` Object
|
||||
* `title` String
|
||||
* `defaultPath` String
|
||||
* `filters` Array
|
||||
* `callback` Function
|
||||
|
||||
On success, returns the path of file chosen by the user, otherwise returns
|
||||
`undefined`.
|
||||
|
||||
The `filters` specifies an array of file types that can be displayed, see
|
||||
`dialog.showOpenDialog` for an example.
|
||||
|
||||
If a `callback` is passed, the API call would be asynchronous and the result
|
||||
would be passed via `callback(filename)`
|
||||
|
||||
|
||||
47
docs/api/global-shortcut.md
Normal file
47
docs/api/global-shortcut.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# global-shortcut
|
||||
|
||||
The `global-shortcut` module can register/unregister a global keyboard shortcut
|
||||
in operating system, so that you can custom the operations for various shortcuts.
|
||||
Note that it is global, even the app does not get focused, it still works.
|
||||
|
||||
```javascript
|
||||
var globalShortcut = require('global-shortcut');
|
||||
|
||||
// Register a 'ctrl+x' shortcut listener.
|
||||
var ret = globalShortcut.register('ctrl+x', function() { console.log('ctrl+x is pressed'); })
|
||||
if (!ret)
|
||||
console.log('registerion fails');
|
||||
|
||||
// Check whether a shortcut is registered.
|
||||
console.log(globalShortcut.isRegistered('ctrl+x'));
|
||||
|
||||
// Unregister a shortcut.
|
||||
globalShortcut.unregister('ctrl+x');
|
||||
|
||||
// Unregister all shortcuts.
|
||||
globalShortcut.unregisterAll();
|
||||
```
|
||||
|
||||
## globalShortcut.register(accelerator, callback)
|
||||
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
* `callback` Function
|
||||
|
||||
Registers a global shortcut of `accelerator`, the `callback` would be called when
|
||||
the registered shortcut is pressed by user.
|
||||
|
||||
## globalShortcut.isRegistered(accelerator)
|
||||
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
|
||||
Returns whether shortcut of `accelerator` is registered.
|
||||
|
||||
## globalShortcut.unregister(accelerator)
|
||||
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
|
||||
Unregisters the global shortcut of `keycode`.
|
||||
|
||||
## globalShortcut.unregisterAll()
|
||||
|
||||
Unregisters all the global shortcuts.
|
||||
@@ -7,7 +7,7 @@ For example when creating tray or setting window's icon, you can pass image's
|
||||
file path as `String` to represent an image:
|
||||
|
||||
```javascript
|
||||
var appIcon = new Tray('/Users/somebody/images/icon@2x.png');
|
||||
var appIcon = new Tray('/Users/somebody/images/icon.png');
|
||||
var window = new BrowserWindow({icon: '/Users/somebody/images/window.png'});
|
||||
```
|
||||
|
||||
@@ -23,3 +23,32 @@ file name's base name to mark it as a high resolution image.
|
||||
For example if `icon.png` is a normal image that has standard resolution, the
|
||||
`icon@2x.png` would be treated as a high resolution image that has double DPI
|
||||
dense.
|
||||
|
||||
If you want to support displays with different DPI denses at the same time, you
|
||||
can put images with different sizes in the same folder, and use the filename
|
||||
without DPI suffixes, like this:
|
||||
|
||||
```text
|
||||
images/
|
||||
├── icon.png
|
||||
├── icon@2x.png
|
||||
└── icon@3x.png
|
||||
```
|
||||
|
||||
|
||||
```javascript
|
||||
var appIcon = new Tray('/Users/somebody/images/icon.png');
|
||||
```
|
||||
|
||||
Following suffixes as DPI denses are also supported:
|
||||
|
||||
* `@1x`
|
||||
* `@1.25x`
|
||||
* `@1.33x`
|
||||
* `@1.4x`
|
||||
* `@1.5x`
|
||||
* `@1.8x`
|
||||
* `@2x`
|
||||
* `@2.5x`
|
||||
* `@3x`
|
||||
|
||||
|
||||
@@ -12,17 +12,9 @@
|
||||
`radio`
|
||||
* `label` String
|
||||
* `sublabel` String
|
||||
* `accelerator` String - In the form of `Command+R`, `Ctrl+C`,
|
||||
`Shift+Command+D`, `D`, etc.
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
* `enabled` Boolean
|
||||
* `visible` Boolean
|
||||
* `checked` Boolean
|
||||
* `submenu` Menu - Should be specified for `submenu` type menu item, when
|
||||
it's specified the `type: 'submenu'` can be omitted for the menu item
|
||||
|
||||
## Notes on accelerator
|
||||
|
||||
On Linux and Windows, the `Command` key would not have any effect, you can
|
||||
use `CommandOrControl` which represents `Command` on OS X and `Control` on
|
||||
Linux and Windows to define some accelerators, you can also use its short
|
||||
alias `CmdOrCtrl`.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## Prerequisites
|
||||
|
||||
* [Node.js](http://nodejs.org)
|
||||
* clang and headers of GTK+ and libnotify
|
||||
* clang, development headers of GTK+ and libnotify
|
||||
|
||||
On Ubuntu you could install the libraries via:
|
||||
|
||||
@@ -56,13 +56,10 @@ $ ./script/build.py -c Debug
|
||||
|
||||
After building is done, you can find `atom` under `out/Debug`.
|
||||
|
||||
## 32bit support
|
||||
|
||||
Currently atom-shell can only be built for 64bit target, support for 32bit would
|
||||
come in future, patches would also be welcomed.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### fatal error: bits/predefs.h: No such file or directory
|
||||
|
||||
If you got an error like this:
|
||||
|
||||
````
|
||||
@@ -80,9 +77,20 @@ this:
|
||||
$ sudo apt-get install gcc-multilib g++-multilib
|
||||
```
|
||||
|
||||
### error adding symbols: DSO missing from command line
|
||||
|
||||
If you got an error like this:
|
||||
|
||||
````
|
||||
/usr/bin/ld: vendor/download/libchromiumcontent/Release/libchromiumcontent.so: undefined reference to symbol 'gconf_client_get'
|
||||
//usr/lib/x86_64-linux-gnu/libgconf-2.so.4: error adding symbols: DSO missing from command line
|
||||
````
|
||||
|
||||
libchromiumcontent.so is build with clang 3.0 which is incompatible with newer
|
||||
versions of clang. Try using clang 3.0, default version in Ubuntu 12.04.
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
$ ./script/test.py
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "atom-shell",
|
||||
"version": "0.14.1",
|
||||
"version": "0.15.4",
|
||||
|
||||
"licenses": [
|
||||
{
|
||||
@@ -10,7 +10,7 @@
|
||||
],
|
||||
|
||||
"devDependencies": {
|
||||
"atom-package-manager": "0.76.x",
|
||||
"atom-package-manager": "0.84.x",
|
||||
"coffee-script": "~1.7.1",
|
||||
"coffeelint": "~1.3.0"
|
||||
},
|
||||
|
||||
@@ -36,12 +36,15 @@ TARGET_BINARIES = {
|
||||
'atom.exe',
|
||||
'chromiumcontent.dll',
|
||||
'content_shell.pak',
|
||||
'd3dcompiler_43.dll',
|
||||
'ffmpegsumo.dll',
|
||||
'icudtl.dat',
|
||||
'libEGL.dll',
|
||||
'libGLESv2.dll',
|
||||
'd3dcompiler_43.dll',
|
||||
'msvcp120.dll',
|
||||
'msvcr120.dll',
|
||||
'ui_resources_200_percent.pak',
|
||||
'vccorlib120.dll',
|
||||
'webkit_resources_200_percent.pak',
|
||||
'xinput1_3.dll',
|
||||
],
|
||||
|
||||
@@ -5,7 +5,7 @@ import sys
|
||||
|
||||
NODE_VERSION = 'v0.11.13'
|
||||
BASE_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent'
|
||||
LIBCHROMIUMCONTENT_COMMIT = '177f00b33d8ba5a6befe646e8d39ce19b6a3c668'
|
||||
LIBCHROMIUMCONTENT_COMMIT = '61d53e9631625fa8e5d5043aabea18b96ed6a950'
|
||||
|
||||
ARCH = {
|
||||
'cygwin': '32bit',
|
||||
|
||||
@@ -4,10 +4,10 @@ import errno
|
||||
import sys
|
||||
import os
|
||||
|
||||
from lib.util import safe_mkdir, extract_zip, tempdir, download
|
||||
from lib.util import safe_mkdir, rm_rf, extract_zip, tempdir, download
|
||||
|
||||
|
||||
VERSION = 'v0.0.3'
|
||||
VERSION = 'v0.1.0'
|
||||
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
FRAMEWORKS_URL = 'https://github.com/atom/atom-shell-frameworks/releases' \
|
||||
'/download/' + VERSION
|
||||
@@ -17,12 +17,11 @@ def main():
|
||||
os.chdir(SOURCE_ROOT)
|
||||
version_file = os.path.join(SOURCE_ROOT, 'external_binaries', '.version')
|
||||
|
||||
safe_mkdir('external_binaries')
|
||||
if (is_updated(version_file, VERSION)):
|
||||
return
|
||||
|
||||
with open(version_file, 'w') as f:
|
||||
f.write(VERSION)
|
||||
rm_rf('external_binaries')
|
||||
safe_mkdir('external_binaries')
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
download_and_unzip('Mantle')
|
||||
@@ -30,6 +29,10 @@ def main():
|
||||
download_and_unzip('Squirrel')
|
||||
elif sys.platform in ['cygwin', 'win32']:
|
||||
download_and_unzip('directxsdk')
|
||||
download_and_unzip('vs2012_crt')
|
||||
|
||||
with open(version_file, 'w') as f:
|
||||
f.write(VERSION)
|
||||
|
||||
|
||||
def is_updated(version_file, version):
|
||||
|
||||
@@ -4,6 +4,8 @@ import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from lib.config import DIST_ARCH
|
||||
|
||||
|
||||
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
@@ -23,11 +25,16 @@ def update_external_binaries():
|
||||
def update_gyp():
|
||||
gyp = os.path.join('vendor', 'brightray', 'vendor', 'gyp', 'gyp_main.py')
|
||||
python = sys.executable
|
||||
if sys.platform == 'cygwin':
|
||||
python = os.path.join('vendor', 'python_26', 'python.exe')
|
||||
arch = 'ia32'
|
||||
arch = DIST_ARCH
|
||||
if sys.platform == 'darwin':
|
||||
# Only have 64bit build on OS X.
|
||||
arch = 'x64'
|
||||
elif sys.platform in ['cygwin', 'win32']:
|
||||
# Only have 32bit build on Windows.
|
||||
arch = 'ia32'
|
||||
if sys.platform == 'cygwin':
|
||||
# Force using win32 python on cygwin.
|
||||
python = os.path.join('vendor', 'python_26', 'python.exe')
|
||||
subprocess.call([python, gyp,
|
||||
'-f', 'ninja', '--depth', '.', 'atom.gyp',
|
||||
'-Icommon.gypi', '-Ivendor/brightray/brightray.gypi',
|
||||
|
||||
@@ -48,18 +48,20 @@ def main():
|
||||
release_id = create_or_get_release_draft(github, args.version)
|
||||
upload_atom_shell(github, release_id, os.path.join(DIST_DIR, DIST_NAME))
|
||||
upload_atom_shell(github, release_id, os.path.join(DIST_DIR, SYMBOLS_NAME))
|
||||
if args.publish_release:
|
||||
# Upload the SHASUMS.txt.
|
||||
execute([sys.executable,
|
||||
os.path.join(SOURCE_ROOT, 'script', 'upload-checksums.py')])
|
||||
|
||||
# Press the publish button.
|
||||
publish_release(github, release_id)
|
||||
|
||||
# Upload node's headers to S3.
|
||||
bucket, access_key, secret_key = s3_config()
|
||||
upload_node(bucket, access_key, secret_key, NODE_VERSION)
|
||||
|
||||
if args.publish_release:
|
||||
# Press the publish button.
|
||||
publish_release(github, release_id)
|
||||
|
||||
# Upload the SHASUMS.txt.
|
||||
execute([sys.executable,
|
||||
os.path.join(SOURCE_ROOT, 'script', 'upload-checksums.py'),
|
||||
'-v', NODE_VERSION])
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='upload distribution file')
|
||||
|
||||
4
spec/fixtures/module/create_socket.js
vendored
Normal file
4
spec/fixtures/module/create_socket.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
var net = require('net');
|
||||
var server = net.createServer(function() {});
|
||||
server.listen(process.argv[2]);
|
||||
process.exit(0);
|
||||
@@ -2,11 +2,13 @@ assert = require 'assert'
|
||||
child_process = require 'child_process'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
os = require 'os'
|
||||
remote = require 'remote'
|
||||
|
||||
describe 'node feature', ->
|
||||
describe 'child_process', ->
|
||||
fixtures = path.join __dirname, 'fixtures'
|
||||
fixtures = path.join __dirname, 'fixtures'
|
||||
|
||||
describe 'child_process', ->
|
||||
describe 'child_process.fork', ->
|
||||
it 'works in current process', (done) ->
|
||||
child = child_process.fork path.join(fixtures, 'module', 'ping.js')
|
||||
@@ -31,6 +33,14 @@ describe 'node feature', ->
|
||||
done()
|
||||
child.send 'message'
|
||||
|
||||
it 'works in browser process', (done) ->
|
||||
fork = remote.require('child_process').fork
|
||||
child = fork path.join(fixtures, 'module', 'ping.js')
|
||||
child.on 'message', (msg) ->
|
||||
assert.equal msg, 'message'
|
||||
done()
|
||||
child.send 'message'
|
||||
|
||||
describe 'contexts', ->
|
||||
describe 'setTimeout in fs callback', ->
|
||||
it 'does not crash', (done) ->
|
||||
@@ -55,6 +65,17 @@ describe 'node feature', ->
|
||||
fs.readFile __filename, ->
|
||||
throw error
|
||||
|
||||
describe 'setTimeout called under Chromium event loop in browser process', ->
|
||||
it 'can be scheduled in time', (done) ->
|
||||
remote.getGlobal('setTimeout')(done, 0)
|
||||
|
||||
describe 'setInterval called under Chromium event loop in browser process', ->
|
||||
it 'can be scheduled in time', (done) ->
|
||||
clear = ->
|
||||
remote.getGlobal('clearInterval')(interval)
|
||||
done()
|
||||
interval = remote.getGlobal('setInterval')(clear, 10)
|
||||
|
||||
describe 'message loop', ->
|
||||
describe 'process.nextTick', ->
|
||||
it 'emits the callback', (done) ->
|
||||
@@ -73,3 +94,16 @@ describe 'node feature', ->
|
||||
setImmediate ->
|
||||
setImmediate ->
|
||||
setImmediate done
|
||||
|
||||
describe 'net.connect', ->
|
||||
it 'emit error when connect to a socket path without listeners', (done) ->
|
||||
return done() if process.platform is 'win32'
|
||||
|
||||
socketPath = path.join os.tmpdir(), 'atom-shell-test.sock'
|
||||
script = path.join(fixtures, 'module', 'create_socket.js')
|
||||
child = child_process.fork script, [socketPath]
|
||||
child.on 'exit', ->
|
||||
client = require('net').connect socketPath
|
||||
client.on 'error', (error) ->
|
||||
assert.equal error.code, 'ECONNREFUSED'
|
||||
done()
|
||||
|
||||
@@ -67,7 +67,7 @@ def GenerateSymbols(options, binaries):
|
||||
print "Generating symbols for %s" % binary
|
||||
|
||||
syms = GetCommandOutput([DUMP_SYMS, binary])
|
||||
module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\r\n", syms)
|
||||
module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-Fa-f]+) (.*)\r\n", syms)
|
||||
if module_line == None:
|
||||
with print_lock:
|
||||
print "Failed to get symbols for %s" % binary
|
||||
|
||||
2
vendor/brightray
vendored
2
vendor/brightray
vendored
Submodule vendor/brightray updated: 13628c0d7f...b06cc7a19a
2
vendor/node
vendored
2
vendor/node
vendored
Submodule vendor/node updated: a3ec3c2552...6d772c3cda
Reference in New Issue
Block a user