commit
57639133a9
44 changed files with 2456 additions and 38 deletions
|
@ -20,9 +20,12 @@
|
|||
#include "common/crash_reporter/win/crash_service_main.h"
|
||||
#include "content/public/app/startup_helper_win.h"
|
||||
#include "sandbox/win/src/sandbox_types.h"
|
||||
#else // defined(OS_WIN)
|
||||
#elif defined(OS_LINUX) // defined(OS_WIN)
|
||||
#include "app/atom_main_delegate.h" // NOLINT
|
||||
#include "content/public/app/content_main.h"
|
||||
#else // defined(OS_LINUX)
|
||||
#include "app/atom_library_main.h"
|
||||
#endif // defined(OS_MACOSX) || defined(OS_LINUX)
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
// Declaration of node::Start.
|
||||
namespace node {
|
||||
|
@ -98,7 +101,18 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
|
|||
return content::ContentMain(instance, &sandbox_info, &delegate);
|
||||
}
|
||||
|
||||
#else // defined(OS_WIN)
|
||||
#elif defined(OS_LINUX) // defined(OS_WIN)
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
char* node_indicator = getenv("ATOM_SHELL_INTERNAL_RUN_AS_NODE");
|
||||
if (node_indicator != NULL && strcmp(node_indicator, "1") == 0)
|
||||
return node::Start(argc, const_cast<char**>(argv));
|
||||
|
||||
atom::AtomMainDelegate delegate;
|
||||
return content::ContentMain(argc, argv, &delegate);
|
||||
}
|
||||
|
||||
#else // defined(OS_LINUX)
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
char* node_indicator = getenv("ATOM_SHELL_INTERNAL_RUN_AS_NODE");
|
||||
|
@ -108,4 +122,4 @@ int main(int argc, const char* argv[]) {
|
|||
return AtomMain(argc, argv);
|
||||
}
|
||||
|
||||
#endif // defined(OS_MACOSX) || defined(OS_LINUX)
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
|
|
@ -60,8 +60,8 @@ void AtomMainDelegate::PreSandboxStartup() {
|
|||
std::string process_type = command_line->GetSwitchValueASCII(
|
||||
switches::kProcessType);
|
||||
|
||||
// Don't append arguments for renderer process.
|
||||
if (process_type == switches::kRendererProcess)
|
||||
// Only append arguments for browser process.
|
||||
if (!process_type.empty())
|
||||
return;
|
||||
|
||||
// Add a flag to mark the start of switches added by atom-shell.
|
||||
|
|
65
atom.gyp
65
atom.gyp
|
@ -51,6 +51,8 @@
|
|||
'browser/api/atom_api_event.h',
|
||||
'browser/api/atom_api_menu.cc',
|
||||
'browser/api/atom_api_menu.h',
|
||||
'browser/api/atom_api_menu_gtk.cc',
|
||||
'browser/api/atom_api_menu_gtk.h',
|
||||
'browser/api/atom_api_menu_mac.h',
|
||||
'browser/api/atom_api_menu_mac.mm',
|
||||
'browser/api/atom_api_menu_win.cc',
|
||||
|
@ -66,6 +68,7 @@
|
|||
'browser/auto_updater.cc',
|
||||
'browser/auto_updater.h',
|
||||
'browser/auto_updater_delegate.h',
|
||||
'browser/auto_updater_linux.cc',
|
||||
'browser/auto_updater_mac.mm',
|
||||
'browser/auto_updater_win.cc',
|
||||
'browser/atom_application_mac.h',
|
||||
|
@ -83,11 +86,14 @@
|
|||
'browser/atom_javascript_dialog_manager.h',
|
||||
'browser/browser.cc',
|
||||
'browser/browser.h',
|
||||
'browser/browser_linux.cc',
|
||||
'browser/browser_mac.mm',
|
||||
'browser/browser_win.cc',
|
||||
'browser/browser_observer.h',
|
||||
'browser/native_window.cc',
|
||||
'browser/native_window.h',
|
||||
'browser/native_window_gtk.cc',
|
||||
'browser/native_window_gtk.h',
|
||||
'browser/native_window_mac.h',
|
||||
'browser/native_window_mac.mm',
|
||||
'browser/native_window_win.cc',
|
||||
|
@ -103,6 +109,7 @@
|
|||
'browser/net/url_request_string_job.h',
|
||||
'browser/ui/accelerator_util.cc',
|
||||
'browser/ui/accelerator_util.h',
|
||||
'browser/ui/accelerator_util_gtk.cc',
|
||||
'browser/ui/accelerator_util_mac.mm',
|
||||
'browser/ui/accelerator_util_win.cc',
|
||||
'browser/ui/cocoa/atom_menu_controller.h',
|
||||
|
@ -112,9 +119,17 @@
|
|||
'browser/ui/cocoa/nsalert_synchronous_sheet.h',
|
||||
'browser/ui/cocoa/nsalert_synchronous_sheet.mm',
|
||||
'browser/ui/file_dialog.h',
|
||||
'browser/ui/file_dialog_gtk.cc',
|
||||
'browser/ui/file_dialog_mac.mm',
|
||||
'browser/ui/file_dialog_win.cc',
|
||||
'browser/ui/gtk/gtk_custom_menu.cc',
|
||||
'browser/ui/gtk/gtk_custom_menu.h',
|
||||
'browser/ui/gtk/gtk_custom_menu_item.cc',
|
||||
'browser/ui/gtk/gtk_custom_menu_item.h',
|
||||
'browser/ui/gtk/gtk_window_util.cc',
|
||||
'browser/ui/gtk/gtk_window_util.h',
|
||||
'browser/ui/message_box.h',
|
||||
'browser/ui/message_box_gtk.cc',
|
||||
'browser/ui/message_box_mac.mm',
|
||||
'browser/ui/message_box_win.cc',
|
||||
'browser/ui/win/menu_2.cc',
|
||||
|
@ -147,6 +162,8 @@
|
|||
'common/api/object_life_monitor.h',
|
||||
'common/crash_reporter/crash_reporter.cc',
|
||||
'common/crash_reporter/crash_reporter.h',
|
||||
'common/crash_reporter/crash_reporter_linux.cc',
|
||||
'common/crash_reporter/crash_reporter_linux.h',
|
||||
'common/crash_reporter/crash_reporter_mac.h',
|
||||
'common/crash_reporter/crash_reporter_mac.mm',
|
||||
'common/crash_reporter/crash_reporter_win.cc',
|
||||
|
@ -157,8 +174,11 @@
|
|||
'common/crash_reporter/win/crash_service_main.h',
|
||||
'common/draggable_region.cc',
|
||||
'common/draggable_region.h',
|
||||
'common/linux/application_info.cc',
|
||||
'common/node_bindings.cc',
|
||||
'common/node_bindings.h',
|
||||
'common/node_bindings_linux.cc',
|
||||
'common/node_bindings_linux.h',
|
||||
'common/node_bindings_mac.cc',
|
||||
'common/node_bindings_mac.h',
|
||||
'common/node_bindings_win.cc',
|
||||
|
@ -166,6 +186,7 @@
|
|||
'common/options_switches.cc',
|
||||
'common/options_switches.h',
|
||||
'common/platform_util.h',
|
||||
'common/platform_util_linux.cc',
|
||||
'common/platform_util_mac.mm',
|
||||
'common/platform_util_win.cc',
|
||||
'common/swap_or_assign.h',
|
||||
|
@ -209,9 +230,8 @@
|
|||
],
|
||||
'configurations': {
|
||||
'Debug': {
|
||||
'defines': [
|
||||
'DEBUG',
|
||||
],
|
||||
'defines': [ 'DEBUG', '_DEBUG' ],
|
||||
'cflags': [ '-g', '-O0' ],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -306,6 +326,24 @@
|
|||
},
|
||||
],
|
||||
}], # OS=="win"
|
||||
['OS=="linux"', {
|
||||
'copies': [
|
||||
{
|
||||
'destination': '<(PRODUCT_DIR)',
|
||||
'files': [
|
||||
'<(libchromiumcontent_library_dir)/libchromiumcontent.so',
|
||||
'<(libchromiumcontent_library_dir)/libffmpegsumo.so',
|
||||
'<(libchromiumcontent_resources_dir)/content_shell.pak',
|
||||
],
|
||||
},
|
||||
{
|
||||
'destination': '<(PRODUCT_DIR)/resources/browser',
|
||||
'files': [
|
||||
'browser/default_app',
|
||||
]
|
||||
},
|
||||
],
|
||||
}], # OS=="linux"
|
||||
],
|
||||
}, # target <(project_name)
|
||||
{
|
||||
|
@ -353,12 +391,24 @@
|
|||
'vendor/breakpad/breakpad.gyp:breakpad_handler',
|
||||
'vendor/breakpad/breakpad.gyp:breakpad_sender',
|
||||
],
|
||||
}],
|
||||
}], # OS=="win"
|
||||
['OS=="mac"', {
|
||||
'dependencies': [
|
||||
'vendor/breakpad/breakpad.gyp:breakpad',
|
||||
],
|
||||
}],
|
||||
}], # OS=="mac"
|
||||
['OS=="linux"', {
|
||||
'link_settings': {
|
||||
'ldflags': [
|
||||
# Make binary search for libraries under current directory, so we
|
||||
# don't have to manually set $LD_LIBRARY_PATH:
|
||||
# http://serverfault.com/questions/279068/cant-find-so-in-the-same-directory-as-the-executable
|
||||
'-rpath \$$ORIGIN',
|
||||
# Make native module dynamic loading work.
|
||||
'-rdynamic',
|
||||
],
|
||||
},
|
||||
}], # OS=="linux"
|
||||
],
|
||||
}, # target <(product_name)_lib
|
||||
{
|
||||
|
@ -385,8 +435,7 @@
|
|||
'<(RULE_INPUT_PATH)',
|
||||
'<(PRODUCT_DIR)/<(product_name).app/Contents/Resources/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).js',
|
||||
],
|
||||
}], # OS=="mac"
|
||||
['OS=="win"', {
|
||||
},{ # OS=="mac"
|
||||
'outputs': [
|
||||
'<(PRODUCT_DIR)/resources/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).js',
|
||||
],
|
||||
|
@ -396,7 +445,7 @@
|
|||
'<(RULE_INPUT_PATH)',
|
||||
'<(PRODUCT_DIR)/resources/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).js',
|
||||
],
|
||||
}], # OS=="win"
|
||||
}], # OS=="win" or OS=="linux"
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -340,7 +340,7 @@ void Menu::Initialize(v8::Handle<v8::Object> target) {
|
|||
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "popup", Popup);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#if defined(OS_WIN) || defined(TOOLKIT_GTK)
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "attachToWindow", AttachToWindow);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ class Menu : public EventEmitter,
|
|||
|
||||
static void Popup(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#if defined(OS_WIN) || defined(TOOLKIT_GTK)
|
||||
static void AttachToWindow(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
#elif defined(OS_MACOSX)
|
||||
static void SetApplicationMenu(
|
||||
|
|
32
browser/api/atom_api_menu_gtk.cc
Normal file
32
browser/api/atom_api_menu_gtk.cc
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/api/atom_api_menu_gtk.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
MenuGtk::MenuGtk(v8::Handle<v8::Object> wrapper)
|
||||
: Menu(wrapper) {
|
||||
}
|
||||
|
||||
MenuGtk::~MenuGtk() {
|
||||
}
|
||||
|
||||
void MenuGtk::Popup(NativeWindow* native_window) {
|
||||
}
|
||||
|
||||
// static
|
||||
void Menu::AttachToWindow(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
}
|
||||
|
||||
// static
|
||||
Menu* Menu::Create(v8::Handle<v8::Object> wrapper) {
|
||||
return new MenuGtk(wrapper);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
30
browser/api/atom_api_menu_gtk.h
Normal file
30
browser/api/atom_api_menu_gtk.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_API_ATOM_API_MENU_GTK_H_
|
||||
#define ATOM_BROWSER_API_ATOM_API_MENU_GTK_H_
|
||||
|
||||
#include "browser/api/atom_api_menu.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
class MenuGtk : public Menu {
|
||||
public:
|
||||
explicit MenuGtk(v8::Handle<v8::Object> wrapper);
|
||||
virtual ~MenuGtk();
|
||||
|
||||
protected:
|
||||
virtual void Popup(NativeWindow* window) OVERRIDE;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(MenuGtk);
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_API_ATOM_API_MENU_GTK_H_
|
|
@ -368,6 +368,12 @@ void Protocol::Initialize(v8::Handle<v8::Object> target) {
|
|||
// Remember the protocol object, used for emitting event later.
|
||||
g_protocol_object.reset(target);
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
// Make sure the job factory has been created.
|
||||
AtomBrowserContext::Get()->url_request_context_getter()->
|
||||
GetURLRequestContext();
|
||||
#endif
|
||||
|
||||
NODE_SET_METHOD(target, "registerProtocol", RegisterProtocol);
|
||||
NODE_SET_METHOD(target, "unregisterProtocol", UnregisterProtocol);
|
||||
NODE_SET_METHOD(target, "isHandledProtocol", IsHandledProtocol);
|
||||
|
|
|
@ -34,7 +34,8 @@ BrowserWindow::restart = ->
|
|||
@loadUrl(@getUrl())
|
||||
|
||||
BrowserWindow::setMenu = (menu) ->
|
||||
throw new Error('BrowserWindow.setMenu is only available on Windows') unless process.platform is 'win32'
|
||||
if process.platform is 'darwin'
|
||||
throw new Error('BrowserWindow.setMenu is not available on OS X')
|
||||
|
||||
throw new TypeError('Invalid menu') unless menu?.constructor?.name is 'Menu'
|
||||
|
||||
|
|
17
browser/auto_updater_linux.cc
Normal file
17
browser/auto_updater_linux.cc
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/auto_updater.h"
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetFeedURL(const std::string& url) {
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdates() {
|
||||
}
|
||||
|
||||
} // namespace auto_updater
|
44
browser/browser_linux.cc
Normal file
44
browser/browser_linux.cc
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/browser.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "browser/native_window.h"
|
||||
#include "browser/window_list.h"
|
||||
#include "common/atom_version.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
void Browser::Terminate() {
|
||||
is_quiting_ = true;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void Browser::Focus() {
|
||||
// Focus on the first visible window.
|
||||
WindowList* list = WindowList::GetInstance();
|
||||
for (WindowList::iterator iter = list->begin(); iter != list->end(); ++iter) {
|
||||
NativeWindow* window = *iter;
|
||||
if (window->IsVisible()) {
|
||||
window->Focus(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Browser::GetExecutableFileVersion() const {
|
||||
return ATOM_VERSION_STRING;
|
||||
}
|
||||
|
||||
std::string Browser::GetExecutableFileProductName() const {
|
||||
return "Atom-Shell";
|
||||
}
|
||||
|
||||
void Browser::CancelQuit() {
|
||||
// No way to cancel quit on Linux.
|
||||
}
|
||||
|
||||
} // namespace atom
|
429
browser/native_window_gtk.cc
Normal file
429
browser/native_window_gtk.cc
Normal file
|
@ -0,0 +1,429 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/native_window_gtk.h"
|
||||
|
||||
#include "base/values.h"
|
||||
#include "browser/ui/gtk/gtk_window_util.h"
|
||||
#include "common/draggable_region.h"
|
||||
#include "common/options_switches.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_view.h"
|
||||
#include "content/public/common/renderer_preferences.h"
|
||||
#include "ui/base/x/x11_util.h"
|
||||
#include "ui/gfx/gtk_util.h"
|
||||
#include "ui/gfx/rect.h"
|
||||
#include "ui/gfx/skia_utils_gtk.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// Dividing GTK's cursor blink cycle time (in milliseconds) by this value yields
|
||||
// an appropriate value for content::RendererPreferences::caret_blink_interval.
|
||||
// This matches the logic in the WebKit GTK port.
|
||||
const double kGtkCursorBlinkCycleFactor = 2000.0;
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeWindowGtk::NativeWindowGtk(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options)
|
||||
: NativeWindow(web_contents, options),
|
||||
window_(GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL))),
|
||||
state_(GDK_WINDOW_STATE_WITHDRAWN),
|
||||
is_always_on_top_(false) {
|
||||
gtk_container_add(GTK_CONTAINER(window_),
|
||||
GetWebContents()->GetView()->GetNativeView());
|
||||
|
||||
int width = 800, height = 600;
|
||||
options->GetInteger(switches::kWidth, &width);
|
||||
options->GetInteger(switches::kHeight, &height);
|
||||
gtk_window_set_default_size(window_, width, height);
|
||||
|
||||
if (!icon_.IsEmpty())
|
||||
gtk_window_set_icon(window_, icon_.ToGdkPixbuf());
|
||||
|
||||
// In some (older) versions of compiz, raising top-level windows when they
|
||||
// are partially off-screen causes them to get snapped back on screen, not
|
||||
// always even on the current virtual desktop. If we are running under
|
||||
// compiz, suppress such raises, as they are not necessary in compiz anyway.
|
||||
if (ui::GuessWindowManager() == ui::WM_COMPIZ)
|
||||
suppress_window_raise_ = true;
|
||||
|
||||
g_signal_connect(window_, "delete-event",
|
||||
G_CALLBACK(OnWindowDeleteEventThunk), this);
|
||||
g_signal_connect(window_, "focus-out-event",
|
||||
G_CALLBACK(OnFocusOutThunk), this);
|
||||
|
||||
if (!has_frame_) {
|
||||
gtk_window_set_decorated(window_, false);
|
||||
|
||||
g_signal_connect(window_, "motion-notify-event",
|
||||
G_CALLBACK(OnMouseMoveEventThunk), this);
|
||||
g_signal_connect(window_, "button-press-event",
|
||||
G_CALLBACK(OnButtonPressThunk), this);
|
||||
}
|
||||
|
||||
SetWebKitColorStyle();
|
||||
}
|
||||
|
||||
NativeWindowGtk::~NativeWindowGtk() {
|
||||
if (window_)
|
||||
gtk_widget_destroy(GTK_WIDGET(window_));
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Close() {
|
||||
CloseWebContents();
|
||||
}
|
||||
|
||||
void NativeWindowGtk::CloseImmediately() {
|
||||
gtk_widget_destroy(GTK_WIDGET(window_));
|
||||
window_ = NULL;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Move(const gfx::Rect& pos) {
|
||||
gtk_window_move(window_, pos.x(), pos.y());
|
||||
gtk_window_resize(window_, pos.width(), pos.height());
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Focus(bool focus) {
|
||||
if (!IsVisible())
|
||||
return;
|
||||
|
||||
if (focus)
|
||||
gtk_window_present(window_);
|
||||
else
|
||||
gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_)));
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsFocused() {
|
||||
return gtk_window_is_active(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Show() {
|
||||
gtk_widget_show_all(GTK_WIDGET(window_));
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Hide() {
|
||||
gtk_widget_hide(GTK_WIDGET(window_));
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsVisible() {
|
||||
return gtk_widget_get_visible(GTK_WIDGET(window_));
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Maximize() {
|
||||
gtk_window_maximize(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Unmaximize() {
|
||||
gtk_window_unmaximize(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Minimize() {
|
||||
gtk_window_iconify(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Restore() {
|
||||
gtk_window_present(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetFullscreen(bool fullscreen) {
|
||||
if (fullscreen)
|
||||
gtk_window_fullscreen(window_);
|
||||
else
|
||||
gtk_window_unfullscreen(window_);
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsFullscreen() {
|
||||
return state_ & GDK_WINDOW_STATE_FULLSCREEN;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetSize(const gfx::Size& size) {
|
||||
gtk_window_resize(window_, size.width(), size.height());
|
||||
}
|
||||
|
||||
gfx::Size NativeWindowGtk::GetSize() {
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
|
||||
|
||||
GdkRectangle frame_extents;
|
||||
gdk_window_get_frame_extents(gdk_window, &frame_extents);
|
||||
|
||||
return gfx::Size(frame_extents.width, frame_extents.height);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetMinimumSize(const gfx::Size& size) {
|
||||
minimum_size_ = size;
|
||||
|
||||
GdkGeometry geometry = { 0 };
|
||||
geometry.min_width = size.width();
|
||||
geometry.min_height = size.height();
|
||||
int hints = GDK_HINT_POS | GDK_HINT_MIN_SIZE;
|
||||
gtk_window_set_geometry_hints(
|
||||
window_, GTK_WIDGET(window_), &geometry, (GdkWindowHints)hints);
|
||||
}
|
||||
|
||||
gfx::Size NativeWindowGtk::GetMinimumSize() {
|
||||
return minimum_size_;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetMaximumSize(const gfx::Size& size) {
|
||||
maximum_size_ = size;
|
||||
|
||||
GdkGeometry geometry = { 0 };
|
||||
geometry.max_width = size.width();
|
||||
geometry.max_height = size.height();
|
||||
int hints = GDK_HINT_POS | GDK_HINT_MAX_SIZE;
|
||||
gtk_window_set_geometry_hints(
|
||||
window_, GTK_WIDGET(window_), &geometry, (GdkWindowHints)hints);
|
||||
}
|
||||
|
||||
gfx::Size NativeWindowGtk::GetMaximumSize() {
|
||||
return maximum_size_;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetResizable(bool resizable) {
|
||||
// Should request widget size after setting unresizable, otherwise the
|
||||
// window will shrink to a very small size.
|
||||
if (!IsResizable()) {
|
||||
gint width, height;
|
||||
gtk_window_get_size(window_, &width, &height);
|
||||
gtk_widget_set_size_request(GTK_WIDGET(window_), width, height);
|
||||
}
|
||||
|
||||
gtk_window_set_resizable(window_, resizable);
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsResizable() {
|
||||
return gtk_window_get_resizable(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetAlwaysOnTop(bool top) {
|
||||
is_always_on_top_ = top;
|
||||
gtk_window_set_keep_above(window_, top ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsAlwaysOnTop() {
|
||||
return is_always_on_top_;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Center() {
|
||||
gtk_window_set_position(window_, GTK_WIN_POS_CENTER);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetPosition(const gfx::Point& position) {
|
||||
gtk_window_move(window_, position.x(), position.y());
|
||||
}
|
||||
|
||||
gfx::Point NativeWindowGtk::GetPosition() {
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
|
||||
|
||||
GdkRectangle frame_extents;
|
||||
gdk_window_get_frame_extents(gdk_window, &frame_extents);
|
||||
|
||||
return gfx::Point(frame_extents.x, frame_extents.y);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetTitle(const std::string& title) {
|
||||
gtk_window_set_title(window_, title.c_str());
|
||||
}
|
||||
|
||||
std::string NativeWindowGtk::GetTitle() {
|
||||
return gtk_window_get_title(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::FlashFrame(bool flash) {
|
||||
gtk_window_set_urgency_hint(window_, flash);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetKiosk(bool kiosk) {
|
||||
SetFullscreen(kiosk);
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsKiosk() {
|
||||
return IsFullscreen();
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::HasModalDialog() {
|
||||
// FIXME(zcbenz): Implement me.
|
||||
return false;
|
||||
}
|
||||
|
||||
gfx::NativeWindow NativeWindowGtk::GetNativeWindow() {
|
||||
return window_;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::UpdateDraggableRegions(
|
||||
const std::vector<DraggableRegion>& regions) {
|
||||
// Draggable region is not supported for non-frameless window.
|
||||
if (has_frame_)
|
||||
return;
|
||||
|
||||
SkRegion draggable_region;
|
||||
|
||||
// By default, the whole window is non-draggable. We need to explicitly
|
||||
// include those draggable regions.
|
||||
for (std::vector<DraggableRegion>::const_iterator iter =
|
||||
regions.begin();
|
||||
iter != regions.end(); ++iter) {
|
||||
const DraggableRegion& region = *iter;
|
||||
draggable_region.op(
|
||||
region.bounds.x(),
|
||||
region.bounds.y(),
|
||||
region.bounds.right(),
|
||||
region.bounds.bottom(),
|
||||
region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op);
|
||||
}
|
||||
|
||||
draggable_region_ = draggable_region;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetWebKitColorStyle() {
|
||||
content::RendererPreferences* prefs =
|
||||
GetWebContents()->GetMutableRendererPrefs();
|
||||
GtkStyle* frame_style = gtk_rc_get_style(GTK_WIDGET(window_));
|
||||
prefs->focus_ring_color =
|
||||
gfx::GdkColorToSkColor(frame_style->bg[GTK_STATE_SELECTED]);
|
||||
prefs->thumb_active_color = SkColorSetRGB(244, 244, 244);
|
||||
prefs->thumb_inactive_color = SkColorSetRGB(234, 234, 234);
|
||||
prefs->track_color = SkColorSetRGB(211, 211, 211);
|
||||
|
||||
GtkWidget* url_entry = gtk_entry_new();
|
||||
GtkStyle* entry_style = gtk_rc_get_style(url_entry);
|
||||
prefs->active_selection_bg_color =
|
||||
gfx::GdkColorToSkColor(entry_style->base[GTK_STATE_SELECTED]);
|
||||
prefs->active_selection_fg_color =
|
||||
gfx::GdkColorToSkColor(entry_style->text[GTK_STATE_SELECTED]);
|
||||
prefs->inactive_selection_bg_color =
|
||||
gfx::GdkColorToSkColor(entry_style->base[GTK_STATE_ACTIVE]);
|
||||
prefs->inactive_selection_fg_color =
|
||||
gfx::GdkColorToSkColor(entry_style->text[GTK_STATE_ACTIVE]);
|
||||
gtk_widget_destroy(url_entry);
|
||||
|
||||
const base::TimeDelta cursor_blink_time = gfx::GetCursorBlinkCycle();
|
||||
prefs->caret_blink_interval =
|
||||
cursor_blink_time.InMilliseconds() ?
|
||||
cursor_blink_time.InMilliseconds() / kGtkCursorBlinkCycleFactor :
|
||||
0;
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsMaximized() const {
|
||||
return state_ & GDK_WINDOW_STATE_MAXIMIZED;
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) {
|
||||
if (has_frame_)
|
||||
return false;
|
||||
|
||||
if (IsMaximized() || IsFullscreen())
|
||||
return false;
|
||||
|
||||
return gtk_window_util::GetWindowEdge(GetSize(), 0, x, y, edge);
|
||||
}
|
||||
|
||||
gboolean NativeWindowGtk::OnWindowDeleteEvent(GtkWidget* widget,
|
||||
GdkEvent* event) {
|
||||
Close();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean NativeWindowGtk::OnFocusOut(GtkWidget* window, GdkEventFocus*) {
|
||||
NotifyWindowBlur();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean NativeWindowGtk::OnWindowState(GtkWidget* window,
|
||||
GdkEventWindowState* event) {
|
||||
state_ = event->new_window_state;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean NativeWindowGtk::OnMouseMoveEvent(GtkWidget* widget,
|
||||
GdkEventMotion* event) {
|
||||
if (!IsResizable())
|
||||
return FALSE;
|
||||
|
||||
int win_x, win_y;
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
|
||||
gdk_window_get_origin(gdk_window, &win_x, &win_y);
|
||||
gfx::Point point(static_cast<int>(event->x_root - win_x),
|
||||
static_cast<int>(event->y_root - win_y));
|
||||
|
||||
// Update the cursor if we're on the custom frame border.
|
||||
GdkWindowEdge edge;
|
||||
bool has_hit_edge = GetWindowEdge(point.x(), point.y(), &edge);
|
||||
GdkCursorType new_cursor = GDK_LAST_CURSOR;
|
||||
if (has_hit_edge)
|
||||
new_cursor = gtk_window_util::GdkWindowEdgeToGdkCursorType(edge);
|
||||
|
||||
GdkCursorType last_cursor = GDK_LAST_CURSOR;
|
||||
if (frame_cursor_)
|
||||
last_cursor = frame_cursor_->type;
|
||||
|
||||
if (last_cursor != new_cursor) {
|
||||
frame_cursor_ = has_hit_edge ? gfx::GetCursor(new_cursor) : NULL;
|
||||
gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)),
|
||||
frame_cursor_);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean NativeWindowGtk::OnButtonPress(GtkWidget* widget,
|
||||
GdkEventButton* event) {
|
||||
// Make the button press coordinate relative to the browser window.
|
||||
int win_x, win_y;
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
|
||||
gdk_window_get_origin(gdk_window, &win_x, &win_y);
|
||||
|
||||
bool resizable = IsResizable();
|
||||
GdkWindowEdge edge;
|
||||
gfx::Point point(static_cast<int>(event->x_root - win_x),
|
||||
static_cast<int>(event->y_root - win_y));
|
||||
bool has_hit_edge = resizable && GetWindowEdge(point.x(), point.y(), &edge);
|
||||
bool has_hit_titlebar = !draggable_region_.isEmpty() &&
|
||||
draggable_region_.contains(event->x, event->y);
|
||||
|
||||
if (event->button == 1) {
|
||||
if (GDK_BUTTON_PRESS == event->type) {
|
||||
// Raise the window after a click on either the titlebar or the border to
|
||||
// match the behavior of most window managers, unless that behavior has
|
||||
// been suppressed.
|
||||
if ((has_hit_titlebar || has_hit_edge) && !suppress_window_raise_)
|
||||
gdk_window_raise(GTK_WIDGET(widget)->window);
|
||||
|
||||
if (has_hit_edge) {
|
||||
gtk_window_begin_resize_drag(window_, edge, event->button,
|
||||
static_cast<gint>(event->x_root),
|
||||
static_cast<gint>(event->y_root),
|
||||
event->time);
|
||||
return TRUE;
|
||||
} else if (has_hit_titlebar) {
|
||||
return gtk_window_util::HandleTitleBarLeftMousePress(
|
||||
window_, gfx::Rect(GetPosition(), GetSize()), event);
|
||||
}
|
||||
} else if (GDK_2BUTTON_PRESS == event->type) {
|
||||
if (has_hit_titlebar && resizable) {
|
||||
// Maximize/restore on double click.
|
||||
if (IsMaximized())
|
||||
gtk_window_unmaximize(window_);
|
||||
else
|
||||
gtk_window_maximize(window_);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
} else if (event->button == 2) {
|
||||
if (has_hit_titlebar || has_hit_edge)
|
||||
gdk_window_lower(gdk_window);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// static
|
||||
NativeWindow* NativeWindow::Create(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options) {
|
||||
return new NativeWindowGtk(web_contents, options);
|
||||
}
|
||||
|
||||
} // namespace atom
|
109
browser/native_window_gtk.h
Normal file
109
browser/native_window_gtk.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NATIVE_WINDOW_GTK_H_
|
||||
#define ATOM_BROWSER_NATIVE_WINDOW_GTK_H_
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "browser/native_window.h"
|
||||
#include "third_party/skia/include/core/SkRegion.h"
|
||||
#include "ui/base/gtk/gtk_signal.h"
|
||||
#include "ui/gfx/size.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NativeWindowGtk : public NativeWindow {
|
||||
public:
|
||||
explicit NativeWindowGtk(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options);
|
||||
virtual ~NativeWindowGtk();
|
||||
|
||||
// NativeWindow implementation.
|
||||
virtual void Close() OVERRIDE;
|
||||
virtual void CloseImmediately() OVERRIDE;
|
||||
virtual void Move(const gfx::Rect& pos) OVERRIDE;
|
||||
virtual void Focus(bool focus) OVERRIDE;
|
||||
virtual bool IsFocused() OVERRIDE;
|
||||
virtual void Show() OVERRIDE;
|
||||
virtual void Hide() OVERRIDE;
|
||||
virtual bool IsVisible() OVERRIDE;
|
||||
virtual void Maximize() OVERRIDE;
|
||||
virtual void Unmaximize() OVERRIDE;
|
||||
virtual void Minimize() OVERRIDE;
|
||||
virtual void Restore() OVERRIDE;
|
||||
virtual void SetFullscreen(bool fullscreen) OVERRIDE;
|
||||
virtual bool IsFullscreen() OVERRIDE;
|
||||
virtual void SetSize(const gfx::Size& size) OVERRIDE;
|
||||
virtual gfx::Size GetSize() OVERRIDE;
|
||||
virtual void SetMinimumSize(const gfx::Size& size) OVERRIDE;
|
||||
virtual gfx::Size GetMinimumSize() OVERRIDE;
|
||||
virtual void SetMaximumSize(const gfx::Size& size) OVERRIDE;
|
||||
virtual gfx::Size GetMaximumSize() OVERRIDE;
|
||||
virtual void SetResizable(bool resizable) OVERRIDE;
|
||||
virtual bool IsResizable() OVERRIDE;
|
||||
virtual void SetAlwaysOnTop(bool top) OVERRIDE;
|
||||
virtual bool IsAlwaysOnTop() OVERRIDE;
|
||||
virtual void Center() OVERRIDE;
|
||||
virtual void SetPosition(const gfx::Point& position) OVERRIDE;
|
||||
virtual gfx::Point GetPosition() OVERRIDE;
|
||||
virtual void SetTitle(const std::string& title) OVERRIDE;
|
||||
virtual std::string GetTitle() OVERRIDE;
|
||||
virtual void FlashFrame(bool flash) OVERRIDE;
|
||||
virtual void SetKiosk(bool kiosk) OVERRIDE;
|
||||
virtual bool IsKiosk() OVERRIDE;
|
||||
virtual bool HasModalDialog() OVERRIDE;
|
||||
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual void UpdateDraggableRegions(
|
||||
const std::vector<DraggableRegion>& regions) OVERRIDE;
|
||||
|
||||
private:
|
||||
// Set WebKit's style from current theme.
|
||||
void SetWebKitColorStyle();
|
||||
|
||||
// Whether window is maximized.
|
||||
bool IsMaximized() const;
|
||||
|
||||
// If the point (|x|, |y|) is within the resize border area of the window,
|
||||
// returns true and sets |edge| to the appropriate GdkWindowEdge value.
|
||||
// Otherwise, returns false.
|
||||
bool GetWindowEdge(int x, int y, GdkWindowEdge* edge);
|
||||
|
||||
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnWindowDeleteEvent,
|
||||
GdkEvent*);
|
||||
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnFocusOut, GdkEventFocus*);
|
||||
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnWindowState,
|
||||
GdkEventWindowState*);
|
||||
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnMouseMoveEvent,
|
||||
GdkEventMotion*);
|
||||
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnButtonPress,
|
||||
GdkEventButton*);
|
||||
|
||||
GtkWindow* window_;
|
||||
|
||||
GdkWindowState state_;
|
||||
bool is_always_on_top_;
|
||||
gfx::Size minimum_size_;
|
||||
gfx::Size maximum_size_;
|
||||
|
||||
// The region is treated as title bar, can be dragged to move
|
||||
// and double clicked to maximize.
|
||||
SkRegion draggable_region_;
|
||||
|
||||
// If true, don't call gdk_window_raise() when we get a click in the title
|
||||
// bar or window border. This is to work around a compiz bug.
|
||||
bool suppress_window_raise_;
|
||||
|
||||
// The current window cursor. We set it to a resize cursor when over the
|
||||
// custom frame border. We set it to NULL if we want the default cursor.
|
||||
GdkCursor* frame_cursor_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NativeWindowGtk);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NATIVE_WINDOW_GTK_H_
|
|
@ -5,9 +5,8 @@
|
|||
#ifndef ATOM_BROWSER_NATIVE_WINDOW_WIN_H_
|
||||
#define ATOM_BROWSER_NATIVE_WINDOW_WIN_H_
|
||||
|
||||
#include "base/strings/string16.h"
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "browser/native_window.h"
|
||||
#include "ui/gfx/size.h"
|
||||
#include "ui/views/widget/widget_delegate.h"
|
||||
|
|
|
@ -66,6 +66,7 @@ AtomURLRequestContextGetter::~AtomURLRequestContextGetter() {
|
|||
net::URLRequestContext* AtomURLRequestContextGetter::GetURLRequestContext() {
|
||||
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
||||
|
||||
base::AutoLock auto_lock(lock_);
|
||||
if (!url_request_context_.get()) {
|
||||
url_request_context_.reset(new net::URLRequestContext());
|
||||
network_delegate_ = network_delegate_factory_.Run().Pass();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "base/callback.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "content/public/browser/content_browser_client.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
|
||||
|
@ -59,6 +60,8 @@ class AtomURLRequestContextGetter : public net::URLRequestContextGetter {
|
|||
base::Callback<scoped_ptr<brightray::NetworkDelegate>(void)>
|
||||
network_delegate_factory_;
|
||||
|
||||
base::Lock lock_;
|
||||
|
||||
scoped_ptr<net::ProxyConfigService> proxy_config_service_;
|
||||
scoped_ptr<brightray::NetworkDelegate> network_delegate_;
|
||||
scoped_ptr<net::URLRequestContextStorage> storage_;
|
||||
|
|
20
browser/ui/accelerator_util_gtk.cc
Normal file
20
browser/ui/accelerator_util_gtk.cc
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/ui/accelerator_util.h"
|
||||
|
||||
#include "ui/base/accelerators/accelerator.h"
|
||||
#include "ui/base/accelerators/platform_accelerator_gtk.h"
|
||||
|
||||
namespace accelerator_util {
|
||||
|
||||
void SetPlatformAccelerator(ui::Accelerator* accelerator) {
|
||||
scoped_ptr<ui::PlatformAccelerator> platform_accelerator(
|
||||
new ui::PlatformAcceleratorGtk(
|
||||
GetGdkKeyCodeForAccelerator(*accelerator),
|
||||
GetGdkModifierForAccelerator(*accelerator)));
|
||||
accelerator->set_platform_accelerator(platform_accelerator.Pass());
|
||||
}
|
||||
|
||||
} // namespace accelerator_util
|
41
browser/ui/file_dialog_gtk.cc
Normal file
41
browser/ui/file_dialog_gtk.cc
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/ui/file_dialog.h"
|
||||
|
||||
#include "base/callback.h"
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
bool ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
int properties,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
int properties,
|
||||
const OpenDialogCallback& callback) {
|
||||
callback.Run(false, std::vector<base::FilePath>());
|
||||
}
|
||||
|
||||
bool ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
base::FilePath* path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const SaveDialogCallback& callback) {
|
||||
callback.Run(false, base::FilePath());
|
||||
}
|
||||
|
||||
} // namespace file_dialog
|
150
browser/ui/gtk/gtk_custom_menu.cc
Normal file
150
browser/ui/gtk/gtk_custom_menu.cc
Normal file
|
@ -0,0 +1,150 @@
|
|||
// Copyright (c) 2012 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 "browser/ui/gtk/gtk_custom_menu.h"
|
||||
|
||||
#include "browser/ui/gtk/gtk_custom_menu_item.h"
|
||||
|
||||
G_DEFINE_TYPE(GtkCustomMenu, gtk_custom_menu, GTK_TYPE_MENU)
|
||||
|
||||
// Stolen directly from gtkmenushell.c. I'd love to call the library version
|
||||
// instead, but it's static and isn't exported. :(
|
||||
static gint gtk_menu_shell_is_item(GtkMenuShell* menu_shell,
|
||||
GtkWidget* child) {
|
||||
GtkWidget *parent;
|
||||
|
||||
g_return_val_if_fail(GTK_IS_MENU_SHELL(menu_shell), FALSE);
|
||||
g_return_val_if_fail(child != NULL, FALSE);
|
||||
|
||||
parent = gtk_widget_get_parent(child);
|
||||
while (GTK_IS_MENU_SHELL(parent)) {
|
||||
if (parent == reinterpret_cast<GtkWidget*>(menu_shell))
|
||||
return TRUE;
|
||||
parent = GTK_MENU_SHELL(parent)->parent_menu_shell;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Stolen directly from gtkmenushell.c. I'd love to call the library version
|
||||
// instead, but it's static and isn't exported. :(
|
||||
static GtkWidget* gtk_menu_shell_get_item(GtkMenuShell* menu_shell,
|
||||
GdkEvent* event) {
|
||||
GtkWidget* menu_item = gtk_get_event_widget(event);
|
||||
|
||||
while (menu_item && !GTK_IS_MENU_ITEM(menu_item))
|
||||
menu_item = gtk_widget_get_parent(menu_item);
|
||||
|
||||
if (menu_item && gtk_menu_shell_is_item(menu_shell, menu_item))
|
||||
return menu_item;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// When processing a button event, abort processing if the cursor isn't in a
|
||||
// clickable region.
|
||||
static gboolean gtk_custom_menu_button_press(GtkWidget* widget,
|
||||
GdkEventButton* event) {
|
||||
GtkWidget* menu_item = gtk_menu_shell_get_item(
|
||||
GTK_MENU_SHELL(widget), reinterpret_cast<GdkEvent*>(event));
|
||||
if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
|
||||
if (!gtk_custom_menu_item_is_in_clickable_region(
|
||||
GTK_CUSTOM_MENU_ITEM(menu_item))) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return GTK_WIDGET_CLASS(gtk_custom_menu_parent_class)->
|
||||
button_press_event(widget, event);
|
||||
}
|
||||
|
||||
// When processing a button event, abort processing if the cursor isn't in a
|
||||
// clickable region. If it's in a button that doesn't dismiss the menu, fire
|
||||
// that event and abort having the normal GtkMenu code run.
|
||||
static gboolean gtk_custom_menu_button_release(GtkWidget* widget,
|
||||
GdkEventButton* event) {
|
||||
GtkWidget* menu_item = gtk_menu_shell_get_item(
|
||||
GTK_MENU_SHELL(widget), reinterpret_cast<GdkEvent*>(event));
|
||||
if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
|
||||
if (!gtk_custom_menu_item_is_in_clickable_region(
|
||||
GTK_CUSTOM_MENU_ITEM(menu_item))) {
|
||||
// Stop processing this event. This isn't a clickable region.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (gtk_custom_menu_item_try_no_dismiss_command(
|
||||
GTK_CUSTOM_MENU_ITEM(menu_item))) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return GTK_WIDGET_CLASS(gtk_custom_menu_parent_class)->
|
||||
button_release_event(widget, event);
|
||||
}
|
||||
|
||||
// Manually forward button press events to the menu item (and then do what we'd
|
||||
// do normally).
|
||||
static gboolean gtk_custom_menu_motion_notify(GtkWidget* widget,
|
||||
GdkEventMotion* event) {
|
||||
GtkWidget* menu_item = gtk_menu_shell_get_item(
|
||||
GTK_MENU_SHELL(widget), (GdkEvent*)event);
|
||||
if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
|
||||
gtk_custom_menu_item_receive_motion_event(GTK_CUSTOM_MENU_ITEM(menu_item),
|
||||
event->x, event->y);
|
||||
}
|
||||
|
||||
return GTK_WIDGET_CLASS(gtk_custom_menu_parent_class)->
|
||||
motion_notify_event(widget, event);
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_move_current(GtkMenuShell* menu_shell,
|
||||
GtkMenuDirectionType direction) {
|
||||
// If the currently selected item is custom, we give it first chance to catch
|
||||
// up/down events.
|
||||
|
||||
// TODO(erg): We are breaking a GSEAL by directly accessing this. We'll need
|
||||
// to fix this by the time gtk3 comes out.
|
||||
GtkWidget* menu_item = GTK_MENU_SHELL(menu_shell)->active_menu_item;
|
||||
if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
|
||||
switch (direction) {
|
||||
case GTK_MENU_DIR_PREV:
|
||||
case GTK_MENU_DIR_NEXT:
|
||||
if (gtk_custom_menu_item_handle_move(GTK_CUSTOM_MENU_ITEM(menu_item),
|
||||
direction))
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GTK_MENU_SHELL_CLASS(gtk_custom_menu_parent_class)->
|
||||
move_current(menu_shell, direction);
|
||||
|
||||
// In the case of hitting PREV and transitioning to a custom menu, we want to
|
||||
// make sure we're selecting the final item in the list, not the first one.
|
||||
menu_item = GTK_MENU_SHELL(menu_shell)->active_menu_item;
|
||||
if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
|
||||
gtk_custom_menu_item_select_item_by_direction(
|
||||
GTK_CUSTOM_MENU_ITEM(menu_item), direction);
|
||||
}
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_init(GtkCustomMenu* menu) {
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_class_init(GtkCustomMenuClass* klass) {
|
||||
GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
|
||||
GtkMenuShellClass* menu_shell_class = GTK_MENU_SHELL_CLASS(klass);
|
||||
|
||||
widget_class->button_press_event = gtk_custom_menu_button_press;
|
||||
widget_class->button_release_event = gtk_custom_menu_button_release;
|
||||
widget_class->motion_notify_event = gtk_custom_menu_motion_notify;
|
||||
|
||||
menu_shell_class->move_current = gtk_custom_menu_move_current;
|
||||
}
|
||||
|
||||
GtkWidget* gtk_custom_menu_new() {
|
||||
return GTK_WIDGET(g_object_new(GTK_TYPE_CUSTOM_MENU, NULL));
|
||||
}
|
51
browser/ui/gtk/gtk_custom_menu.h
Normal file
51
browser/ui/gtk/gtk_custom_menu.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) 2011 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_UI_GTK_GTK_CUSTOM_MENU_H_
|
||||
#define CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_H_
|
||||
|
||||
// GtkCustomMenu is a GtkMenu subclass that can contain, and collaborates with,
|
||||
// GtkCustomMenuItem instances. GtkCustomMenuItem is a GtkMenuItem that can
|
||||
// have buttons and other normal widgets embeded in it. GtkCustomMenu exists
|
||||
// only to override most of the button/motion/move callback functions so
|
||||
// that the normal GtkMenu implementation doesn't handle events related to
|
||||
// GtkCustomMenuItem items.
|
||||
//
|
||||
// For a more through overview of this system, see the comments in
|
||||
// gtk_custom_menu_item.h.
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CUSTOM_MENU \
|
||||
(gtk_custom_menu_get_type())
|
||||
#define GTK_CUSTOM_MENU(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CUSTOM_MENU, GtkCustomMenu))
|
||||
#define GTK_CUSTOM_MENU_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CUSTOM_MENU, GtkCustomMenuClass))
|
||||
#define GTK_IS_CUSTOM_MENU(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CUSTOM_MENU))
|
||||
#define GTK_IS_CUSTOM_MENU_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CUSTOM_MENU))
|
||||
#define GTK_CUSTOM_MENU_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CUSTOM_MENU, GtkCustomMenuClass))
|
||||
|
||||
typedef struct _GtkCustomMenu GtkCustomMenu;
|
||||
typedef struct _GtkCustomMenuClass GtkCustomMenuClass;
|
||||
|
||||
struct _GtkCustomMenu {
|
||||
GtkMenu menu;
|
||||
};
|
||||
|
||||
struct _GtkCustomMenuClass {
|
||||
GtkMenuClass parent_class;
|
||||
};
|
||||
|
||||
GType gtk_custom_menu_get_type(void) G_GNUC_CONST;
|
||||
GtkWidget* gtk_custom_menu_new();
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif // CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_H_
|
493
browser/ui/gtk/gtk_custom_menu_item.cc
Normal file
493
browser/ui/gtk/gtk_custom_menu_item.cc
Normal file
|
@ -0,0 +1,493 @@
|
|||
// Copyright (c) 2012 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 "browser/ui/gtk/gtk_custom_menu_item.h"
|
||||
|
||||
#include "base/i18n/rtl.h"
|
||||
#include "browser/ui/gtk/gtk_custom_menu.h"
|
||||
#include "ui/gfx/gtk_compat.h"
|
||||
|
||||
// This method was autogenerated by the program glib-genmarshall, which
|
||||
// generated it from the line "BOOL:INT". Two different attempts at getting gyp
|
||||
// to autogenerate this didn't work. If we need more non-standard marshallers,
|
||||
// this should be deleted, and an actual build step should be added.
|
||||
void chrome_marshall_BOOLEAN__INT(GClosure* closure,
|
||||
GValue* return_value G_GNUC_UNUSED,
|
||||
guint n_param_values,
|
||||
const GValue* param_values,
|
||||
gpointer invocation_hint G_GNUC_UNUSED,
|
||||
gpointer marshal_data) {
|
||||
typedef gboolean(*GMarshalFunc_BOOLEAN__INT)(gpointer data1,
|
||||
gint arg_1,
|
||||
gpointer data2);
|
||||
register GMarshalFunc_BOOLEAN__INT callback;
|
||||
register GCClosure *cc = (GCClosure*)closure;
|
||||
register gpointer data1, data2;
|
||||
gboolean v_return;
|
||||
|
||||
g_return_if_fail(return_value != NULL);
|
||||
g_return_if_fail(n_param_values == 2);
|
||||
|
||||
if (G_CCLOSURE_SWAP_DATA(closure)) {
|
||||
data1 = closure->data;
|
||||
// Note: This line (and the line setting data1 in the other if branch)
|
||||
// were macros in the original autogenerated output. This is with the
|
||||
// macro resolved for release mode. In debug mode, it uses an accessor
|
||||
// that asserts saying that the object pointed to by param_values doesn't
|
||||
// hold a pointer. This appears to be the cause of http://crbug.com/58945.
|
||||
//
|
||||
// This is more than a little odd because the gtype on this first param
|
||||
// isn't set correctly by the time we get here, while I watched it
|
||||
// explicitly set upstack. I verified that v_pointer is still set
|
||||
// correctly. I'm not sure what's going on. :(
|
||||
data2 = (param_values + 0)->data[0].v_pointer;
|
||||
} else {
|
||||
data1 = (param_values + 0)->data[0].v_pointer;
|
||||
data2 = closure->data;
|
||||
}
|
||||
callback = (GMarshalFunc_BOOLEAN__INT)(marshal_data ? marshal_data :
|
||||
cc->callback);
|
||||
|
||||
v_return = callback(data1,
|
||||
g_value_get_int(param_values + 1),
|
||||
data2);
|
||||
|
||||
g_value_set_boolean(return_value, v_return);
|
||||
}
|
||||
|
||||
enum {
|
||||
BUTTON_PUSHED,
|
||||
TRY_BUTTON_PUSHED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint custom_menu_item_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE(GtkCustomMenuItem, gtk_custom_menu_item, GTK_TYPE_MENU_ITEM)
|
||||
|
||||
static void set_selected(GtkCustomMenuItem* item, GtkWidget* selected) {
|
||||
if (selected != item->currently_selected_button) {
|
||||
if (item->currently_selected_button) {
|
||||
gtk_widget_set_state(item->currently_selected_button, GTK_STATE_NORMAL);
|
||||
gtk_widget_set_state(
|
||||
gtk_bin_get_child(GTK_BIN(item->currently_selected_button)),
|
||||
GTK_STATE_NORMAL);
|
||||
}
|
||||
|
||||
item->currently_selected_button = selected;
|
||||
if (item->currently_selected_button) {
|
||||
gtk_widget_set_state(item->currently_selected_button, GTK_STATE_SELECTED);
|
||||
gtk_widget_set_state(
|
||||
gtk_bin_get_child(GTK_BIN(item->currently_selected_button)),
|
||||
GTK_STATE_PRELIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When GtkButtons set the label text, they rebuild the widget hierarchy each
|
||||
// and every time. Therefore, we can't just fish out the label from the button
|
||||
// and set some properties; we have to create this callback function that
|
||||
// listens on the button's "notify" signal, which is emitted right after the
|
||||
// label has been (re)created. (Label values can change dynamically.)
|
||||
static void on_button_label_set(GObject* object) {
|
||||
GtkButton* button = GTK_BUTTON(object);
|
||||
GtkWidget* child = gtk_bin_get_child(GTK_BIN(button));
|
||||
gtk_widget_set_sensitive(child, FALSE);
|
||||
gtk_misc_set_padding(GTK_MISC(child), 2, 0);
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_finalize(GObject *object);
|
||||
static gint gtk_custom_menu_item_expose(GtkWidget* widget,
|
||||
GdkEventExpose* event);
|
||||
static gboolean gtk_custom_menu_item_hbox_expose(GtkWidget* widget,
|
||||
GdkEventExpose* event,
|
||||
GtkCustomMenuItem* menu_item);
|
||||
static void gtk_custom_menu_item_select(GtkItem *item);
|
||||
static void gtk_custom_menu_item_deselect(GtkItem *item);
|
||||
static void gtk_custom_menu_item_activate(GtkMenuItem* menu_item);
|
||||
|
||||
static void gtk_custom_menu_item_init(GtkCustomMenuItem* item) {
|
||||
item->all_widgets = NULL;
|
||||
item->button_widgets = NULL;
|
||||
item->currently_selected_button = NULL;
|
||||
item->previously_selected_button = NULL;
|
||||
|
||||
GtkWidget* menu_hbox = gtk_hbox_new(FALSE, 0);
|
||||
gtk_container_add(GTK_CONTAINER(item), menu_hbox);
|
||||
|
||||
item->label = gtk_label_new(NULL);
|
||||
gtk_misc_set_alignment(GTK_MISC(item->label), 0.0, 0.5);
|
||||
gtk_box_pack_start(GTK_BOX(menu_hbox), item->label, TRUE, TRUE, 0);
|
||||
|
||||
item->hbox = gtk_hbox_new(FALSE, 0);
|
||||
gtk_box_pack_end(GTK_BOX(menu_hbox), item->hbox, FALSE, FALSE, 0);
|
||||
|
||||
g_signal_connect(item->hbox, "expose-event",
|
||||
G_CALLBACK(gtk_custom_menu_item_hbox_expose),
|
||||
item);
|
||||
|
||||
gtk_widget_show_all(menu_hbox);
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_class_init(GtkCustomMenuItemClass* klass) {
|
||||
GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
|
||||
GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
|
||||
GtkItemClass* item_class = GTK_ITEM_CLASS(klass);
|
||||
GtkMenuItemClass* menu_item_class = GTK_MENU_ITEM_CLASS(klass);
|
||||
|
||||
gobject_class->finalize = gtk_custom_menu_item_finalize;
|
||||
|
||||
widget_class->expose_event = gtk_custom_menu_item_expose;
|
||||
|
||||
item_class->select = gtk_custom_menu_item_select;
|
||||
item_class->deselect = gtk_custom_menu_item_deselect;
|
||||
|
||||
menu_item_class->activate = gtk_custom_menu_item_activate;
|
||||
|
||||
custom_menu_item_signals[BUTTON_PUSHED] =
|
||||
g_signal_new("button-pushed",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__INT,
|
||||
G_TYPE_NONE, 1, G_TYPE_INT);
|
||||
custom_menu_item_signals[TRY_BUTTON_PUSHED] =
|
||||
g_signal_new("try-button-pushed",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
chrome_marshall_BOOLEAN__INT,
|
||||
G_TYPE_BOOLEAN, 1, G_TYPE_INT);
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_finalize(GObject *object) {
|
||||
GtkCustomMenuItem* item = GTK_CUSTOM_MENU_ITEM(object);
|
||||
g_list_free(item->all_widgets);
|
||||
g_list_free(item->button_widgets);
|
||||
|
||||
G_OBJECT_CLASS(gtk_custom_menu_item_parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
static gint gtk_custom_menu_item_expose(GtkWidget* widget,
|
||||
GdkEventExpose* event) {
|
||||
if (gtk_widget_get_visible(widget) &&
|
||||
gtk_widget_get_mapped(widget) &&
|
||||
gtk_bin_get_child(GTK_BIN(widget))) {
|
||||
// We skip the drawing in the GtkMenuItem class it draws the highlighted
|
||||
// background and we don't want that.
|
||||
gtk_container_propagate_expose(GTK_CONTAINER(widget),
|
||||
gtk_bin_get_child(GTK_BIN(widget)),
|
||||
event);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_expose_button(GtkWidget* hbox,
|
||||
GdkEventExpose* event,
|
||||
GList* button_item) {
|
||||
// We search backwards to find the leftmost and rightmost buttons. The
|
||||
// current button may be that button.
|
||||
GtkWidget* current_button = GTK_WIDGET(button_item->data);
|
||||
GtkWidget* first_button = current_button;
|
||||
for (GList* i = button_item; i && GTK_IS_BUTTON(i->data);
|
||||
i = g_list_previous(i)) {
|
||||
first_button = GTK_WIDGET(i->data);
|
||||
}
|
||||
|
||||
GtkWidget* last_button = current_button;
|
||||
for (GList* i = button_item; i && GTK_IS_BUTTON(i->data);
|
||||
i = g_list_next(i)) {
|
||||
last_button = GTK_WIDGET(i->data);
|
||||
}
|
||||
|
||||
if (base::i18n::IsRTL())
|
||||
std::swap(first_button, last_button);
|
||||
|
||||
GtkAllocation first_allocation;
|
||||
gtk_widget_get_allocation(first_button, &first_allocation);
|
||||
GtkAllocation current_allocation;
|
||||
gtk_widget_get_allocation(current_button, ¤t_allocation);
|
||||
GtkAllocation last_allocation;
|
||||
gtk_widget_get_allocation(last_button, &last_allocation);
|
||||
|
||||
int x = first_allocation.x;
|
||||
int y = first_allocation.y;
|
||||
int width = last_allocation.width + last_allocation.x - first_allocation.x;
|
||||
int height = last_allocation.height;
|
||||
|
||||
gtk_paint_box(gtk_widget_get_style(hbox),
|
||||
gtk_widget_get_window(hbox),
|
||||
gtk_widget_get_state(current_button),
|
||||
GTK_SHADOW_OUT,
|
||||
¤t_allocation, hbox, "button",
|
||||
x, y, width, height);
|
||||
|
||||
// Propagate to the button's children.
|
||||
gtk_container_propagate_expose(
|
||||
GTK_CONTAINER(current_button),
|
||||
gtk_bin_get_child(GTK_BIN(current_button)),
|
||||
event);
|
||||
}
|
||||
|
||||
static gboolean gtk_custom_menu_item_hbox_expose(GtkWidget* widget,
|
||||
GdkEventExpose* event,
|
||||
GtkCustomMenuItem* menu_item) {
|
||||
// First render all the buttons that aren't the currently selected item.
|
||||
for (GList* current_item = menu_item->all_widgets;
|
||||
current_item != NULL; current_item = g_list_next(current_item)) {
|
||||
if (GTK_IS_BUTTON(current_item->data)) {
|
||||
if (GTK_WIDGET(current_item->data) !=
|
||||
menu_item->currently_selected_button) {
|
||||
gtk_custom_menu_item_expose_button(widget, event, current_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As a separate pass, draw the buton separators above. We need to draw the
|
||||
// separators in a separate pass because we are drawing on top of the
|
||||
// buttons. Otherwise, the vlines are overwritten by the next button.
|
||||
for (GList* current_item = menu_item->all_widgets;
|
||||
current_item != NULL; current_item = g_list_next(current_item)) {
|
||||
if (GTK_IS_BUTTON(current_item->data)) {
|
||||
// Check to see if this is the last button in a run.
|
||||
GList* next_item = g_list_next(current_item);
|
||||
if (next_item && GTK_IS_BUTTON(next_item->data)) {
|
||||
GtkWidget* current_button = GTK_WIDGET(current_item->data);
|
||||
GtkAllocation button_allocation;
|
||||
gtk_widget_get_allocation(current_button, &button_allocation);
|
||||
GtkAllocation child_alloc;
|
||||
gtk_widget_get_allocation(gtk_bin_get_child(GTK_BIN(current_button)),
|
||||
&child_alloc);
|
||||
GtkStyle* style = gtk_widget_get_style(widget);
|
||||
int half_offset = style->xthickness / 2;
|
||||
gtk_paint_vline(style,
|
||||
gtk_widget_get_window(widget),
|
||||
gtk_widget_get_state(current_button),
|
||||
&event->area, widget, "button",
|
||||
child_alloc.y,
|
||||
child_alloc.y + child_alloc.height,
|
||||
button_allocation.x +
|
||||
button_allocation.width - half_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, draw the selected item on top of the separators so there are no
|
||||
// artifacts inside the button area.
|
||||
GList* selected = g_list_find(menu_item->all_widgets,
|
||||
menu_item->currently_selected_button);
|
||||
if (selected) {
|
||||
gtk_custom_menu_item_expose_button(widget, event, selected);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_select(GtkItem* item) {
|
||||
GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(item);
|
||||
|
||||
// When we are selected, the only thing we do is clear information from
|
||||
// previous selections. Actual selection of a button is done either in the
|
||||
// "mouse-motion-event" or is manually set from GtkCustomMenu's overridden
|
||||
// "move-current" handler.
|
||||
custom_item->previously_selected_button = NULL;
|
||||
|
||||
gtk_widget_queue_draw(GTK_WIDGET(item));
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_deselect(GtkItem* item) {
|
||||
GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(item);
|
||||
|
||||
// When we are deselected, we store the item that was currently selected so
|
||||
// that it can be acted on. Menu items are first deselected before they are
|
||||
// activated.
|
||||
custom_item->previously_selected_button =
|
||||
custom_item->currently_selected_button;
|
||||
if (custom_item->currently_selected_button)
|
||||
set_selected(custom_item, NULL);
|
||||
|
||||
gtk_widget_queue_draw(GTK_WIDGET(item));
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_activate(GtkMenuItem* menu_item) {
|
||||
GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(menu_item);
|
||||
|
||||
// We look at |previously_selected_button| because by the time we've been
|
||||
// activated, we've already gone through our deselect handler.
|
||||
if (custom_item->previously_selected_button) {
|
||||
gpointer id_ptr = g_object_get_data(
|
||||
G_OBJECT(custom_item->previously_selected_button), "command-id");
|
||||
if (id_ptr != NULL) {
|
||||
int command_id = GPOINTER_TO_INT(id_ptr);
|
||||
g_signal_emit(custom_item, custom_menu_item_signals[BUTTON_PUSHED], 0,
|
||||
command_id);
|
||||
set_selected(custom_item, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GtkWidget* gtk_custom_menu_item_new(const char* title) {
|
||||
GtkCustomMenuItem* item = GTK_CUSTOM_MENU_ITEM(
|
||||
g_object_new(GTK_TYPE_CUSTOM_MENU_ITEM, NULL));
|
||||
gtk_label_set_text(GTK_LABEL(item->label), title);
|
||||
return GTK_WIDGET(item);
|
||||
}
|
||||
|
||||
GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item,
|
||||
int command_id) {
|
||||
GtkWidget* button = gtk_button_new();
|
||||
g_object_set_data(G_OBJECT(button), "command-id",
|
||||
GINT_TO_POINTER(command_id));
|
||||
gtk_box_pack_start(GTK_BOX(menu_item->hbox), button, FALSE, FALSE, 0);
|
||||
gtk_widget_show(button);
|
||||
|
||||
menu_item->all_widgets = g_list_append(menu_item->all_widgets, button);
|
||||
menu_item->button_widgets = g_list_append(menu_item->button_widgets, button);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
GtkWidget* gtk_custom_menu_item_add_button_label(GtkCustomMenuItem* menu_item,
|
||||
int command_id) {
|
||||
GtkWidget* button = gtk_button_new_with_label("");
|
||||
g_object_set_data(G_OBJECT(button), "command-id",
|
||||
GINT_TO_POINTER(command_id));
|
||||
gtk_box_pack_start(GTK_BOX(menu_item->hbox), button, FALSE, FALSE, 0);
|
||||
g_signal_connect(button, "notify::label",
|
||||
G_CALLBACK(on_button_label_set), NULL);
|
||||
gtk_widget_show(button);
|
||||
|
||||
menu_item->all_widgets = g_list_append(menu_item->all_widgets, button);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item) {
|
||||
GtkWidget* fixed = gtk_fixed_new();
|
||||
gtk_widget_set_size_request(fixed, 5, -1);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(menu_item->hbox), fixed, FALSE, FALSE, 0);
|
||||
gtk_widget_show(fixed);
|
||||
|
||||
menu_item->all_widgets = g_list_append(menu_item->all_widgets, fixed);
|
||||
}
|
||||
|
||||
void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item,
|
||||
gdouble x, gdouble y) {
|
||||
GtkWidget* new_selected_widget = NULL;
|
||||
GList* current = menu_item->button_widgets;
|
||||
for (; current != NULL; current = current->next) {
|
||||
GtkWidget* current_widget = GTK_WIDGET(current->data);
|
||||
GtkAllocation alloc;
|
||||
gtk_widget_get_allocation(current_widget, &alloc);
|
||||
int offset_x, offset_y;
|
||||
gtk_widget_translate_coordinates(current_widget, GTK_WIDGET(menu_item),
|
||||
0, 0, &offset_x, &offset_y);
|
||||
if (x >= offset_x && x < (offset_x + alloc.width) &&
|
||||
y >= offset_y && y < (offset_y + alloc.height)) {
|
||||
new_selected_widget = current_widget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
set_selected(menu_item, new_selected_widget);
|
||||
}
|
||||
|
||||
gboolean gtk_custom_menu_item_handle_move(GtkCustomMenuItem* menu_item,
|
||||
GtkMenuDirectionType direction) {
|
||||
GtkWidget* current = menu_item->currently_selected_button;
|
||||
if (menu_item->button_widgets && current) {
|
||||
switch (direction) {
|
||||
case GTK_MENU_DIR_PREV: {
|
||||
if (g_list_first(menu_item->button_widgets)->data == current)
|
||||
return FALSE;
|
||||
|
||||
set_selected(menu_item, GTK_WIDGET(g_list_previous(g_list_find(
|
||||
menu_item->button_widgets, current))->data));
|
||||
break;
|
||||
}
|
||||
case GTK_MENU_DIR_NEXT: {
|
||||
if (g_list_last(menu_item->button_widgets)->data == current)
|
||||
return FALSE;
|
||||
|
||||
set_selected(menu_item, GTK_WIDGET(g_list_next(g_list_find(
|
||||
menu_item->button_widgets, current))->data));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void gtk_custom_menu_item_select_item_by_direction(
|
||||
GtkCustomMenuItem* menu_item, GtkMenuDirectionType direction) {
|
||||
menu_item->previously_selected_button = NULL;
|
||||
|
||||
// If we're just told to be selected by the menu system, select the first
|
||||
// item.
|
||||
if (menu_item->button_widgets) {
|
||||
switch (direction) {
|
||||
case GTK_MENU_DIR_PREV: {
|
||||
GtkWidget* last_button =
|
||||
GTK_WIDGET(g_list_last(menu_item->button_widgets)->data);
|
||||
if (last_button)
|
||||
set_selected(menu_item, last_button);
|
||||
break;
|
||||
}
|
||||
case GTK_MENU_DIR_NEXT: {
|
||||
GtkWidget* first_button =
|
||||
GTK_WIDGET(g_list_first(menu_item->button_widgets)->data);
|
||||
if (first_button)
|
||||
set_selected(menu_item, first_button);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw(GTK_WIDGET(menu_item));
|
||||
}
|
||||
|
||||
gboolean gtk_custom_menu_item_is_in_clickable_region(
|
||||
GtkCustomMenuItem* menu_item) {
|
||||
return menu_item->currently_selected_button != NULL;
|
||||
}
|
||||
|
||||
gboolean gtk_custom_menu_item_try_no_dismiss_command(
|
||||
GtkCustomMenuItem* menu_item) {
|
||||
GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(menu_item);
|
||||
gboolean activated = TRUE;
|
||||
|
||||
// We work with |currently_selected_button| instead of
|
||||
// |previously_selected_button| since we haven't been "deselect"ed yet.
|
||||
gpointer id_ptr = g_object_get_data(
|
||||
G_OBJECT(custom_item->currently_selected_button), "command-id");
|
||||
if (id_ptr != NULL) {
|
||||
int command_id = GPOINTER_TO_INT(id_ptr);
|
||||
g_signal_emit(custom_item, custom_menu_item_signals[TRY_BUTTON_PUSHED], 0,
|
||||
command_id, &activated);
|
||||
}
|
||||
|
||||
return activated;
|
||||
}
|
||||
|
||||
void gtk_custom_menu_item_foreach_button(GtkCustomMenuItem* menu_item,
|
||||
GtkCallback callback,
|
||||
gpointer callback_data) {
|
||||
// Even though we're filtering |all_widgets| on GTK_IS_BUTTON(), this isn't
|
||||
// equivalent to |button_widgets| because we also want the button-labels.
|
||||
for (GList* i = menu_item->all_widgets; i && GTK_IS_BUTTON(i->data);
|
||||
i = g_list_next(i)) {
|
||||
if (GTK_IS_BUTTON(i->data)) {
|
||||
callback(GTK_WIDGET(i->data), callback_data);
|
||||
}
|
||||
}
|
||||
}
|
139
browser/ui/gtk/gtk_custom_menu_item.h
Normal file
139
browser/ui/gtk/gtk_custom_menu_item.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
// Copyright (c) 2011 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_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
|
||||
#define CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
|
||||
|
||||
// GtkCustomMenuItem is a GtkMenuItem subclass that has buttons in it and acts
|
||||
// to support this. GtkCustomMenuItems only render properly when put in a
|
||||
// GtkCustomMenu; there's a lot of collaboration between these two classes
|
||||
// necessary to work around how gtk normally does menus.
|
||||
//
|
||||
// We can't rely on the normal event infrastructure. While a menu is up, the
|
||||
// GtkMenu has a grab on all events. Instead of trying to pump events through
|
||||
// the normal channels, we have the GtkCustomMenu selectively forward mouse
|
||||
// motion through a back channel. The GtkCustomMenu only listens for button
|
||||
// press information so it can block the effects of the click if the cursor
|
||||
// isn't in a button in the menu item.
|
||||
//
|
||||
// A GtkCustomMenuItem doesn't try to take these signals and forward them to
|
||||
// the buttons it owns. The GtkCustomMenu class keeps track of which button is
|
||||
// selected (due to key events and mouse movement) and otherwise acts like a
|
||||
// normal GtkItem. The buttons are only for sizing and rendering; they don't
|
||||
// respond to events. Instead, when the GtkCustomMenuItem is activated by the
|
||||
// GtkMenu, it uses which button was selected as a signal of what to do.
|
||||
//
|
||||
// Users should connect to the "button-pushed" signal to be notified when a
|
||||
// button was pushed. We don't go through the normal "activate" signal because
|
||||
// we need to communicate additional information, namely which button was
|
||||
// activated.
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CUSTOM_MENU_ITEM \
|
||||
(gtk_custom_menu_item_get_type())
|
||||
#define GTK_CUSTOM_MENU_ITEM(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CUSTOM_MENU_ITEM, \
|
||||
GtkCustomMenuItem))
|
||||
#define GTK_CUSTOM_MENU_ITEM_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CUSTOM_MENU_ITEM, \
|
||||
GtkCustomMenuItemClass))
|
||||
#define GTK_IS_CUSTOM_MENU_ITEM(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CUSTOM_MENU_ITEM))
|
||||
#define GTK_IS_CUSTOM_MENU_ITEM_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CUSTOM_MENU_ITEM))
|
||||
#define GTK_CUSTOM_MENU_ITEM_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CUSTOM_MENU_ITEM, \
|
||||
GtkCustomMenuItemClass))
|
||||
|
||||
typedef struct _GtkCustomMenuItem GtkCustomMenuItem;
|
||||
typedef struct _GtkCustomMenuItemClass GtkCustomMenuItemClass;
|
||||
|
||||
struct _GtkCustomMenuItem {
|
||||
GtkMenuItem menu_item;
|
||||
|
||||
// Container for button widgets.
|
||||
GtkWidget* hbox;
|
||||
|
||||
// Label on left side of menu item.
|
||||
GtkWidget* label;
|
||||
|
||||
// List of all widgets we added. Used to find the leftmost and rightmost
|
||||
// continuous buttons.
|
||||
GList* all_widgets;
|
||||
|
||||
// Possible button widgets. Used for keyboard navigation.
|
||||
GList* button_widgets;
|
||||
|
||||
// The widget that currently has highlight.
|
||||
GtkWidget* currently_selected_button;
|
||||
|
||||
// The widget that was selected *before* |currently_selected_button|. Why do
|
||||
// we hang on to this? Because the menu system sends us a deselect signal
|
||||
// right before activating us. We need to listen to deselect since that's
|
||||
// what we receive when the mouse cursor leaves us entirely.
|
||||
GtkWidget* previously_selected_button;
|
||||
};
|
||||
|
||||
struct _GtkCustomMenuItemClass {
|
||||
GtkMenuItemClass parent_class;
|
||||
};
|
||||
|
||||
GType gtk_custom_menu_item_get_type(void) G_GNUC_CONST;
|
||||
GtkWidget* gtk_custom_menu_item_new(const char* title);
|
||||
|
||||
// Adds a button to our list of items in the |hbox|.
|
||||
GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item,
|
||||
int command_id);
|
||||
|
||||
// Adds a button to our list of items in the |hbox|, but that isn't part of
|
||||
// |button_widgets| to prevent it from being activatable.
|
||||
GtkWidget* gtk_custom_menu_item_add_button_label(GtkCustomMenuItem* menu_item,
|
||||
int command_id);
|
||||
|
||||
// Adds a vertical space in the |hbox|.
|
||||
void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item);
|
||||
|
||||
// Receives a motion event from the GtkCustomMenu that contains us. We can't
|
||||
// just subscribe to motion-event or the individual widget enter/leave events
|
||||
// because the top level GtkMenu has an event grab.
|
||||
void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item,
|
||||
gdouble x, gdouble y);
|
||||
|
||||
// Notification that the menu got a cursor key event. Used to move up/down
|
||||
// within the menu buttons. Returns TRUE to stop the default signal handler
|
||||
// from running.
|
||||
gboolean gtk_custom_menu_item_handle_move(GtkCustomMenuItem* menu_item,
|
||||
GtkMenuDirectionType direction);
|
||||
|
||||
// Because we only get a generic "selected" signal when we've changed, we need
|
||||
// to have a way for the GtkCustomMenu to tell us that we were just
|
||||
// selected.
|
||||
void gtk_custom_menu_item_select_item_by_direction(
|
||||
GtkCustomMenuItem* menu_item, GtkMenuDirectionType direction);
|
||||
|
||||
// Whether we are currently hovering over a clickable region on the menu
|
||||
// item. Used by GtkCustomMenu to determine whether it should discard click
|
||||
// events.
|
||||
gboolean gtk_custom_menu_item_is_in_clickable_region(
|
||||
GtkCustomMenuItem* menu_item);
|
||||
|
||||
// If the button is released while the |currently_selected_button| isn't
|
||||
// supposed to dismiss the menu, this signals to our listeners that we want to
|
||||
// run this command if it doesn't dismiss the menu. Returns TRUE if we acted
|
||||
// on this button click (and should prevent the normal GtkMenu machinery from
|
||||
// firing an "activate" signal).
|
||||
gboolean gtk_custom_menu_item_try_no_dismiss_command(
|
||||
GtkCustomMenuItem* menu_item);
|
||||
|
||||
// Calls |callback| with every button and button-label in the container.
|
||||
void gtk_custom_menu_item_foreach_button(GtkCustomMenuItem* menu_item,
|
||||
GtkCallback callback,
|
||||
gpointer callback_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif // CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
|
295
browser/ui/gtk/gtk_window_util.cc
Normal file
295
browser/ui/gtk/gtk_window_util.cc
Normal file
|
@ -0,0 +1,295 @@
|
|||
// Copyright (c) 2012 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 "browser/ui/gtk/gtk_window_util.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_view.h"
|
||||
|
||||
using content::RenderWidgetHost;
|
||||
using content::WebContents;
|
||||
|
||||
namespace gtk_window_util {
|
||||
|
||||
const int kFrameBorderThickness = 4;
|
||||
const int kResizeAreaCornerSize = 16;
|
||||
|
||||
// Keep track of the last click time and the last click position so we can
|
||||
// filter out extra GDK_BUTTON_PRESS events when a double click happens.
|
||||
static guint32 last_click_time;
|
||||
static int last_click_x;
|
||||
static int last_click_y;
|
||||
|
||||
// Performs Cut/Copy/Paste operation on the |window|.
|
||||
// If the current render view is focused, then just call the specified |method|
|
||||
// against the current render view host, otherwise emit the specified |signal|
|
||||
// against the focused widget.
|
||||
// TODO(suzhe): This approach does not work for plugins.
|
||||
void DoCutCopyPaste(GtkWindow* window,
|
||||
WebContents* web_contents,
|
||||
void (RenderWidgetHost::*method)(),
|
||||
const char* signal) {
|
||||
GtkWidget* widget = gtk_window_get_focus(window);
|
||||
if (widget == NULL)
|
||||
return; // Do nothing if no focused widget.
|
||||
|
||||
if (web_contents &&
|
||||
widget == web_contents->GetView()->GetContentNativeView()) {
|
||||
(web_contents->GetRenderViewHost()->*method)();
|
||||
} else {
|
||||
guint id;
|
||||
if ((id = g_signal_lookup(signal, G_OBJECT_TYPE(widget))) != 0)
|
||||
g_signal_emit(widget, id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void DoCut(GtkWindow* window, WebContents* web_contents) {
|
||||
DoCutCopyPaste(window, web_contents,
|
||||
&RenderWidgetHost::Cut, "cut-clipboard");
|
||||
}
|
||||
|
||||
void DoCopy(GtkWindow* window, WebContents* web_contents) {
|
||||
DoCutCopyPaste(window, web_contents,
|
||||
&RenderWidgetHost::Copy, "copy-clipboard");
|
||||
}
|
||||
|
||||
void DoPaste(GtkWindow* window, WebContents* web_contents) {
|
||||
DoCutCopyPaste(window, web_contents,
|
||||
&RenderWidgetHost::Paste, "paste-clipboard");
|
||||
}
|
||||
|
||||
// Ubuntu patches their version of GTK+ so that there is always a
|
||||
// gripper in the bottom right corner of the window. We dynamically
|
||||
// look up this symbol because it's a non-standard Ubuntu extension to
|
||||
// GTK+. We always need to disable this feature since we can't
|
||||
// communicate this to WebKit easily.
|
||||
typedef void (*gtk_window_set_has_resize_grip_func)(GtkWindow*, gboolean);
|
||||
gtk_window_set_has_resize_grip_func gtk_window_set_has_resize_grip_sym;
|
||||
|
||||
void DisableResizeGrip(GtkWindow* window) {
|
||||
static bool resize_grip_looked_up = false;
|
||||
if (!resize_grip_looked_up) {
|
||||
resize_grip_looked_up = true;
|
||||
gtk_window_set_has_resize_grip_sym =
|
||||
reinterpret_cast<gtk_window_set_has_resize_grip_func>(
|
||||
dlsym(NULL, "gtk_window_set_has_resize_grip"));
|
||||
}
|
||||
if (gtk_window_set_has_resize_grip_sym)
|
||||
gtk_window_set_has_resize_grip_sym(window, FALSE);
|
||||
}
|
||||
|
||||
GdkCursorType GdkWindowEdgeToGdkCursorType(GdkWindowEdge edge) {
|
||||
switch (edge) {
|
||||
case GDK_WINDOW_EDGE_NORTH_WEST:
|
||||
return GDK_TOP_LEFT_CORNER;
|
||||
case GDK_WINDOW_EDGE_NORTH:
|
||||
return GDK_TOP_SIDE;
|
||||
case GDK_WINDOW_EDGE_NORTH_EAST:
|
||||
return GDK_TOP_RIGHT_CORNER;
|
||||
case GDK_WINDOW_EDGE_WEST:
|
||||
return GDK_LEFT_SIDE;
|
||||
case GDK_WINDOW_EDGE_EAST:
|
||||
return GDK_RIGHT_SIDE;
|
||||
case GDK_WINDOW_EDGE_SOUTH_WEST:
|
||||
return GDK_BOTTOM_LEFT_CORNER;
|
||||
case GDK_WINDOW_EDGE_SOUTH:
|
||||
return GDK_BOTTOM_SIDE;
|
||||
case GDK_WINDOW_EDGE_SOUTH_EAST:
|
||||
return GDK_BOTTOM_RIGHT_CORNER;
|
||||
default:
|
||||
NOTREACHED();
|
||||
}
|
||||
return GDK_LAST_CURSOR;
|
||||
}
|
||||
|
||||
bool BoundsMatchMonitorSize(GtkWindow* window, gfx::Rect bounds) {
|
||||
// A screen can be composed of multiple monitors.
|
||||
GdkScreen* screen = gtk_window_get_screen(window);
|
||||
GdkRectangle monitor_size;
|
||||
|
||||
if (gtk_widget_get_realized(GTK_WIDGET(window))) {
|
||||
// |window| has been realized.
|
||||
gint monitor_num = gdk_screen_get_monitor_at_window(screen,
|
||||
gtk_widget_get_window(GTK_WIDGET(window)));
|
||||
gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor_size);
|
||||
return bounds.size() == gfx::Size(monitor_size.width, monitor_size.height);
|
||||
}
|
||||
|
||||
// Make sure the window doesn't match any monitor size. We compare against
|
||||
// all monitors because we don't know which monitor the window is going to
|
||||
// open on before window realized.
|
||||
gint num_monitors = gdk_screen_get_n_monitors(screen);
|
||||
for (gint i = 0; i < num_monitors; ++i) {
|
||||
GdkRectangle monitor_size;
|
||||
gdk_screen_get_monitor_geometry(screen, i, &monitor_size);
|
||||
if (bounds.size() == gfx::Size(monitor_size.width, monitor_size.height))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HandleTitleBarLeftMousePress(
|
||||
GtkWindow* window,
|
||||
const gfx::Rect& bounds,
|
||||
GdkEventButton* event) {
|
||||
// We want to start a move when the user single clicks, but not start a
|
||||
// move when the user double clicks. However, a double click sends the
|
||||
// following GDK events: GDK_BUTTON_PRESS, GDK_BUTTON_RELEASE,
|
||||
// GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_BUTTON_RELEASE. If we
|
||||
// start a gtk_window_begin_move_drag on the second GDK_BUTTON_PRESS,
|
||||
// the call to gtk_window_maximize fails. To work around this, we
|
||||
// keep track of the last click and if it's going to be a double click,
|
||||
// we don't call gtk_window_begin_move_drag.
|
||||
DCHECK_EQ(event->type, GDK_BUTTON_PRESS);
|
||||
DCHECK_EQ(event->button, 1);
|
||||
|
||||
static GtkSettings* settings = gtk_settings_get_default();
|
||||
gint double_click_time = 250;
|
||||
gint double_click_distance = 5;
|
||||
g_object_get(G_OBJECT(settings),
|
||||
"gtk-double-click-time", &double_click_time,
|
||||
"gtk-double-click-distance", &double_click_distance,
|
||||
NULL);
|
||||
|
||||
guint32 click_time = event->time - last_click_time;
|
||||
int click_move_x = abs(event->x - last_click_x);
|
||||
int click_move_y = abs(event->y - last_click_y);
|
||||
|
||||
last_click_time = event->time;
|
||||
last_click_x = static_cast<int>(event->x);
|
||||
last_click_y = static_cast<int>(event->y);
|
||||
|
||||
if (click_time > static_cast<guint32>(double_click_time) ||
|
||||
click_move_x > double_click_distance ||
|
||||
click_move_y > double_click_distance) {
|
||||
// Ignore drag requests if the window is the size of the screen.
|
||||
// We do this to avoid triggering fullscreen mode in metacity
|
||||
// (without the --no-force-fullscreen flag) and in compiz (with
|
||||
// Legacy Fullscreen Mode enabled).
|
||||
if (!BoundsMatchMonitorSize(window, bounds)) {
|
||||
gtk_window_begin_move_drag(window, event->button,
|
||||
static_cast<gint>(event->x_root),
|
||||
static_cast<gint>(event->y_root),
|
||||
event->time);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void UnMaximize(GtkWindow* window,
|
||||
const gfx::Rect& bounds,
|
||||
const gfx::Rect& restored_bounds) {
|
||||
gtk_window_unmaximize(window);
|
||||
|
||||
// It can happen that you end up with a window whose restore size is the same
|
||||
// as the size of the screen, so unmaximizing it merely remaximizes it due to
|
||||
// the same WM feature that SetWindowSize() works around. We try to detect
|
||||
// this and resize the window to work around the issue.
|
||||
if (bounds.size() == restored_bounds.size())
|
||||
gtk_window_resize(window, bounds.width(), bounds.height() - 1);
|
||||
}
|
||||
|
||||
void SetWindowCustomClass(GtkWindow* window, const std::string& wmclass) {
|
||||
gtk_window_set_wmclass(window,
|
||||
wmclass.c_str(),
|
||||
gdk_get_program_class());
|
||||
|
||||
// Set WM_WINDOW_ROLE for session management purposes.
|
||||
// See http://tronche.com/gui/x/icccm/sec-5.html .
|
||||
gtk_window_set_role(window, wmclass.c_str());
|
||||
}
|
||||
|
||||
void SetWindowSize(GtkWindow* window, const gfx::Size& size) {
|
||||
gfx::Size new_size = size;
|
||||
gint current_width = 0;
|
||||
gint current_height = 0;
|
||||
gtk_window_get_size(window, ¤t_width, ¤t_height);
|
||||
GdkRectangle size_with_decorations = {0};
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
|
||||
if (gdk_window) {
|
||||
gdk_window_get_frame_extents(gdk_window,
|
||||
&size_with_decorations);
|
||||
}
|
||||
|
||||
if (current_width == size_with_decorations.width &&
|
||||
current_height == size_with_decorations.height) {
|
||||
// Make sure the window doesn't match any monitor size. We compare against
|
||||
// all monitors because we don't know which monitor the window is going to
|
||||
// open on (the WM decides that).
|
||||
GdkScreen* screen = gtk_window_get_screen(window);
|
||||
gint num_monitors = gdk_screen_get_n_monitors(screen);
|
||||
for (gint i = 0; i < num_monitors; ++i) {
|
||||
GdkRectangle monitor_size;
|
||||
gdk_screen_get_monitor_geometry(screen, i, &monitor_size);
|
||||
if (gfx::Size(monitor_size.width, monitor_size.height) == size) {
|
||||
gtk_window_resize(window, size.width(), size.height() - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// gtk_window_resize is the size of the window not including decorations,
|
||||
// but we are given the |size| including window decorations.
|
||||
if (size_with_decorations.width > current_width) {
|
||||
new_size.set_width(size.width() - size_with_decorations.width +
|
||||
current_width);
|
||||
}
|
||||
if (size_with_decorations.height > current_height) {
|
||||
new_size.set_height(size.height() - size_with_decorations.height +
|
||||
current_height);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_window_resize(window, new_size.width(), new_size.height());
|
||||
}
|
||||
|
||||
bool GetWindowEdge(const gfx::Size& window_size,
|
||||
int top_edge_inset,
|
||||
int x,
|
||||
int y,
|
||||
GdkWindowEdge* edge) {
|
||||
gfx::Rect middle(window_size);
|
||||
middle.Inset(kFrameBorderThickness,
|
||||
kFrameBorderThickness - top_edge_inset,
|
||||
kFrameBorderThickness,
|
||||
kFrameBorderThickness);
|
||||
if (middle.Contains(x, y))
|
||||
return false;
|
||||
|
||||
gfx::Rect north(0, 0, window_size.width(),
|
||||
kResizeAreaCornerSize - top_edge_inset);
|
||||
gfx::Rect west(0, 0, kResizeAreaCornerSize, window_size.height());
|
||||
gfx::Rect south(0, window_size.height() - kResizeAreaCornerSize,
|
||||
window_size.width(), kResizeAreaCornerSize);
|
||||
gfx::Rect east(window_size.width() - kResizeAreaCornerSize, 0,
|
||||
kResizeAreaCornerSize, window_size.height());
|
||||
|
||||
if (north.Contains(x, y)) {
|
||||
if (west.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_NORTH_WEST;
|
||||
else if (east.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_NORTH_EAST;
|
||||
else
|
||||
*edge = GDK_WINDOW_EDGE_NORTH;
|
||||
} else if (south.Contains(x, y)) {
|
||||
if (west.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_SOUTH_WEST;
|
||||
else if (east.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_SOUTH_EAST;
|
||||
else
|
||||
*edge = GDK_WINDOW_EDGE_SOUTH;
|
||||
} else {
|
||||
if (west.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_WEST;
|
||||
else if (east.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_EAST;
|
||||
else
|
||||
return false; // The cursor must be outside the window.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gtk_window_util
|
74
browser/ui/gtk/gtk_window_util.h
Normal file
74
browser/ui/gtk/gtk_window_util.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) 2012 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 ATOM_BROWSER_UI_GTK_GTK_WINDOW_UTIL_H_
|
||||
#define ATOM_BROWSER_UI_GTK_GTK_WINDOW_UTIL_H_
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdk.h>
|
||||
#include "ui/gfx/rect.h"
|
||||
|
||||
namespace content {
|
||||
class WebContents;
|
||||
}
|
||||
|
||||
namespace gtk_window_util {
|
||||
|
||||
// The frame border is only visible in restored mode and is hardcoded to 4 px
|
||||
// on each side regardless of the system window border size.
|
||||
extern const int kFrameBorderThickness;
|
||||
// In the window corners, the resize areas don't actually expand bigger, but
|
||||
// the 16 px at the end of each edge triggers diagonal resizing.
|
||||
extern const int kResizeAreaCornerSize;
|
||||
|
||||
// Performs Cut/Copy/Paste operation on the |window|'s |web_contents|.
|
||||
void DoCut(GtkWindow* window, content::WebContents* web_contents);
|
||||
void DoCopy(GtkWindow* window, content::WebContents* web_contents);
|
||||
void DoPaste(GtkWindow* window, content::WebContents* web_contents);
|
||||
|
||||
// Ubuntu patches their version of GTK+ to that there is always a
|
||||
// gripper in the bottom right corner of the window. We always need to
|
||||
// disable this feature since we can't communicate this to WebKit easily.
|
||||
void DisableResizeGrip(GtkWindow* window);
|
||||
|
||||
// Returns the resize cursor corresponding to the window |edge|.
|
||||
GdkCursorType GdkWindowEdgeToGdkCursorType(GdkWindowEdge edge);
|
||||
|
||||
// Returns |true| if the window bounds match the monitor size.
|
||||
bool BoundsMatchMonitorSize(GtkWindow* window, gfx::Rect bounds);
|
||||
|
||||
bool HandleTitleBarLeftMousePress(GtkWindow* window,
|
||||
const gfx::Rect& bounds,
|
||||
GdkEventButton* event);
|
||||
|
||||
// Request the underlying window to unmaximize. Also tries to work around
|
||||
// a window manager "feature" that can prevent this in some edge cases.
|
||||
void UnMaximize(GtkWindow* window,
|
||||
const gfx::Rect& bounds,
|
||||
const gfx::Rect& restored_bounds);
|
||||
|
||||
// Set a custom WM_CLASS for a window.
|
||||
void SetWindowCustomClass(GtkWindow* window, const std::string& wmclass);
|
||||
|
||||
// A helper method for setting the GtkWindow size that should be used in place
|
||||
// of calling gtk_window_resize directly. This is done to avoid a WM "feature"
|
||||
// where setting the window size to the monitor size causes the WM to set the
|
||||
// EWMH for full screen mode.
|
||||
void SetWindowSize(GtkWindow* window, const gfx::Size& size);
|
||||
|
||||
// If the point (|x|, |y|) is within the resize border area of the window,
|
||||
// returns true and sets |edge| to the appropriate GdkWindowEdge value.
|
||||
// Otherwise, returns false.
|
||||
// |top_edge_inset| specifies how much smaller (in px) than the default edge
|
||||
// size the top edge should be, used by browser windows to make it easier to
|
||||
// move the window since a lot of title bar space is taken by the tabs.
|
||||
bool GetWindowEdge(const gfx::Size& window_size,
|
||||
int top_edge_inset,
|
||||
int x,
|
||||
int y,
|
||||
GdkWindowEdge* edge);
|
||||
|
||||
} // namespace gtk_window_util
|
||||
|
||||
#endif // ATOM_BROWSER_UI_GTK_GTK_WINDOW_UTIL_H_
|
30
browser/ui/message_box_gtk.cc
Normal file
30
browser/ui/message_box_gtk.cc
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/ui/message_box.h"
|
||||
|
||||
#include "base/callback.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
int ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const MessageBoxCallback& callback) {
|
||||
callback.Run(0);
|
||||
}
|
||||
|
||||
} // namespace atom
|
|
@ -19,14 +19,17 @@ class WindowListObserver;
|
|||
class WindowList {
|
||||
public:
|
||||
typedef std::vector<NativeWindow*> WindowVector;
|
||||
typedef WindowVector::iterator iterator;
|
||||
typedef WindowVector::const_iterator const_iterator;
|
||||
typedef WindowVector::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
// Windows are added to the list before they have constructed windows,
|
||||
// so the |window()| member function may return NULL.
|
||||
const_iterator begin() const { return windows_.begin(); }
|
||||
const_iterator end() const { return windows_.end(); }
|
||||
|
||||
iterator begin() { return windows_.begin(); }
|
||||
iterator end() { return windows_.end(); }
|
||||
|
||||
bool empty() const { return windows_.empty(); }
|
||||
size_t size() const { return windows_.size(); }
|
||||
|
||||
|
|
21
common.gypi
21
common.gypi
|
@ -2,7 +2,7 @@
|
|||
'variables': {
|
||||
'clang': 0,
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
['OS=="mac" or OS=="linux"', {
|
||||
'clang': 1,
|
||||
}],
|
||||
['OS=="win" and (MSVS_VERSION=="2012e" or MSVS_VERSION=="2010e")', {
|
||||
|
@ -76,6 +76,21 @@
|
|||
'-Wno-return-type',
|
||||
],
|
||||
},
|
||||
'conditions': [
|
||||
['OS=="linux"', {
|
||||
'cflags': [
|
||||
'-Wno-parentheses-equality',
|
||||
'-Wno-unused-function',
|
||||
'-Wno-sometimes-uninitialized',
|
||||
'-Wno-pointer-sign',
|
||||
'-Wno-string-plus-int',
|
||||
'-Wno-unused-variable',
|
||||
'-Wno-unused-value',
|
||||
'-Wno-deprecated-declarations',
|
||||
'-Wno-return-type',
|
||||
],
|
||||
}],
|
||||
],
|
||||
}],
|
||||
['_target_name in ["node_lib", "atom_lib"]', {
|
||||
'include_dirs': [
|
||||
|
@ -153,7 +168,9 @@
|
|||
],
|
||||
'target_defaults': {
|
||||
'cflags_cc': [
|
||||
'-std=c++11',
|
||||
# Use gnu++11 instead of c++11 here, see:
|
||||
# https://code.google.com/p/chromium/issues/detail?id=224515
|
||||
'-std=gnu++11',
|
||||
],
|
||||
'xcode_settings': {
|
||||
'CC': '/usr/bin/clang',
|
||||
|
|
|
@ -9,6 +9,11 @@
|
|||
|
||||
#include "common/v8/node_common.h"
|
||||
|
||||
#if defined(TOOLKIT_GTK)
|
||||
#include "base/command_line.h"
|
||||
#include "ui/gfx/gtk_util.h"
|
||||
#endif
|
||||
|
||||
#define UNWRAP_SCREEN_AND_CHECK \
|
||||
Screen* self = ObjectWrap::Unwrap<Screen>(args.This()); \
|
||||
if (self == NULL) \
|
||||
|
@ -67,6 +72,10 @@ void Screen::GetPrimaryDisplay(
|
|||
|
||||
// static
|
||||
void Screen::Initialize(v8::Handle<v8::Object> target) {
|
||||
#if defined(TOOLKIT_GTK)
|
||||
gfx::GdkInitFromCommandLine(*CommandLine::ForCurrentProcess());
|
||||
#endif
|
||||
|
||||
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
t->SetClassName(v8::String::NewSymbol("Screen"));
|
||||
|
|
38
common/crash_reporter/crash_reporter_linux.cc
Normal file
38
common/crash_reporter/crash_reporter_linux.cc
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/crash_reporter/crash_reporter_linux.h"
|
||||
|
||||
#include "base/memory/singleton.h"
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
CrashReporterLinux::CrashReporterLinux() {
|
||||
}
|
||||
|
||||
CrashReporterLinux::~CrashReporterLinux() {
|
||||
}
|
||||
|
||||
void CrashReporterLinux::InitBreakpad(const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler) {
|
||||
}
|
||||
|
||||
void CrashReporterLinux::SetUploadParameters() {
|
||||
}
|
||||
|
||||
// static
|
||||
CrashReporterLinux* CrashReporterLinux::GetInstance() {
|
||||
return Singleton<CrashReporterLinux>::get();
|
||||
}
|
||||
|
||||
// static
|
||||
CrashReporter* CrashReporter::GetInstance() {
|
||||
return CrashReporterLinux::GetInstance();
|
||||
}
|
||||
|
||||
} // namespace crash_reporter
|
38
common/crash_reporter/crash_reporter_linux.h
Normal file
38
common/crash_reporter/crash_reporter_linux.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
|
||||
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "common/crash_reporter/crash_reporter.h"
|
||||
|
||||
template <typename T> struct DefaultSingletonTraits;
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
class CrashReporterLinux : public CrashReporter {
|
||||
public:
|
||||
static CrashReporterLinux* GetInstance();
|
||||
|
||||
virtual void InitBreakpad(const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler) OVERRIDE;
|
||||
virtual void SetUploadParameters() OVERRIDE;
|
||||
|
||||
private:
|
||||
friend struct DefaultSingletonTraits<CrashReporterLinux>;
|
||||
|
||||
CrashReporterLinux();
|
||||
virtual ~CrashReporterLinux();
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashReporterLinux);
|
||||
};
|
||||
|
||||
} // namespace crash_reporter
|
||||
|
||||
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
|
19
common/linux/application_info.cc
Normal file
19
common/linux/application_info.cc
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/atom_version.h"
|
||||
|
||||
namespace brightray {
|
||||
|
||||
std::string GetApplicationName() {
|
||||
return "Atom-Shell";
|
||||
}
|
||||
|
||||
std::string GetApplicationVersion() {
|
||||
return ATOM_VERSION_STRING;
|
||||
}
|
||||
|
||||
} // namespace brightray
|
57
common/node_bindings_linux.cc
Normal file
57
common/node_bindings_linux.cc
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/node_bindings_linux.h"
|
||||
|
||||
#include <sys/epoll.h>
|
||||
|
||||
namespace atom {
|
||||
|
||||
NodeBindingsLinux::NodeBindingsLinux(bool is_browser)
|
||||
: NodeBindings(is_browser),
|
||||
epoll_(epoll_create(1)) {
|
||||
int backend_fd = uv_backend_fd(uv_loop_);
|
||||
struct epoll_event ev = { 0 };
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = backend_fd;
|
||||
epoll_ctl(epoll_, EPOLL_CTL_ADD, backend_fd, &ev);
|
||||
}
|
||||
|
||||
NodeBindingsLinux::~NodeBindingsLinux() {
|
||||
}
|
||||
|
||||
void NodeBindingsLinux::RunMessageLoop() {
|
||||
// Get notified when libuv's watcher queue changes.
|
||||
uv_loop_->data = this;
|
||||
uv_loop_->on_watcher_queue_updated = OnWatcherQueueChanged;
|
||||
|
||||
NodeBindings::RunMessageLoop();
|
||||
}
|
||||
|
||||
// static
|
||||
void NodeBindingsLinux::OnWatcherQueueChanged(uv_loop_t* loop) {
|
||||
NodeBindingsLinux* self = static_cast<NodeBindingsLinux*>(loop->data);
|
||||
|
||||
// We need to break the io polling in the epoll thread when loop's watcher
|
||||
// queue changes, otherwise new events cannot be notified.
|
||||
self->WakeupEmbedThread();
|
||||
}
|
||||
|
||||
void NodeBindingsLinux::PollEvents() {
|
||||
int timeout = uv_backend_timeout(uv_loop_);
|
||||
|
||||
// Wait for new libuv events.
|
||||
int r;
|
||||
do {
|
||||
struct epoll_event ev;
|
||||
r = epoll_wait(epoll_, &ev, 1, timeout);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
}
|
||||
|
||||
// static
|
||||
NodeBindings* NodeBindings::Create(bool is_browser) {
|
||||
return new NodeBindingsLinux(is_browser);
|
||||
}
|
||||
|
||||
} // namespace atom
|
34
common/node_bindings_linux.h
Normal file
34
common/node_bindings_linux.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_NODE_BINDINGS_LINUX_H_
|
||||
#define ATOM_COMMON_NODE_BINDINGS_LINUX_H_
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "common/node_bindings.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NodeBindingsLinux : public NodeBindings {
|
||||
public:
|
||||
explicit NodeBindingsLinux(bool is_browser);
|
||||
virtual ~NodeBindingsLinux();
|
||||
|
||||
virtual void RunMessageLoop() OVERRIDE;
|
||||
|
||||
private:
|
||||
// Called when uv's watcher queue changes.
|
||||
static void OnWatcherQueueChanged(uv_loop_t* loop);
|
||||
|
||||
virtual void PollEvents() OVERRIDE;
|
||||
|
||||
// Epoll to poll for uv's backend fd.
|
||||
int epoll_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NodeBindingsLinux);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_COMMON_NODE_BINDINGS_LINUX_H_
|
80
common/platform_util_linux.cc
Normal file
80
common/platform_util_linux.cc
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/platform_util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "base/file_util.h"
|
||||
#include "base/process/kill.h"
|
||||
#include "base/process/launch.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void XDGUtil(const std::string& util, const std::string& arg) {
|
||||
std::vector<std::string> argv;
|
||||
argv.push_back(util);
|
||||
argv.push_back(arg);
|
||||
|
||||
base::LaunchOptions options;
|
||||
// xdg-open can fall back on mailcap which eventually might plumb through
|
||||
// to a command that needs a terminal. Set the environment variable telling
|
||||
// it that we definitely don't have a terminal available and that it should
|
||||
// bring up a new terminal if necessary. See "man mailcap".
|
||||
options.environ["MM_NOTTTY"] = "1";
|
||||
|
||||
base::ProcessHandle handle;
|
||||
if (base::LaunchProcess(argv, options, &handle))
|
||||
base::EnsureProcessGetsReaped(handle);
|
||||
}
|
||||
|
||||
void XDGOpen(const std::string& path) {
|
||||
XDGUtil("xdg-open", path);
|
||||
}
|
||||
|
||||
void XDGEmail(const std::string& email) {
|
||||
XDGUtil("xdg-email", email);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace platform_util {
|
||||
|
||||
// TODO(estade): It would be nice to be able to select the file in the file
|
||||
// manager, but that probably requires extending xdg-open. For now just
|
||||
// show the folder.
|
||||
void ShowItemInFolder(const base::FilePath& full_path) {
|
||||
base::FilePath dir = full_path.DirName();
|
||||
if (!base::DirectoryExists(dir))
|
||||
return;
|
||||
|
||||
XDGOpen(dir.value());
|
||||
}
|
||||
|
||||
void OpenItem(const base::FilePath& full_path) {
|
||||
XDGOpen(full_path.value());
|
||||
}
|
||||
|
||||
void OpenExternal(const GURL& url) {
|
||||
if (url.SchemeIs("mailto"))
|
||||
XDGEmail(url.spec());
|
||||
else
|
||||
XDGOpen(url.spec());
|
||||
}
|
||||
|
||||
void MoveItemToTrash(const base::FilePath& full_path) {
|
||||
XDGUtil("gvfs-trash", full_path.value());
|
||||
}
|
||||
|
||||
void Beep() {
|
||||
// echo '\a' > /dev/console
|
||||
FILE* console = fopen("/dev/console", "r");
|
||||
if (console == NULL)
|
||||
return;
|
||||
fprintf(console, "\a");
|
||||
fclose(console);
|
||||
}
|
||||
|
||||
} // namespace platform_util
|
|
@ -3,7 +3,7 @@
|
|||
## Guides
|
||||
|
||||
* [Quick start](quick-start.md)
|
||||
* [Build native modules](build-native-modules.md)
|
||||
* [Use native modules](use-native-modules.md)
|
||||
|
||||
## Development
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
|||
* [Source code directory structure](development/source-code-directory-structure.md)
|
||||
* [Build instructions (Mac)](development/build-instructions-mac.md)
|
||||
* [Build instructions (Windows)](development/build-instructions-windows.md)
|
||||
* [Build instructions (Linux)](development/build-instructions-linux.md)
|
||||
|
||||
## API References
|
||||
|
||||
|
|
|
@ -116,6 +116,22 @@ shouldn't!).
|
|||
Emitted when the memory taken by the native window is released. Usually you
|
||||
should dereference the javascript object when received this event.
|
||||
|
||||
### Event: 'unresponsive'
|
||||
|
||||
Emiited when the web page becomes unresponsive.
|
||||
|
||||
### Event: 'responsive'
|
||||
|
||||
Emitted when the unresponsive web page becomes responsive again.
|
||||
|
||||
### Event: 'crashed'
|
||||
|
||||
Emitted when the renderer process is crashed.
|
||||
|
||||
### Event: 'blur'
|
||||
|
||||
Emiited when window loses focus.
|
||||
|
||||
### Class Method: BrowserWindow.getAllWindows()
|
||||
|
||||
Returns an array of all opened browser windows.
|
||||
|
|
71
docs/development/build-instructions-linux.md
Normal file
71
docs/development/build-instructions-linux.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
# Build instructions (Linux)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* [node.js](http://nodejs.org)
|
||||
* clang and headers of GTK+ and libnotify
|
||||
|
||||
On Ubuntu you could install the libraries via:
|
||||
|
||||
```bash
|
||||
$ sudo apt-get install clang libgtk2.0-dev libnotify-dev
|
||||
```
|
||||
|
||||
## Getting the code
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/atom/atom-shell.git
|
||||
```
|
||||
|
||||
## Bootstrapping
|
||||
|
||||
The bootstrap script will download all necessary build dependencies and create
|
||||
build project files. Notice that we're using `ninja` to build `atom-shell` so
|
||||
there is no `Makefile` generated.
|
||||
|
||||
```bash
|
||||
$ cd atom-shell
|
||||
$ ./script/bootstrap.py
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
Build both `Release` and `Debug` targets:
|
||||
|
||||
```bash
|
||||
$ ./script/build.py
|
||||
```
|
||||
|
||||
You can also only build the `Debug` target:
|
||||
|
||||
```bash
|
||||
$ ./script/build.py -c Debug
|
||||
```
|
||||
|
||||
After building is done, you can find `Atom.app` under `out/Debug`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you got an error like this:
|
||||
|
||||
````
|
||||
In file included from /usr/include/stdio.h:28:0,
|
||||
from ../../../svnsrc/libgcc/../gcc/tsystem.h:88,
|
||||
from ../../../svnsrc/libgcc/libgcc2.c:29:
|
||||
/usr/include/features.h:324:26: fatal error: bits/predefs.h: No such file or directory
|
||||
#include <bits/predefs.h>
|
||||
````
|
||||
|
||||
Then you need to install `gcc-multilib` and `g++-multilib`, on Ubuntu you can do
|
||||
this:
|
||||
|
||||
```bash
|
||||
$ sudo apt-get install gcc-multilib g++-multilib
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
$ ./script/test.py
|
||||
```
|
||||
|
|
@ -1,15 +1,24 @@
|
|||
# Build native modules
|
||||
# Use native modules
|
||||
|
||||
Since atom-shell is using a different V8 version from the official node, you
|
||||
need to build native module against atom-shell's headers to use them.
|
||||
|
||||
You need to use node-gyp to compile native modules, you can install node-gyp
|
||||
via npm if you hadn't:
|
||||
The [apm](https://github.com/atom/apm) provided a easy way to do this, after
|
||||
installing it you could use it to install dependencies just like using `npm`:
|
||||
|
||||
```bash
|
||||
$ npm install -g node-gyp
|
||||
$ cd /path/to/atom-shell/project/
|
||||
$ apm install .
|
||||
```
|
||||
|
||||
But you should notice that `apm install module` wont' work because it will
|
||||
install a user package for [Atom](https://github.com/atom/atom) instead.
|
||||
|
||||
Apart from `apm`, you can also use `node-gyp` and `npm` to manually build the
|
||||
native modules.
|
||||
|
||||
## The node-gyp way
|
||||
|
||||
First you need to check which node release atom-shell is carrying via
|
||||
`process.version` (at the time of writing it is v0.10.5), then you can
|
||||
configure and build native modules via following commands:
|
||||
|
@ -23,13 +32,7 @@ The `HOME=~/.atom-shell-gyp` changes where to find development headers. The
|
|||
`--target=0.10.5` is specifying node's version. The `--dist-url=...` specifies
|
||||
where to download the headers.
|
||||
|
||||
## Use npm to build native modules
|
||||
|
||||
Under most circumstances you would want to use npm to install modules, if
|
||||
you're using npm >= v1.2.19 (because [a
|
||||
patch](https://github.com/TooTallNate/node-gyp/commit/afbcdea1ffd25c02bc88d119b10337852c44d400)
|
||||
is needed to make `npm_config_disturl` work) you can use following code to
|
||||
download and build native modules against atom-shell's headers:
|
||||
## The npm way
|
||||
|
||||
```bash
|
||||
export npm_config_disturl=https://gh-contractor-zcbenz.s3.amazonaws.com/atom-shell/dist
|
|
@ -9,7 +9,7 @@
|
|||
"mocha": "~1.13.0",
|
||||
"pathwatcher": "0.14.0",
|
||||
"q": "0.9.7",
|
||||
"runas": "0.3.0",
|
||||
"runas": "0.5.*",
|
||||
"temp": "~0.6.0",
|
||||
"walkdir": "~0.0.7"
|
||||
},
|
||||
|
|
2
script/cpplint.py
vendored
2
script/cpplint.py
vendored
|
@ -13,6 +13,8 @@ IGNORE_FILES = [
|
|||
'browser/ui/cocoa/event_processing_window.h',
|
||||
'browser/ui/cocoa/atom_menu_controller.h',
|
||||
'browser/ui/cocoa/nsalert_synchronous_sheet.h',
|
||||
'browser/ui/gtk/gtk_custom_menu.cc',
|
||||
'browser/ui/gtk/gtk_custom_menu_item.cc',
|
||||
'common/api/api_messages.cc',
|
||||
'common/api/api_messages.h',
|
||||
'common/atom_version.h',
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
NODE_VERSION = 'v0.11.10'
|
||||
BASE_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent'
|
||||
LIBCHROMIUMCONTENT_COMMIT = 'b27290717c08f8c6a58067d3c3725d68b4e6a2e5'
|
||||
LIBCHROMIUMCONTENT_COMMIT = 'fe05f53f3080889ced2696b2741d93953e654b49'
|
||||
|
|
|
@ -14,8 +14,10 @@ def main():
|
|||
if sys.platform == 'darwin':
|
||||
atom_shell = os.path.join(SOURCE_ROOT, 'out', 'Debug', 'Atom.app',
|
||||
'Contents', 'MacOS', 'Atom')
|
||||
else:
|
||||
elif sys.platform == 'win32':
|
||||
atom_shell = os.path.join(SOURCE_ROOT, 'out', 'Debug', 'atom.exe')
|
||||
else:
|
||||
atom_shell = os.path.join(SOURCE_ROOT, 'out', 'Debug', 'atom')
|
||||
|
||||
subprocess.check_call([atom_shell, 'spec'] + sys.argv[1:])
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ def update_gyp():
|
|||
subprocess.call([python, gyp,
|
||||
'-f', 'ninja', '--depth', '.', 'atom.gyp',
|
||||
'-Icommon.gypi', '-Ivendor/brightray/brightray.gypi',
|
||||
'-Dlinux_clang=0', # Disable brightray's clang setting
|
||||
'-Dtarget_arch={0}'.format(arch),
|
||||
'-Dlibrary=static_library'])
|
||||
|
||||
|
|
2
vendor/apm
vendored
2
vendor/apm
vendored
|
@ -1 +1 @@
|
|||
Subproject commit d976d63c8df4ac87d65ab9bbbef042517179e31f
|
||||
Subproject commit a9e5498a838bf228fca53c32f66e2e6d5adcf783
|
Loading…
Add table
Reference in a new issue