diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/gtk | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/gtk')
63 files changed, 12788 insertions, 0 deletions
diff --git a/Source/WebCore/platform/gtk/ClipboardGtk.cpp b/Source/WebCore/platform/gtk/ClipboardGtk.cpp new file mode 100644 index 0000000..eb7248b --- /dev/null +++ b/Source/WebCore/platform/gtk/ClipboardGtk.cpp @@ -0,0 +1,335 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "ClipboardGtk.h" + +#include "CachedImage.h" +#include "DragData.h" +#include "Editor.h" +#include "Element.h" +#include "FileList.h" +#include "Frame.h" +#include "HTMLNames.h" +#include "Image.h" +#include "NotImplemented.h" +#include "Pasteboard.h" +#include "PasteboardHelper.h" +#include "RenderImage.h" +#include "ScriptExecutionContext.h" +#include "markup.h" +#include <wtf/text/CString.h> +#include <wtf/text/StringHash.h> +#include <gtk/gtk.h> + +namespace WebCore { + +enum ClipboardDataType { + ClipboardDataTypeText, + ClipboardDataTypeMarkup, + ClipboardDataTypeURIList, + ClipboardDataTypeURL, + ClipboardDataTypeImage, + ClipboardDataTypeUnknown +}; + +PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy, Frame* frame) +{ + return ClipboardGtk::create(policy, gtk_clipboard_get_for_display(gdk_display_get_default(), GDK_SELECTION_CLIPBOARD), frame); +} + +PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData* dragData, Frame* frame) +{ + return ClipboardGtk::create(policy, dragData->platformData(), DragAndDrop, frame); +} + +ClipboardGtk::ClipboardGtk(ClipboardAccessPolicy policy, GtkClipboard* clipboard, Frame* frame) + : Clipboard(policy, CopyAndPaste) + , m_dataObject(DataObjectGtk::forClipboard(clipboard)) + , m_clipboard(clipboard) + , m_helper(Pasteboard::generalPasteboard()->helper()) + , m_frame(frame) +{ +} + +ClipboardGtk::ClipboardGtk(ClipboardAccessPolicy policy, PassRefPtr<DataObjectGtk> dataObject, ClipboardType clipboardType, Frame* frame) + : Clipboard(policy, clipboardType) + , m_dataObject(dataObject) + , m_clipboard(0) + , m_helper(Pasteboard::generalPasteboard()->helper()) + , m_frame(frame) +{ +} + +ClipboardGtk::~ClipboardGtk() +{ +} + +static ClipboardDataType dataObjectTypeFromHTMLClipboardType(const String& rawType) +{ + String type(rawType.stripWhiteSpace()); + + // Two special cases for IE compatibility + if (type == "Text") + return ClipboardDataTypeText; + if (type == "URL") + return ClipboardDataTypeURL; + + // From the Mac port: Ignore any trailing charset - JS strings are + // Unicode, which encapsulates the charset issue. + if (type == "text/plain" || type.startsWith("text/plain;")) + return ClipboardDataTypeText; + if (type == "text/html" || type.startsWith("text/html;")) + return ClipboardDataTypeMarkup; + if (type == "Files" || type == "text/uri-list" || type.startsWith("text/uri-list;")) + return ClipboardDataTypeURIList; + + // Not a known type, so just default to using the text portion. + return ClipboardDataTypeUnknown; +} + +void ClipboardGtk::clearData(const String& typeString) +{ + if (policy() != ClipboardWritable) + return; + + ClipboardDataType type = dataObjectTypeFromHTMLClipboardType(typeString); + switch (type) { + case ClipboardDataTypeURIList: + case ClipboardDataTypeURL: + m_dataObject->clearURIList(); + break; + case ClipboardDataTypeMarkup: + m_dataObject->clearMarkup(); + break; + case ClipboardDataTypeText: + m_dataObject->clearText(); + break; + case ClipboardDataTypeUnknown: + default: + m_dataObject->clear(); + } + + if (m_clipboard) + m_helper->writeClipboardContents(m_clipboard); +} + + +void ClipboardGtk::clearAllData() +{ + if (policy() != ClipboardWritable) + return; + + m_dataObject->clear(); + + if (m_clipboard) + m_helper->writeClipboardContents(m_clipboard); +} + +String ClipboardGtk::getData(const String& typeString, bool& success) const +{ + success = true; // According to http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html + // "The getData(format) method must return the data that is associated with the type format converted + // to ASCII lowercase, if any, and must return the empty string otherwise." Since success == false + // results in an 'undefined' return value, we always want to return success == true. This parameter + // should eventually be removed. + if (policy() != ClipboardReadable || !m_dataObject) + return String(); + + if (m_clipboard) + m_helper->getClipboardContents(m_clipboard); + + ClipboardDataType type = dataObjectTypeFromHTMLClipboardType(typeString); + if (type == ClipboardDataTypeURIList) + return m_dataObject->uriList(); + if (type == ClipboardDataTypeURL) + return m_dataObject->url(); + if (type == ClipboardDataTypeMarkup) + return m_dataObject->markup(); + if (type == ClipboardDataTypeText) + return m_dataObject->text(); + + return String(); +} + +bool ClipboardGtk::setData(const String& typeString, const String& data) +{ + if (policy() != ClipboardWritable) + return false; + + bool success = false; + ClipboardDataType type = dataObjectTypeFromHTMLClipboardType(typeString); + if (type == ClipboardDataTypeURIList || type == ClipboardDataTypeURL) { + m_dataObject->setURIList(data); + success = true; + } else if (type == ClipboardDataTypeMarkup) { + m_dataObject->setMarkup(data); + success = true; + } else if (type == ClipboardDataTypeText) { + m_dataObject->setText(data); + success = true; + } + + if (success && m_clipboard) + m_helper->writeClipboardContents(m_clipboard); + + return success; +} + +HashSet<String> ClipboardGtk::types() const +{ + if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) + return HashSet<String>(); + + if (m_clipboard) + m_helper->getClipboardContents(m_clipboard); + + HashSet<String> types; + if (m_dataObject->hasText()) { + types.add("text/plain"); + types.add("Text"); + } + + if (m_dataObject->hasMarkup()) + types.add("text/html"); + + if (m_dataObject->hasURIList()) { + types.add("text/uri-list"); + types.add("URL"); + } + + if (m_dataObject->hasFilenames()) + types.add("Files"); + + return types; +} + +PassRefPtr<FileList> ClipboardGtk::files() const +{ + if (policy() != ClipboardReadable) + return FileList::create(); + + if (m_clipboard) + m_helper->getClipboardContents(m_clipboard); + + RefPtr<FileList> fileList = FileList::create(); + const Vector<String>& filenames = m_dataObject->filenames(); + for (size_t i = 0; i < filenames.size(); i++) + fileList->append(File::create(filenames[i])); + return fileList.release(); +} + +void ClipboardGtk::setDragImage(CachedImage* image, const IntPoint& location) +{ + setDragImage(image, 0, location); +} + +void ClipboardGtk::setDragImageElement(Node* element, const IntPoint& location) +{ + setDragImage(0, element, location); +} + +void ClipboardGtk::setDragImage(CachedImage* image, Node* element, const IntPoint& location) +{ + if (policy() != ClipboardImageWritable && policy() != ClipboardWritable) + return; + + if (m_dragImage) + m_dragImage->removeClient(this); + m_dragImage = image; + if (m_dragImage) + m_dragImage->addClient(this); + + m_dragLoc = location; + m_dragImageElement = element; +} + +DragImageRef ClipboardGtk::createDragImage(IntPoint& location) const +{ + location = m_dragLoc; + if (!m_dragImage) + return 0; + + return createDragImageFromImage(m_dragImage->image()); +} + +static CachedImage* getCachedImage(Element* element) +{ + // Attempt to pull CachedImage from element + ASSERT(element); + RenderObject* renderer = element->renderer(); + if (!renderer || !renderer->isImage()) + return 0; + + RenderImage* image = static_cast<RenderImage*>(renderer); + if (image->cachedImage() && !image->cachedImage()->errorOccurred()) + return image->cachedImage(); + + return 0; +} + +void ClipboardGtk::declareAndWriteDragImage(Element* element, const KURL& url, const String& label, Frame* frame) +{ + m_dataObject->setURL(url, label); + m_dataObject->setMarkup(createMarkup(element, IncludeNode, 0, AbsoluteURLs)); + + CachedImage* image = getCachedImage(element); + if (!image || !image->isLoaded()) + return; + + GRefPtr<GdkPixbuf> pixbuf = adoptGRef(image->image()->getGdkPixbuf()); + if (!pixbuf) + return; + + m_dataObject->setImage(pixbuf.get()); +} + +void ClipboardGtk::writeURL(const KURL& url, const String& label, Frame*) +{ + m_dataObject->setURL(url, label); + if (m_clipboard) + m_helper->writeClipboardContents(m_clipboard); +} + +void ClipboardGtk::writeRange(Range* range, Frame* frame) +{ + ASSERT(range); + + m_dataObject->setText(frame->editor()->selectedText()); + m_dataObject->setMarkup(createMarkup(range, 0, AnnotateForInterchange, false, AbsoluteURLs)); + + if (m_clipboard) + m_helper->writeClipboardContents(m_clipboard); +} + +void ClipboardGtk::writePlainText(const String& text) +{ + m_dataObject->setText(text); + + if (m_clipboard) + m_helper->writeClipboardContents(m_clipboard); +} + +bool ClipboardGtk::hasData() +{ + if (m_clipboard) + m_helper->getClipboardContents(m_clipboard); + + return m_dataObject->hasText() || m_dataObject->hasMarkup() + || m_dataObject->hasURIList() || m_dataObject->hasImage(); +} + +} diff --git a/Source/WebCore/platform/gtk/ClipboardGtk.h b/Source/WebCore/platform/gtk/ClipboardGtk.h new file mode 100644 index 0000000..e14a583 --- /dev/null +++ b/Source/WebCore/platform/gtk/ClipboardGtk.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007, Holger Hans Peter Freyther + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ClipboardGtk_h +#define ClipboardGtk_h + +#include "CachedResourceClient.h" +#include "Clipboard.h" +#include "DataObjectGtk.h" + +namespace WebCore { + class CachedImage; + class Frame; + class PasteboardHelper; + + // State available during IE's events for drag and drop and copy/paste + // Created from the EventHandlerGtk to be used by the dom + class ClipboardGtk : public Clipboard, public CachedResourceClient { + public: + static PassRefPtr<ClipboardGtk> create(ClipboardAccessPolicy policy, GtkClipboard* clipboard, Frame* frame) + { + return adoptRef(new ClipboardGtk(policy, clipboard, frame)); + } + + static PassRefPtr<ClipboardGtk> create(ClipboardAccessPolicy policy, PassRefPtr<DataObjectGtk> dataObject, ClipboardType clipboardType, Frame* frame) + { + return adoptRef(new ClipboardGtk(policy, dataObject, clipboardType, frame)); + } + virtual ~ClipboardGtk(); + + void clearData(const String&); + void clearAllData(); + String getData(const String&, bool&) const; + bool setData(const String&, const String&); + + virtual HashSet<String> types() const; + virtual PassRefPtr<FileList> files() const; + + void setDragImage(CachedImage*, const IntPoint&); + void setDragImageElement(Node*, const IntPoint&); + void setDragImage(CachedImage*, Node*, const IntPoint&); + + virtual DragImageRef createDragImage(IntPoint&) const; +#if ENABLE(DRAG_SUPPORT) + virtual void declareAndWriteDragImage(Element*, const KURL&, const String&, Frame*); +#endif + virtual void writeURL(const KURL&, const String&, Frame*); + virtual void writeRange(Range*, Frame*); + virtual void writePlainText(const String&); + + virtual bool hasData(); + + PasteboardHelper* helper() { return m_helper; } + PassRefPtr<DataObjectGtk> dataObject() { return m_dataObject; } + + private: + ClipboardGtk(ClipboardAccessPolicy, GtkClipboard*, Frame*); + ClipboardGtk(ClipboardAccessPolicy, PassRefPtr<DataObjectGtk>, ClipboardType, Frame*); + + RefPtr<DataObjectGtk> m_dataObject; + GtkClipboard* m_clipboard; + PasteboardHelper* m_helper; + Frame* m_frame; + }; +} + +#endif diff --git a/Source/WebCore/platform/gtk/ClipboardUtilitiesGtk.cpp b/Source/WebCore/platform/gtk/ClipboardUtilitiesGtk.cpp new file mode 100644 index 0000000..9fc0e74 --- /dev/null +++ b/Source/WebCore/platform/gtk/ClipboardUtilitiesGtk.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010, Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "ClipboardUtilitiesGtk.h" + +namespace WebCore { + +GdkDragAction dragOperationToGdkDragActions(DragOperation coreAction) +{ + GdkDragAction gdkAction = static_cast<GdkDragAction>(0); + if (coreAction == DragOperationNone) + return gdkAction; + + if (coreAction & DragOperationCopy) + gdkAction = static_cast<GdkDragAction>(GDK_ACTION_COPY | gdkAction); + if (coreAction & DragOperationMove) + gdkAction = static_cast<GdkDragAction>(GDK_ACTION_MOVE | gdkAction); + if (coreAction & DragOperationLink) + gdkAction = static_cast<GdkDragAction>(GDK_ACTION_LINK | gdkAction); + if (coreAction & DragOperationPrivate) + gdkAction = static_cast<GdkDragAction>(GDK_ACTION_PRIVATE | gdkAction); + + return gdkAction; +} + +GdkDragAction dragOperationToSingleGdkDragAction(DragOperation coreAction) +{ + if (coreAction == DragOperationEvery || coreAction & DragOperationCopy) + return GDK_ACTION_COPY; + if (coreAction & DragOperationMove) + return GDK_ACTION_MOVE; + if (coreAction & DragOperationLink) + return GDK_ACTION_LINK; + if (coreAction & DragOperationPrivate) + return GDK_ACTION_PRIVATE; + return static_cast<GdkDragAction>(0); +} + +DragOperation gdkDragActionToDragOperation(GdkDragAction gdkAction) +{ + // We have no good way to detect DragOperationEvery other than + // to use it when all applicable flags are on. + if (gdkAction & GDK_ACTION_COPY && gdkAction & GDK_ACTION_MOVE + && gdkAction & GDK_ACTION_LINK && gdkAction & GDK_ACTION_PRIVATE) + return DragOperationEvery; + + unsigned int action = DragOperationNone; + if (gdkAction & GDK_ACTION_COPY) + action |= DragOperationCopy; + if (gdkAction & GDK_ACTION_MOVE) + action |= DragOperationMove; + if (gdkAction & GDK_ACTION_LINK) + action |= DragOperationLink; + if (gdkAction & GDK_ACTION_PRIVATE) + action |= DragOperationPrivate; + return static_cast<DragOperation>(action); +} + +} diff --git a/Source/WebCore/platform/gtk/ClipboardUtilitiesGtk.h b/Source/WebCore/platform/gtk/ClipboardUtilitiesGtk.h new file mode 100644 index 0000000..26f6346 --- /dev/null +++ b/Source/WebCore/platform/gtk/ClipboardUtilitiesGtk.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010, Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ClipboardUtilitiesGtk_h +#define ClipboardUtilitiesGtk_h + +#include "DragActions.h" +#include <gdk/gdk.h> + +namespace WebCore { + +GdkDragAction dragOperationToGdkDragActions(DragOperation); +GdkDragAction dragOperationToSingleGdkDragAction(DragOperation); +DragOperation gdkDragActionToDragOperation(GdkDragAction); + +} + +#endif // ClipboardUtilitiesGtk_h diff --git a/Source/WebCore/platform/gtk/ContextMenuGtk.cpp b/Source/WebCore/platform/gtk/ContextMenuGtk.cpp new file mode 100644 index 0000000..423959a --- /dev/null +++ b/Source/WebCore/platform/gtk/ContextMenuGtk.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007 Holger Hans Peter Freyther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "ContextMenu.h" + +#include <gtk/gtk.h> + +namespace WebCore { + +ContextMenu::ContextMenu() +{ + m_platformDescription = GTK_MENU(gtk_menu_new()); + + g_object_ref_sink(G_OBJECT(m_platformDescription)); +} + +ContextMenu::~ContextMenu() +{ + if (m_platformDescription) + g_object_unref(m_platformDescription); +} + +void ContextMenu::appendItem(ContextMenuItem& item) +{ + ASSERT(m_platformDescription); + + GtkMenuItem* platformItem = item.releasePlatformDescription(); + ASSERT(platformItem); + gtk_menu_shell_append(GTK_MENU_SHELL(m_platformDescription), GTK_WIDGET(platformItem)); + gtk_widget_show(GTK_WIDGET(platformItem)); +} + +void ContextMenu::setPlatformDescription(PlatformMenuDescription menu) +{ + ASSERT(menu); + if (m_platformDescription) + g_object_unref(m_platformDescription); + + m_platformDescription = menu; + g_object_ref(m_platformDescription); +} + +PlatformMenuDescription ContextMenu::platformDescription() const +{ + return m_platformDescription; +} + +PlatformMenuDescription ContextMenu::releasePlatformDescription() +{ + PlatformMenuDescription description = m_platformDescription; + m_platformDescription = 0; + + return description; +} + +} diff --git a/Source/WebCore/platform/gtk/ContextMenuItemGtk.cpp b/Source/WebCore/platform/gtk/ContextMenuItemGtk.cpp new file mode 100644 index 0000000..4d79f13 --- /dev/null +++ b/Source/WebCore/platform/gtk/ContextMenuItemGtk.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2010 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "ContextMenuItem.h" + +#include "ContextMenu.h" +#include "GOwnPtr.h" +#include "NotImplemented.h" +#include <gtk/gtk.h> +#include <wtf/text/CString.h> + +#define WEBKIT_CONTEXT_MENU_ACTION "webkit-context-menu" + +namespace WebCore { + +static const char* gtkStockIDFromContextMenuAction(const ContextMenuAction& action) +{ + switch (action) { + case ContextMenuItemTagCopyLinkToClipboard: + case ContextMenuItemTagCopyImageToClipboard: + case ContextMenuItemTagCopyMediaLinkToClipboard: + case ContextMenuItemTagCopy: + return GTK_STOCK_COPY; + case ContextMenuItemTagOpenLinkInNewWindow: + case ContextMenuItemTagOpenImageInNewWindow: + case ContextMenuItemTagOpenFrameInNewWindow: + case ContextMenuItemTagOpenMediaInNewWindow: + return GTK_STOCK_OPEN; + case ContextMenuItemTagDownloadLinkToDisk: + case ContextMenuItemTagDownloadImageToDisk: + return GTK_STOCK_SAVE; + case ContextMenuItemTagGoBack: + return GTK_STOCK_GO_BACK; + case ContextMenuItemTagGoForward: + return GTK_STOCK_GO_FORWARD; + case ContextMenuItemTagStop: + return GTK_STOCK_STOP; + case ContextMenuItemTagReload: + return GTK_STOCK_REFRESH; + case ContextMenuItemTagCut: + return GTK_STOCK_CUT; + case ContextMenuItemTagPaste: + return GTK_STOCK_PASTE; + case ContextMenuItemTagDelete: + return GTK_STOCK_DELETE; + case ContextMenuItemTagSelectAll: + return GTK_STOCK_SELECT_ALL; + case ContextMenuItemTagSpellingGuess: + return 0; + case ContextMenuItemTagIgnoreSpelling: + return GTK_STOCK_NO; + case ContextMenuItemTagLearnSpelling: + return GTK_STOCK_OK; + case ContextMenuItemTagOther: + return GTK_STOCK_MISSING_IMAGE; + case ContextMenuItemTagSearchInSpotlight: + return GTK_STOCK_FIND; + case ContextMenuItemTagSearchWeb: + return GTK_STOCK_FIND; + case ContextMenuItemTagOpenWithDefaultApplication: + return GTK_STOCK_OPEN; + case ContextMenuItemPDFZoomIn: + return GTK_STOCK_ZOOM_IN; + case ContextMenuItemPDFZoomOut: + return GTK_STOCK_ZOOM_OUT; + case ContextMenuItemPDFAutoSize: + return GTK_STOCK_ZOOM_FIT; + case ContextMenuItemPDFNextPage: + return GTK_STOCK_GO_FORWARD; + case ContextMenuItemPDFPreviousPage: + return GTK_STOCK_GO_BACK; + // New tags, not part of API + case ContextMenuItemTagOpenLink: + return GTK_STOCK_OPEN; + case ContextMenuItemTagCheckSpelling: + return GTK_STOCK_SPELL_CHECK; + case ContextMenuItemTagFontMenu: + return GTK_STOCK_SELECT_FONT; + case ContextMenuItemTagShowFonts: + return GTK_STOCK_SELECT_FONT; + case ContextMenuItemTagBold: + return GTK_STOCK_BOLD; + case ContextMenuItemTagItalic: + return GTK_STOCK_ITALIC; + case ContextMenuItemTagUnderline: + return GTK_STOCK_UNDERLINE; + case ContextMenuItemTagShowColors: + return GTK_STOCK_SELECT_COLOR; + case ContextMenuItemTagToggleMediaControls: + case ContextMenuItemTagToggleMediaLoop: + // No icon for this. + return 0; + case ContextMenuItemTagEnterVideoFullscreen: + return GTK_STOCK_FULLSCREEN; + default: + return 0; + } +} + +// Extract the ActionType from the menu item +ContextMenuItem::ContextMenuItem(PlatformMenuItemDescription item) + : m_platformDescription(item) +{ +} + +ContextMenuItem::ContextMenuItem(ContextMenu*) +{ + notImplemented(); +} + +ContextMenuItem::ContextMenuItem(ContextMenuItemType type, ContextMenuAction action, const String& title, ContextMenu* subMenu) +{ + if (type == SeparatorType) { + m_platformDescription = GTK_MENU_ITEM(gtk_separator_menu_item_new()); + return; + } + + GOwnPtr<char> actionName(g_strdup_printf("context-menu-action-%d", action)); + GtkAction* platformAction = 0; + + if (type == CheckableActionType) + platformAction = GTK_ACTION(gtk_toggle_action_new(actionName.get(), title.utf8().data(), 0, gtkStockIDFromContextMenuAction(action))); + else + platformAction = gtk_action_new(actionName.get(), title.utf8().data(), 0, gtkStockIDFromContextMenuAction(action)); + + m_platformDescription = GTK_MENU_ITEM(gtk_action_create_menu_item(platformAction)); + g_object_unref(platformAction); + + g_object_set_data(G_OBJECT(m_platformDescription.get()), WEBKIT_CONTEXT_MENU_ACTION, GINT_TO_POINTER(action)); + + if (subMenu) + setSubMenu(subMenu); +} + +ContextMenuItem::~ContextMenuItem() +{ +} + +PlatformMenuItemDescription ContextMenuItem::releasePlatformDescription() +{ + return m_platformDescription.leakRef(); +} + +ContextMenuItemType ContextMenuItem::type() const +{ + if (GTK_IS_SEPARATOR_MENU_ITEM(m_platformDescription.get())) + return SeparatorType; + if (GTK_IS_CHECK_MENU_ITEM(m_platformDescription.get())) + return CheckableActionType; + if (gtk_menu_item_get_submenu(m_platformDescription.get())) + return SubmenuType; + return ActionType; +} + +void ContextMenuItem::setType(ContextMenuItemType type) +{ + if (type == SeparatorType) + m_platformDescription = GTK_MENU_ITEM(gtk_separator_menu_item_new()); +} + +ContextMenuAction ContextMenuItem::action() const +{ + return static_cast<ContextMenuAction>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(m_platformDescription.get()), WEBKIT_CONTEXT_MENU_ACTION))); +} + +void ContextMenuItem::setAction(ContextMenuAction action) +{ + g_object_set_data(G_OBJECT(m_platformDescription.get()), WEBKIT_CONTEXT_MENU_ACTION, GINT_TO_POINTER(action)); +} + +String ContextMenuItem::title() const +{ + GtkAction* action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(m_platformDescription.get())); + return action ? String(gtk_action_get_label(action)) : String(); +} + +void ContextMenuItem::setTitle(const String& title) +{ + GtkAction* action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(m_platformDescription.get())); + if (action) + gtk_action_set_label(action, title.utf8().data()); +} + +PlatformMenuDescription ContextMenuItem::platformSubMenu() const +{ + GtkWidget* subMenu = gtk_menu_item_get_submenu(m_platformDescription.get()); + return subMenu ? GTK_MENU(subMenu) : 0; +} + +void ContextMenuItem::setSubMenu(ContextMenu* menu) +{ + gtk_menu_item_set_submenu(m_platformDescription.get(), GTK_WIDGET(menu->platformDescription())); +} + +void ContextMenuItem::setChecked(bool shouldCheck) +{ + GtkAction* action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(m_platformDescription.get())); + if (action && GTK_IS_TOGGLE_ACTION(action)) + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), shouldCheck); +} + +void ContextMenuItem::setEnabled(bool shouldEnable) +{ + GtkAction* action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(m_platformDescription.get())); + if (action) + gtk_action_set_sensitive(action, shouldEnable); +} + +} diff --git a/Source/WebCore/platform/gtk/CursorGtk.cpp b/Source/WebCore/platform/gtk/CursorGtk.cpp new file mode 100644 index 0000000..9c5c16c --- /dev/null +++ b/Source/WebCore/platform/gtk/CursorGtk.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007 Christian Dywan <christian@twotoasts.de> + * Copyright (C) 2010 Igalia S.L. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CursorGtk.h" +#include "GtkVersioning.h" + +#include "Image.h" +#include "IntPoint.h" +#include "RefPtrCairo.h" +#include <gdk/gdk.h> +#include <gtk/gtk.h> +#include <wtf/Assertions.h> + +namespace WebCore { + +static GRefPtr<GdkCursor> createNamedCursor(CustomCursorType cursorType) +{ + CustomCursor cursor = CustomCursors[cursorType]; + GRefPtr<GdkCursor> c = adoptGRef(gdk_cursor_new_from_name(gdk_display_get_default(), cursor.name)); + if (c) + return c; + + IntSize cursorSize = IntSize(32, 32); + RefPtr<cairo_surface_t> source = adoptRef(cairo_image_surface_create_for_data(const_cast<unsigned char*>(cursor.bits), CAIRO_FORMAT_A1, 32, 32, 4)); + RefPtr<cairo_surface_t> mask = adoptRef(cairo_image_surface_create_for_data(const_cast<unsigned char*>(cursor.mask_bits), CAIRO_FORMAT_A1, 32, 32, 4)); + RefPtr<cairo_surface_t> surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_A1, 32, 32)); + RefPtr<cairo_t> cr = adoptRef(cairo_create(surface.get())); + + cairo_set_source_surface(cr.get(), source.get(), cursor.hot_x, cursor.hot_y); + cairo_mask_surface(cr.get(), mask.get(), cursor.hot_x, cursor.hot_y); + + GRefPtr<GdkPixbuf> pixbuf = adoptGRef(gdk_pixbuf_get_from_surface(surface.get(), 0, 0, 32, 32)); + return adoptGRef(gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pixbuf.get(), 0, 0)); +} + +static GRefPtr<GdkCursor> createCustomCursor(Image* image, const IntPoint& hotSpot) +{ + IntPoint effectiveHotSpot = determineHotSpot(image, hotSpot); + GRefPtr<GdkPixbuf> pixbuf = adoptGRef(image->getGdkPixbuf()); + return adoptGRef(gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pixbuf.get(), effectiveHotSpot.x(), effectiveHotSpot.y())); +} + +void Cursor::ensurePlatformCursor() const +{ + if (m_platformCursor || m_type == Cursor::Pointer) + return; + + switch (m_type) { + case Cursor::Pointer: + // A null GdkCursor is the default cursor for the window. + m_platformCursor = 0; + break; + case Cursor::Cross: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_CROSS)); + break; + case Cursor::Hand: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_HAND2)); + break; + case Cursor::IBeam: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_XTERM)); + break; + case Cursor::Wait: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_WATCH)); + break; + case Cursor::Help: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_QUESTION_ARROW)); + break; + case Cursor::Move: + case Cursor::MiddlePanning: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_FLEUR)); + break; + case Cursor::EastResize: + case Cursor::EastPanning: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_RIGHT_SIDE)); + break; + case Cursor::NorthResize: + case Cursor::NorthPanning: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_TOP_SIDE)); + break; + case Cursor::NorthEastResize: + case Cursor::NorthEastPanning: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_LEFT_SIDE)); + break; + case Cursor::NorthWestResize: + case Cursor::NorthWestPanning: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_TOP_LEFT_CORNER)); + break; + case Cursor::SouthResize: + case Cursor::SouthPanning: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_BOTTOM_SIDE)); + break; + case Cursor::SouthEastResize: + case Cursor::SouthEastPanning: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER)); + break; + case Cursor::SouthWestResize: + case Cursor::SouthWestPanning: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_BOTTOM_LEFT_CORNER)); + break; + case Cursor::WestResize: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_LEFT_SIDE)); + break; + case Cursor::NorthSouthResize: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_TOP_TEE)); + break; + case Cursor::EastWestResize: + case Cursor::WestPanning: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_LEFT_SIDE)); + break; + case Cursor::NorthEastSouthWestResize: + case Cursor::NorthWestSouthEastResize: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_SIZING)); + break; + case Cursor::ColumnResize: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW)); + break; + case Cursor::RowResize: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW)); + break; + case Cursor::VerticalText: + m_platformCursor = createNamedCursor(CustomCursorVerticalText); + break; + case Cursor::Cell: + m_platformCursor = adoptGRef(gdk_cursor_new(GDK_PLUS)); + break; + case Cursor::ContextMenu: + m_platformCursor = createNamedCursor(CustomCursorContextMenu); + break; + case Cursor::Alias: + m_platformCursor = createNamedCursor(CustomCursorAlias); + break; + case Cursor::Progress: + m_platformCursor = createNamedCursor(CustomCursorProgress); + break; + case Cursor::NoDrop: + case Cursor::NotAllowed: + m_platformCursor = createNamedCursor(CustomCursorNoDrop); + break; + case Cursor::Copy: + m_platformCursor = createNamedCursor(CustomCursorCopy); + break; + case Cursor::None: + m_platformCursor = createNamedCursor(CustomCursorNone); + break; + case Cursor::ZoomIn: + m_platformCursor = createNamedCursor(CustomCursorZoomIn); + break; + case Cursor::ZoomOut: + m_platformCursor = createNamedCursor(CustomCursorZoomOut); + break; + case Cursor::Grab: + m_platformCursor = createNamedCursor(CustomCursorGrab); + break; + case Cursor::Grabbing: + m_platformCursor = createNamedCursor(CustomCursorGrabbing); + break; + case Cursor::Custom: + m_platformCursor = createCustomCursor(m_image.get(), m_hotSpot); + break; + } +} + +Cursor::Cursor(const Cursor& other) + : m_type(other.m_type) + , m_image(other.m_image) + , m_hotSpot(other.m_hotSpot) + , m_platformCursor(other.m_platformCursor) +{ +} + +Cursor& Cursor::operator=(const Cursor& other) +{ + m_type = other.m_type; + m_image = other.m_image; + m_hotSpot = other.m_hotSpot; + m_platformCursor = other.m_platformCursor; + return *this; +} + +Cursor::~Cursor() +{ +} + +} diff --git a/Source/WebCore/platform/gtk/CursorGtk.h b/Source/WebCore/platform/gtk/CursorGtk.h new file mode 100644 index 0000000..568919b --- /dev/null +++ b/Source/WebCore/platform/gtk/CursorGtk.h @@ -0,0 +1,383 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Tim Copperfield. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Tim Copperfield <timecop@network.email.ne.jp> + * Christian Dywan <christian@twotoasts.de> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef CursorGtk_h +#define CursorGtk_h + +#include "Cursor.h" + +/* + These cursors are copied from Mozilla code: + http://lxr.mozilla.org/mozilla1.8/source/widget/src/gtk2/nsGtkCursors.h +*/ + +/* MOZ_CURSOR_VERTICAL_TEXT */ +static const unsigned char moz_vertical_text_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, + 0x06, 0x60, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, + 0x02, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_vertical_text_mask_bits[] = { + 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x0f, 0xf0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MOZ_CURSOR_CONTEXT_MENU */ +static const unsigned char moz_menu_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xfc, 0xfd, 0x00, 0x00, + 0xfc, 0xff, 0x00, 0x00, 0x7c, 0x84, 0x00, 0x00, 0x6c, 0xfc, 0x00, 0x00, + 0xc4, 0x84, 0x00, 0x00, 0xc0, 0xfc, 0x00, 0x00, 0x80, 0x85, 0x00, 0x00, + 0x80, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_menu_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x00, 0x00, 0xfe, 0xfd, 0x00, 0x00, 0xfe, 0xff, 0x01, 0x00, + 0xfe, 0xff, 0x01, 0x00, 0xfe, 0xff, 0x01, 0x00, 0xfe, 0xfe, 0x01, 0x00, + 0xee, 0xff, 0x01, 0x00, 0xe4, 0xff, 0x01, 0x00, 0xc0, 0xff, 0x01, 0x00, + 0xc0, 0xff, 0x01, 0x00, 0x80, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MOZ_CURSOR_COPY */ +static const unsigned char moz_copy_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, + 0xfc, 0x03, 0x00, 0x00, 0x7c, 0x30, 0x00, 0x00, 0x6c, 0x30, 0x00, 0x00, + 0xc4, 0xfc, 0x00, 0x00, 0xc0, 0xfc, 0x00, 0x00, 0x80, 0x31, 0x00, 0x00, + 0x80, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_copy_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x00, + 0xfe, 0x37, 0x00, 0x00, 0xfe, 0x7b, 0x00, 0x00, 0xfe, 0xfc, 0x00, 0x00, + 0xee, 0xff, 0x01, 0x00, 0xe4, 0xff, 0x01, 0x00, 0xc0, 0xff, 0x00, 0x00, + 0xc0, 0x7b, 0x00, 0x00, 0x80, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MOZ_CURSOR_ALIAS */ +static const unsigned char moz_alias_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, + 0xfc, 0x03, 0x00, 0x00, 0x7c, 0xf0, 0x00, 0x00, 0x6c, 0xe0, 0x00, 0x00, + 0xc4, 0xf0, 0x00, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, + 0x80, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_alias_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x00, + 0xfe, 0xf7, 0x00, 0x00, 0xfe, 0xfb, 0x01, 0x00, 0xfe, 0xf0, 0x01, 0x00, + 0xee, 0xf9, 0x01, 0x00, 0xe4, 0xf9, 0x01, 0x00, 0xc0, 0xbf, 0x00, 0x00, + 0xc0, 0x3f, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MOZ_CURSOR_ZOOM_IN */ +static const unsigned char moz_zoom_in_bits[] = { + 0xf0, 0x00, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, + 0x62, 0x04, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, 0xf9, 0x09, 0x00, 0x00, + 0xf9, 0x09, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, 0x62, 0x04, 0x00, 0x00, + 0x02, 0x04, 0x00, 0x00, 0x0c, 0x0f, 0x00, 0x00, 0xf0, 0x1c, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_zoom_in_mask_bits[] = { + 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, + 0xfe, 0x07, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, + 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, + 0xfe, 0x07, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0xf0, 0x1c, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MOZ_CURSOR_ZOOM_OUT */ +static const unsigned char moz_zoom_out_bits[] = { + 0xf0, 0x00, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, + 0x02, 0x04, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0xf9, 0x09, 0x00, 0x00, + 0xf9, 0x09, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, + 0x02, 0x04, 0x00, 0x00, 0x0c, 0x0f, 0x00, 0x00, 0xf0, 0x1c, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_zoom_out_mask_bits[] = { + 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, + 0xfe, 0x07, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, + 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, + 0xfe, 0x07, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0xf0, 0x1c, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MOZ_CURSOR_NOT_ALLOWED */ +static const unsigned char moz_not_allowed_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, + 0xf0, 0xf0, 0x00, 0x00, 0x38, 0xc0, 0x01, 0x00, 0x7c, 0x80, 0x03, 0x00, + 0xec, 0x00, 0x03, 0x00, 0xce, 0x01, 0x07, 0x00, 0x86, 0x03, 0x06, 0x00, + 0x06, 0x07, 0x06, 0x00, 0x06, 0x0e, 0x06, 0x00, 0x06, 0x1c, 0x06, 0x00, + 0x0e, 0x38, 0x07, 0x00, 0x0c, 0x70, 0x03, 0x00, 0x1c, 0xe0, 0x03, 0x00, + 0x38, 0xc0, 0x01, 0x00, 0xf0, 0xf0, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, + 0x80, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_not_allowed_mask_bits[] = { + 0x80, 0x1f, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0xf0, 0xff, 0x00, 0x00, + 0xf8, 0xff, 0x01, 0x00, 0xfc, 0xf0, 0x03, 0x00, 0xfe, 0xc0, 0x07, 0x00, + 0xfe, 0x81, 0x07, 0x00, 0xff, 0x83, 0x0f, 0x00, 0xcf, 0x07, 0x0f, 0x00, + 0x8f, 0x0f, 0x0f, 0x00, 0x0f, 0x1f, 0x0f, 0x00, 0x0f, 0x3e, 0x0f, 0x00, + 0x1f, 0xfc, 0x0f, 0x00, 0x1e, 0xf8, 0x07, 0x00, 0x3e, 0xf0, 0x07, 0x00, + 0xfc, 0xf0, 0x03, 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x00, 0x00, + 0xe0, 0x7f, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MOZ_CURSOR_SPINNING */ +static const unsigned char moz_spinning_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, + 0xfc, 0x3b, 0x00, 0x00, 0x7c, 0x38, 0x00, 0x00, 0x6c, 0x54, 0x00, 0x00, + 0xc4, 0xdc, 0x00, 0x00, 0xc0, 0x44, 0x00, 0x00, 0x80, 0x39, 0x00, 0x00, + 0x80, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_spinning_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x3b, 0x00, 0x00, + 0xfe, 0x7f, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, + 0xee, 0xff, 0x01, 0x00, 0xe4, 0xff, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, + 0xc0, 0x7f, 0x00, 0x00, 0x80, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MOZ_CURSOR_NONE */ +static const unsigned char moz_none_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_none_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MOZ_CURSOR_HAND_GRAB */ +static const unsigned char moz_hand_grab_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x60, 0x39, 0x00, 0x00, 0x90, 0x49, 0x00, 0x00, 0x90, 0x49, 0x01, 0x00, + 0x20, 0xc9, 0x02, 0x00, 0x20, 0x49, 0x02, 0x00, 0x58, 0x40, 0x02, 0x00, + 0x64, 0x00, 0x02, 0x00, 0x44, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x10, 0x00, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_hand_grab_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x60, 0x3f, 0x00, 0x00, + 0xf0, 0x7f, 0x00, 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf8, 0xff, 0x03, 0x00, + 0xf0, 0xff, 0x07, 0x00, 0xf8, 0xff, 0x07, 0x00, 0xfc, 0xff, 0x07, 0x00, + 0xfe, 0xff, 0x07, 0x00, 0xfe, 0xff, 0x03, 0x00, 0xfc, 0xff, 0x03, 0x00, + 0xf8, 0xff, 0x03, 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x01, 0x00, + 0xe0, 0xff, 0x00, 0x00, 0xc0, 0xff, 0x00, 0x00, 0xc0, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MOZ_CURSOR_HAND_GRABBING */ +static const unsigned char moz_hand_grabbing_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x36, 0x00, 0x00, 0x20, 0xc9, 0x00, 0x00, 0x20, 0x40, 0x01, 0x00, + 0x40, 0x00, 0x01, 0x00, 0x60, 0x00, 0x01, 0x00, 0x10, 0x00, 0x01, 0x00, + 0x10, 0x00, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char moz_hand_grabbing_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x36, 0x00, 0x00, + 0xe0, 0xff, 0x00, 0x00, 0xf0, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x03, 0x00, + 0xe0, 0xff, 0x03, 0x00, 0xf0, 0xff, 0x03, 0x00, 0xf8, 0xff, 0x03, 0x00, + 0xf8, 0xff, 0x03, 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x01, 0x00, + 0xe0, 0xff, 0x00, 0x00, 0xc0, 0xff, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +enum CustomCursorType { + CustomCursorCopy = 0, + CustomCursorAlias, + CustomCursorContextMenu, + CustomCursorZoomIn, + CustomCursorZoomOut, + CustomCursorVerticalText, + CustomCursorNoDrop, + CustomCursorProgress, + CustomCursorNone, + CustomCursorGrab, + CustomCursorGrabbing, +}; + +typedef struct { + const char* name; + const unsigned char* bits; + const unsigned char* mask_bits; + int hot_x; + int hot_y; +} CustomCursor; + +// create custom pixmap cursor from cursors in nsGTKCursorData.h +static const CustomCursor CustomCursors[] = { + { "copy", moz_copy_bits, moz_copy_mask_bits, 2, 2 }, + { "alias", moz_alias_bits, moz_alias_mask_bits, 2, 2 }, + { "context-menu", moz_menu_bits, moz_menu_mask_bits, 2, 2 }, + { "zoom-in", moz_zoom_in_bits, moz_zoom_in_mask_bits, 6, 6 }, + { "zoom-out", moz_zoom_out_bits, moz_zoom_out_mask_bits, 6, 6 }, + { "vertical-text", moz_vertical_text_bits, moz_vertical_text_mask_bits, 8, 4 }, + { "dnd-no-drop", moz_not_allowed_bits, moz_not_allowed_mask_bits, 9, 9 }, + { "left_ptr_watch", moz_spinning_bits, moz_spinning_mask_bits, 2, 2}, + { "none", moz_none_bits, moz_none_mask_bits, 0, 0 }, + { "grab", moz_hand_grab_bits, moz_hand_grab_mask_bits, 10, 10 }, + { "grabbing", moz_hand_grabbing_bits, moz_hand_grabbing_mask_bits, 10, 10 } +}; + +#endif // CursorGtk_h diff --git a/Source/WebCore/platform/gtk/DataObjectGtk.cpp b/Source/WebCore/platform/gtk/DataObjectGtk.cpp new file mode 100644 index 0000000..05a2da9 --- /dev/null +++ b/Source/WebCore/platform/gtk/DataObjectGtk.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2009, Martin Robinson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "DataObjectGtk.h" + +#include "markup.h" +#include <gtk/gtk.h> +#include <wtf/gobject/GOwnPtr.h> + +namespace WebCore { + +static void replaceNonBreakingSpaceWithSpace(String& str) +{ + static const UChar NonBreakingSpaceCharacter = 0xA0; + static const UChar SpaceCharacter = ' '; + str.replace(NonBreakingSpaceCharacter, SpaceCharacter); +} + +String DataObjectGtk::text() +{ + if (m_range) + return m_range->text(); + return m_text; +} + +String DataObjectGtk::markup() +{ + if (m_range) + return createMarkup(m_range.get(), 0, AnnotateForInterchange, false, AbsoluteURLs); + return m_markup; +} + +void DataObjectGtk::setText(const String& newText) +{ + m_range = 0; + m_text = newText; + replaceNonBreakingSpaceWithSpace(m_text); +} + +void DataObjectGtk::setMarkup(const String& newMarkup) +{ + m_range = 0; + m_markup = newMarkup; +} + +void DataObjectGtk::setURIList(const String& uriListString) +{ + m_uriList = uriListString; + + // This code is originally from: platform/chromium/ChromiumDataObject.cpp. + // FIXME: We should make this code cross-platform eventually. + + // Line separator is \r\n per RFC 2483 - however, for compatibility + // reasons we also allow just \n here. + Vector<String> uriList; + uriListString.split('\n', uriList); + + // Process the input and copy the first valid URL into the url member. + // In case no URLs can be found, subsequent calls to getData("URL") + // will get an empty string. This is in line with the HTML5 spec (see + // "The DragEvent and DataTransfer interfaces"). Also extract all filenames + // from the URI list. + bool setURL = false; + for (size_t i = 0; i < uriList.size(); ++i) { + String& line = uriList[i]; + line = line.stripWhiteSpace(); + if (line.isEmpty()) + continue; + if (line[0] == '#') + continue; + + KURL url = KURL(KURL(), line); + if (url.isValid()) { + if (!setURL) { + m_url = url; + setURL = true; + } + + GOwnPtr<GError> error; + GOwnPtr<gchar> filename(g_filename_from_uri(line.utf8().data(), 0, &error.outPtr())); + if (!error && filename) + m_filenames.append(String::fromUTF8(filename.get())); + } + } +} + +void DataObjectGtk::setURL(const KURL& url, const String& label) +{ + m_url = url; + m_uriList = url; + setText(url.string()); + + String actualLabel(label); + if (actualLabel.isEmpty()) + actualLabel = url; + + Vector<UChar> markup; + append(markup, "<a href=\""); + append(markup, url.string()); + append(markup, "\">"); + GOwnPtr<gchar> escaped(g_markup_escape_text(actualLabel.utf8().data(), -1)); + append(markup, String::fromUTF8(escaped.get())); + append(markup, "</a>"); + setMarkup(String::adopt(markup)); +} + +void DataObjectGtk::clearText() +{ + m_range = 0; + m_text = ""; +} + +void DataObjectGtk::clearMarkup() +{ + m_range = 0; + m_markup = ""; +} + +String DataObjectGtk::urlLabel() +{ + if (hasText()) + return text(); + + if (hasURL()) + return url(); + + return String(); +} + +void DataObjectGtk::clear() +{ + m_text = ""; + m_markup = ""; + m_uriList = ""; + m_url = KURL(); + m_image = 0; + m_range = 0; + + // We do not clear filenames. According to the spec: "The clearData() method + // does not affect whether any files were included in the drag, so the types + // attribute's list might still not be empty after calling clearData() (it would + // still contain the "Files" string if any files were included in the drag)." +} + +DataObjectGtk* DataObjectGtk::forClipboard(GtkClipboard* clipboard) +{ + static HashMap<GtkClipboard*, RefPtr<DataObjectGtk> > objectMap; + + if (!objectMap.contains(clipboard)) { + RefPtr<DataObjectGtk> dataObject = DataObjectGtk::create(); + objectMap.set(clipboard, dataObject); + return dataObject.get(); + } + + HashMap<GtkClipboard*, RefPtr<DataObjectGtk> >::iterator it = objectMap.find(clipboard); + return it->second.get(); +} + +} diff --git a/Source/WebCore/platform/gtk/DataObjectGtk.h b/Source/WebCore/platform/gtk/DataObjectGtk.h new file mode 100644 index 0000000..5423f81 --- /dev/null +++ b/Source/WebCore/platform/gtk/DataObjectGtk.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009, Martin Robinson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DataObjectGtk_h +#define DataObjectGtk_h + +#include "FileList.h" +#include <GRefPtr.h> +#include "KURL.h" +#include "Range.h" +#include <wtf/RefCounted.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +class DataObjectGtk : public RefCounted<DataObjectGtk> { +public: + static PassRefPtr<DataObjectGtk> create() + { + return adoptRef(new DataObjectGtk()); + } + + const KURL& url() { return m_url; } + const String& uriList() { return m_uriList; } + const Vector<String>& filenames() { return m_filenames; } + GdkPixbuf* image() { return m_image.get(); } + void setRange(PassRefPtr<Range> newRange) { m_range = newRange; } + void setImage(GdkPixbuf* newImage) { m_image = newImage; } + void setDragContext(GdkDragContext* newDragContext) { m_dragContext = newDragContext; } + void setURL(const KURL&, const String&); + bool hasText() { return m_range || !m_text.isEmpty(); } + bool hasMarkup() { return m_range || !m_markup.isEmpty(); } + bool hasURIList() { return !m_uriList.isEmpty(); } + bool hasURL() { return !m_url.isEmpty() && m_url.isValid(); } + bool hasFilenames() { return !m_filenames.isEmpty(); } + bool hasImage() { return m_image; } + void clearURIList() { m_uriList = ""; } + void clearURL() { m_url = KURL(); } + void clearImage() { m_image = 0; } + GdkDragContext* dragContext() { return m_dragContext.get(); } + + String text(); + String markup(); + void setText(const String&); + void setMarkup(const String&); + void setURIList(const String&); + String urlLabel(); + void clear(); + void clearText(); + void clearMarkup(); + + static DataObjectGtk* forClipboard(GtkClipboard*); + +private: + String m_text; + String m_markup; + KURL m_url; + String m_uriList; + Vector<String> m_filenames; + GRefPtr<GdkPixbuf> m_image; + GRefPtr<GdkDragContext> m_dragContext; + RefPtr<Range> m_range; +}; + +} + +#endif // DataObjectGtk_h diff --git a/Source/WebCore/platform/gtk/DragDataGtk.cpp b/Source/WebCore/platform/gtk/DragDataGtk.cpp new file mode 100644 index 0000000..42ddb16 --- /dev/null +++ b/Source/WebCore/platform/gtk/DragDataGtk.cpp @@ -0,0 +1,90 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "DragData.h" + +#include "Clipboard.h" +#include "ClipboardGtk.h" +#include "Document.h" +#include "DocumentFragment.h" +#include "markup.h" + +namespace WebCore { + +bool DragData::canSmartReplace() const +{ + return false; +} + +bool DragData::containsColor() const +{ + return false; +} + +bool DragData::containsFiles() const +{ + return m_platformDragData->hasFilenames(); +} + +void DragData::asFilenames(Vector<String>& result) const +{ + result = m_platformDragData->filenames(); +} + +bool DragData::containsPlainText() const +{ + return m_platformDragData->hasText(); +} + +String DragData::asPlainText() const +{ + return m_platformDragData->text(); +} + +Color DragData::asColor() const +{ + return Color(); +} + +bool DragData::containsCompatibleContent() const +{ + return containsPlainText() || containsURL() || m_platformDragData->hasMarkup() || containsColor() || containsFiles(); +} + +bool DragData::containsURL(FilenameConversionPolicy filenamePolicy) const +{ + return m_platformDragData->hasURL(); +} + +String DragData::asURL(FilenameConversionPolicy filenamePolicy, String* title) const +{ + String url(m_platformDragData->url()); + if (title) + *title = m_platformDragData->urlLabel(); + return url; +} + + +PassRefPtr<DocumentFragment> DragData::asFragment(Document* document) const +{ + if (!m_platformDragData->hasMarkup()) + return 0; + + return createFragmentFromMarkup(document, m_platformDragData->markup(), ""); +} + +} diff --git a/Source/WebCore/platform/gtk/DragImageGtk.cpp b/Source/WebCore/platform/gtk/DragImageGtk.cpp new file mode 100644 index 0000000..71a65e1 --- /dev/null +++ b/Source/WebCore/platform/gtk/DragImageGtk.cpp @@ -0,0 +1,86 @@ +/* + * 2010 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "DragImage.h" + +#include "CachedImage.h" +#include "Image.h" +#include "RefPtrCairo.h" +#include <cairo.h> + +namespace WebCore { + +IntSize dragImageSize(DragImageRef image) +{ + if (image) + return IntSize(cairo_image_surface_get_width(image), cairo_image_surface_get_height(image)); + + return IntSize(0, 0); +} + +void deleteDragImage(DragImageRef image) +{ + if (image) + cairo_surface_destroy(image); +} + +DragImageRef scaleDragImage(DragImageRef image, FloatSize scale) +{ + if (!image) + return 0; + + int newWidth = scale.width() * cairo_image_surface_get_width(image); + int newHeight = scale.height() * cairo_image_surface_get_height(image); + cairo_surface_t* scaledSurface = cairo_surface_create_similar(image, CAIRO_CONTENT_COLOR_ALPHA, newWidth, newHeight); + + RefPtr<cairo_t> context = adoptRef(cairo_create(scaledSurface)); + cairo_scale(context.get(), scale.width(), scale.height()); + cairo_pattern_set_extend(cairo_get_source(context.get()), CAIRO_EXTEND_PAD); + cairo_pattern_set_filter(cairo_get_source(context.get()), CAIRO_FILTER_BEST); + cairo_set_operator(context.get(), CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface(context.get(), image, 0, 0); + cairo_paint(context.get()); + + deleteDragImage(image); + return scaledSurface; +} + +DragImageRef dissolveDragImageToFraction(DragImageRef image, float fraction) +{ + if (!image) + return 0; + + RefPtr<cairo_t> context = adoptRef(cairo_create(image)); + cairo_set_operator(context.get(), CAIRO_OPERATOR_DEST_IN); + cairo_set_source_rgba(context.get(), 0, 0, 0, fraction); + cairo_paint(context.get()); + return image; +} + +DragImageRef createDragImageFromImage(Image* image) +{ + return cairo_surface_reference(image->nativeImageForCurrentFrame()); +} + +DragImageRef createDragImageIconForCachedImage(CachedImage*) +{ + return 0; +} + +} diff --git a/Source/WebCore/platform/gtk/EventLoopGtk.cpp b/Source/WebCore/platform/gtk/EventLoopGtk.cpp new file mode 100644 index 0000000..4ef7b5c --- /dev/null +++ b/Source/WebCore/platform/gtk/EventLoopGtk.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "EventLoop.h" + +#include <glib.h> + +namespace WebCore { + +void EventLoop::cycle() +{ + g_main_context_iteration(NULL, FALSE); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/gtk/FileChooserGtk.cpp b/Source/WebCore/platform/gtk/FileChooserGtk.cpp new file mode 100644 index 0000000..54763d4 --- /dev/null +++ b/Source/WebCore/platform/gtk/FileChooserGtk.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Holger Hans Peter Freyther + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FileChooser.h" + +#include "FileSystem.h" +#include "Icon.h" +#include "LocalizedStrings.h" +#include "StringTruncator.h" +#include <wtf/text/CString.h> + +#include <glib.h> + +namespace WebCore { + +static bool stringByAdoptingFileSystemRepresentation(gchar* systemFilename, String& result) +{ + if (!systemFilename) + return false; + + result = filenameToString(systemFilename); + g_free(systemFilename); + + return true; +} + +String FileChooser::basenameForWidth(const Font& font, int width) const +{ + if (width <= 0) + return String(); + + String string = fileButtonNoFileSelectedLabel(); + + if (m_filenames.size() == 1) { + CString systemFilename = fileSystemRepresentation(m_filenames[0]); + gchar* systemBasename = g_path_get_basename(systemFilename.data()); + stringByAdoptingFileSystemRepresentation(systemBasename, string); + } else if (m_filenames.size() > 1) + return StringTruncator::rightTruncate(multipleFileUploadText(m_filenames.size()), width, font, false); + + return StringTruncator::centerTruncate(string, width, font, false); +} +} diff --git a/Source/WebCore/platform/gtk/FileSystemGtk.cpp b/Source/WebCore/platform/gtk/FileSystemGtk.cpp new file mode 100644 index 0000000..b8aa102 --- /dev/null +++ b/Source/WebCore/platform/gtk/FileSystemGtk.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2007, 2009 Holger Hans Peter Freyther + * Copyright (C) 2008 Collabora, Ltd. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "FileSystem.h" + +#include "GOwnPtr.h" +#include "PlatformString.h" +#include "UUID.h" +#include <gio/gio.h> +#include <glib.h> +#include <glib/gstdio.h> +#include <wtf/gobject/GRefPtr.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +/* On linux file names are just raw bytes, so also strings that cannot be encoded in any way + * are valid file names. This mean that we cannot just store a file name as-is in a String + * but we have to escape it. + * On Windows the GLib file name encoding is always UTF-8 so we can optimize this case. */ +String filenameToString(const char* filename) +{ + if (!filename) + return String(); + +#if OS(WINDOWS) + return String::fromUTF8(filename); +#else + gchar* escapedString = g_uri_escape_string(filename, "/:", false); + String string(escapedString); + g_free(escapedString); + return string; +#endif +} + +CString fileSystemRepresentation(const String& path) +{ +#if OS(WINDOWS) + return path.utf8(); +#else + char* filename = g_uri_unescape_string(path.utf8().data(), 0); + CString cfilename(filename); + g_free(filename); + return cfilename; +#endif +} + +// Converts a string to something suitable to be displayed to the user. +String filenameForDisplay(const String& string) +{ +#if OS(WINDOWS) + return string; +#else + CString filename = fileSystemRepresentation(string); + gchar* display = g_filename_to_utf8(filename.data(), 0, 0, 0, 0); + if (!display) + return string; + + String displayString = String::fromUTF8(display); + g_free(display); + + return displayString; +#endif +} + +bool fileExists(const String& path) +{ + bool result = false; + CString filename = fileSystemRepresentation(path); + + if (!filename.isNull()) + result = g_file_test(filename.data(), G_FILE_TEST_EXISTS); + + return result; +} + +bool deleteFile(const String& path) +{ + bool result = false; + CString filename = fileSystemRepresentation(path); + + if (!filename.isNull()) + result = g_remove(filename.data()) == 0; + + return result; +} + +bool deleteEmptyDirectory(const String& path) +{ + bool result = false; + CString filename = fileSystemRepresentation(path); + + if (!filename.isNull()) + result = g_rmdir(filename.data()) == 0; + + return result; +} + +bool getFileSize(const String& path, long long& resultSize) +{ + CString filename = fileSystemRepresentation(path); + if (filename.isNull()) + return false; + + struct stat statResult; + gint result = g_stat(filename.data(), &statResult); + if (result != 0) + return false; + + resultSize = statResult.st_size; + return true; +} + +bool getFileModificationTime(const String& path, time_t& modifiedTime) +{ + CString filename = fileSystemRepresentation(path); + if (filename.isNull()) + return false; + + struct stat statResult; + gint result = g_stat(filename.data(), &statResult); + if (result != 0) + return false; + + modifiedTime = statResult.st_mtime; + return true; + +} + +String pathByAppendingComponent(const String& path, const String& component) +{ + if (path.endsWith(G_DIR_SEPARATOR_S)) + return path + component; + else + return path + G_DIR_SEPARATOR_S + component; +} + +bool makeAllDirectories(const String& path) +{ + CString filename = fileSystemRepresentation(path); + if (filename.isNull()) + return false; + + gint result = g_mkdir_with_parents(filename.data(), S_IRWXU); + + return result == 0; +} + +String homeDirectoryPath() +{ + return filenameToString(g_get_home_dir()); +} + +String pathGetFileName(const String& pathName) +{ + if (pathName.isEmpty()) + return pathName; + + CString tmpFilename = fileSystemRepresentation(pathName); + char* baseName = g_path_get_basename(tmpFilename.data()); + String fileName = String::fromUTF8(baseName); + g_free(baseName); + + return fileName; +} + +String directoryName(const String& path) +{ + /* No null checking needed */ + GOwnPtr<char> dirname(g_path_get_dirname(fileSystemRepresentation(path).data())); + return String::fromUTF8(dirname.get()); +} + +Vector<String> listDirectory(const String& path, const String& filter) +{ + Vector<String> entries; + + CString filename = fileSystemRepresentation(path); + GDir* dir = g_dir_open(filename.data(), 0, 0); + if (!dir) + return entries; + + GPatternSpec *pspec = g_pattern_spec_new((filter.utf8()).data()); + while (const char* name = g_dir_read_name(dir)) { + if (!g_pattern_match_string(pspec, name)) + continue; + + GOwnPtr<gchar> entry(g_build_filename(filename.data(), name, NULL)); + entries.append(filenameToString(entry.get())); + } + g_pattern_spec_free(pspec); + g_dir_close(dir); + + return entries; +} + +CString openTemporaryFile(const char* prefix, PlatformFileHandle& handle) +{ + GOwnPtr<gchar> filename(g_strdup_printf("%s%s", prefix, createCanonicalUUIDString().utf8().data())); + GOwnPtr<gchar> tempPath(g_build_filename(g_get_tmp_dir(), filename.get(), NULL)); + GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(tempPath.get())); + + handle = g_file_create_readwrite(file.get(), G_FILE_CREATE_NONE, 0, 0); + if (!isHandleValid(handle)) + return CString(); + return tempPath.get(); +} + +PlatformFileHandle openFile(const String& path, FileOpenMode mode) +{ + CString fsRep = fileSystemRepresentation(path); + if (fsRep.isNull()) + return invalidPlatformFileHandle; + + GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(fsRep.data())); + GFileIOStream* ioStream = 0; + if (mode == OpenForRead) + ioStream = g_file_open_readwrite(file.get(), 0, 0); + else if (mode == OpenForWrite) { + if (g_file_test(fsRep.data(), static_cast<GFileTest>(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) + ioStream = g_file_open_readwrite(file.get(), 0, 0); + else + ioStream = g_file_create_readwrite(file.get(), G_FILE_CREATE_NONE, 0, 0); + } + + return ioStream; +} + +void closeFile(PlatformFileHandle& handle) +{ + if (!isHandleValid(handle)) + return; + + g_io_stream_close(G_IO_STREAM(handle), 0, 0); + g_object_unref(handle); + handle = invalidPlatformFileHandle; +} + +long long seekFile(PlatformFileHandle handle, long long offset, FileSeekOrigin origin) +{ + GSeekType seekType = G_SEEK_SET; + switch (origin) { + case SeekFromBeginning: + seekType = G_SEEK_SET; + break; + case SeekFromCurrent: + seekType = G_SEEK_CUR; + break; + case SeekFromEnd: + seekType = G_SEEK_END; + break; + default: + ASSERT_NOT_REACHED(); + } + + if (!g_seekable_seek(G_SEEKABLE(g_io_stream_get_input_stream(G_IO_STREAM(handle))), + offset, seekType, 0, 0)) + return -1; + return g_seekable_tell(G_SEEKABLE(g_io_stream_get_input_stream(G_IO_STREAM(handle)))); +} + +int writeToFile(PlatformFileHandle handle, const char* data, int length) +{ + gsize bytesWritten; + g_output_stream_write_all(g_io_stream_get_output_stream(G_IO_STREAM(handle)), + data, length, &bytesWritten, 0, 0); + return bytesWritten; +} + +int readFromFile(PlatformFileHandle handle, char* data, int length) +{ + GOwnPtr<GError> error; + do { + gssize bytesRead = g_input_stream_read(g_io_stream_get_input_stream(G_IO_STREAM(handle)), + data, length, 0, &error.outPtr()); + if (bytesRead >= 0) + return bytesRead; + } while (error && error->code == G_FILE_ERROR_INTR); + return -1; +} + +bool unloadModule(PlatformModule module) +{ + return g_module_close(module); +} +} diff --git a/Source/WebCore/platform/gtk/GOwnPtrGtk.cpp b/Source/WebCore/platform/gtk/GOwnPtrGtk.cpp new file mode 100644 index 0000000..9b693f4 --- /dev/null +++ b/Source/WebCore/platform/gtk/GOwnPtrGtk.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "GOwnPtrGtk.h" + +#include <gtk/gtk.h> + +namespace WTF { + +template <> void freeOwnedGPtr<GdkEvent>(GdkEvent* ptr) +{ + if (ptr) + gdk_event_free(ptr); +} + +template <> void freeOwnedGPtr<GtkIconInfo>(GtkIconInfo* info) +{ + if (info) + gtk_icon_info_free(info); +} + +} diff --git a/Source/WebCore/platform/gtk/GOwnPtrGtk.h b/Source/WebCore/platform/gtk/GOwnPtrGtk.h new file mode 100644 index 0000000..c5d9cdc --- /dev/null +++ b/Source/WebCore/platform/gtk/GOwnPtrGtk.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GOwnPtrGtk_h +#define GOwnPtrGtk_h + +#include "GOwnPtr.h" + +namespace WTF { + +template <> void freeOwnedGPtr<GdkEvent>(GdkEvent*); +template <> void freeOwnedGPtr<GtkIconInfo>(GtkIconInfo*); + +} + +#endif diff --git a/Source/WebCore/platform/gtk/GRefPtrGtk.cpp b/Source/WebCore/platform/gtk/GRefPtrGtk.cpp new file mode 100644 index 0000000..6647b99 --- /dev/null +++ b/Source/WebCore/platform/gtk/GRefPtrGtk.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2009 Martin Robinson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "GRefPtrGtk.h" + +#include <glib.h> +#include <gtk/gtk.h> + +namespace WTF { + +template <> GtkTargetList* refGPtr(GtkTargetList* ptr) +{ + if (ptr) + gtk_target_list_ref(ptr); + return ptr; +} + +template <> void derefGPtr(GtkTargetList* ptr) +{ + if (ptr) + gtk_target_list_unref(ptr); +} + +template <> GdkCursor* refGPtr(GdkCursor* ptr) +{ + if (ptr) + gdk_cursor_ref(ptr); + return ptr; +} + +template <> void derefGPtr(GdkCursor* ptr) +{ + if (ptr) + gdk_cursor_unref(ptr); +} + +} diff --git a/Source/WebCore/platform/gtk/GRefPtrGtk.h b/Source/WebCore/platform/gtk/GRefPtrGtk.h new file mode 100644 index 0000000..1fb9772 --- /dev/null +++ b/Source/WebCore/platform/gtk/GRefPtrGtk.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2009 Martin Robinson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GRefPtrGtk_h +#define GRefPtrGtk_h + +#include <wtf/gobject/GRefPtr.h> + +namespace WTF { + +template <> GtkTargetList* refGPtr(GtkTargetList* ptr); +template <> void derefGPtr(GtkTargetList* ptr); + +template <> GdkCursor* refGPtr(GdkCursor* ptr); +template <> void derefGPtr(GdkCursor* ptr); + +} + +#endif diff --git a/Source/WebCore/platform/gtk/GeolocationServiceGtk.cpp b/Source/WebCore/platform/gtk/GeolocationServiceGtk.cpp new file mode 100644 index 0000000..5b34c68 --- /dev/null +++ b/Source/WebCore/platform/gtk/GeolocationServiceGtk.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GeolocationServiceGtk.h" +#if ENABLE(GEOLOCATION) + +#include "GOwnPtr.h" +#include "NotImplemented.h" +#include "PositionOptions.h" +#include <wtf/text/CString.h> + +namespace WTF { + template<> void freeOwnedGPtr<GeoclueAccuracy>(GeoclueAccuracy* accuracy) + { + if (!accuracy) + return; + + geoclue_accuracy_free(accuracy); + } +} + +namespace WebCore { + +GeolocationService* GeolocationServiceGtk::create(GeolocationServiceClient* client) +{ + return new GeolocationServiceGtk(client); +} + +GeolocationService::FactoryFunction* GeolocationService::s_factoryFunction = &GeolocationServiceGtk::create; + +GeolocationServiceGtk::GeolocationServiceGtk(GeolocationServiceClient* client) + : GeolocationService(client) + , m_geoclueClient(0) + , m_geocluePosition(0) + , m_latitude(0.0) + , m_longitude(0.0) + , m_altitude(0.0) + , m_altitudeAccuracy(0.0) + , m_timestamp(0) +{ +} + +GeolocationServiceGtk::~GeolocationServiceGtk() +{ + if (m_geoclueClient) + g_object_unref(m_geoclueClient); + + if (m_geocluePosition) + g_object_unref(m_geocluePosition); +} + +// +// 1.) Initialize Geoclue with our requirements +// 2.) Try to get a GeocluePosition +// 3.) Update the Information and get the current position +// +// TODO: Also get GeoclueVelocity but there is no master client +// API for that. +// +bool GeolocationServiceGtk::startUpdating(PositionOptions* options) +{ + ASSERT(!m_geoclueClient); + + m_lastPosition = 0; + m_lastError = 0; + + GOwnPtr<GError> error; + GeoclueMaster* master = geoclue_master_get_default(); + GeoclueMasterClient* client = geoclue_master_create_client(master, 0, 0); + g_object_unref(master); + + if (!client) { + setError(PositionError::POSITION_UNAVAILABLE, "Could not connect to location provider."); + return false; + } + + GeoclueAccuracyLevel accuracyLevel = GEOCLUE_ACCURACY_LEVEL_LOCALITY; + int timeout = 0; + if (options) { + accuracyLevel = options->enableHighAccuracy() ? GEOCLUE_ACCURACY_LEVEL_DETAILED : GEOCLUE_ACCURACY_LEVEL_LOCALITY; + if (options->hasTimeout()) + timeout = options->timeout(); + } + + gboolean result = geoclue_master_client_set_requirements(client, accuracyLevel, timeout, + false, GEOCLUE_RESOURCE_ALL, &error.outPtr()); + + if (!result) { + setError(PositionError::POSITION_UNAVAILABLE, error->message); + g_object_unref(client); + return false; + } + + m_geocluePosition = geoclue_master_client_create_position(client, &error.outPtr()); + if (!m_geocluePosition) { + setError(PositionError::POSITION_UNAVAILABLE, error->message); + g_object_unref(client); + return false; + } + + m_geoclueClient = client; + + geoclue_position_get_position_async(m_geocluePosition, (GeocluePositionCallback)getPositionCallback, this); + + g_signal_connect(G_OBJECT(m_geocluePosition), "position-changed", + G_CALLBACK(position_changed), this); + + return true; +} + +void GeolocationServiceGtk::stopUpdating() +{ + if (!m_geoclueClient) + return; + + g_object_unref(m_geocluePosition); + g_object_unref(m_geoclueClient); + + m_geocluePosition = 0; + m_geoclueClient = 0; +} + +void GeolocationServiceGtk::suspend() +{ + // not available with geoclue + notImplemented(); +} + +void GeolocationServiceGtk::resume() +{ + // not available with geoclue + notImplemented(); +} + +Geoposition* GeolocationServiceGtk::lastPosition() const +{ + return m_lastPosition.get(); +} + +PositionError* GeolocationServiceGtk::lastError() const +{ + return m_lastError.get(); +} + +void GeolocationServiceGtk::updatePosition() +{ + m_lastError = 0; + + RefPtr<Coordinates> coordinates = Coordinates::create(m_latitude, m_longitude, + true, m_altitude, m_accuracy, + true, m_altitudeAccuracy, false, 0.0, false, 0.0); + m_lastPosition = Geoposition::create(coordinates.release(), m_timestamp * 1000.0); + positionChanged(); +} + +void GeolocationServiceGtk::getPositionCallback(GeocluePosition *position, + GeocluePositionFields fields, + int timestamp, + double latitude, + double longitude, + double altitude, + GeoclueAccuracy* accuracy, + GError* error, + GeolocationServiceGtk* that) +{ + if (error) { + that->setError(PositionError::POSITION_UNAVAILABLE, error->message); + g_error_free(error); + return; + } + position_changed(position, fields, timestamp, latitude, longitude, altitude, accuracy, that); +} + +void GeolocationServiceGtk::position_changed(GeocluePosition*, GeocluePositionFields fields, int timestamp, double latitude, double longitude, double altitude, GeoclueAccuracy* accuracy, GeolocationServiceGtk* that) +{ + if (!(fields & GEOCLUE_POSITION_FIELDS_LATITUDE && fields & GEOCLUE_POSITION_FIELDS_LONGITUDE)) { + that->setError(PositionError::POSITION_UNAVAILABLE, "Position could not be determined."); + return; + } + + that->m_timestamp = timestamp; + that->m_latitude = latitude; + that->m_longitude = longitude; + that->m_altitude = altitude; + + GeoclueAccuracyLevel level; + geoclue_accuracy_get_details(accuracy, &level, &that->m_accuracy, &that->m_altitudeAccuracy); + that->updatePosition(); +} + +void GeolocationServiceGtk::setError(PositionError::ErrorCode errorCode, const char* message) +{ + m_lastPosition = 0; + m_lastError = PositionError::create(errorCode, String::fromUTF8(message)); +} + +} +#endif // ENABLE(GEOLOCATION) diff --git a/Source/WebCore/platform/gtk/GeolocationServiceGtk.h b/Source/WebCore/platform/gtk/GeolocationServiceGtk.h new file mode 100644 index 0000000..46249ed --- /dev/null +++ b/Source/WebCore/platform/gtk/GeolocationServiceGtk.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GeolocationServiceGtk_h +#define GeolocationServiceGtk_h +#if ENABLE(GEOLOCATION) + +#include "GeolocationService.h" +#include "Geoposition.h" +#include "PositionError.h" +#include "RefPtr.h" + +#include <geoclue/geoclue-master.h> +#include <geoclue/geoclue-position.h> + +namespace WebCore { + class GeolocationServiceGtk : public GeolocationService { + public: + static GeolocationService* create(GeolocationServiceClient*); + ~GeolocationServiceGtk(); + + virtual bool startUpdating(PositionOptions*); + virtual void stopUpdating(); + + virtual void suspend(); + virtual void resume(); + + Geoposition* lastPosition() const; + PositionError* lastError() const; + + private: + GeolocationServiceGtk(GeolocationServiceClient*); + + void setError(PositionError::ErrorCode, const char* message); + void updatePosition(); + + static void position_changed(GeocluePosition*, GeocluePositionFields, int, double, double, double, GeoclueAccuracy*, GeolocationServiceGtk*); + static void getPositionCallback(GeocluePosition*, GeocluePositionFields, int, double, double, double, GeoclueAccuracy*, GError*, GeolocationServiceGtk*); + + private: + RefPtr<Geoposition> m_lastPosition; + RefPtr<PositionError> m_lastError; + + // state objects + GeoclueMasterClient* m_geoclueClient; + GeocluePosition* m_geocluePosition; + + // Error and Position state + double m_latitude; + double m_longitude; + double m_altitude; + double m_accuracy; + double m_altitudeAccuracy; + int m_timestamp; + }; +} + +#endif // ENABLE(GEOLOCATION) +#endif diff --git a/Source/WebCore/platform/gtk/GtkPluginWidget.cpp b/Source/WebCore/platform/gtk/GtkPluginWidget.cpp new file mode 100644 index 0000000..331f60f --- /dev/null +++ b/Source/WebCore/platform/gtk/GtkPluginWidget.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2009 Holger Hans Peter Freyther + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "GtkPluginWidget.h" + +#include "GraphicsContext.h" +#include "GtkVersioning.h" +#include "ScrollView.h" + +#include <gtk/gtk.h> + +namespace WebCore { + +GtkPluginWidget::GtkPluginWidget(GtkWidget* widget) + : Widget(widget) +{ + gtk_widget_hide(widget); +} + +void GtkPluginWidget::invalidateRect(const IntRect& _rect) +{ + /* no need to */ + if (!gtk_widget_get_has_window(platformWidget())) + return; + + GdkWindow* window = gtk_widget_get_window(platformWidget()); + if (!window) + return; + + GdkRectangle rect = _rect; + gdk_window_invalidate_rect(window, &rect, FALSE); +} + +void GtkPluginWidget::frameRectsChanged() +{ + IntRect rect = frameRect(); + IntPoint loc = parent()->contentsToWindow(rect.location()); + GtkAllocation allocation = { loc.x(), loc.y(), rect.width(), rect.height() }; + + gtk_widget_set_size_request(platformWidget(), rect.width(), rect.height()); + gtk_widget_size_allocate(platformWidget(), &allocation); + gtk_widget_show(platformWidget()); +} + +void GtkPluginWidget::paint(GraphicsContext* context, const IntRect& rect) +{ + if (!context->gdkExposeEvent()) + return; + + /* only paint widgets with no window this way */ + if (gtk_widget_get_has_window(platformWidget())) + return; + + GtkWidget* widget = platformWidget(); + ASSERT(!gtk_widget_get_has_window(widget)); + + GdkEvent* event = gdk_event_new(GDK_EXPOSE); + event->expose = *context->gdkExposeEvent(); + event->expose.area = static_cast<GdkRectangle>(rect); + + IntPoint loc = parent()->contentsToWindow(rect.location()); + + event->expose.area.x = loc.x(); + event->expose.area.y = loc.y(); + +#ifdef GTK_API_VERSION_2 + event->expose.region = gdk_region_rectangle(&event->expose.area); +#else + event->expose.region = cairo_region_create_rectangle(&event->expose.area); +#endif + + /* + * This will be unref'ed by gdk_event_free. + */ + g_object_ref(event->expose.window); + + /* + * If we are going to paint do the translation and GtkAllocation manipulation. + */ +#ifdef GTK_API_VERSION_2 + if (!gdk_region_empty(event->expose.region)) +#else + if (!cairo_region_is_empty(event->expose.region)) +#endif + gtk_widget_send_expose(widget, event); + + gdk_event_free(event); +} + +} diff --git a/Source/WebCore/platform/gtk/GtkPluginWidget.h b/Source/WebCore/platform/gtk/GtkPluginWidget.h new file mode 100644 index 0000000..cad3462 --- /dev/null +++ b/Source/WebCore/platform/gtk/GtkPluginWidget.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 Holger Hans Peter Freyther + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GtkPluginWidget_h +#define GtkPluginWidget_h + +#include "Widget.h" + +namespace WebCore { + class GtkPluginWidget : public Widget { + public: + GtkPluginWidget(GtkWidget*); + void invalidateRect(const IntRect&); + void frameRectsChanged(); + void paint(GraphicsContext*, const IntRect&); + }; +} + +#endif diff --git a/Source/WebCore/platform/gtk/GtkVersioning.c b/Source/WebCore/platform/gtk/GtkVersioning.c new file mode 100644 index 0000000..c3407ea --- /dev/null +++ b/Source/WebCore/platform/gtk/GtkVersioning.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2010 Collabora Ltd. + * Copyright (C) 2010 Igalia, S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "GtkVersioning.h" + +#include <gtk/gtk.h> + +#if !GTK_CHECK_VERSION(2, 14, 0) +void gtk_adjustment_set_value(GtkAdjustment* adjusment, gdouble value) +{ + m_adjustment->value = m_currentPos; + gtk_adjustment_value_changed(m_adjustment); +} + +void gtk_adjustment_configure(GtkAdjustment* adjustment, gdouble value, gdouble lower, gdouble upper, + gdouble stepIncrement, gdouble pageIncrement, gdouble pageSize) +{ + g_object_freeze_notify(G_OBJECT(adjustment)); + + g_object_set(adjustment, + "lower", lower, + "upper", upper, + "step-increment", stepIncrement, + "page-increment", pageIncrement, + "page-size", pageSize, + NULL); + + g_object_thaw_notify(G_OBJECT(adjustment)); + + gtk_adjustment_changed(adjustment); + gtk_adjustment_value_changed(adjustment); +} +#endif + +GdkDevice *getDefaultGDKPointerDevice(GdkWindow* window) +{ +#ifndef GTK_API_VERSION_2 + GdkDeviceManager *manager = gdk_display_get_device_manager(gdk_window_get_display(window)); + return gdk_device_manager_get_client_pointer(manager); +#else + return gdk_device_get_core_pointer(); +#endif // GTK_API_VERSION_2 +} + +#if !GTK_CHECK_VERSION(2, 17, 3) +void gdk_window_get_root_coords(GdkWindow* window, gint x, gint y, gint* rootX, gint* rootY) +{ + gdk_window_get_root_origin(window, rootX, rootY); + *rootX = *rootX + x; + *rootY = *rootY + y; +} +#endif + +GdkCursor * blankCursor() +{ +#if GTK_CHECK_VERSION(2, 16, 0) + return gdk_cursor_new(GDK_BLANK_CURSOR); +#else + GdkCursor * cursor; + GdkPixmap * source; + GdkPixmap * mask; + GdkColor foreground = { 0, 65535, 0, 0 }; // Red. + GdkColor background = { 0, 0, 0, 65535 }; // Blue. + static gchar cursorBits[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + + source = gdk_bitmap_create_from_data(0, cursorBits, 8, 8); + mask = gdk_bitmap_create_from_data(0, cursorBits, 8, 8); + cursor = gdk_cursor_new_from_pixmap(source, mask, &foreground, &background, 8, 8); + gdk_pixmap_unref(source); + gdk_pixmap_unref(mask); + return cursor; +#endif // GTK_CHECK_VERSION(2, 16, 0) +} + +#if !GTK_CHECK_VERSION(2, 16, 0) +const gchar* gtk_menu_item_get_label(GtkMenuItem* menuItem) +{ + GtkWidget * label = gtk_bin_get_child(GTK_BIN(menuItem)); + if (GTK_IS_LABEL(label)) + return gtk_label_get_text(GTK_LABEL(label)); + return 0; +} +#endif // GTK_CHECK_VERSION(2, 16, 0) + +#ifdef GTK_API_VERSION_2 +static cairo_format_t +gdk_cairo_format_for_content(cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_COLOR: + return CAIRO_FORMAT_RGB24; + case CAIRO_CONTENT_ALPHA: + return CAIRO_FORMAT_A8; + case CAIRO_CONTENT_COLOR_ALPHA: + default: + return CAIRO_FORMAT_ARGB32; + } +} + +static cairo_surface_t* +gdk_cairo_surface_coerce_to_image(cairo_surface_t* surface, + cairo_content_t content, + int width, + int height) +{ + cairo_surface_t * copy; + cairo_t * cr; + + if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE + && cairo_surface_get_content(surface) == content + && cairo_image_surface_get_width(surface) >= width + && cairo_image_surface_get_height(surface) >= height) + return cairo_surface_reference(surface); + + copy = cairo_image_surface_create(gdk_cairo_format_for_content(content), + width, + height); + + cr = cairo_create(copy); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface(cr, surface, 0, 0); + cairo_paint(cr); + cairo_destroy(cr); + + return copy; +} + +static void +convert_alpha(guchar * destData, int destStride, + guchar * srcData, int srcStride, + int srcX, int srcY, int width, int height) +{ + int x, y; + + srcData += srcStride * srcY + srcY * 4; + + for (y = 0; y < height; y++) { + guint32 * src = (guint32 *) srcData; + + for (x = 0; x < width; x++) { + guint alpha = src[x] >> 24; + + if (!alpha) { + destData[x * 4 + 0] = 0; + destData[x * 4 + 1] = 0; + destData[x * 4 + 2] = 0; + } else { + destData[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; + destData[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; + destData[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; + } + destData[x * 4 + 3] = alpha; + } + + srcData += srcStride; + destData += destStride; + } +} + +static void +convert_no_alpha(guchar * destData, int destStride, guchar * srcData, + int srcStride, int srcX, int srcY, + int width, int height) +{ + int x, y; + + srcData += srcStride * srcY + srcX * 4; + + for (y = 0; y < height; y++) { + guint32 * src = (guint32 *) srcData; + + for (x = 0; x < width; x++) { + destData[x * 3 + 0] = src[x] >> 16; + destData[x * 3 + 1] = src[x] >> 8; + destData[x * 3 + 2] = src[x]; + } + + srcData += srcStride; + destData += destStride; + } +} + +/** + * gdk_pixbuf_get_from_surface: + * @surface: surface to copy from + * @src_x: Source X coordinate within @surface + * @src_y: Source Y coordinate within @surface + * @width: Width in pixels of region to get + * @height: Height in pixels of region to get + * + * Transfers image data from a #cairo_surface_t and converts it to an RGB(A) + * representation inside a #GdkPixbuf. This allows you to efficiently read + * individual pixels from cairo surfaces. For #GdkWindows, use + * gdk_pixbuf_get_from_window() instead. + * + * This function will create an RGB pixbuf with 8 bits per channel. The pixbuf + * will contain an alpha channel if the @surface contains one. + * + * Return value: (transfer full): A newly-created pixbuf with a reference count + * of 1, or %NULL on error + **/ +GdkPixbuf* +gdk_pixbuf_get_from_surface(cairo_surface_t * surface, + int srcX, int srcY, + int width, int height) +{ + cairo_content_t content; + GdkPixbuf * dest; + + /* General sanity checks */ + g_return_val_if_fail(surface, NULL); + g_return_val_if_fail(srcX >= 0 && srcY >= 0, NULL); + g_return_val_if_fail(width > 0 && height > 0, NULL); + + content = cairo_surface_get_content(surface) | CAIRO_CONTENT_COLOR; + dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, + !!(content & CAIRO_CONTENT_ALPHA), + 8, + width, height); + + surface = gdk_cairo_surface_coerce_to_image(surface, content, srcX + width, srcY + height); + cairo_surface_flush(surface); + if (cairo_surface_status(surface) || !dest) { + cairo_surface_destroy(surface); + return NULL; + } + + if (gdk_pixbuf_get_has_alpha(dest)) + convert_alpha(gdk_pixbuf_get_pixels(dest), + gdk_pixbuf_get_rowstride(dest), + cairo_image_surface_get_data(surface), + cairo_image_surface_get_stride(surface), + srcX, srcY, + width, height); + else + convert_no_alpha(gdk_pixbuf_get_pixels(dest), + gdk_pixbuf_get_rowstride(dest), + cairo_image_surface_get_data(surface), + cairo_image_surface_get_stride(surface), + srcX, srcY, + width, height); + + cairo_surface_destroy(surface); + return dest; +} + +#endif // GTK_API_VERSION_2 + +#if !GLIB_CHECK_VERSION(2, 27, 1) +gboolean g_signal_accumulator_first_wins(GSignalInvocationHint *invocationHint, GValue *returnAccumulator, const GValue *handlerReturn, gpointer data) +{ + g_value_copy(handlerReturn, returnAccumulator); + return FALSE; +} +#endif + diff --git a/Source/WebCore/platform/gtk/GtkVersioning.h b/Source/WebCore/platform/gtk/GtkVersioning.h new file mode 100644 index 0000000..11d1f8a --- /dev/null +++ b/Source/WebCore/platform/gtk/GtkVersioning.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010 Collabora Ltd. + * Copyright (C) 2010 Igalia, S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GtkVersioning_h +#define GtkVersioning_h + +#include <gtk/gtk.h> + +#ifndef GTK_API_VERSION_2 +#include <gdk/gdkkeysyms-compat.h> +#endif + +G_BEGIN_DECLS + +// Macros to avoid deprecation checking churn +#ifndef GTK_API_VERSION_2 +#define GDK_WINDOW_XWINDOW(window) (gdk_x11_window_get_xid(window)) +#else +GdkPixbuf* gdk_pixbuf_get_from_surface(cairo_surface_t* surface, int srcX, int srcY, + int width, int height); +#endif + +#if !GTK_CHECK_VERSION(2, 24, 0) +#define gdk_window_get_display(window) gdk_drawable_get_display(window) +#ifdef GDK_DISABLE_DEPRECATED +#define gdk_window_get_visual gdk_drawable_get_visual +#endif +#endif // GTK_CHECK_VERSION(2, 24, 0) + +#if !GTK_CHECK_VERSION(2, 21, 2) +#define gdk_visual_get_depth(visual) (visual)->depth +#define gdk_visual_get_bits_per_rgb(visual) (visual)->bits_per_rgb +#define gdk_drag_context_get_selected_action(context) (context)->action +#define gdk_drag_context_get_actions(context) (context)->actions +#endif // GTK_CHECK_VERSION(2, 21, 2) + +#if !GTK_CHECK_VERSION(2, 20, 0) +#define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget) +#define gtk_widget_set_realized(widget, TRUE) GTK_WIDGET_SET_FLAGS((widget), GTK_REALIZED) +#define gtk_range_get_min_slider_size(range) (range)->min_slider_size +#endif // GTK_CHECK_VERSION(2, 20, 0) + +#if !GTK_CHECK_VERSION(2, 19, 0) +#define gtk_widget_is_toplevel(widget) GTK_WIDGET_TOPLEVEL(widget) +#define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget) +#define gtk_widget_get_has_window(widget) !GTK_WIDGET_NO_WINDOW(widget) +#define gtk_widget_get_can_focus(widget) GTK_WIDGET_CAN_FOCUS(widget) +#define gtk_widget_is_sensitive(widget) GTK_WIDGET_IS_SENSITIVE(widget) +#endif // GTK_CHECK_VERSION(2, 19, 0) + +#if !GTK_CHECK_VERSION(2, 18, 0) +#define gtk_widget_set_visible(widget, FALSE) GTK_WIDGET_UNSET_FLAGS((widget), GTK_VISIBLE) +#define gtk_widget_get_visible(widget) (GTK_WIDGET_FLAGS(widget) & GTK_VISIBLE) + +#define gtk_widget_set_window(widget, new_window) (widget)->window = (new_window) +#define gtk_widget_set_can_focus(widget, TRUE) GTK_WIDGET_SET_FLAGS((widget), GTK_CAN_FOCUS) +#define gtk_widget_get_allocation(widget, alloc) (*(alloc) = (widget)->allocation) +#define gtk_widget_set_allocation(widget, alloc) ((widget)->allocation = *(alloc)) +#endif // GTK_CHECK_VERSION(2, 18, 0) + +#if !GTK_CHECK_VERSION(2, 17, 3) +void gdk_window_get_root_coords(GdkWindow* window, gint x, gint y, gint* rootX, gint* rootY); +#endif // GTK_CHECK_VERSION(2, 17, 3) + +#if !GTK_CHECK_VERSION(2, 16, 0) +const gchar* gtk_menu_item_get_label(GtkMenuItem*); +#endif // GTK_CHECK_VERSION(2, 16, 0) + + +#if !GTK_CHECK_VERSION(2, 14, 0) +#define gtk_widget_get_window(widget) (widget)->window +#define gtk_adjustment_get_value(adj) (adj)->value +#define gtk_dialog_get_content_area(dialog) (dialog)->vbox +#define gtk_dialog_get_action_area(dialog) (dialog)->action_area +#define gtk_selection_data_get_length(data) (data)->length +#define gtk_selection_data_get_data(data) (data)->data +#define gtk_selection_data_get_target(data) (data)->target +#define gtk_adjustment_set_page_size(adj, value) (adj)->page_size = value + +void gtk_adjustment_configure(GtkAdjustment* adjustment, gdouble value, gdouble lower, gdouble upper, + gdouble stepIncrement, gdouble pageIncrement, gdouble pageSize); + +void gtk_adjustment_set_value(GtkAdjustment* adjusment, gdouble value); +#endif // GTK_CHECK_VERSION(2, 14, 0) + +GdkDevice* getDefaultGDKPointerDevice(GdkWindow* window); +GdkCursor* blankCursor(); + +#if !GLIB_CHECK_VERSION(2, 27, 1) +gboolean g_signal_accumulator_first_wins(GSignalInvocationHint* invocationHint, GValue* returnAccumulator, const GValue* handlerReturn, gpointer data); +#endif + +G_END_DECLS + +#endif // GtkVersioning_h diff --git a/Source/WebCore/platform/gtk/KURLGtk.cpp b/Source/WebCore/platform/gtk/KURLGtk.cpp new file mode 100644 index 0000000..47bc48b --- /dev/null +++ b/Source/WebCore/platform/gtk/KURLGtk.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "KURL.h" + +#include "FileSystem.h" +#include <wtf/text/CString.h> + +#include <glib.h> + +namespace WebCore { + +String KURL::fileSystemPath() const +{ + gchar* filename = g_filename_from_uri(m_string.utf8().data(), 0, 0); + if (!filename) + return String(); + + String path = filenameToString(filename); + g_free(filename); + return path; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/gtk/KeyEventGtk.cpp b/Source/WebCore/platform/gtk/KeyEventGtk.cpp new file mode 100644 index 0000000..50dfa4c --- /dev/null +++ b/Source/WebCore/platform/gtk/KeyEventGtk.cpp @@ -0,0 +1,604 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PlatformKeyboardEvent.h" + +#include "GtkVersioning.h" +#include "NotImplemented.h" +#include "TextEncoding.h" +#include "WindowsKeyboardCodes.h" + +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +namespace WebCore { + +// FIXME: This is incomplete. We should change this to mirror +// more like what Firefox does, and generate these switch statements +// at build time. +static String keyIdentifierForGdkKeyCode(guint keyCode) +{ + switch (keyCode) { + case GDK_Menu: + case GDK_Alt_L: + case GDK_Alt_R: + return "Alt"; + case GDK_Clear: + return "Clear"; + case GDK_Down: + return "Down"; + // "End" + case GDK_End: + return "End"; + // "Enter" + case GDK_ISO_Enter: + case GDK_KP_Enter: + case GDK_Return: + return "Enter"; + case GDK_Execute: + return "Execute"; + case GDK_F1: + return "F1"; + case GDK_F2: + return "F2"; + case GDK_F3: + return "F3"; + case GDK_F4: + return "F4"; + case GDK_F5: + return "F5"; + case GDK_F6: + return "F6"; + case GDK_F7: + return "F7"; + case GDK_F8: + return "F8"; + case GDK_F9: + return "F9"; + case GDK_F10: + return "F10"; + case GDK_F11: + return "F11"; + case GDK_F12: + return "F12"; + case GDK_F13: + return "F13"; + case GDK_F14: + return "F14"; + case GDK_F15: + return "F15"; + case GDK_F16: + return "F16"; + case GDK_F17: + return "F17"; + case GDK_F18: + return "F18"; + case GDK_F19: + return "F19"; + case GDK_F20: + return "F20"; + case GDK_F21: + return "F21"; + case GDK_F22: + return "F22"; + case GDK_F23: + return "F23"; + case GDK_F24: + return "F24"; + case GDK_Help: + return "Help"; + case GDK_Home: + return "Home"; + case GDK_Insert: + return "Insert"; + case GDK_Left: + return "Left"; + case GDK_Page_Down: + return "PageDown"; + case GDK_Page_Up: + return "PageUp"; + case GDK_Pause: + return "Pause"; + case GDK_3270_PrintScreen: + case GDK_Print: + return "PrintScreen"; + case GDK_Right: + return "Right"; + case GDK_Select: + return "Select"; + case GDK_Up: + return "Up"; + // Standard says that DEL becomes U+007F. + case GDK_Delete: + return "U+007F"; + case GDK_BackSpace: + return "U+0008"; + case GDK_ISO_Left_Tab: + case GDK_3270_BackTab: + case GDK_Tab: + return "U+0009"; + default: + return String::format("U+%04X", gdk_keyval_to_unicode(gdk_keyval_to_upper(keyCode))); + } +} + +static int windowsKeyCodeForKeyEvent(unsigned int keycode) +{ + switch (keycode) { + case GDK_KP_0: + return VK_NUMPAD0;// (60) Numeric keypad 0 key + case GDK_KP_1: + return VK_NUMPAD1;// (61) Numeric keypad 1 key + case GDK_KP_2: + return VK_NUMPAD2; // (62) Numeric keypad 2 key + case GDK_KP_3: + return VK_NUMPAD3; // (63) Numeric keypad 3 key + case GDK_KP_4: + return VK_NUMPAD4; // (64) Numeric keypad 4 key + case GDK_KP_5: + return VK_NUMPAD5; //(65) Numeric keypad 5 key + case GDK_KP_6: + return VK_NUMPAD6; // (66) Numeric keypad 6 key + case GDK_KP_7: + return VK_NUMPAD7; // (67) Numeric keypad 7 key + case GDK_KP_8: + return VK_NUMPAD8; // (68) Numeric keypad 8 key + case GDK_KP_9: + return VK_NUMPAD9; // (69) Numeric keypad 9 key + case GDK_KP_Multiply: + return VK_MULTIPLY; // (6A) Multiply key + case GDK_KP_Add: + return VK_ADD; // (6B) Add key + case GDK_KP_Subtract: + return VK_SUBTRACT; // (6D) Subtract key + case GDK_KP_Decimal: + return VK_DECIMAL; // (6E) Decimal key + case GDK_KP_Divide: + return VK_DIVIDE; // (6F) Divide key + + case GDK_KP_Page_Up: + return VK_PRIOR; // (21) PAGE UP key + case GDK_KP_Page_Down: + return VK_NEXT; // (22) PAGE DOWN key + case GDK_KP_End: + return VK_END; // (23) END key + case GDK_KP_Home: + return VK_HOME; // (24) HOME key + case GDK_KP_Left: + return VK_LEFT; // (25) LEFT ARROW key + case GDK_KP_Up: + return VK_UP; // (26) UP ARROW key + case GDK_KP_Right: + return VK_RIGHT; // (27) RIGHT ARROW key + case GDK_KP_Down: + return VK_DOWN; // (28) DOWN ARROW key + + case GDK_BackSpace: + return VK_BACK; // (08) BACKSPACE key + case GDK_ISO_Left_Tab: + case GDK_3270_BackTab: + case GDK_Tab: + return VK_TAB; // (09) TAB key + case GDK_Clear: + return VK_CLEAR; // (0C) CLEAR key + case GDK_ISO_Enter: + case GDK_KP_Enter: + case GDK_Return: + return VK_RETURN; //(0D) Return key + case GDK_Shift_L: + case GDK_Shift_R: + return VK_SHIFT; // (10) SHIFT key + case GDK_Control_L: + case GDK_Control_R: + return VK_CONTROL; // (11) CTRL key + case GDK_Menu: + return VK_APPS; // (5D) Applications key (Natural keyboard) + case GDK_Alt_L: + case GDK_Alt_R: + return VK_MENU; // (12) ALT key + + case GDK_Pause: + return VK_PAUSE; // (13) PAUSE key + case GDK_Caps_Lock: + return VK_CAPITAL; // (14) CAPS LOCK key + case GDK_Kana_Lock: + case GDK_Kana_Shift: + return VK_KANA; // (15) Input Method Editor (IME) Kana mode + case GDK_Hangul: + return VK_HANGUL; // VK_HANGUL (15) IME Hangul mode + // VK_JUNJA (17) IME Junja mode + // VK_FINAL (18) IME final mode + case GDK_Hangul_Hanja: + return VK_HANJA; // (19) IME Hanja mode + case GDK_Kanji: + return VK_KANJI; // (19) IME Kanji mode + case GDK_Escape: + return VK_ESCAPE; // (1B) ESC key + // VK_CONVERT (1C) IME convert + // VK_NONCONVERT (1D) IME nonconvert + // VK_ACCEPT (1E) IME accept + // VK_MODECHANGE (1F) IME mode change request + case GDK_space: + return VK_SPACE; // (20) SPACEBAR + case GDK_Page_Up: + return VK_PRIOR; // (21) PAGE UP key + case GDK_Page_Down: + return VK_NEXT; // (22) PAGE DOWN key + case GDK_End: + return VK_END; // (23) END key + case GDK_Home: + return VK_HOME; // (24) HOME key + case GDK_Left: + return VK_LEFT; // (25) LEFT ARROW key + case GDK_Up: + return VK_UP; // (26) UP ARROW key + case GDK_Right: + return VK_RIGHT; // (27) RIGHT ARROW key + case GDK_Down: + return VK_DOWN; // (28) DOWN ARROW key + case GDK_Select: + return VK_SELECT; // (29) SELECT key + case GDK_Print: + return VK_SNAPSHOT; // (2C) PRINT SCREEN key + case GDK_Execute: + return VK_EXECUTE;// (2B) EXECUTE key + case GDK_Insert: + case GDK_KP_Insert: + return VK_INSERT; // (2D) INS key + case GDK_Delete: + case GDK_KP_Delete: + return VK_DELETE; // (2E) DEL key + case GDK_Help: + return VK_HELP; // (2F) HELP key + case GDK_0: + case GDK_parenright: + return VK_0; // (30) 0) key + case GDK_1: + case GDK_exclam: + return VK_1; // (31) 1 ! key + case GDK_2: + case GDK_at: + return VK_2; // (32) 2 & key + case GDK_3: + case GDK_numbersign: + return VK_3; //case '3': case '#'; + case GDK_4: + case GDK_dollar: // (34) 4 key '$'; + return VK_4; + case GDK_5: + case GDK_percent: + return VK_5; // (35) 5 key '%' + case GDK_6: + case GDK_asciicircum: + return VK_6; // (36) 6 key '^' + case GDK_7: + case GDK_ampersand: + return VK_7; // (37) 7 key case '&' + case GDK_8: + case GDK_asterisk: + return VK_8; // (38) 8 key '*' + case GDK_9: + case GDK_parenleft: + return VK_9; // (39) 9 key '(' + case GDK_a: + case GDK_A: + return VK_A; // (41) A key case 'a': case 'A': return 0x41; + case GDK_b: + case GDK_B: + return VK_B; // (42) B key case 'b': case 'B': return 0x42; + case GDK_c: + case GDK_C: + return VK_C; // (43) C key case 'c': case 'C': return 0x43; + case GDK_d: + case GDK_D: + return VK_D; // (44) D key case 'd': case 'D': return 0x44; + case GDK_e: + case GDK_E: + return VK_E; // (45) E key case 'e': case 'E': return 0x45; + case GDK_f: + case GDK_F: + return VK_F; // (46) F key case 'f': case 'F': return 0x46; + case GDK_g: + case GDK_G: + return VK_G; // (47) G key case 'g': case 'G': return 0x47; + case GDK_h: + case GDK_H: + return VK_H; // (48) H key case 'h': case 'H': return 0x48; + case GDK_i: + case GDK_I: + return VK_I; // (49) I key case 'i': case 'I': return 0x49; + case GDK_j: + case GDK_J: + return VK_J; // (4A) J key case 'j': case 'J': return 0x4A; + case GDK_k: + case GDK_K: + return VK_K; // (4B) K key case 'k': case 'K': return 0x4B; + case GDK_l: + case GDK_L: + return VK_L; // (4C) L key case 'l': case 'L': return 0x4C; + case GDK_m: + case GDK_M: + return VK_M; // (4D) M key case 'm': case 'M': return 0x4D; + case GDK_n: + case GDK_N: + return VK_N; // (4E) N key case 'n': case 'N': return 0x4E; + case GDK_o: + case GDK_O: + return VK_O; // (4F) O key case 'o': case 'O': return 0x4F; + case GDK_p: + case GDK_P: + return VK_P; // (50) P key case 'p': case 'P': return 0x50; + case GDK_q: + case GDK_Q: + return VK_Q; // (51) Q key case 'q': case 'Q': return 0x51; + case GDK_r: + case GDK_R: + return VK_R; // (52) R key case 'r': case 'R': return 0x52; + case GDK_s: + case GDK_S: + return VK_S; // (53) S key case 's': case 'S': return 0x53; + case GDK_t: + case GDK_T: + return VK_T; // (54) T key case 't': case 'T': return 0x54; + case GDK_u: + case GDK_U: + return VK_U; // (55) U key case 'u': case 'U': return 0x55; + case GDK_v: + case GDK_V: + return VK_V; // (56) V key case 'v': case 'V': return 0x56; + case GDK_w: + case GDK_W: + return VK_W; // (57) W key case 'w': case 'W': return 0x57; + case GDK_x: + case GDK_X: + return VK_X; // (58) X key case 'x': case 'X': return 0x58; + case GDK_y: + case GDK_Y: + return VK_Y; // (59) Y key case 'y': case 'Y': return 0x59; + case GDK_z: + case GDK_Z: + return VK_Z; // (5A) Z key case 'z': case 'Z': return 0x5A; + case GDK_Meta_L: + return VK_LWIN; // (5B) Left Windows key (Microsoft Natural keyboard) + case GDK_Meta_R: + return VK_RWIN; // (5C) Right Windows key (Natural keyboard) + // VK_SLEEP (5F) Computer Sleep key + // VK_SEPARATOR (6C) Separator key + // VK_SUBTRACT (6D) Subtract key + // VK_DECIMAL (6E) Decimal key + // VK_DIVIDE (6F) Divide key + // handled by key code above + + case GDK_Num_Lock: + return VK_NUMLOCK; // (90) NUM LOCK key + + case GDK_Scroll_Lock: + return VK_SCROLL; // (91) SCROLL LOCK key + + // VK_LSHIFT (A0) Left SHIFT key + // VK_RSHIFT (A1) Right SHIFT key + // VK_LCONTROL (A2) Left CONTROL key + // VK_RCONTROL (A3) Right CONTROL key + // VK_LMENU (A4) Left MENU key + // VK_RMENU (A5) Right MENU key + // VK_BROWSER_BACK (A6) Windows 2000/XP: Browser Back key + // VK_BROWSER_FORWARD (A7) Windows 2000/XP: Browser Forward key + // VK_BROWSER_REFRESH (A8) Windows 2000/XP: Browser Refresh key + // VK_BROWSER_STOP (A9) Windows 2000/XP: Browser Stop key + // VK_BROWSER_SEARCH (AA) Windows 2000/XP: Browser Search key + // VK_BROWSER_FAVORITES (AB) Windows 2000/XP: Browser Favorites key + // VK_BROWSER_HOME (AC) Windows 2000/XP: Browser Start and Home key + // VK_VOLUME_MUTE (AD) Windows 2000/XP: Volume Mute key + // VK_VOLUME_DOWN (AE) Windows 2000/XP: Volume Down key + // VK_VOLUME_UP (AF) Windows 2000/XP: Volume Up key + // VK_MEDIA_NEXT_TRACK (B0) Windows 2000/XP: Next Track key + // VK_MEDIA_PREV_TRACK (B1) Windows 2000/XP: Previous Track key + // VK_MEDIA_STOP (B2) Windows 2000/XP: Stop Media key + // VK_MEDIA_PLAY_PAUSE (B3) Windows 2000/XP: Play/Pause Media key + // VK_LAUNCH_MAIL (B4) Windows 2000/XP: Start Mail key + // VK_LAUNCH_MEDIA_SELECT (B5) Windows 2000/XP: Select Media key + // VK_LAUNCH_APP1 (B6) Windows 2000/XP: Start Application 1 key + // VK_LAUNCH_APP2 (B7) Windows 2000/XP: Start Application 2 key + + // VK_OEM_1 (BA) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the ';:' key + case GDK_semicolon: + case GDK_colon: + return VK_OEM_1; //case ';': case ':': return 0xBA; + // VK_OEM_PLUS (BB) Windows 2000/XP: For any country/region, the '+' key + case GDK_plus: + case GDK_equal: + return VK_OEM_PLUS; //case '=': case '+': return 0xBB; + // VK_OEM_COMMA (BC) Windows 2000/XP: For any country/region, the ',' key + case GDK_comma: + case GDK_less: + return VK_OEM_COMMA; //case ',': case '<': return 0xBC; + // VK_OEM_MINUS (BD) Windows 2000/XP: For any country/region, the '-' key + case GDK_minus: + case GDK_underscore: + return VK_OEM_MINUS; //case '-': case '_': return 0xBD; + // VK_OEM_PERIOD (BE) Windows 2000/XP: For any country/region, the '.' key + case GDK_period: + case GDK_greater: + return VK_OEM_PERIOD; //case '.': case '>': return 0xBE; + // VK_OEM_2 (BF) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the '/?' key + case GDK_slash: + case GDK_question: + return VK_OEM_2; //case '/': case '?': return 0xBF; + // VK_OEM_3 (C0) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the '`~' key + case GDK_asciitilde: + case GDK_quoteleft: + return VK_OEM_3; //case '`': case '~': return 0xC0; + // VK_OEM_4 (DB) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the '[{' key + case GDK_bracketleft: + case GDK_braceleft: + return VK_OEM_4; //case '[': case '{': return 0xDB; + // VK_OEM_5 (DC) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the '\|' key + case GDK_backslash: + case GDK_bar: + return VK_OEM_5; //case '\\': case '|': return 0xDC; + // VK_OEM_6 (DD) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the ']}' key + case GDK_bracketright: + case GDK_braceright: + return VK_OEM_6; // case ']': case '}': return 0xDD; + // VK_OEM_7 (DE) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key + case GDK_quoteright: + case GDK_quotedbl: + return VK_OEM_7; // case '\'': case '"': return 0xDE; + // VK_OEM_8 (DF) Used for miscellaneous characters; it can vary by keyboard. + // VK_OEM_102 (E2) Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard + // VK_PROCESSKEY (E5) Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCESS key + // VK_PACKET (E7) Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT,SendInput, WM_KEYDOWN, and WM_KEYUP + // VK_ATTN (F6) Attn key + // VK_CRSEL (F7) CrSel key + // VK_EXSEL (F8) ExSel key + // VK_EREOF (F9) Erase EOF key + // VK_PLAY (FA) Play key + // VK_ZOOM (FB) Zoom key + // VK_NONAME (FC) Reserved for future use + // VK_PA1 (FD) PA1 key + // VK_OEM_CLEAR (FE) Clear key + case GDK_F1: + case GDK_F2: + case GDK_F3: + case GDK_F4: + case GDK_F5: + case GDK_F6: + case GDK_F7: + case GDK_F8: + case GDK_F9: + case GDK_F10: + case GDK_F11: + case GDK_F12: + case GDK_F13: + case GDK_F14: + case GDK_F15: + case GDK_F16: + case GDK_F17: + case GDK_F18: + case GDK_F19: + case GDK_F20: + case GDK_F21: + case GDK_F22: + case GDK_F23: + case GDK_F24: + return VK_F1 + (keycode - GDK_F1); + + default: + return 0; + } + +} + +static String singleCharacterString(guint val) +{ + switch (val) { + case GDK_ISO_Enter: + case GDK_KP_Enter: + case GDK_Return: + return String("\r"); + case GDK_BackSpace: + return String("\x8"); + case GDK_Tab: + return String("\t"); + default: + gunichar c = gdk_keyval_to_unicode(val); + glong nwc; + gunichar2* uchar16 = g_ucs4_to_utf16(&c, 1, 0, &nwc, 0); + + String retVal; + if (uchar16) + retVal = String((UChar*)uchar16, nwc); + else + retVal = String(); + + g_free(uchar16); + + return retVal; + } +} + +// Keep this in sync with the other platform event constructors +// TODO: m_gdkEventKey should be refcounted +PlatformKeyboardEvent::PlatformKeyboardEvent(GdkEventKey* event) + : m_type((event->type == GDK_KEY_RELEASE) ? KeyUp : KeyDown) + , m_text(singleCharacterString(event->keyval)) + , m_unmodifiedText(singleCharacterString(event->keyval)) + , m_keyIdentifier(keyIdentifierForGdkKeyCode(event->keyval)) + , m_autoRepeat(false) + , m_windowsVirtualKeyCode(windowsKeyCodeForKeyEvent(event->keyval)) + , m_nativeVirtualKeyCode(event->keyval) + , m_isKeypad(event->keyval >= GDK_KP_Space && event->keyval <= GDK_KP_9) + , m_shiftKey((event->state & GDK_SHIFT_MASK) || (event->keyval == GDK_3270_BackTab)) + , m_ctrlKey(event->state & GDK_CONTROL_MASK) + , m_altKey(event->state & GDK_MOD1_MASK) + , m_metaKey(event->state & GDK_META_MASK) + , m_gdkEventKey(event) +{ +} + +void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool backwardCompatibilityMode) +{ + // Can only change type from KeyDown to RawKeyDown or Char, as we lack information for other conversions. + ASSERT(m_type == KeyDown); + m_type = type; + + if (backwardCompatibilityMode) + return; + + if (type == RawKeyDown) { + m_text = String(); + m_unmodifiedText = String(); + } else { + m_keyIdentifier = String(); + m_windowsVirtualKeyCode = 0; + } +} + +bool PlatformKeyboardEvent::currentCapsLockState() +{ + notImplemented(); + return false; +} + +void PlatformKeyboardEvent::getCurrentModifierState(bool& shiftKey, bool& ctrlKey, bool& altKey, bool& metaKey) +{ + notImplemented(); + shiftKey = false; + ctrlKey = false; + altKey = false; + metaKey = false; +} + +GdkEventKey* PlatformKeyboardEvent::gdkEventKey() const +{ + return m_gdkEventKey; +} + +} diff --git a/Source/WebCore/platform/gtk/LanguageGtk.cpp b/Source/WebCore/platform/gtk/LanguageGtk.cpp new file mode 100644 index 0000000..7d7a66d --- /dev/null +++ b/Source/WebCore/platform/gtk/LanguageGtk.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "Language.h" + +#include "GOwnPtr.h" +#include "PlatformString.h" +#include <wtf/text/CString.h> + +#include <glib.h> +#include <locale.h> + +namespace WebCore { + +// Using pango_language_get_default() here is not an option, because +// it doesn't support changing the locale in runtime, so it returns +// always the same value. +String platformDefaultLanguage() +{ + char* localeDefault = setlocale(LC_CTYPE, NULL); + + if (!localeDefault) + return String("c"); + + GOwnPtr<gchar> normalizedDefault(g_ascii_strdown(localeDefault, -1)); + char* ptr = strchr(normalizedDefault.get(), '_'); + + if (ptr) + *ptr = '-'; + + ptr = strchr(normalizedDefault.get(), '.'); + + if (ptr) + *ptr = '\0'; + + return String(normalizedDefault.get()); +} + +} diff --git a/Source/WebCore/platform/gtk/LocalizedStringsGtk.cpp b/Source/WebCore/platform/gtk/LocalizedStringsGtk.cpp new file mode 100644 index 0000000..65e8852 --- /dev/null +++ b/Source/WebCore/platform/gtk/LocalizedStringsGtk.cpp @@ -0,0 +1,635 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2008 Christian Dywan <christian@imendio.com> + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2010 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "LocalizedStrings.h" +#include "GOwnPtr.h" +#include "IntSize.h" +#include "NotImplemented.h" +#include "PlatformString.h" +#include <wtf/text/CString.h> + +#include <glib/gi18n-lib.h> +#include <gtk/gtk.h> +#include <math.h> + +namespace WebCore { + +static const char* gtkStockLabel(const char* stockID) +{ + GtkStockItem item; + if (!gtk_stock_lookup(stockID, &item)) + return stockID; + return item.label; +} + +String submitButtonDefaultLabel() +{ + return String::fromUTF8(_("Submit")); +} + +String inputElementAltText() +{ + return String::fromUTF8(_("Submit")); +} + +String resetButtonDefaultLabel() +{ + return String::fromUTF8(_("Reset")); +} + +String searchableIndexIntroduction() +{ + return String::fromUTF8(_("This is a searchable index. Enter search keywords: ")); +} + +String fileButtonChooseFileLabel() +{ + return String::fromUTF8(_("Choose File")); +} + +String fileButtonNoFileSelectedLabel() +{ + return String::fromUTF8(_("(None)")); +} + +String contextMenuItemTagOpenLinkInNewWindow() +{ + return String::fromUTF8(_("Open Link in New _Window")); +} + +String contextMenuItemTagDownloadLinkToDisk() +{ + return String::fromUTF8(_("_Download Linked File")); +} + +String contextMenuItemTagCopyLinkToClipboard() +{ + return String::fromUTF8(_("Copy Link Loc_ation")); +} + +String contextMenuItemTagOpenImageInNewWindow() +{ + return String::fromUTF8(_("Open _Image in New Window")); +} + +String contextMenuItemTagDownloadImageToDisk() +{ + return String::fromUTF8(_("Sa_ve Image As")); +} + +String contextMenuItemTagCopyImageToClipboard() +{ + return String::fromUTF8(_("Cop_y Image")); +} + +String contextMenuItemTagOpenVideoInNewWindow() +{ + return String::fromUTF8(_("Open _Video in New Window")); +} + +String contextMenuItemTagOpenAudioInNewWindow() +{ + return String::fromUTF8(_("Open _Audio in New Window")); +} + +String contextMenuItemTagCopyVideoLinkToClipboard() +{ + return String::fromUTF8(_("Cop_y Video Link Location")); +} + +String contextMenuItemTagCopyAudioLinkToClipboard() +{ + return String::fromUTF8(_("Cop_y Audio Link Location")); +} + +String contextMenuItemTagToggleMediaControls() +{ + return String::fromUTF8(_("_Toggle Media Controls")); +} + +String contextMenuItemTagToggleMediaLoop() +{ + return String::fromUTF8(_("Toggle Media _Loop Playback")); +} + +String contextMenuItemTagEnterVideoFullscreen() +{ + return String::fromUTF8(_("Switch Video to _Fullscreen")); +} + +String contextMenuItemTagMediaPlay() +{ + return String::fromUTF8(_("_Play")); +} + +String contextMenuItemTagMediaPause() +{ + return String::fromUTF8(_("_Pause")); +} + +String contextMenuItemTagMediaMute() +{ + return String::fromUTF8(_("_Mute")); +} + +String contextMenuItemTagOpenFrameInNewWindow() +{ + return String::fromUTF8(_("Open _Frame in New Window")); +} + +String contextMenuItemTagCopy() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_COPY)); + return stockLabel; +} + +String contextMenuItemTagDelete() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_DELETE)); + return stockLabel; +} + +String contextMenuItemTagSelectAll() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_SELECT_ALL)); + return stockLabel; +} + +String contextMenuItemTagUnicode() +{ + return String::fromUTF8(_("_Insert Unicode Control Character")); +} + +String contextMenuItemTagInputMethods() +{ + return String::fromUTF8(_("Input _Methods")); +} + +String contextMenuItemTagGoBack() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_GO_BACK)); + return stockLabel; +} + +String contextMenuItemTagGoForward() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_GO_FORWARD)); + return stockLabel; +} + +String contextMenuItemTagStop() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_STOP)); + return stockLabel; +} + +String contextMenuItemTagReload() +{ + return String::fromUTF8(_("_Reload")); +} + +String contextMenuItemTagCut() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_CUT)); + return stockLabel; +} + +String contextMenuItemTagPaste() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_PASTE)); + return stockLabel; +} + +String contextMenuItemTagNoGuessesFound() +{ + return String::fromUTF8(_("No Guesses Found")); +} + +String contextMenuItemTagIgnoreSpelling() +{ + return String::fromUTF8(_("_Ignore Spelling")); +} + +String contextMenuItemTagLearnSpelling() +{ + return String::fromUTF8(_("_Learn Spelling")); +} + +String contextMenuItemTagSearchWeb() +{ + return String::fromUTF8(_("_Search the Web")); +} + +String contextMenuItemTagLookUpInDictionary() +{ + return String::fromUTF8(_("_Look Up in Dictionary")); +} + +String contextMenuItemTagOpenLink() +{ + return String::fromUTF8(_("_Open Link")); +} + +String contextMenuItemTagIgnoreGrammar() +{ + return String::fromUTF8(_("Ignore _Grammar")); +} + +String contextMenuItemTagSpellingMenu() +{ + return String::fromUTF8(_("Spelling and _Grammar")); +} + +String contextMenuItemTagShowSpellingPanel(bool show) +{ + return String::fromUTF8(show ? _("_Show Spelling and Grammar") : _("_Hide Spelling and Grammar")); +} + +String contextMenuItemTagCheckSpelling() +{ + return String::fromUTF8(_("_Check Document Now")); +} + +String contextMenuItemTagCheckSpellingWhileTyping() +{ + return String::fromUTF8(_("Check Spelling While _Typing")); +} + +String contextMenuItemTagCheckGrammarWithSpelling() +{ + return String::fromUTF8(_("Check _Grammar With Spelling")); +} + +String contextMenuItemTagFontMenu() +{ + return String::fromUTF8(_("_Font")); +} + +String contextMenuItemTagBold() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_BOLD)); + return stockLabel; +} + +String contextMenuItemTagItalic() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_ITALIC)); + return stockLabel; +} + +String contextMenuItemTagUnderline() +{ + static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_UNDERLINE)); + return stockLabel; +} + +String contextMenuItemTagOutline() +{ + return String::fromUTF8(_("_Outline")); +} + +String contextMenuItemTagInspectElement() +{ + return String::fromUTF8(_("Inspect _Element")); +} + +String searchMenuNoRecentSearchesText() +{ + return String::fromUTF8(_("No recent searches")); +} + +String searchMenuRecentSearchesText() +{ + return String::fromUTF8(_("Recent searches")); +} + +String searchMenuClearRecentSearchesText() +{ + return String::fromUTF8(_("_Clear recent searches")); +} + +String AXDefinitionListTermText() +{ + return String::fromUTF8(_("term")); +} + +String AXDefinitionListDefinitionText() +{ + return String::fromUTF8(_("definition")); +} + +String AXButtonActionVerb() +{ + return String::fromUTF8(_("press")); +} + +String AXRadioButtonActionVerb() +{ + return String::fromUTF8(_("select")); +} + +String AXTextFieldActionVerb() +{ + return String::fromUTF8(_("activate")); +} + +String AXCheckedCheckBoxActionVerb() +{ + return String::fromUTF8(_("uncheck")); +} + +String AXUncheckedCheckBoxActionVerb() +{ + return String::fromUTF8(_("check")); +} + +String AXLinkActionVerb() +{ + return String::fromUTF8(_("jump")); +} + +String AXMenuListPopupActionVerb() +{ + return String(); +} + +String AXMenuListActionVerb() +{ + return String(); +} + +String missingPluginText() +{ + return String::fromUTF8(_("Missing Plug-in")); +} + +String crashedPluginText() +{ + notImplemented(); + return String::fromUTF8(_("Plug-in Failure")); +} + +String multipleFileUploadText(unsigned numberOfFiles) +{ + // FIXME: If this file gets localized, this should really be localized as one string with a wildcard for the number. + return String::number(numberOfFiles) + String::fromUTF8(_(" files")); +} + +String unknownFileSizeText() +{ + return String::fromUTF8(_("Unknown")); +} + +String imageTitle(const String& filename, const IntSize& size) +{ + GOwnPtr<gchar> string(g_strdup_printf(C_("Title string for images", "%s (%dx%d pixels)"), + filename.utf8().data(), + size.width(), size.height())); + + return String::fromUTF8(string.get()); +} + + +#if ENABLE(VIDEO) + +String mediaElementLoadingStateText() +{ + return String::fromUTF8(_("Loading...")); +} + +String mediaElementLiveBroadcastStateText() +{ + return String::fromUTF8(_("Live Broadcast")); +} + +String localizedMediaControlElementString(const String& name) +{ + if (name == "AudioElement") + return String::fromUTF8(_("audio element controller")); + if (name == "VideoElement") + return String::fromUTF8(_("video element controller")); + if (name == "MuteButton") + return String::fromUTF8(_("mute")); + if (name == "UnMuteButton") + return String::fromUTF8(_("unmute")); + if (name == "PlayButton") + return String::fromUTF8(_("play")); + if (name == "PauseButton") + return String::fromUTF8(_("pause")); + if (name == "Slider") + return String::fromUTF8(_("movie time")); + if (name == "SliderThumb") + return String::fromUTF8(_("timeline slider thumb")); + if (name == "RewindButton") + return String::fromUTF8(_("back 30 seconds")); + if (name == "ReturnToRealtimeButton") + return String::fromUTF8(_("return to realtime")); + if (name == "CurrentTimeDisplay") + return String::fromUTF8(_("elapsed time")); + if (name == "TimeRemainingDisplay") + return String::fromUTF8(_("remaining time")); + if (name == "StatusDisplay") + return String::fromUTF8(_("status")); + if (name == "FullscreenButton") + return String::fromUTF8(_("fullscreen")); + if (name == "SeekForwardButton") + return String::fromUTF8(_("fast forward")); + if (name == "SeekBackButton") + return String::fromUTF8(_("fast reverse")); + if (name == "ShowClosedCaptionsButton") + return String::fromUTF8(_("show closed captions")); + if (name == "HideClosedCaptionsButton") + return String::fromUTF8(_("hide closed captions")); + + ASSERT_NOT_REACHED(); + return String(); +} + +String localizedMediaControlElementHelpText(const String& name) +{ + if (name == "AudioElement") + return String::fromUTF8(_("audio element playback controls and status display")); + if (name == "VideoElement") + return String::fromUTF8(_("video element playback controls and status display")); + if (name == "MuteButton") + return String::fromUTF8(_("mute audio tracks")); + if (name == "UnMuteButton") + return String::fromUTF8(_("unmute audio tracks")); + if (name == "PlayButton") + return String::fromUTF8(_("begin playback")); + if (name == "PauseButton") + return String::fromUTF8(_("pause playback")); + if (name == "Slider") + return String::fromUTF8(_("movie time scrubber")); + if (name == "SliderThumb") + return String::fromUTF8(_("movie time scrubber thumb")); + if (name == "RewindButton") + return String::fromUTF8(_("seek movie back 30 seconds")); + if (name == "ReturnToRealtimeButton") + return String::fromUTF8(_("return streaming movie to real time")); + if (name == "CurrentTimeDisplay") + return String::fromUTF8(_("current movie time in seconds")); + if (name == "TimeRemainingDisplay") + return String::fromUTF8(_("number of seconds of movie remaining")); + if (name == "StatusDisplay") + return String::fromUTF8(_("current movie status")); + if (name == "SeekBackButton") + return String::fromUTF8(_("seek quickly back")); + if (name == "SeekForwardButton") + return String::fromUTF8(_("seek quickly forward")); + if (name == "FullscreenButton") + return String::fromUTF8(_("Play movie in fullscreen mode")); + if (name == "ShowClosedCaptionsButton") + return String::fromUTF8(_("start displaying closed captions")); + if (name == "HideClosedCaptionsButton") + return String::fromUTF8(_("stop displaying closed captions")); + + ASSERT_NOT_REACHED(); + return String(); +} + +String localizedMediaTimeDescription(float time) +{ + if (!isfinite(time)) + return String::fromUTF8(_("indefinite time")); + + int seconds = (int)fabsf(time); + int days = seconds / (60 * 60 * 24); + int hours = seconds / (60 * 60); + int minutes = (seconds / 60) % 60; + seconds %= 60; + + if (days) { + GOwnPtr<gchar> string(g_strdup_printf("%d days %d hours %d minutes %d seconds", days, hours, minutes, seconds)); + return String::fromUTF8(string.get()); + } + + if (hours) { + GOwnPtr<gchar> string(g_strdup_printf("%d hours %d minutes %d seconds", hours, minutes, seconds)); + return String::fromUTF8(string.get()); + } + + if (minutes) { + GOwnPtr<gchar> string(g_strdup_printf("%d minutes %d seconds", minutes, seconds)); + return String::fromUTF8(string.get()); + } + + GOwnPtr<gchar> string(g_strdup_printf("%d seconds", seconds)); + return String::fromUTF8(string.get()); +} +#endif // ENABLE(VIDEO) + +String validationMessageValueMissingText() +{ + return String::fromUTF8(_("value missing")); +} + +String validationMessageValueMissingForCheckboxText() +{ + notImplemented(); + return validationMessageValueMissingText(); +} + +String validationMessageValueMissingForFileText() +{ + notImplemented(); + return validationMessageValueMissingText(); +} + +String validationMessageValueMissingForMultipleFileText() +{ + notImplemented(); + return validationMessageValueMissingText(); +} + +String validationMessageValueMissingForRadioText() +{ + notImplemented(); + return validationMessageValueMissingText(); +} + +String validationMessageValueMissingForSelectText() +{ + notImplemented(); + return validationMessageValueMissingText(); +} + +String validationMessageTypeMismatchText() +{ + notImplemented(); + return String::fromUTF8(_("type mismatch")); +} + +String validationMessageTypeMismatchForEmailText() +{ + notImplemented(); + return validationMessageTypeMismatchText(); +} + +String validationMessageTypeMismatchForMultipleEmailText() +{ + notImplemented(); + return validationMessageTypeMismatchText(); +} + +String validationMessageTypeMismatchForURLText() +{ + notImplemented(); + return validationMessageTypeMismatchText(); +} + +String validationMessagePatternMismatchText() +{ + return String::fromUTF8(_("pattern mismatch")); +} + +String validationMessageTooLongText(int, int) +{ + return String::fromUTF8(_("too long")); +} + +String validationMessageRangeUnderflowText(const String&) +{ + return String::fromUTF8(_("range underflow")); +} + +String validationMessageRangeOverflowText(const String&) +{ + return String::fromUTF8(_("range overflow")); +} + +String validationMessageStepMismatchText(const String&, const String&) +{ + return String::fromUTF8(_("step mismatch")); +} + +} diff --git a/Source/WebCore/platform/gtk/LoggingGtk.cpp b/Source/WebCore/platform/gtk/LoggingGtk.cpp new file mode 100644 index 0000000..6350036 --- /dev/null +++ b/Source/WebCore/platform/gtk/LoggingGtk.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "Logging.h" +#include "PlatformString.h" + +#include <glib.h> +#include <string.h> + +namespace WebCore { + +// Inspired by the code used by the Qt port + +void InitializeLoggingChannelsIfNecessary() +{ + static bool didInitializeLoggingChannels = false; + if (didInitializeLoggingChannels) + return; + + didInitializeLoggingChannels = true; + + char* logEnv = getenv("WEBKIT_DEBUG"); + if (!logEnv) + return; + + // we set up the logs anyway because some of our logging, such as + // soup's is available in release builds +#if defined(NDEBUG) + g_warning("WEBKIT_DEBUG is not empty, but this is a release build. Notice that many log messages will only appear in a debug build."); +#endif + + char** logv = g_strsplit(logEnv, " ", -1); + + for (int i = 0; logv[i]; i++) { + if (WTFLogChannel* channel = getChannelFromName(logv[i])) + channel->state = WTFLogChannelOn; + } + + g_strfreev(logv); + + // to disable logging notImplemented set the DISABLE_NI_WARNING + // environment variable to 1 + LogNotYetImplemented.state = WTFLogChannelOn; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp b/Source/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp new file mode 100644 index 0000000..8fc3020 --- /dev/null +++ b/Source/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MIMETypeRegistry.h" + +namespace WebCore { + +struct ExtensionMap { + const char* extension; + const char* mimeType; +}; + +static const ExtensionMap extensionMap [] = { + { "bmp", "image/bmp" }, + { "css", "text/css" }, + { "gif", "image/gif" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "ico", "image/x-icon" }, + { "jpeg", "image/jpeg" }, + { "jpg", "image/jpeg" }, + { "js", "application/x-javascript" }, + { "pdf", "application/pdf" }, + { "png", "image/png" }, + { "rss", "application/rss+xml" }, + { "svg", "image/svg+xml" }, + { "text", "text/plain" }, + { "txt", "text/plain" }, + { "xbm", "image/x-xbitmap" }, + { "xml", "text/xml" }, + { "xsl", "text/xsl" }, + { "xhtml", "application/xhtml+xml" }, + { "wml", "text/vnd.wap.wml" }, + { "wmlc", "application/vnd.wap.wmlc" }, + { 0, 0 } +}; + +String MIMETypeRegistry::getMIMETypeForExtension(const String &ext) +{ + String s = ext.lower(); + const ExtensionMap *e = extensionMap; + while (e->extension) { + if (s == e->extension) + return e->mimeType; + ++e; + } + + return String(); +} + +bool MIMETypeRegistry::isApplicationPluginMIMEType(const String&) +{ + return false; +} + +} diff --git a/Source/WebCore/platform/gtk/MainFrameScrollbarGtk.cpp b/Source/WebCore/platform/gtk/MainFrameScrollbarGtk.cpp new file mode 100644 index 0000000..c2e24e0 --- /dev/null +++ b/Source/WebCore/platform/gtk/MainFrameScrollbarGtk.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2007, 2009 Holger Hans Peter Freyther zecke@selfish.org + * Copyright (C) 2010 Gustavo Noronha Silva <gns@gnome.org> + * Copyright (C) 2010 Collabora Ltd. + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "MainFrameScrollbarGtk.h" + +#include "GraphicsContext.h" +#include "GtkVersioning.h" +#include "IntRect.h" +#include <gtk/gtk.h> + +using namespace WebCore; + +PassRefPtr<MainFrameScrollbarGtk> MainFrameScrollbarGtk::create(ScrollbarClient* client, ScrollbarOrientation orientation, GtkAdjustment* adj) +{ + return adoptRef(new MainFrameScrollbarGtk(client, orientation, adj)); +} + +// Main frame scrollbars are slaves to a GtkAdjustment. If a main frame +// scrollbar has an m_adjustment, it belongs to the container (a GtkWidget such +// as GtkScrolledWindow). The adjustment may also be null, in which case there +// is no containing view or the parent ScrollView is in some sort of transition +// state. These scrollbars are never painted, as the container takes care of +// that. They exist only to shuttle data from the GtkWidget container into +// WebCore and vice-versa. +MainFrameScrollbarGtk::MainFrameScrollbarGtk(ScrollbarClient* client, ScrollbarOrientation orientation, GtkAdjustment* adjustment) + : Scrollbar(client, orientation, RegularScrollbar) + , m_adjustment(0) +{ + attachAdjustment(adjustment); + + // We have nothing to show as we are solely operating on the GtkAdjustment. + resize(0, 0); +} + +MainFrameScrollbarGtk::~MainFrameScrollbarGtk() +{ + if (m_adjustment) + detachAdjustment(); +} + +void MainFrameScrollbarGtk::attachAdjustment(GtkAdjustment* adjustment) +{ + if (m_adjustment) + detachAdjustment(); + + m_adjustment = adjustment; + if (!m_adjustment) + return; + + g_signal_connect(m_adjustment.get(), "value-changed", G_CALLBACK(MainFrameScrollbarGtk::gtkValueChanged), this); + updateThumbProportion(); + updateThumbPosition(); +} + +void MainFrameScrollbarGtk::detachAdjustment() +{ + if (!m_adjustment) + return; + + g_signal_handlers_disconnect_by_func(G_OBJECT(m_adjustment.get()), (gpointer)MainFrameScrollbarGtk::gtkValueChanged, this); + + // For the case where we only operate on the GtkAdjustment it is best to + // reset the values so that the surrounding scrollbar gets updated, or + // e.g. for a GtkScrolledWindow the scrollbar gets hidden. + gtk_adjustment_configure(m_adjustment.get(), 0, 0, 0, 0, 0, 0); + + m_adjustment = 0; +} + +void MainFrameScrollbarGtk::updateThumbPosition() +{ + if (!m_adjustment || gtk_adjustment_get_value(m_adjustment.get()) == m_currentPos) + return; + gtk_adjustment_set_value(m_adjustment.get(), m_currentPos); +} + +void MainFrameScrollbarGtk::updateThumbProportion() +{ + if (!m_adjustment) + return; + gtk_adjustment_configure(m_adjustment.get(), + gtk_adjustment_get_value(m_adjustment.get()), + gtk_adjustment_get_lower(m_adjustment.get()), + m_totalSize, + m_lineStep, + m_pageStep, + m_visibleSize); +} + +void MainFrameScrollbarGtk::gtkValueChanged(GtkAdjustment*, MainFrameScrollbarGtk* that) +{ + that->setValue(static_cast<int>(gtk_adjustment_get_value(that->m_adjustment.get())), NotFromScrollAnimator); +} + +void MainFrameScrollbarGtk::paint(GraphicsContext* context, const IntRect& rect) +{ + // Main frame scrollbars are not painted by WebCore. + return; +} diff --git a/Source/WebCore/platform/gtk/MainFrameScrollbarGtk.h b/Source/WebCore/platform/gtk/MainFrameScrollbarGtk.h new file mode 100644 index 0000000..f184425 --- /dev/null +++ b/Source/WebCore/platform/gtk/MainFrameScrollbarGtk.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef MainFrameScrollbarGtk_h +#define MainFrameScrollbarGtk_h + +#include "GRefPtrGtk.h" +#include "Scrollbar.h" +#include <wtf/PassRefPtr.h> + +typedef struct _GtkAdjustment GtkAdjustment; + +namespace WebCore { + +class MainFrameScrollbarGtk : public Scrollbar { +public: + ~MainFrameScrollbarGtk(); + virtual void paint(GraphicsContext*, const IntRect&); + void detachAdjustment(); + void attachAdjustment(GtkAdjustment*); + static PassRefPtr<MainFrameScrollbarGtk> create(ScrollbarClient*, ScrollbarOrientation, GtkAdjustment*); + +protected: + virtual void updateThumbPosition(); + virtual void updateThumbProportion(); + +private: + MainFrameScrollbarGtk(ScrollbarClient*, ScrollbarOrientation, GtkAdjustment*); + static void gtkValueChanged(GtkAdjustment*, MainFrameScrollbarGtk*); + + GRefPtr<GtkAdjustment> m_adjustment; +}; + +} + +#endif // ScrollbarGtk_h diff --git a/Source/WebCore/platform/gtk/MouseEventGtk.cpp b/Source/WebCore/platform/gtk/MouseEventGtk.cpp new file mode 100644 index 0000000..69f938f --- /dev/null +++ b/Source/WebCore/platform/gtk/MouseEventGtk.cpp @@ -0,0 +1,105 @@ +/* +* Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" +#include "PlatformMouseEvent.h" + +#include "Assertions.h" + +#include <gdk/gdk.h> + +namespace WebCore { + +// FIXME: Would be even better to figure out which modifier is Alt instead of always using GDK_MOD1_MASK. + +// Keep this in sync with the other platform event constructors +PlatformMouseEvent::PlatformMouseEvent(GdkEventButton* event) +{ + m_timestamp = event->time; + m_position = IntPoint((int)event->x, (int)event->y); + m_globalPosition = IntPoint((int)event->x_root, (int)event->y_root); + m_shiftKey = event->state & GDK_SHIFT_MASK; + m_ctrlKey = event->state & GDK_CONTROL_MASK; + m_altKey = event->state & GDK_MOD1_MASK; + m_metaKey = event->state & GDK_META_MASK; + + switch (event->type) { + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + m_eventType = MouseEventPressed; + if (event->type == GDK_BUTTON_RELEASE) { + m_eventType = MouseEventReleased; + m_clickCount = 0; + } else if (event->type == GDK_BUTTON_PRESS) + m_clickCount = 1; + else if (event->type == GDK_2BUTTON_PRESS) + m_clickCount = 2; + else if (event->type == GDK_3BUTTON_PRESS) + m_clickCount = 3; + + if (event->button == 1) + m_button = LeftButton; + else if (event->button == 2) + m_button = MiddleButton; + else if (event->button == 3) + m_button = RightButton; + break; + + default: + ASSERT_NOT_REACHED(); + }; +} + +PlatformMouseEvent::PlatformMouseEvent(GdkEventMotion* motion) +{ + m_timestamp = motion->time; + m_position = IntPoint((int)motion->x, (int)motion->y); + m_globalPosition = IntPoint((int)motion->x_root, (int)motion->y_root); + m_shiftKey = motion->state & GDK_SHIFT_MASK; + m_ctrlKey = motion->state & GDK_CONTROL_MASK; + m_altKey = motion->state & GDK_MOD1_MASK; + m_metaKey = motion->state & GDK_MOD2_MASK; + + switch (motion->type) { + case GDK_MOTION_NOTIFY: + m_eventType = MouseEventMoved; + m_button = NoButton; + m_clickCount = 0; + break; + default: + ASSERT_NOT_REACHED(); + }; + + if (motion->state & GDK_BUTTON1_MASK) + m_button = LeftButton; + else if (motion->state & GDK_BUTTON2_MASK) + m_button = MiddleButton; + else if (motion->state & GDK_BUTTON3_MASK) + m_button = RightButton; +} +} diff --git a/Source/WebCore/platform/gtk/PasteboardGtk.cpp b/Source/WebCore/platform/gtk/PasteboardGtk.cpp new file mode 100644 index 0000000..4cd5dc0 --- /dev/null +++ b/Source/WebCore/platform/gtk/PasteboardGtk.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "Pasteboard.h" + +#include "DataObjectGtk.h" +#include "DocumentFragment.h" +#include "Frame.h" +#include "NotImplemented.h" +#include "PlatformString.h" +#include "TextResourceDecoder.h" +#include "Image.h" +#include "RenderImage.h" +#include "KURL.h" +#include "markup.h" +#include <wtf/gobject/GRefPtr.h> +#include <wtf/text/CString.h> + +#include <gtk/gtk.h> + +namespace WebCore { + +Pasteboard* Pasteboard::generalPasteboard() +{ + static Pasteboard* pasteboard = new Pasteboard(); + return pasteboard; +} + +Pasteboard::Pasteboard() +{ + notImplemented(); +} + +Pasteboard::~Pasteboard() +{ + delete m_helper; +} + +PasteboardHelper* Pasteboard::helper() +{ + return m_helper; +} + +void Pasteboard::setHelper(PasteboardHelper* helper) +{ + m_helper = helper; +} + +void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame) +{ + GtkClipboard* clipboard = m_helper->getClipboard(frame); + DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); + dataObject->setText(frame->editor()->selectedText()); + dataObject->setMarkup(createMarkup(selectedRange, 0, AnnotateForInterchange, false, AbsoluteURLs)); + m_helper->writeClipboardContents(clipboard); +} + +void Pasteboard::writePlainText(const String& text) +{ + GtkClipboard* clipboard = gtk_clipboard_get_for_display(gdk_display_get_default(), GDK_SELECTION_CLIPBOARD); + DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); + dataObject->setText(text); + m_helper->writeClipboardContents(clipboard); +} + +void Pasteboard::writeURL(const KURL& url, const String& label, Frame* frame) +{ + if (url.isEmpty()) + return; + + GtkClipboard* clipboard = m_helper->getClipboard(frame); + DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); + dataObject->setURL(url, label); + m_helper->writeClipboardContents(clipboard); +} + +void Pasteboard::writeImage(Node* node, const KURL&, const String&) +{ + GtkClipboard* clipboard = gtk_clipboard_get_for_display(gdk_display_get_default(), GDK_SELECTION_CLIPBOARD); + + ASSERT(node && node->renderer() && node->renderer()->isImage()); + RenderImage* renderer = toRenderImage(node->renderer()); + CachedImage* cachedImage = renderer->cachedImage(); + if (!cachedImage || cachedImage->errorOccurred()) + return; + Image* image = cachedImage->image(); + ASSERT(image); + + GRefPtr<GdkPixbuf> pixbuf = adoptGRef(image->getGdkPixbuf()); + DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); + dataObject->setImage(pixbuf.get()); + m_helper->writeClipboardContents(clipboard); +} + +void Pasteboard::clear() +{ + gtk_clipboard_clear(gtk_clipboard_get_for_display(gdk_display_get_default(), GDK_SELECTION_CLIPBOARD)); +} + +bool Pasteboard::canSmartReplace() +{ + notImplemented(); + return false; +} + +PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefPtr<Range> context, + bool allowPlainText, bool& chosePlainText) +{ + GtkClipboard* clipboard = m_helper->getCurrentClipboard(frame); + DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); + m_helper->getClipboardContents(clipboard); + + chosePlainText = false; + + if (dataObject->hasMarkup()) { + RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(frame->document(), dataObject->markup(), "", FragmentScriptingNotAllowed); + if (fragment) + return fragment.release(); + } + + if (!allowPlainText) + return 0; + + if (dataObject->hasText()) { + chosePlainText = true; + RefPtr<DocumentFragment> fragment = createFragmentFromText(context.get(), dataObject->text()); + if (fragment) + return fragment.release(); + } + + return 0; +} + +String Pasteboard::plainText(Frame* frame) +{ + GtkClipboard* clipboard = m_helper->getCurrentClipboard(frame); + DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); + + m_helper->getClipboardContents(clipboard); + return dataObject->text(); +} + +} diff --git a/Source/WebCore/platform/gtk/PasteboardHelper.cpp b/Source/WebCore/platform/gtk/PasteboardHelper.cpp new file mode 100644 index 0000000..013789f --- /dev/null +++ b/Source/WebCore/platform/gtk/PasteboardHelper.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2010 Martin Robinson <mrobinson@webkit.org> + * Copyright (C) Igalia S.L. + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#include "config.h" +#include "PasteboardHelper.h" + +#include "Chrome.h" +#include "DataObjectGtk.h" +#include "Frame.h" +#include "GtkVersioning.h" +#include "Page.h" +#include "Pasteboard.h" +#include "TextResourceDecoder.h" +#include <gtk/gtk.h> +#include <wtf/gobject/GOwnPtr.h> + +namespace WebCore { + +static GdkAtom textPlainAtom; +static GdkAtom markupAtom; +static GdkAtom netscapeURLAtom; +static GdkAtom uriListAtom; +static String gMarkupPrefix; + +static void removeMarkupPrefix(String& markup) +{ + + // The markup prefix is not harmful, but we remove it from the string anyway, so that + // we can have consistent results with other ports during the layout tests. + if (markup.startsWith(gMarkupPrefix)) + markup.remove(0, gMarkupPrefix.length()); +} + +static void initGdkAtoms() +{ + static gboolean initialized = FALSE; + + if (initialized) + return; + + initialized = TRUE; + + textPlainAtom = gdk_atom_intern("text/plain;charset=utf-8", FALSE); + markupAtom = gdk_atom_intern("text/html", FALSE); + netscapeURLAtom = gdk_atom_intern("_NETSCAPE_URL", FALSE); + uriListAtom = gdk_atom_intern("text/uri-list", FALSE); + gMarkupPrefix = "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">"; +} + +PasteboardHelper::PasteboardHelper() + : m_targetList(gtk_target_list_new(0, 0)) +{ + initGdkAtoms(); +} + +PasteboardHelper::~PasteboardHelper() +{ + gtk_target_list_unref(m_targetList); +} + +void PasteboardHelper::initializeTargetList() +{ + gtk_target_list_add_text_targets(m_targetList, getIdForTargetType(TargetTypeText)); + gtk_target_list_add(m_targetList, markupAtom, 0, getIdForTargetType(TargetTypeMarkup)); + gtk_target_list_add_uri_targets(m_targetList, getIdForTargetType(TargetTypeURIList)); + gtk_target_list_add(m_targetList, netscapeURLAtom, 0, getIdForTargetType(TargetTypeNetscapeURL)); + gtk_target_list_add_image_targets(m_targetList, getIdForTargetType(TargetTypeImage), TRUE); +} + +static inline GtkWidget* widgetFromFrame(Frame* frame) +{ + ASSERT(frame); + Page* page = frame->page(); + ASSERT(page); + Chrome* chrome = page->chrome(); + ASSERT(chrome); + PlatformPageClient client = chrome->platformPageClient(); + ASSERT(client); + return client; +} + +GtkClipboard* PasteboardHelper::getCurrentClipboard(Frame* frame) +{ + if (usePrimarySelectionClipboard(widgetFromFrame(frame))) + return getPrimarySelectionClipboard(frame); + return getClipboard(frame); +} + +GtkClipboard* PasteboardHelper::getClipboard(Frame* frame) const +{ + return gtk_widget_get_clipboard(widgetFromFrame(frame), GDK_SELECTION_CLIPBOARD); +} + +GtkClipboard* PasteboardHelper::getPrimarySelectionClipboard(Frame* frame) const +{ + return gtk_widget_get_clipboard(widgetFromFrame(frame), GDK_SELECTION_PRIMARY); +} + +GtkTargetList* PasteboardHelper::targetList() const +{ + return m_targetList; +} + +static String selectionDataToUTF8String(GtkSelectionData* data) +{ + // g_strndup guards against selection data that is not null-terminated. + GOwnPtr<gchar> markupString(g_strndup(reinterpret_cast<const char*>(gtk_selection_data_get_data(data)), gtk_selection_data_get_length(data))); + return String::fromUTF8(markupString.get()); +} + +void PasteboardHelper::getClipboardContents(GtkClipboard* clipboard) +{ + DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); + ASSERT(dataObject); + + if (gtk_clipboard_wait_is_text_available(clipboard)) { + GOwnPtr<gchar> textData(gtk_clipboard_wait_for_text(clipboard)); + if (textData) + dataObject->setText(String::fromUTF8(textData.get())); + } + + if (gtk_clipboard_wait_is_target_available(clipboard, markupAtom)) { + if (GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard, markupAtom)) { + String markup(selectionDataToUTF8String(data)); + removeMarkupPrefix(markup); + dataObject->setMarkup(markup); + gtk_selection_data_free(data); + } + } + + if (gtk_clipboard_wait_is_target_available(clipboard, uriListAtom)) { + if (GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard, uriListAtom)) { + dataObject->setURIList(selectionDataToUTF8String(data)); + gtk_selection_data_free(data); + } + } +} + +void PasteboardHelper::fillSelectionData(GtkSelectionData* selectionData, guint info, DataObjectGtk* dataObject) +{ + if (info == getIdForTargetType(TargetTypeText)) + gtk_selection_data_set_text(selectionData, dataObject->text().utf8().data(), -1); + + else if (info == getIdForTargetType(TargetTypeMarkup)) { + // Some Linux applications refuse to accept pasted markup unless it is + // prefixed by a content-type meta tag. + CString markup = (gMarkupPrefix + dataObject->markup()).utf8(); + gtk_selection_data_set(selectionData, markupAtom, 8, + reinterpret_cast<const guchar*>(markup.data()), markup.length() + 1); + + } else if (info == getIdForTargetType(TargetTypeURIList)) { + CString uriList = dataObject->uriList().utf8(); + gtk_selection_data_set(selectionData, uriListAtom, 8, + reinterpret_cast<const guchar*>(uriList.data()), uriList.length() + 1); + + } else if (info == getIdForTargetType(TargetTypeNetscapeURL) && dataObject->hasURL()) { + String url(dataObject->url()); + String result(url); + result.append("\n"); + + if (dataObject->hasText()) + result.append(dataObject->text()); + else + result.append(url); + + GOwnPtr<gchar> resultData(g_strdup(result.utf8().data())); + gtk_selection_data_set(selectionData, netscapeURLAtom, 8, + reinterpret_cast<const guchar*>(resultData.get()), strlen(resultData.get()) + 1); + + } else if (info == getIdForTargetType(TargetTypeImage)) + gtk_selection_data_set_pixbuf(selectionData, dataObject->image()); +} + +GtkTargetList* PasteboardHelper::targetListForDataObject(DataObjectGtk* dataObject) +{ + GtkTargetList* list = gtk_target_list_new(0, 0); + + if (dataObject->hasText()) + gtk_target_list_add_text_targets(list, getIdForTargetType(TargetTypeText)); + + if (dataObject->hasMarkup()) + gtk_target_list_add(list, markupAtom, 0, getIdForTargetType(TargetTypeMarkup)); + + if (dataObject->hasURIList()) { + gtk_target_list_add_uri_targets(list, getIdForTargetType(TargetTypeURIList)); + gtk_target_list_add(list, netscapeURLAtom, 0, getIdForTargetType(TargetTypeNetscapeURL)); + } + + if (dataObject->hasImage()) + gtk_target_list_add_image_targets(list, getIdForTargetType(TargetTypeImage), TRUE); + + return list; +} + +void PasteboardHelper::fillDataObjectFromDropData(GtkSelectionData* data, guint info, DataObjectGtk* dataObject) +{ + if (!gtk_selection_data_get_data(data)) + return; + + GdkAtom target = gtk_selection_data_get_target(data); + if (target == textPlainAtom) + dataObject->setText(selectionDataToUTF8String(data)); + else if (target == markupAtom) { + String markup(selectionDataToUTF8String(data)); + removeMarkupPrefix(markup); + dataObject->setMarkup(markup); + } else if (target == uriListAtom) { + dataObject->setURIList(selectionDataToUTF8String(data)); + } else if (target == netscapeURLAtom) { + String urlWithLabel(selectionDataToUTF8String(data)); + Vector<String> pieces; + urlWithLabel.split("\n", pieces); + + // Give preference to text/uri-list here, as it can hold more + // than one URI but still take the label if there is one. + if (!dataObject->hasURIList()) + dataObject->setURIList(pieces[0]); + if (pieces.size() > 1) + dataObject->setText(pieces[1]); + } +} + +Vector<GdkAtom> PasteboardHelper::dropAtomsForContext(GtkWidget* widget, GdkDragContext* context) +{ + // Always search for these common atoms. + Vector<GdkAtom> dropAtoms; + dropAtoms.append(textPlainAtom); + dropAtoms.append(markupAtom); + dropAtoms.append(uriListAtom); + dropAtoms.append(netscapeURLAtom); + + // For images, try to find the most applicable image type. + GRefPtr<GtkTargetList> list(gtk_target_list_new(0, 0)); + gtk_target_list_add_image_targets(list.get(), getIdForTargetType(TargetTypeImage), TRUE); + GdkAtom atom = gtk_drag_dest_find_target(widget, context, list.get()); + if (atom != GDK_NONE) + dropAtoms.append(atom); + + return dropAtoms; +} + +static DataObjectGtk* settingClipboardDataObject = 0; + +static void getClipboardContentsCallback(GtkClipboard* clipboard, GtkSelectionData *selectionData, guint info, gpointer data) +{ + DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); + ASSERT(dataObject); + Pasteboard::generalPasteboard()->helper()->fillSelectionData(selectionData, info, dataObject); +} + +static void clearClipboardContentsCallback(GtkClipboard* clipboard, gpointer data) +{ + DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); + ASSERT(dataObject); + + // Only clear the DataObject for this clipboard if we are not currently setting it. + if (dataObject != settingClipboardDataObject) + dataObject->clear(); + + if (!data) + return; + + GClosure* callback = static_cast<GClosure*>(data); + GValue firstArgument = {0, {{0}}}; + g_value_init(&firstArgument, G_TYPE_POINTER); + g_value_set_pointer(&firstArgument, clipboard); + g_closure_invoke(callback, 0, 1, &firstArgument, 0); + g_closure_unref(callback); +} + +void PasteboardHelper::writeClipboardContents(GtkClipboard* clipboard, GClosure* callback) +{ + DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard); + GtkTargetList* list = targetListForDataObject(dataObject); + + int numberOfTargets; + GtkTargetEntry* table = gtk_target_table_new_from_list(list, &numberOfTargets); + + if (numberOfTargets > 0 && table) { + settingClipboardDataObject = dataObject; + + gtk_clipboard_set_with_data(clipboard, table, numberOfTargets, + getClipboardContentsCallback, clearClipboardContentsCallback, callback); + gtk_clipboard_set_can_store(clipboard, 0, 0); + + settingClipboardDataObject = 0; + + } else + gtk_clipboard_clear(clipboard); + + if (table) + gtk_target_table_free(table, numberOfTargets); + gtk_target_list_unref(list); +} + +} + diff --git a/Source/WebCore/platform/gtk/PasteboardHelper.h b/Source/WebCore/platform/gtk/PasteboardHelper.h new file mode 100644 index 0000000..bb1b580 --- /dev/null +++ b/Source/WebCore/platform/gtk/PasteboardHelper.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 Luca Bruno <lethalman88@gmail.com> + * Copyright (C) 2009 Holger Hans Peter Freyther + * Copyright (C) 2010 Martin Robinson <mrobinson@webkit.org> + * Copyright (C) 2010 Igalia S.L. + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef PasteboardHelper_h +#define PasteboardHelper_h + +/* + * FIXME: this is for WebCore support and must be removed once + * a better solution is found + */ + +#include "Frame.h" + +namespace WebCore { + +class DataObjectGtk; + +class PasteboardHelper { +public: + PasteboardHelper(); + virtual ~PasteboardHelper(); + + GtkClipboard* getCurrentClipboard(Frame*); + GtkClipboard* getClipboard(Frame*) const; + GtkClipboard* getPrimarySelectionClipboard(Frame*) const; + GtkTargetList* targetList() const; + GtkTargetList* targetListForDataObject(DataObjectGtk*); + void fillSelectionData(GtkSelectionData*, guint, DataObjectGtk*); + void fillDataObjectFromDropData(GtkSelectionData*, guint, DataObjectGtk*); + Vector<GdkAtom> dropAtomsForContext(GtkWidget*, GdkDragContext*); + void writeClipboardContents(GtkClipboard*, GClosure* closure = 0); + void getClipboardContents(GtkClipboard*); + + enum PasteboardTargetType { TargetTypeText, TargetTypeMarkup, TargetTypeURIList, TargetTypeNetscapeURL, TargetTypeImage, TargetTypeUnknown }; + virtual guint getIdForTargetType(PasteboardTargetType) = 0; + +protected: + void initializeTargetList(); + virtual bool usePrimarySelectionClipboard(GtkWidget*) = 0; + +private: + GtkTargetList* m_targetList; +}; + +} + +#endif // PasteboardHelper_h diff --git a/Source/WebCore/platform/gtk/PlatformScreenGtk.cpp b/Source/WebCore/platform/gtk/PlatformScreenGtk.cpp new file mode 100644 index 0000000..40b509e --- /dev/null +++ b/Source/WebCore/platform/gtk/PlatformScreenGtk.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2008 Christian Dywan <christian@imendio.com> + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2009 Holger Hans Peter Freyther + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PlatformScreen.h" + +#include "GtkVersioning.h" +#include "HostWindow.h" +#include "ScrollView.h" +#include "Widget.h" + +#include <gtk/gtk.h> + +#if PLATFORM(X11) +#include <gdk/gdkx.h> +#include <X11/Xatom.h> +#endif + +namespace WebCore { + +static GdkVisual* getVisual(Widget* widget) +{ + if (!widget) + return 0; + + GtkWidget* container = GTK_WIDGET(widget->root()->hostWindow()->platformPageClient()); + + if (!container) + return 0; + + if (!gtk_widget_get_realized(container)) { + GtkWidget* toplevel = gtk_widget_get_toplevel(container); + if (gtk_widget_is_toplevel(toplevel)) + container = toplevel; + else + return 0; + } + + return gdk_window_get_visual(gtk_widget_get_window(container)); +} + +int screenDepth(Widget* widget) +{ + GdkVisual* visual = getVisual(widget); + if (!visual) + return 24; + return gdk_visual_get_depth(visual); +} + +int screenDepthPerComponent(Widget* widget) +{ + GdkVisual* visual = getVisual(widget); + if (!visual) + return 8; + + return gdk_visual_get_bits_per_rgb(visual); +} + +bool screenIsMonochrome(Widget* widget) +{ + return screenDepth(widget) < 2; +} + +FloatRect screenRect(Widget* widget) +{ + if (!widget) + return FloatRect(); + + GtkWidget* container = gtk_widget_get_toplevel(GTK_WIDGET(widget->root()->hostWindow()->platformPageClient())); + if (!gtk_widget_is_toplevel(container)) + return FloatRect(); + + GdkScreen* screen = gtk_widget_has_screen(container) ? gtk_widget_get_screen(container) : gdk_screen_get_default(); + if (!screen) + return FloatRect(); + + gint monitor = gdk_screen_get_monitor_at_window(screen, gtk_widget_get_window(GTK_WIDGET(container))); + GdkRectangle geometry; + gdk_screen_get_monitor_geometry(screen, monitor, &geometry); + + return FloatRect(geometry.x, geometry.y, geometry.width, geometry.height); +} + +FloatRect screenAvailableRect(Widget* widget) +{ + if (!widget) + return FloatRect(); + +#if PLATFORM(X11) + GtkWidget* container = GTK_WIDGET(widget->root()->hostWindow()->platformPageClient()); + if (!container) + return FloatRect(); + + if (!gtk_widget_get_realized(container)) + return screenRect(widget); + + GdkWindow* rootWindow = gtk_widget_get_root_window(container); + GdkDisplay* display = gdk_window_get_display(rootWindow); + Atom xproperty = gdk_x11_get_xatom_by_name_for_display(display, "_NET_WORKAREA"); + + Atom retType; + int retFormat; + long *workAreaPos = NULL; + unsigned long retNItems; + unsigned long retAfter; + int xRes = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display), GDK_WINDOW_XWINDOW(rootWindow), xproperty, + 0, 4, FALSE, XA_CARDINAL, &retType, &retFormat, &retNItems, &retAfter, (guchar**)&workAreaPos); + + FloatRect rect; + if (xRes == Success && workAreaPos != NULL && retType == XA_CARDINAL && retNItems == 4 && retFormat == 32) { + rect = FloatRect(workAreaPos[0], workAreaPos[1], workAreaPos[2], workAreaPos[3]); + // rect contains the available space in the whole screen not just in the monitor + // containing the widget, so we intersect it with the monitor rectangle. + rect.intersect(screenRect(widget)); + } else + rect = screenRect(widget); + + if (workAreaPos) + XFree(workAreaPos); + + return rect; +#else + return screenRect(widget); +#endif +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/gtk/PopupMenuGtk.cpp b/Source/WebCore/platform/gtk/PopupMenuGtk.cpp new file mode 100644 index 0000000..b2466c5 --- /dev/null +++ b/Source/WebCore/platform/gtk/PopupMenuGtk.cpp @@ -0,0 +1,270 @@ +/* + * This file is part of the popup menu implementation for <select> elements in WebCore. + * + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "PopupMenuGtk.h" + +#include "FrameView.h" +#include "GOwnPtr.h" +#include "GtkVersioning.h" +#include "HostWindow.h" +#include "PlatformString.h" +#include <gdk/gdk.h> +#include <gtk/gtk.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +static const uint32_t gSearchTimeoutMs = 1000; + +PopupMenuGtk::PopupMenuGtk(PopupMenuClient* client) + : m_popupClient(client) + , m_previousKeyEventCharacter(0) + , m_currentlySelectedMenuItem(0) +{ +} + +PopupMenuGtk::~PopupMenuGtk() +{ + if (m_popup) { + g_signal_handlers_disconnect_matched(m_popup.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); + hide(); + } +} + +void PopupMenuGtk::show(const IntRect& rect, FrameView* view, int index) +{ + ASSERT(client()); + + if (!m_popup) { + m_popup = GTK_MENU(gtk_menu_new()); + g_signal_connect(m_popup.get(), "unmap", G_CALLBACK(PopupMenuGtk::menuUnmapped), this); + g_signal_connect(m_popup.get(), "key-press-event", G_CALLBACK(PopupMenuGtk::keyPressEventCallback), this); + } else + gtk_container_foreach(GTK_CONTAINER(m_popup.get()), reinterpret_cast<GtkCallback>(menuRemoveItem), this); + + int x = 0; + int y = 0; + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(view->hostWindow()->platformPageClient())); + if (window) + gdk_window_get_origin(window, &x, &y); + m_menuPosition = view->contentsToWindow(rect.location()); + m_menuPosition = IntPoint(m_menuPosition.x() + x, m_menuPosition.y() + y + rect.height()); + m_indexMap.clear(); + + const int size = client()->listSize(); + for (int i = 0; i < size; ++i) { + GtkWidget* item; + if (client()->itemIsSeparator(i)) + item = gtk_separator_menu_item_new(); + else + item = gtk_menu_item_new_with_label(client()->itemText(i).utf8().data()); + + m_indexMap.add(item, i); + g_signal_connect(item, "activate", G_CALLBACK(PopupMenuGtk::menuItemActivated), this); + g_signal_connect(item, "select", G_CALLBACK(PopupMenuGtk::selectItemCallback), this); + + // FIXME: Apply the PopupMenuStyle from client()->itemStyle(i) + gtk_widget_set_sensitive(item, client()->itemIsEnabled(i)); + gtk_menu_shell_append(GTK_MENU_SHELL(m_popup.get()), item); + gtk_widget_show(item); + } + + gtk_menu_set_active(m_popup.get(), index); + + + // The size calls are directly copied from gtkcombobox.c which is LGPL + GtkRequisition requisition; + gtk_widget_set_size_request(GTK_WIDGET(m_popup.get()), -1, -1); +#ifdef GTK_API_VERSION_2 + gtk_widget_size_request(GTK_WIDGET(m_popup.get()), &requisition); +#else + gtk_widget_get_preferred_size(GTK_WIDGET(m_popup.get()), &requisition, 0); +#endif + + gtk_widget_set_size_request(GTK_WIDGET(m_popup.get()), std::max(rect.width(), requisition.width), -1); + + GList* children = gtk_container_get_children(GTK_CONTAINER(m_popup.get())); + GList* p = children; + if (size) { + for (int i = 0; i < size; i++) { + if (i > index) + break; + + GtkWidget* item = reinterpret_cast<GtkWidget*>(p->data); + GtkRequisition itemRequisition; +#ifdef GTK_API_VERSION_2 + gtk_widget_get_child_requisition(item, &itemRequisition); +#else + gtk_widget_get_preferred_size(item, &itemRequisition, 0); +#endif + m_menuPosition.setY(m_menuPosition.y() - itemRequisition.height); + + p = g_list_next(p); + } + } else { + // Center vertically the empty popup in the combo box area + m_menuPosition.setY(m_menuPosition.y() - rect.height() / 2); + } + + g_list_free(children); + gtk_menu_popup(m_popup.get(), 0, 0, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, 0, gtk_get_current_event_time()); +} + +void PopupMenuGtk::hide() +{ + ASSERT(m_popup); + gtk_menu_popdown(m_popup.get()); +} + +void PopupMenuGtk::updateFromElement() +{ + client()->setTextFromItem(client()->selectedIndex()); +} + +void PopupMenuGtk::disconnectClient() +{ + m_popupClient = 0; +} + +bool PopupMenuGtk::typeAheadFind(GdkEventKey* event) +{ + // If we were given a non-printable character just skip it. + gunichar unicodeCharacter = gdk_keyval_to_unicode(event->keyval); + if (!unicodeCharacter) { + resetTypeAheadFindState(); + return false; + } + + glong charactersWritten; + GOwnPtr<gunichar2> utf16String(g_ucs4_to_utf16(&unicodeCharacter, 1, 0, &charactersWritten, 0)); + if (!utf16String) { + resetTypeAheadFindState(); + return false; + } + + // If the character is the same as the last character, the user is probably trying to + // cycle through the menulist entries. This matches the WebCore behavior for collapsed + // menulists. + bool repeatingCharacter = unicodeCharacter != m_previousKeyEventCharacter; + if (event->time - m_previousKeyEventTimestamp > gSearchTimeoutMs) + m_currentSearchString = String(static_cast<UChar*>(utf16String.get()), charactersWritten); + else if (repeatingCharacter) + m_currentSearchString.append(String(static_cast<UChar*>(utf16String.get()), charactersWritten)); + + m_previousKeyEventTimestamp = event->time; + m_previousKeyEventCharacter = unicodeCharacter; + + // Like the Chromium port, we case fold before searching, because + // strncmp does not handle non-ASCII characters. + GOwnPtr<gchar> searchStringWithCaseFolded(g_utf8_casefold(m_currentSearchString.utf8().data(), -1)); + size_t prefixLength = strlen(searchStringWithCaseFolded.get()); + + GList* children = gtk_container_get_children(GTK_CONTAINER(m_popup.get())); + if (!children) + return true; + + // If a menu item has already been selected, start searching from the current + // item down the list. This will make multiple key presses of the same character + // advance the selection. + GList* currentChild = children; + if (m_currentlySelectedMenuItem) { + currentChild = g_list_find(children, m_currentlySelectedMenuItem); + if (!currentChild) { + m_currentlySelectedMenuItem = 0; + currentChild = children; + } + + // Repeating characters should iterate. + if (repeatingCharacter) { + if (GList* nextChild = g_list_next(currentChild)) + currentChild = nextChild; + } + } + + GList* firstChild = currentChild; + do { + currentChild = g_list_next(currentChild); + if (!currentChild) + currentChild = children; + + GOwnPtr<gchar> itemText(g_utf8_casefold(gtk_menu_item_get_label(GTK_MENU_ITEM(currentChild->data)), -1)); + if (!strncmp(searchStringWithCaseFolded.get(), itemText.get(), prefixLength)) { + gtk_menu_shell_select_item(GTK_MENU_SHELL(m_popup.get()), GTK_WIDGET(currentChild->data)); + return true; + } + } while (currentChild != firstChild); + + return true; +} + +void PopupMenuGtk::menuItemActivated(GtkMenuItem* item, PopupMenuGtk* that) +{ + ASSERT(that->client()); + ASSERT(that->m_indexMap.contains(GTK_WIDGET(item))); + that->client()->valueChanged(that->m_indexMap.get(GTK_WIDGET(item))); +} + +void PopupMenuGtk::menuUnmapped(GtkWidget*, PopupMenuGtk* that) +{ + ASSERT(that->client()); + that->resetTypeAheadFindState(); + that->client()->popupDidHide(); +} + +void PopupMenuGtk::menuPositionFunction(GtkMenu*, gint* x, gint* y, gboolean* pushIn, PopupMenuGtk* that) +{ + *x = that->m_menuPosition.x(); + *y = that->m_menuPosition.y(); + *pushIn = true; +} + +void PopupMenuGtk::resetTypeAheadFindState() +{ + m_currentlySelectedMenuItem = 0; + m_previousKeyEventCharacter = 0; + m_currentSearchString = ""; +} + +void PopupMenuGtk::menuRemoveItem(GtkWidget* widget, PopupMenuGtk* that) +{ + ASSERT(that->m_popup); + gtk_container_remove(GTK_CONTAINER(that->m_popup.get()), widget); +} + +int PopupMenuGtk::selectItemCallback(GtkMenuItem* item, PopupMenuGtk* that) +{ + that->m_currentlySelectedMenuItem = GTK_WIDGET(item); + return FALSE; +} + +int PopupMenuGtk::keyPressEventCallback(GtkWidget* widget, GdkEventKey* event, PopupMenuGtk* that) +{ + return that->typeAheadFind(event); +} + +} + diff --git a/Source/WebCore/platform/gtk/PopupMenuGtk.h b/Source/WebCore/platform/gtk/PopupMenuGtk.h new file mode 100644 index 0000000..7f2fc36 --- /dev/null +++ b/Source/WebCore/platform/gtk/PopupMenuGtk.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef PopupMenuGtk_h +#define PopupMenuGtk_h + +#include "GRefPtrGtk.h" +#include "IntRect.h" +#include "PopupMenu.h" +#include "PopupMenuClient.h" +#include <wtf/HashMap.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +typedef struct _GdkEventKey GdkEventKey; + +namespace WebCore { + +class FrameView; +class Scrollbar; + +class PopupMenuGtk : public PopupMenu { +public: + PopupMenuGtk(PopupMenuClient*); + ~PopupMenuGtk(); + + virtual void show(const IntRect&, FrameView*, int index); + virtual void hide(); + virtual void updateFromElement(); + virtual void disconnectClient(); + bool typeAheadFind(GdkEventKey*); + +private: + PopupMenuClient* client() const { return m_popupClient; } + void resetTypeAheadFindState(); + + static void menuItemActivated(GtkMenuItem* item, PopupMenuGtk*); + static void menuUnmapped(GtkWidget*, PopupMenuGtk*); + static void menuPositionFunction(GtkMenu*, gint*, gint*, gboolean*, PopupMenuGtk*); + static void menuRemoveItem(GtkWidget*, PopupMenuGtk*); + static int selectItemCallback(GtkMenuItem*, PopupMenuGtk*); + static int keyPressEventCallback(GtkWidget*, GdkEventKey*, PopupMenuGtk*); + + PopupMenuClient* m_popupClient; + IntPoint m_menuPosition; + GRefPtr<GtkMenu> m_popup; + HashMap<GtkWidget*, int> m_indexMap; + String m_currentSearchString; + uint32_t m_previousKeyEventTimestamp; + unsigned int m_previousKeyEventCharacter; + GtkWidget* m_currentlySelectedMenuItem; +}; + +} + +#endif // PopupMenuGtk_h diff --git a/Source/WebCore/platform/gtk/RenderThemeGtk.cpp b/Source/WebCore/platform/gtk/RenderThemeGtk.cpp new file mode 100644 index 0000000..c194946 --- /dev/null +++ b/Source/WebCore/platform/gtk/RenderThemeGtk.cpp @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2007 Apple Inc. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2009 Kenneth Rohde Christiansen + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderThemeGtk.h" + +#include "CSSValueKeywords.h" +#include "GOwnPtr.h" +#include "Gradient.h" +#include "GraphicsContext.h" +#include "GtkVersioning.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "MediaControlElements.h" +#include "RenderBox.h" +#include "RenderObject.h" +#include "TimeRanges.h" +#include "UserAgentStyleSheets.h" +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +namespace WebCore { + +using namespace HTMLNames; + +#if ENABLE(VIDEO) +static HTMLMediaElement* getMediaElementFromRenderObject(RenderObject* o) +{ + Node* node = o->node(); + Node* mediaNode = node ? node->shadowAncestorNode() : 0; + if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) + return 0; + + return static_cast<HTMLMediaElement*>(mediaNode); +} + +static GtkIconSize getMediaButtonIconSize(int mediaIconSize) +{ + GtkIconSize iconSize = gtk_icon_size_from_name("webkit-media-button-size"); + if (!iconSize) + iconSize = gtk_icon_size_register("webkit-media-button-size", mediaIconSize, mediaIconSize); + return iconSize; +} + +void RenderThemeGtk::initMediaButtons() +{ + static bool iconsInitialized = false; + + if (iconsInitialized) + return; + + GRefPtr<GtkIconFactory> iconFactory = adoptGRef(gtk_icon_factory_new()); + GtkIconSource* iconSource = gtk_icon_source_new(); + const char* icons[] = { "audio-volume-high", "audio-volume-muted" }; + + gtk_icon_factory_add_default(iconFactory.get()); + + for (size_t i = 0; i < G_N_ELEMENTS(icons); ++i) { + gtk_icon_source_set_icon_name(iconSource, icons[i]); + GtkIconSet* iconSet = gtk_icon_set_new(); + gtk_icon_set_add_source(iconSet, iconSource); + gtk_icon_factory_add(iconFactory.get(), icons[i], iconSet); + gtk_icon_set_unref(iconSet); + } + + gtk_icon_source_free(iconSource); + + iconsInitialized = true; +} +#endif + +PassRefPtr<RenderTheme> RenderThemeGtk::create() +{ + return adoptRef(new RenderThemeGtk()); +} + +PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) +{ + static RenderTheme* rt = RenderThemeGtk::create().releaseRef(); + return rt; +} + +static int mozGtkRefCount = 0; + +RenderThemeGtk::RenderThemeGtk() + : m_gtkWindow(0) + , m_gtkContainer(0) + , m_gtkButton(0) + , m_gtkEntry(0) + , m_gtkTreeView(0) + , m_gtkVScale(0) + , m_gtkHScale(0) + , m_panelColor(Color::white) + , m_sliderColor(Color::white) + , m_sliderThumbColor(Color::white) + , m_mediaIconSize(16) + , m_mediaSliderHeight(14) + , m_mediaSliderThumbWidth(12) + , m_mediaSliderThumbHeight(12) +#ifdef GTK_API_VERSION_2 + , m_themePartsHaveRGBAColormap(true) +#endif +{ + + memset(&m_themeParts, 0, sizeof(GtkThemeParts)); +#ifdef GTK_API_VERSION_2 + GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default()); + if (!colormap) { + m_themePartsHaveRGBAColormap = false; + colormap = gdk_screen_get_default_colormap(gdk_screen_get_default()); + } + m_themeParts.colormap = colormap; +#endif + + // Initialize the Mozilla theme drawing code. + if (!mozGtkRefCount) { + moz_gtk_init(); + moz_gtk_use_theme_parts(&m_themeParts); + } + ++mozGtkRefCount; + +#if ENABLE(VIDEO) + initMediaColors(); + initMediaButtons(); +#endif +} + +RenderThemeGtk::~RenderThemeGtk() +{ + --mozGtkRefCount; + + if (!mozGtkRefCount) + moz_gtk_shutdown(); + + gtk_widget_destroy(m_gtkWindow); +} + +void RenderThemeGtk::getIndicatorMetrics(ControlPart part, int& indicatorSize, int& indicatorSpacing) const +{ + ASSERT(part == CheckboxPart || part == RadioPart); + if (part == CheckboxPart) { + moz_gtk_checkbox_get_metrics(&indicatorSize, &indicatorSpacing); + return; + } + + // RadioPart + moz_gtk_radio_get_metrics(&indicatorSize, &indicatorSpacing); +} + +static bool supportsFocus(ControlPart appearance) +{ + switch (appearance) { + case PushButtonPart: + case ButtonPart: + case TextFieldPart: + case TextAreaPart: + case SearchFieldPart: + case MenulistPart: + case RadioPart: + case CheckboxPart: + case SliderHorizontalPart: + case SliderVerticalPart: + return true; + default: + return false; + } +} + +bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const +{ + return supportsFocus(style->appearance()); +} + +bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const +{ + return isEnabled(o); +} + +int RenderThemeGtk::baselinePosition(const RenderObject* o) const +{ + if (!o->isBox()) + return 0; + + // FIXME: This strategy is possibly incorrect for the GTK+ port. + if (o->style()->appearance() == CheckboxPart + || o->style()->appearance() == RadioPart) { + const RenderBox* box = toRenderBox(o); + return box->marginTop() + box->height() - 2; + } + + return RenderTheme::baselinePosition(o); +} + +// This is used in RenderThemeGtk2 and RenderThemeGtk3. Normally, it would be in +// the RenderThemeGtk header (perhaps as a static method), but we want to avoid +// having to include GTK+ headers only for the GtkTextDirection enum. +GtkTextDirection gtkTextDirection(TextDirection direction) +{ + switch (direction) { + case RTL: + return GTK_TEXT_DIR_RTL; + case LTR: + return GTK_TEXT_DIR_LTR; + default: + return GTK_TEXT_DIR_NONE; + } +} + +GtkStateType RenderThemeGtk::gtkIconState(RenderObject* renderObject) +{ + if (!isEnabled(renderObject)) + return GTK_STATE_INSENSITIVE; + if (isPressed(renderObject)) + return GTK_STATE_ACTIVE; + if (isHovered(renderObject)) + return GTK_STATE_PRELIGHT; + + return GTK_STATE_NORMAL; +} + +void RenderThemeGtk::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const +{ + // Some layout tests check explicitly that buttons ignore line-height. + if (style->appearance() == PushButtonPart) + style->setLineHeight(RenderStyle::initialLineHeight()); +} + +void RenderThemeGtk::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // The tests check explicitly that select menu buttons ignore line height. + style->setLineHeight(RenderStyle::initialLineHeight()); + + // We cannot give a proper rendering when border radius is active, unfortunately. + style->resetBorderRadius(); +} + +void RenderThemeGtk::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + adjustMenuListStyle(selector, style, e); +} + +bool RenderThemeGtk::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + return paintMenuList(object, info, rect); +} + +bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + return paintTextField(o, i, r); +} + +static void paintGdkPixbuf(GraphicsContext* context, const GdkPixbuf* icon, const IntPoint& iconPoint) +{ + cairo_t* cr = context->platformContext(); + cairo_save(cr); + gdk_cairo_set_source_pixbuf(cr, icon, iconPoint.x(), iconPoint.y()); + cairo_paint(cr); + cairo_restore(cr); +} + +void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + adjustSearchFieldCancelButtonStyle(selector, style, e); +} + +bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) +{ + return paintSearchFieldResultsDecoration(o, i, rect); +} + +void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + style->resetBorder(); + style->resetPadding(); + + gint width = 0, height = 0; + gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height); + style->setWidth(Length(width, Fixed)); + style->setHeight(Length(height, Fixed)); +} + +static IntPoint centerRectVerticallyInParentInputElement(RenderObject* object, const IntRect& rect) +{ + Node* input = object->node()->shadowAncestorNode(); // Get the renderer of <input> element. + if (!input->renderer()->isBox()) + return rect.topLeft(); + + // If possible center the y-coordinate of the rect vertically in the parent input element. + // We also add one pixel here to ensure that the y coordinate is rounded up for box heights + // that are even, which looks in relation to the box text. + IntRect inputContentBox = toRenderBox(input->renderer())->absoluteContentBox(); + + return IntPoint(rect.x(), inputContentBox.y() + (inputContentBox.height() - rect.height() + 1) / 2); +} + +bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_FIND, + gtkTextDirection(renderObject->style()->direction()), + gtkIconState(renderObject), GTK_ICON_SIZE_MENU); + paintGdkPixbuf(paintInfo.context, icon.get(), centerRectVerticallyInParentInputElement(renderObject, rect)); + return false; +} + +void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + style->resetBorder(); + style->resetPadding(); + + gint width = 0, height = 0; + gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height); + style->setWidth(Length(width, Fixed)); + style->setHeight(Length(height, Fixed)); +} + +bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_CLEAR, + gtkTextDirection(renderObject->style()->direction()), + gtkIconState(renderObject), GTK_ICON_SIZE_MENU); + paintGdkPixbuf(paintInfo.context, icon.get(), centerRectVerticallyInParentInputElement(renderObject, rect)); + return false; +} + +void RenderThemeGtk::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // We cannot give a proper rendering when border radius is active, unfortunately. + style->resetBorderRadius(); + style->setLineHeight(RenderStyle::initialLineHeight()); +} + +bool RenderThemeGtk::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& rect) +{ + return paintTextField(o, i, rect); +} + +void RenderThemeGtk::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + style->setBoxShadow(0); +} + +void RenderThemeGtk::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + style->setBoxShadow(0); +} + +double RenderThemeGtk::caretBlinkInterval() const +{ + GtkSettings* settings = gtk_settings_get_default(); + + gboolean shouldBlink; + gint time; + + g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL); + + if (!shouldBlink) + return 0; + + return time / 2000.; +} + +static double getScreenDPI() +{ + // FIXME: Really this should be the widget's screen. + GdkScreen* screen = gdk_screen_get_default(); + if (!screen) + return 96; // Default to 96 DPI. + + float dpi = gdk_screen_get_resolution(screen); + if (dpi <= 0) + return 96; + return dpi; +} + +void RenderThemeGtk::systemFont(int, FontDescription& fontDescription) const +{ + GtkSettings* settings = gtk_settings_get_default(); + if (!settings) + return; + + // This will be a font selection string like "Sans 10" so we cannot use it as the family name. + GOwnPtr<gchar> fontName; + g_object_get(settings, "gtk-font-name", &fontName.outPtr(), NULL); + + PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get()); + if (!pangoDescription) + return; + + fontDescription.firstFamily().setFamily(pango_font_description_get_family(pangoDescription)); + + int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE; + // If the size of the font is in points, we need to convert it to pixels. + if (!pango_font_description_get_size_is_absolute(pangoDescription)) + size = size * (getScreenDPI() / 72.0); + + fontDescription.setSpecifiedSize(size); + fontDescription.setIsAbsoluteSize(true); + fontDescription.setGenericFamily(FontDescription::NoFamily); + fontDescription.setWeight(FontWeightNormal); + fontDescription.setItalic(false); + pango_font_description_free(pangoDescription); +} + +void RenderThemeGtk::platformColorsDidChange() +{ +#if ENABLE(VIDEO) + initMediaColors(); +#endif + RenderTheme::platformColorsDidChange(); +} + +#if ENABLE(VIDEO) +String RenderThemeGtk::extraMediaControlsStyleSheet() +{ + return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet)); +} + +void RenderThemeGtk::adjustMediaSliderThumbSize(RenderObject* renderObject) const +{ + ControlPart part = renderObject->style()->appearance(); + + if (part == MediaSliderThumbPart) { + renderObject->style()->setWidth(Length(m_mediaSliderThumbWidth, Fixed)); + renderObject->style()->setHeight(Length(m_mediaSliderThumbHeight, Fixed)); + } +} + +bool RenderThemeGtk::paintMediaButton(RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, const char* iconName) +{ + GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_CONTAINER, iconName, + gtkTextDirection(renderObject->style()->direction()), + gtkIconState(renderObject), + getMediaButtonIconSize(m_mediaIconSize)); + IntPoint iconPoint(rect.x() + (rect.width() - m_mediaIconSize) / 2, + rect.y() + (rect.height() - m_mediaIconSize) / 2); + context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB); + paintGdkPixbuf(context, icon.get(), iconPoint); + return false; +} + +bool RenderThemeGtk::paintMediaFullscreenButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_FULLSCREEN); +} + +bool RenderThemeGtk::paintMediaMuteButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(renderObject); + if (!mediaElement) + return false; + + return paintMediaButton(renderObject, paintInfo.context, rect, mediaElement->muted() ? "audio-volume-muted" : "audio-volume-high"); +} + +bool RenderThemeGtk::paintMediaPlayButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + Node* node = renderObject->node(); + if (!node) + return false; + + MediaControlPlayButtonElement* button = static_cast<MediaControlPlayButtonElement*>(node); + return paintMediaButton(renderObject, paintInfo.context, rect, button->displayType() == MediaPlayButton ? GTK_STOCK_MEDIA_PLAY : GTK_STOCK_MEDIA_PAUSE); +} + +bool RenderThemeGtk::paintMediaSeekBackButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_REWIND); +} + +bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_FORWARD); +} + +bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + GraphicsContext* context = paintInfo.context; + + context->fillRect(FloatRect(r), m_panelColor, ColorSpaceDeviceRGB); + context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2, + r.width(), m_mediaSliderHeight)), m_sliderColor, ColorSpaceDeviceRGB); + + RenderStyle* style = o->style(); + HTMLMediaElement* mediaElement = toParentMediaElement(o); + + if (!mediaElement) + return false; + + // Draw the buffered ranges. This code is highly inspired from + // Chrome for the gradient code. + float mediaDuration = mediaElement->duration(); + RefPtr<TimeRanges> timeRanges = mediaElement->buffered(); + IntRect trackRect = r; + int totalWidth = trackRect.width(); + + trackRect.inflate(-style->borderLeftWidth()); + context->save(); + context->setStrokeStyle(NoStroke); + + for (unsigned index = 0; index < timeRanges->length(); ++index) { + ExceptionCode ignoredException; + float start = timeRanges->start(index, ignoredException); + float end = timeRanges->end(index, ignoredException); + int width = ((end - start) * totalWidth) / mediaDuration; + IntRect rangeRect; + if (!index) { + rangeRect = trackRect; + rangeRect.setWidth(width); + } else { + rangeRect.setLocation(IntPoint(trackRect.x() + start / mediaDuration* totalWidth, trackRect.y())); + rangeRect.setSize(IntSize(width, trackRect.height())); + } + + // Don't bother drawing empty range. + if (rangeRect.isEmpty()) + continue; + + IntPoint sliderTopLeft = rangeRect.location(); + IntPoint sliderTopRight = sliderTopLeft; + sliderTopRight.move(0, rangeRect.height()); + + RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight); + Color startColor = m_panelColor; + gradient->addColorStop(0.0, startColor); + gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha())); + + context->setFillGradient(gradient); + context->fillRect(rangeRect); + } + + context->restore(); + return false; +} + +bool RenderThemeGtk::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + // Make the thumb nicer with rounded corners. + paintInfo.context->fillRoundedRect(r, IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), m_sliderThumbColor, ColorSpaceDeviceRGB); + return false; +} + +String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const +{ + return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration); +} + +bool RenderThemeGtk::paintMediaCurrentTime(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + GraphicsContext* context = paintInfo.context; + + context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB); + return false; +} +#endif + +#if ENABLE(PROGRESS_TAG) +double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const +{ + // FIXME: It doesn't look like there is a good way yet to support animated + // progress bars with the Mozilla theme drawing code. + return 0; +} + +double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const +{ + // FIXME: It doesn't look like there is a good way yet to support animated + // progress bars with the Mozilla theme drawing code. + return 0; +} + +void RenderThemeGtk::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + style->setBoxShadow(0); +} +#endif + +} diff --git a/Source/WebCore/platform/gtk/RenderThemeGtk.h b/Source/WebCore/platform/gtk/RenderThemeGtk.h new file mode 100644 index 0000000..ef1df05 --- /dev/null +++ b/Source/WebCore/platform/gtk/RenderThemeGtk.h @@ -0,0 +1,209 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2010 Igalia S.L. + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderThemeGtk_h +#define RenderThemeGtk_h + +#include "GRefPtr.h" +#include "gtkdrawing.h" +#include "RenderTheme.h" + +namespace WebCore { + +class RenderThemeGtk : public RenderTheme { +private: + RenderThemeGtk(); + virtual ~RenderThemeGtk(); + +public: + static PassRefPtr<RenderTheme> create(); + + // A method asking if the theme's controls actually care about redrawing when hovered. + virtual bool supportsHover(const RenderStyle* style) const { return true; } + + // A method asking if the theme is able to draw the focus ring. + virtual bool supportsFocusRing(const RenderStyle*) const; + + // A method asking if the control changes its tint when the window has focus or not. + virtual bool controlSupportsTints(const RenderObject*) const; + + // A general method asking if any control tinting is supported at all. + virtual bool supportsControlTints() const { return true; } + + virtual void adjustRepaintRect(const RenderObject*, IntRect&); + + // A method to obtain the baseline position for a "leaf" control. This will only be used if a baseline + // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of + // controls that need to do this. + virtual int baselinePosition(const RenderObject*) const; + + // The platform selection color. + virtual Color platformActiveSelectionBackgroundColor() const; + virtual Color platformInactiveSelectionBackgroundColor() const; + virtual Color platformActiveSelectionForegroundColor() const; + virtual Color platformInactiveSelectionForegroundColor() const; + + // List Box selection color + virtual Color activeListBoxSelectionBackgroundColor() const; + virtual Color activeListBoxSelectionForegroundColor() const; + virtual Color inactiveListBoxSelectionBackgroundColor() const; + virtual Color inactiveListBoxSelectionForegroundColor() const; + + virtual double caretBlinkInterval() const; + + virtual void platformColorsDidChange(); + + // System fonts and colors. + virtual void systemFont(int propId, FontDescription&) const; + virtual Color systemColor(int cssValueId) const; + +#if ENABLE(VIDEO) + virtual String extraMediaControlsStyleSheet(); + virtual String formatMediaControlsCurrentTime(float currentTime, float duration) const; +#endif + + void getIndicatorMetrics(ControlPart, int& indicatorSize, int& indicatorSpacing) const; + +#ifdef GTK_API_VERSION_2 + GtkWidget* gtkScrollbar(); +#else + GtkStyleContext* gtkScrollbarStyle(); +#endif + +protected: + virtual bool paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r); + virtual void setCheckboxSize(RenderStyle* style) const; + + virtual bool paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r); + virtual void setRadioSize(RenderStyle* style) const; + + virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&); + + virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintTextArea(RenderObject*, const PaintInfo&, const IntRect&); + + int popupInternalPaddingLeft(RenderStyle*) const; + int popupInternalPaddingRight(RenderStyle*) const; + int popupInternalPaddingTop(RenderStyle*) const; + int popupInternalPaddingBottom(RenderStyle*) const; + + // The Mac port differentiates between the "menu list" and the "menu list button." + // The former is used when a menu list button has been styled. This is used to ensure + // Aqua themed controls whenever possible. We always want to use GTK+ theming, so + // we don't maintain this differentiation. + virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element* e) const; + virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMenuListButton(RenderObject*, const PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsDecoration(RenderObject*, const PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchField(RenderObject*, const PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsButton(RenderObject*, const PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldCancelButton(RenderObject*, const PaintInfo&, const IntRect&); + + virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual void adjustSliderThumbSize(RenderObject* object) const; + +#if ENABLE(VIDEO) + void initMediaColors(); + void initMediaButtons(); + void adjustMediaSliderThumbSize(RenderObject*) const; + virtual bool paintMediaFullscreenButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaCurrentTime(RenderObject*, const PaintInfo&, const IntRect&); +#endif + +#if ENABLE(PROGRESS_TAG) + virtual double animationRepeatIntervalForProgressBar(RenderProgress*) const; + virtual double animationDurationForProgressBar(RenderProgress*) const; + virtual void adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&); +#endif + +private: + GtkWidget* gtkButton() const; + GtkWidget* gtkEntry() const; + GtkWidget* gtkTreeView() const; + GtkWidget* gtkVScale() const; + GtkWidget* gtkHScale() const; + GtkWidget* gtkContainer() const; + + void setupWidgetAndAddToContainer(GtkWidget*, GtkWidget*) const; + GtkStateType getGtkStateType(RenderObject* object); + bool paintRenderObject(GtkThemeWidgetType, RenderObject*, GraphicsContext*, const IntRect& rect, int flags = 0); +#if ENABLE(VIDEO) + bool paintMediaButton(RenderObject*, GraphicsContext*, const IntRect&, const char* iconName); +#endif + GtkStateType gtkIconState(RenderObject*); + static void setTextInputBorders(RenderStyle*); + GRefPtr<GdkPixbuf> getStockIcon(GType, const char* iconName, gint direction, gint state, gint iconSize); + + mutable GtkWidget* m_gtkWindow; + mutable GtkWidget* m_gtkContainer; + mutable GtkWidget* m_gtkButton; + mutable GtkWidget* m_gtkEntry; + mutable GtkWidget* m_gtkTreeView; + mutable GtkWidget* m_gtkVScale; + mutable GtkWidget* m_gtkHScale; + + mutable Color m_panelColor; + mutable Color m_sliderColor; + mutable Color m_sliderThumbColor; + + const int m_mediaIconSize; + const int m_mediaSliderHeight; + const int m_mediaSliderThumbWidth; + const int m_mediaSliderThumbHeight; + + GtkThemeParts m_themeParts; +#ifdef GTK_API_VERSION_2 + bool m_themePartsHaveRGBAColormap; +#endif + friend class WidgetRenderingContext; +}; + +} + +#endif // RenderThemeGtk_h diff --git a/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp b/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp new file mode 100644 index 0000000..fd391b7 --- /dev/null +++ b/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2007 Apple Inc. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2009 Kenneth Rohde Christiansen + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderThemeGtk.h" + +#ifdef GTK_API_VERSION_2 + +#include "CSSValueKeywords.h" +#include "GraphicsContext.h" +#include "GtkVersioning.h" +#include "HTMLNames.h" +#include "MediaControlElements.h" +#include "RenderObject.h" +#include "TextDirection.h" +#include "UserAgentStyleSheets.h" +#include "WidgetRenderingContext.h" +#include "gtkdrawing.h" +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +#if ENABLE(PROGRESS_TAG) +#include "RenderProgress.h" +#endif + +namespace WebCore { + +// This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h. +extern GtkTextDirection gtkTextDirection(TextDirection); + +#if ENABLE(VIDEO) +void RenderThemeGtk::initMediaColors() +{ + GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkContainer())); + m_panelColor = style->bg[GTK_STATE_NORMAL]; + m_sliderColor = style->bg[GTK_STATE_ACTIVE]; + m_sliderThumbColor = style->bg[GTK_STATE_SELECTED]; +} +#endif + +void RenderThemeGtk::adjustRepaintRect(const RenderObject*, IntRect&) +{ +} + +GtkStateType RenderThemeGtk::getGtkStateType(RenderObject* object) +{ + if (!isEnabled(object) || isReadOnlyControl(object)) + return GTK_STATE_INSENSITIVE; + if (isPressed(object)) + return GTK_STATE_ACTIVE; + if (isHovered(object)) + return GTK_STATE_PRELIGHT; + return GTK_STATE_NORMAL; +} + +bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, int flags) +{ + // Painting is disabled so just claim to have succeeded + if (context->paintingDisabled()) + return false; + + GtkWidgetState widgetState; + widgetState.active = isPressed(renderObject); + widgetState.focused = isFocused(renderObject); + + // https://bugs.webkit.org/show_bug.cgi?id=18364 + // The Mozilla theme drawing code, only paints a button as pressed when it's pressed + // while hovered. Until we move away from the Mozila code, work-around the issue by + // forcing a pressed button into the hovered state. This ensures that buttons activated + // via the keyboard have the proper rendering. + widgetState.inHover = isHovered(renderObject) || (type == MOZ_GTK_BUTTON && isPressed(renderObject)); + + // FIXME: Disabled does not always give the correct appearance for ReadOnly + widgetState.disabled = !isEnabled(renderObject) || isReadOnlyControl(renderObject); + widgetState.isDefault = false; + widgetState.canDefault = false; + widgetState.depressed = false; + + WidgetRenderingContext widgetContext(context, rect); + return !widgetContext.paintMozillaWidget(type, &widgetState, flags, + gtkTextDirection(renderObject->style()->direction())); +} + +static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, ControlPart appearance) +{ + // The width and height are both specified, so we shouldn't change them. + if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) + return; + + // FIXME: This is probably not correct use of indicatorSize and indicatorSpacing. + gint indicatorSize, indicatorSpacing; + theme->getIndicatorMetrics(appearance, indicatorSize, indicatorSpacing); + + // Other ports hard-code this to 13, but GTK+ users tend to demand the native look. + // It could be made a configuration option values other than 13 actually break site compatibility. + int length = indicatorSize + indicatorSpacing; + if (style->width().isIntrinsicOrAuto()) + style->setWidth(Length(length, Fixed)); + + if (style->height().isAuto()) + style->setHeight(Length(length, Fixed)); +} + +void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const +{ + setToggleSize(this, style, RadioPart); +} + +bool RenderThemeGtk::paintCheckbox(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + return paintRenderObject(MOZ_GTK_CHECKBUTTON, object, info.context, rect, isChecked(object)); +} + +void RenderThemeGtk::setRadioSize(RenderStyle* style) const +{ + setToggleSize(this, style, RadioPart); +} + +bool RenderThemeGtk::paintRadio(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + return paintRenderObject(MOZ_GTK_RADIOBUTTON, object, info.context, rect, isChecked(object)); +} + +bool RenderThemeGtk::paintButton(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + if (info.context->paintingDisabled()) + return false; + + GtkWidget* widget = gtkButton(); + IntRect buttonRect(IntPoint(), rect.size()); + IntRect focusRect(buttonRect); + + GtkStateType state = getGtkStateType(object); + gtk_widget_set_state(widget, state); + gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction())); + + if (isFocused(object)) { + if (isEnabled(object)) { +#if !GTK_CHECK_VERSION(2, 22, 0) + GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); +#endif + g_object_set(widget, "has-focus", TRUE, NULL); + } + + gboolean interiorFocus = 0, focusWidth = 0, focusPadding = 0; + gtk_widget_style_get(widget, + "interior-focus", &interiorFocus, + "focus-line-width", &focusWidth, + "focus-padding", &focusPadding, NULL); + // If we are using exterior focus, we shrink the button rect down before + // drawing. If we are using interior focus we shrink the focus rect. This + // approach originates from the Mozilla theme drawing code (gtk2drawing.c). + if (interiorFocus) { + GtkStyle* style = gtk_widget_get_style(widget); + focusRect.inflateX(-style->xthickness - focusPadding); + focusRect.inflateY(-style->ythickness - focusPadding); + } else { + buttonRect.inflateX(-focusWidth - focusPadding); + buttonRect.inflateY(-focusPadding - focusPadding); + } + } + + WidgetRenderingContext widgetContext(info.context, rect); + GtkShadowType shadowType = state == GTK_STATE_ACTIVE ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + widgetContext.gtkPaintBox(buttonRect, widget, state, shadowType, "button"); + if (isFocused(object)) + widgetContext.gtkPaintFocus(focusRect, widget, state, "button"); + +#if !GTK_CHECK_VERSION(2, 22, 0) + GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS); +#endif + g_object_set(widget, "has-focus", FALSE, NULL); + return false; +} + +static void getComboBoxPadding(RenderStyle* style, int& left, int& top, int& right, int& bottom) +{ + // If this menu list button isn't drawn using the native theme, we + // don't add any extra padding beyond what WebCore already uses. + if (style->appearance() == NoControlPart) + return; + moz_gtk_get_widget_border(MOZ_GTK_DROPDOWN, &left, &top, &right, &bottom, + gtkTextDirection(style->direction()), TRUE); +} + +int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const +{ + int left = 0, top = 0, right = 0, bottom = 0; + getComboBoxPadding(style, left, top, right, bottom); + return left; +} + +int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const +{ + int left = 0, top = 0, right = 0, bottom = 0; + getComboBoxPadding(style, left, top, right, bottom); + return right; +} + +int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const +{ + int left = 0, top = 0, right = 0, bottom = 0; + getComboBoxPadding(style, left, top, right, bottom); + return top; +} + +int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const +{ + int left = 0, top = 0, right = 0, bottom = 0; + getComboBoxPadding(style, left, top, right, bottom); + return bottom; +} + +bool RenderThemeGtk::paintMenuList(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + return paintRenderObject(MOZ_GTK_DROPDOWN, object, info.context, rect); +} + +bool RenderThemeGtk::paintTextField(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + return paintRenderObject(MOZ_GTK_ENTRY, object, info.context, rect); +} + +bool RenderThemeGtk::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + if (info.context->paintingDisabled()) + return false; + + ControlPart part = object->style()->appearance(); + ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart); + + // We shrink the trough rect slightly to make room for the focus indicator. + IntRect troughRect(IntPoint(), rect.size()); // This is relative to rect. + GtkWidget* widget = 0; + if (part == SliderVerticalPart) { + widget = gtkVScale(); + troughRect.inflateY(-gtk_widget_get_style(widget)->ythickness); + } else { + widget = gtkHScale(); + troughRect.inflateX(-gtk_widget_get_style(widget)->xthickness); + } + gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction())); + + WidgetRenderingContext widgetContext(info.context, rect); + widgetContext.gtkPaintBox(troughRect, widget, GTK_STATE_ACTIVE, GTK_SHADOW_OUT, "trough"); + if (isFocused(object)) + widgetContext.gtkPaintFocus(IntRect(IntPoint(), rect.size()), widget, getGtkStateType(object), "trough"); + + return false; +} + +bool RenderThemeGtk::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + if (info.context->paintingDisabled()) + return false; + + ControlPart part = object->style()->appearance(); + ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart); + + GtkWidget* widget = 0; + const char* detail = 0; + GtkOrientation orientation; + if (part == SliderThumbVerticalPart) { + widget = gtkVScale(); + detail = "vscale"; + orientation = GTK_ORIENTATION_VERTICAL; + } else { + widget = gtkHScale(); + detail = "hscale"; + orientation = GTK_ORIENTATION_HORIZONTAL; + } + gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction())); + + // Only some themes have slider thumbs respond to clicks and some don't. This information is + // gathered via the 'activate-slider' property, but it's deprecated in GTK+ 2.22 and removed in + // GTK+ 3.x. The drawback of not honoring it is that slider thumbs change color when you click + // on them. + IntRect thumbRect(IntPoint(), rect.size()); + WidgetRenderingContext widgetContext(info.context, rect); + widgetContext.gtkPaintSlider(thumbRect, widget, getGtkStateType(object), GTK_SHADOW_OUT, detail, orientation); + return false; +} + +void RenderThemeGtk::adjustSliderThumbSize(RenderObject* o) const +{ + ControlPart part = o->style()->appearance(); +#if ENABLE(VIDEO) + if (part == MediaSliderThumbPart || part == MediaVolumeSliderThumbPart) { + adjustMediaSliderThumbSize(o); + return; + } +#endif + + GtkWidget* widget = part == SliderThumbHorizontalPart ? gtkHScale() : gtkVScale(); + int length = 0, width = 0; + gtk_widget_style_get(widget, + "slider_length", &length, + "slider_width", &width, + NULL); + + if (part == SliderThumbHorizontalPart) { + o->style()->setWidth(Length(length, Fixed)); + o->style()->setHeight(Length(width, Fixed)); + return; + } + ASSERT(part == SliderThumbVerticalPart); + o->style()->setWidth(Length(width, Fixed)); + o->style()->setHeight(Length(length, Fixed)); +} + +#if ENABLE(PROGRESS_TAG) +bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + if (!renderObject->isProgress()) + return true; + + GtkWidget* progressBarWidget = moz_gtk_get_progress_widget(); + if (!progressBarWidget) + return true; + + if (paintRenderObject(MOZ_GTK_PROGRESSBAR, renderObject, paintInfo.context, rect)) + return true; + + IntRect chunkRect(rect); + RenderProgress* renderProgress = toRenderProgress(renderObject); + + GtkStyle* style = gtk_widget_get_style(progressBarWidget); + chunkRect.setHeight(chunkRect.height() - (2 * style->ythickness)); + chunkRect.setY(chunkRect.y() + style->ythickness); + chunkRect.setWidth((chunkRect.width() - (2 * style->xthickness)) * renderProgress->position()); + if (renderObject->style()->direction() == RTL) + chunkRect.setX(rect.x() + rect.width() - chunkRect.width() - style->xthickness); + else + chunkRect.setX(chunkRect.x() + style->xthickness); + + return paintRenderObject(MOZ_GTK_PROGRESS_CHUNK, renderObject, paintInfo.context, chunkRect); +} +#endif + +GRefPtr<GdkPixbuf> RenderThemeGtk::getStockIcon(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize) +{ + ASSERT(widgetType == GTK_TYPE_CONTAINER || widgetType == GTK_TYPE_ENTRY); + GtkWidget* widget = widgetType == GTK_TYPE_CONTAINER ? GTK_WIDGET(gtkContainer()) : gtkEntry(); + GtkStyle* style = gtk_widget_get_style(widget); + GtkIconSet* iconSet = gtk_style_lookup_icon_set(style, iconName); + return adoptGRef(gtk_icon_set_render_icon(iconSet, style, + static_cast<GtkTextDirection>(direction), + static_cast<GtkStateType>(state), + static_cast<GtkIconSize>(iconSize), 0, 0)); +} + + +Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const +{ + GtkWidget* widget = gtkEntry(); + return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED]; +} + +Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const +{ + GtkWidget* widget = gtkEntry(); + return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE]; +} + +Color RenderThemeGtk::platformActiveSelectionForegroundColor() const +{ + GtkWidget* widget = gtkEntry(); + return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED]; +} + +Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const +{ + GtkWidget* widget = gtkEntry(); + return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE]; +} + +Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const +{ + GtkWidget* widget = gtkTreeView(); + return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED]; +} + +Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const +{ + GtkWidget* widget = gtkTreeView(); + return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE]; +} + +Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const +{ + GtkWidget* widget = gtkTreeView(); + return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED]; +} + +Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const +{ + GtkWidget* widget = gtkTreeView(); + return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE]; +} + +Color RenderThemeGtk::systemColor(int cssValueId) const +{ + switch (cssValueId) { + case CSSValueButtontext: + return Color(gtk_widget_get_style(gtkButton())->fg[GTK_STATE_NORMAL]); + case CSSValueCaptiontext: + return Color(gtk_widget_get_style(gtkEntry())->fg[GTK_STATE_NORMAL]); + default: + return RenderTheme::systemColor(cssValueId); + } +} + +static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, RenderTheme* renderTheme) +{ + // FIXME: Make sure this function doesn't get called many times for a single GTK+ style change signal. + renderTheme->platformColorsDidChange(); +} + +void RenderThemeGtk::setupWidgetAndAddToContainer(GtkWidget* widget, GtkWidget* window) const +{ + gtk_container_add(GTK_CONTAINER(window), widget); + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + + // FIXME: Perhaps this should only be called for the containing window or parent container. + g_signal_connect(widget, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this)); +} + +GtkWidget* RenderThemeGtk::gtkContainer() const +{ + if (m_gtkContainer) + return m_gtkContainer; + + m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP); + gtk_widget_set_colormap(m_gtkWindow, m_themeParts.colormap); + gtk_widget_realize(m_gtkWindow); + gtk_widget_set_name(m_gtkWindow, "MozillaGtkWidget"); + + m_gtkContainer = gtk_fixed_new(); + setupWidgetAndAddToContainer(m_gtkContainer, m_gtkWindow); + return m_gtkContainer; +} + +GtkWidget* RenderThemeGtk::gtkButton() const +{ + if (m_gtkButton) + return m_gtkButton; + m_gtkButton = gtk_button_new(); + setupWidgetAndAddToContainer(m_gtkButton, gtkContainer()); + return m_gtkButton; +} + +GtkWidget* RenderThemeGtk::gtkEntry() const +{ + if (m_gtkEntry) + return m_gtkEntry; + m_gtkEntry = gtk_entry_new(); + setupWidgetAndAddToContainer(m_gtkEntry, gtkContainer()); + return m_gtkEntry; +} + +GtkWidget* RenderThemeGtk::gtkTreeView() const +{ + if (m_gtkTreeView) + return m_gtkTreeView; + m_gtkTreeView = gtk_tree_view_new(); + setupWidgetAndAddToContainer(m_gtkTreeView, gtkContainer()); + return m_gtkTreeView; +} + +GtkWidget* RenderThemeGtk::gtkVScale() const +{ + if (m_gtkVScale) + return m_gtkVScale; + m_gtkVScale = gtk_vscale_new(0); + setupWidgetAndAddToContainer(m_gtkVScale, gtkContainer()); + return m_gtkVScale; +} + +GtkWidget* RenderThemeGtk::gtkHScale() const +{ + if (m_gtkHScale) + return m_gtkHScale; + m_gtkHScale = gtk_hscale_new(0); + setupWidgetAndAddToContainer(m_gtkHScale, gtkContainer()); + return m_gtkHScale; +} + +GtkWidget* RenderThemeGtk::gtkScrollbar() +{ + return moz_gtk_get_scrollbar_widget(); +} + +} // namespace WebCore + +#endif // GTK_API_VERSION_2 diff --git a/Source/WebCore/platform/gtk/RenderThemeGtk3.cpp b/Source/WebCore/platform/gtk/RenderThemeGtk3.cpp new file mode 100644 index 0000000..663404d --- /dev/null +++ b/Source/WebCore/platform/gtk/RenderThemeGtk3.cpp @@ -0,0 +1,643 @@ +/* + * Copyright (C) 2007 Apple Inc. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2009 Kenneth Rohde Christiansen + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderThemeGtk.h" + +#ifndef GTK_API_VERSION_2 + +#include "CSSValueKeywords.h" +#include "GraphicsContext.h" +#include "GtkVersioning.h" +#include "HTMLNames.h" +#include "MediaControlElements.h" +#include "Page.h" +#include "RenderObject.h" +#include "TextDirection.h" +#include "UserAgentStyleSheets.h" +#include "WidgetRenderingContext.h" +#include "gtkdrawing.h" +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +#if ENABLE(PROGRESS_TAG) +#include "RenderProgress.h" +#endif + +namespace WebCore { + +typedef HashMap<GType, GRefPtr<GtkStyleContext> > StyleContextMap; +static StyleContextMap& styleContextMap(); + +static void gtkStyleChangedCallback(GObject*, GParamSpec*) +{ + StyleContextMap::const_iterator end = styleContextMap().end(); + for (StyleContextMap::const_iterator iter = styleContextMap().begin(); iter != end; ++iter) + gtk_style_context_invalidate(iter->second.get()); + + Page::scheduleForcedStyleRecalcForAllPages(); +} + +static StyleContextMap& styleContextMap() +{ + DEFINE_STATIC_LOCAL(StyleContextMap, map, ()); + + static bool initialized = false; + if (!initialized) { + GtkSettings* settings = gtk_settings_get_default(); + g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(gtkStyleChangedCallback), 0); + g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(gtkStyleChangedCallback), 0); + initialized = true; + } + return map; +} + +static GtkStyleContext* getStyleContext(GType widgetType) +{ + std::pair<StyleContextMap::iterator, bool> result = styleContextMap().add(widgetType, 0); + if (!result.second) + return result.first->second.get(); + + GtkWidgetPath* path = gtk_widget_path_new(); + gtk_widget_path_append_type(path, widgetType); + + GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new()); + gtk_style_context_set_path(context.get(), path); + gtk_widget_path_free(path); + + result.first->second = context; + return context.get(); +} + +// This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h. +extern GtkTextDirection gtkTextDirection(TextDirection); + +void RenderThemeGtk::initMediaColors() +{ + GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkContainer())); + m_panelColor = style->bg[GTK_STATE_NORMAL]; + m_sliderColor = style->bg[GTK_STATE_ACTIVE]; + m_sliderThumbColor = style->bg[GTK_STATE_SELECTED]; +} + +static void adjustRectForFocus(GtkStyleContext* context, IntRect& rect) +{ + gint focusWidth, focusPad; + gtk_style_context_get_style(context, + "focus-line-width", &focusWidth, + "focus-padding", &focusPad, NULL); + rect.inflate(focusWidth + focusPad); +} + +void RenderThemeGtk::adjustRepaintRect(const RenderObject* renderObject, IntRect& rect) +{ + GtkStyleContext* context = 0; + ControlPart part = renderObject->style()->appearance(); + switch (part) { + case SliderVerticalPart: + case SliderHorizontalPart: + context = getStyleContext(part == SliderThumbHorizontalPart ? GTK_TYPE_HSCALE : GTK_TYPE_VSCALE); + break; + case ButtonPart: + context = getStyleContext(GTK_TYPE_BUTTON); + + gboolean interiorFocus; + gtk_style_context_get_style(context, "interior-focus", &interiorFocus, NULL); + if (interiorFocus) + return; + + break; + default: + return; + } + + ASSERT(context); + adjustRectForFocus(context, rect); +} + +GtkStateType RenderThemeGtk::getGtkStateType(RenderObject* object) +{ + if (!isEnabled(object) || isReadOnlyControl(object)) + return GTK_STATE_INSENSITIVE; + if (isPressed(object)) + return GTK_STATE_ACTIVE; + if (isHovered(object)) + return GTK_STATE_PRELIGHT; + return GTK_STATE_NORMAL; +} + +bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, int flags) +{ + // Painting is disabled so just claim to have succeeded + if (context->paintingDisabled()) + return false; + + GtkWidgetState widgetState; + widgetState.active = isPressed(renderObject); + widgetState.focused = isFocused(renderObject); + + // https://bugs.webkit.org/show_bug.cgi?id=18364 + // The Mozilla theme drawing code, only paints a button as pressed when it's pressed + // while hovered. Until we move away from the Mozila code, work-around the issue by + // forcing a pressed button into the hovered state. This ensures that buttons activated + // via the keyboard have the proper rendering. + widgetState.inHover = isHovered(renderObject) || (type == MOZ_GTK_BUTTON && isPressed(renderObject)); + + // FIXME: Disabled does not always give the correct appearance for ReadOnly + widgetState.disabled = !isEnabled(renderObject) || isReadOnlyControl(renderObject); + widgetState.isDefault = false; + widgetState.canDefault = false; + widgetState.depressed = false; + + WidgetRenderingContext widgetContext(context, rect); + return !widgetContext.paintMozillaWidget(type, &widgetState, flags, + gtkTextDirection(renderObject->style()->direction())); +} + +static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, ControlPart appearance) +{ + // The width and height are both specified, so we shouldn't change them. + if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) + return; + + // FIXME: This is probably not correct use of indicatorSize and indicatorSpacing. + gint indicatorSize, indicatorSpacing; + theme->getIndicatorMetrics(appearance, indicatorSize, indicatorSpacing); + + // Other ports hard-code this to 13, but GTK+ users tend to demand the native look. + // It could be made a configuration option values other than 13 actually break site compatibility. + int length = indicatorSize + indicatorSpacing; + if (style->width().isIntrinsicOrAuto()) + style->setWidth(Length(length, Fixed)); + + if (style->height().isAuto()) + style->setHeight(Length(length, Fixed)); +} + +void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const +{ + setToggleSize(this, style, RadioPart); +} + +bool RenderThemeGtk::paintCheckbox(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + return paintRenderObject(MOZ_GTK_CHECKBUTTON, object, info.context, rect, isChecked(object)); +} + +void RenderThemeGtk::setRadioSize(RenderStyle* style) const +{ + setToggleSize(this, style, RadioPart); +} + +bool RenderThemeGtk::paintRadio(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + return paintRenderObject(MOZ_GTK_RADIOBUTTON, object, info.context, rect, isChecked(object)); +} + +bool RenderThemeGtk::paintButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + GtkStyleContext* context = getStyleContext(GTK_TYPE_BUTTON); + gtk_style_context_save(context); + + gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()))); + gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); + + IntRect buttonRect(rect); + + if (isDefault(renderObject)) { + GtkBorder* borderPtr = 0; + GtkBorder border = { 1, 1, 1, 1 }; + + gtk_style_context_get_style(context, "default-border", &borderPtr, NULL); + if (borderPtr) { + border = *borderPtr; + gtk_border_free(borderPtr); + } + + buttonRect.move(border.left, border.top); + buttonRect.setWidth(buttonRect.width() - (border.left + border.right)); + buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom)); + + gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT); + } + + guint flags = 0; + if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) + flags |= GTK_STATE_FLAG_INSENSITIVE; + else if (isHovered(renderObject)) + flags |= GTK_STATE_FLAG_PRELIGHT; + if (isPressed(renderObject)) + flags |= GTK_STATE_FLAG_ACTIVE; + gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); + + gtk_render_background(context, paintInfo.context->platformContext(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); + gtk_render_frame(context, paintInfo.context->platformContext(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); + + if (isFocused(renderObject)) { + gint focusWidth, focusPad; + gboolean displaceFocus, interiorFocus; + gtk_style_context_get_style(context, + "focus-line-width", &focusWidth, + "focus-padding", &focusPad, + "interior-focus", &interiorFocus, + "displace-focus", &displaceFocus, + NULL); + + if (interiorFocus) { + GtkBorder borderWidth; + gtk_style_context_get_border(context, static_cast<GtkStateFlags>(flags), &borderWidth); + + buttonRect = IntRect(buttonRect.x() + borderWidth.left + focusPad, buttonRect.y() + borderWidth.top + focusPad, + buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right), + buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom)); + } else + buttonRect.inflate(focusWidth + focusPad); + + if (displaceFocus && isPressed(renderObject)) { + gint childDisplacementX; + gint childDisplacementY; + gtk_style_context_get_style(context, + "child-displacement-x", &childDisplacementX, + "child-displacement-y", &childDisplacementY, + NULL); + buttonRect.move(childDisplacementX, childDisplacementY); + } + + gtk_render_focus(context, paintInfo.context->platformContext(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); + } + + gtk_style_context_restore(context); + + return false; +} + +static void getComboBoxPadding(RenderStyle* style, int& left, int& top, int& right, int& bottom) +{ + // If this menu list button isn't drawn using the native theme, we + // don't add any extra padding beyond what WebCore already uses. + if (style->appearance() == NoControlPart) + return; + moz_gtk_get_widget_border(MOZ_GTK_DROPDOWN, &left, &top, &right, &bottom, + gtkTextDirection(style->direction()), TRUE); +} + +int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const +{ + int left = 0, top = 0, right = 0, bottom = 0; + getComboBoxPadding(style, left, top, right, bottom); + return left; +} + +int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const +{ + int left = 0, top = 0, right = 0, bottom = 0; + getComboBoxPadding(style, left, top, right, bottom); + return right; +} + +int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const +{ + int left = 0, top = 0, right = 0, bottom = 0; + getComboBoxPadding(style, left, top, right, bottom); + return top; +} + +int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const +{ + int left = 0, top = 0, right = 0, bottom = 0; + getComboBoxPadding(style, left, top, right, bottom); + return bottom; +} + +bool RenderThemeGtk::paintMenuList(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + return paintRenderObject(MOZ_GTK_DROPDOWN, object, info.context, rect); +} + +bool RenderThemeGtk::paintTextField(RenderObject* object, const PaintInfo& info, const IntRect& rect) +{ + return paintRenderObject(MOZ_GTK_ENTRY, object, info.context, rect); +} + +bool RenderThemeGtk::paintSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + ControlPart part = renderObject->style()->appearance(); + ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart); + + GtkStyleContext* context = getStyleContext(part == SliderThumbHorizontalPart ? GTK_TYPE_HSCALE : GTK_TYPE_VSCALE); + gtk_style_context_save(context); + + gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction())); + gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE); + gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH); + + if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) + gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE); + + gtk_render_background(context, paintInfo.context->platformContext(), + rect.x(), rect.y(), rect.width(), rect.height()); + gtk_render_frame(context, paintInfo.context->platformContext(), + rect.x(), rect.y(), rect.width(), rect.height()); + + if (isFocused(renderObject)) { + gint focusWidth, focusPad; + gtk_style_context_get_style(context, + "focus-line-width", &focusWidth, + "focus-padding", &focusPad, NULL); + IntRect focusRect(rect); + focusRect.inflate(focusWidth + focusPad); + gtk_render_focus(context, paintInfo.context->platformContext(), + focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height()); + } + + gtk_style_context_restore(context); + return false; +} + +bool RenderThemeGtk::paintSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + ControlPart part = renderObject->style()->appearance(); + ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart); + + GtkStyleContext* context = getStyleContext(part == SliderThumbHorizontalPart ? GTK_TYPE_HSCALE : GTK_TYPE_VSCALE); + gtk_style_context_save(context); + + gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction())); + gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE); + gtk_style_context_add_class(context, GTK_STYLE_CLASS_SLIDER); + + gint troughBorder; + gtk_style_context_get_style(context, "trough-border", &troughBorder, NULL); + + IntRect sliderRect(rect); + sliderRect.inflate(-troughBorder); + + guint flags = 0; + if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) + flags |= GTK_STATE_FLAG_INSENSITIVE; + else if (isHovered(renderObject)) + flags |= GTK_STATE_FLAG_PRELIGHT; + if (isPressed(renderObject)) + flags |= GTK_STATE_FLAG_ACTIVE; + gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); + + gtk_render_slider(context, paintInfo.context->platformContext(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height(), + part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); + + gtk_style_context_restore(context); + + return false; +} + +void RenderThemeGtk::adjustSliderThumbSize(RenderObject* renderObject) const +{ + ControlPart part = renderObject->style()->appearance(); +#if ENABLE(VIDEO) + if (part == MediaSliderThumbPart || part == MediaVolumeSliderThumbPart) { + adjustMediaSliderThumbSize(renderObject); + return; + } +#endif + + gint sliderWidth, sliderLength; + gtk_style_context_get_style(getStyleContext(part == SliderThumbHorizontalPart ? GTK_TYPE_HSCALE : GTK_TYPE_VSCALE), + "slider-width", &sliderWidth, + "slider-length", &sliderLength, + NULL); + if (part == SliderThumbHorizontalPart) { + renderObject->style()->setWidth(Length(sliderLength, Fixed)); + renderObject->style()->setHeight(Length(sliderWidth, Fixed)); + return; + } + ASSERT(part == SliderThumbVerticalPart); + renderObject->style()->setWidth(Length(sliderWidth, Fixed)); + renderObject->style()->setHeight(Length(sliderLength, Fixed)); +} + +#if ENABLE(PROGRESS_TAG) +bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + if (!renderObject->isProgress()) + return true; + + GtkWidget* progressBarWidget = moz_gtk_get_progress_widget(); + if (!progressBarWidget) + return true; + + if (paintRenderObject(MOZ_GTK_PROGRESSBAR, renderObject, paintInfo.context, rect)) + return true; + + IntRect chunkRect(rect); + RenderProgress* renderProgress = toRenderProgress(renderObject); + + GtkStyle* style = gtk_widget_get_style(progressBarWidget); + chunkRect.setHeight(chunkRect.height() - (2 * style->ythickness)); + chunkRect.setY(chunkRect.y() + style->ythickness); + chunkRect.setWidth((chunkRect.width() - (2 * style->xthickness)) * renderProgress->position()); + if (renderObject->style()->direction() == RTL) + chunkRect.setX(rect.x() + rect.width() - chunkRect.width() - style->xthickness); + else + chunkRect.setX(chunkRect.x() + style->xthickness); + + return paintRenderObject(MOZ_GTK_PROGRESS_CHUNK, renderObject, paintInfo.context, chunkRect); +} +#endif + +GRefPtr<GdkPixbuf> RenderThemeGtk::getStockIcon(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize) +{ + GtkStyleContext* context = getStyleContext(widgetType); + GtkIconSet* iconSet = gtk_style_context_lookup_icon_set(context, iconName); + + gtk_style_context_save(context); + + guint flags = 0; + if (state == GTK_STATE_PRELIGHT) + flags |= GTK_STATE_FLAG_PRELIGHT; + else if (state == GTK_STATE_INSENSITIVE) + flags |= GTK_STATE_FLAG_INSENSITIVE; + + gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); + gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction)); + GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context, static_cast<GtkIconSize>(iconSize)); + + gtk_style_context_restore(context); + + return adoptGRef(icon); +} + +Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const +{ + GdkRGBA gdkRGBAColor; + gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); + return gdkRGBAColor; +} + +Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const +{ + GdkRGBA gdkRGBAColor; + gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); + return gdkRGBAColor; +} + +Color RenderThemeGtk::platformActiveSelectionForegroundColor() const +{ + GdkRGBA gdkRGBAColor; + gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); + return gdkRGBAColor; +} + +Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const +{ + GdkRGBA gdkRGBAColor; + gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); + return gdkRGBAColor; +} + +Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const +{ + GdkRGBA gdkRGBAColor; + gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); + return gdkRGBAColor; +} + +Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const +{ + GdkRGBA gdkRGBAColor; + gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); + return gdkRGBAColor; +} + +Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const +{ + GdkRGBA gdkRGBAColor; + gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); + return gdkRGBAColor; +} + +Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const +{ + GdkRGBA gdkRGBAColor; + gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); + return gdkRGBAColor; +} + +Color RenderThemeGtk::systemColor(int cssValueId) const +{ + GdkRGBA gdkRGBAColor; + + switch (cssValueId) { + case CSSValueButtontext: + gtk_style_context_get_color(getStyleContext(GTK_TYPE_BUTTON), static_cast<GtkStateFlags>(0), &gdkRGBAColor); + return gdkRGBAColor; + case CSSValueCaptiontext: + gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(0), &gdkRGBAColor); + return gdkRGBAColor; + default: + return RenderTheme::systemColor(cssValueId); + } +} + +static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, RenderTheme* renderTheme) +{ + // FIXME: Make sure this function doesn't get called many times for a single GTK+ style change signal. + renderTheme->platformColorsDidChange(); +} + +void RenderThemeGtk::setupWidgetAndAddToContainer(GtkWidget* widget, GtkWidget* window) const +{ + gtk_container_add(GTK_CONTAINER(window), widget); + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + + // FIXME: Perhaps this should only be called for the containing window or parent container. + g_signal_connect(widget, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this)); +} + +GtkWidget* RenderThemeGtk::gtkContainer() const +{ + if (m_gtkContainer) + return m_gtkContainer; + + m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP); + gtk_widget_realize(m_gtkWindow); + gtk_widget_set_name(m_gtkWindow, "MozillaGtkWidget"); + + m_gtkContainer = gtk_fixed_new(); + setupWidgetAndAddToContainer(m_gtkContainer, m_gtkWindow); + return m_gtkContainer; +} + +GtkWidget* RenderThemeGtk::gtkButton() const +{ + if (m_gtkButton) + return m_gtkButton; + m_gtkButton = gtk_button_new(); + setupWidgetAndAddToContainer(m_gtkButton, gtkContainer()); + return m_gtkButton; +} + +GtkWidget* RenderThemeGtk::gtkEntry() const +{ + if (m_gtkEntry) + return m_gtkEntry; + m_gtkEntry = gtk_entry_new(); + setupWidgetAndAddToContainer(m_gtkEntry, gtkContainer()); + return m_gtkEntry; +} + +GtkWidget* RenderThemeGtk::gtkTreeView() const +{ + if (m_gtkTreeView) + return m_gtkTreeView; + m_gtkTreeView = gtk_tree_view_new(); + setupWidgetAndAddToContainer(m_gtkTreeView, gtkContainer()); + return m_gtkTreeView; +} + +GtkWidget* RenderThemeGtk::gtkVScale() const +{ + if (m_gtkVScale) + return m_gtkVScale; + m_gtkVScale = gtk_vscale_new(0); + setupWidgetAndAddToContainer(m_gtkVScale, gtkContainer()); + return m_gtkVScale; +} + +GtkWidget* RenderThemeGtk::gtkHScale() const +{ + if (m_gtkHScale) + return m_gtkHScale; + m_gtkHScale = gtk_hscale_new(0); + setupWidgetAndAddToContainer(m_gtkHScale, gtkContainer()); + return m_gtkHScale; +} + +GtkStyleContext* RenderThemeGtk::gtkScrollbarStyle() +{ + return getStyleContext(GTK_TYPE_SCROLLBAR); +} + +} // namespace WebCore + +#endif // !GTK_API_VERSION_2 diff --git a/Source/WebCore/platform/gtk/ScrollViewGtk.cpp b/Source/WebCore/platform/gtk/ScrollViewGtk.cpp new file mode 100644 index 0000000..a8c7562 --- /dev/null +++ b/Source/WebCore/platform/gtk/ScrollViewGtk.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007, 2009 Holger Hans Peter Freyther + * Copyright (C) 2008, 2010 Collabora Ltd. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ScrollView.h" + +#include "ChromeClient.h" +#include "FloatRect.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "GtkVersioning.h" +#include "HostWindow.h" +#include "IntRect.h" +#include "MainFrameScrollbarGtk.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "PlatformWheelEvent.h" +#include "ScrollbarTheme.h" + +#include <gtk/gtk.h> + +using namespace std; + +namespace WebCore { + +void ScrollView::platformInit() +{ +} + +void ScrollView::platformDestroy() +{ + m_horizontalAdjustment = 0; + m_verticalAdjustment = 0; +} + +PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation) +{ + // If this is an interior frame scrollbar, we want to create a totally fake + // scrollbar with no GtkAdjustment backing it. + if (parent()) + return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); + + // If this is the main frame, we want to create a Scrollbar that does no painting + // and defers to our GtkAdjustment for all of its state. Note that the GtkAdjustment + // may be null here. + if (orientation == HorizontalScrollbar) + return MainFrameScrollbarGtk::create(this, orientation, m_horizontalAdjustment.get()); + + // VerticalScrollbar + return MainFrameScrollbarGtk::create(this, orientation, m_verticalAdjustment.get()); +} + +void ScrollView::setHorizontalAdjustment(GtkAdjustment* hadj, bool resetValues) +{ + ASSERT(!parent() || !hadj); + if (parent()) + return; + + m_horizontalAdjustment = hadj; + + if (!m_horizontalAdjustment) { + MainFrameScrollbarGtk* hScrollbar = reinterpret_cast<MainFrameScrollbarGtk*>(horizontalScrollbar()); + if (hScrollbar) + hScrollbar->detachAdjustment(); + + return; + } + + // We may be lacking scrollbars when returning to a cached + // page, this kicks the page to recreate the scrollbars. + setHasHorizontalScrollbar(true); + + MainFrameScrollbarGtk* hScrollbar = reinterpret_cast<MainFrameScrollbarGtk*>(horizontalScrollbar()); + hScrollbar->attachAdjustment(m_horizontalAdjustment.get()); + + // We used to reset everything to 0 here, but when page cache + // is enabled we reuse FrameViews that are cached. Since their + // size is not going to change when being restored, (which is + // what would cause the upper limit in the adjusments to be + // set in the normal case), we make sure they are up-to-date + // here. This is needed for the parent scrolling widget to be + // able to report correct values. + int horizontalPageStep = max(max<int>(frameRect().width() * Scrollbar::minFractionToStepWhenPaging(), frameRect().width() - Scrollbar::maxOverlapBetweenPages()), 1); + gtk_adjustment_configure(m_horizontalAdjustment.get(), + resetValues ? 0 : scrollOffset().width(), 0, + resetValues ? 0 : contentsSize().width(), + resetValues ? 0 : Scrollbar::pixelsPerLineStep(), + resetValues ? 0 : horizontalPageStep, + resetValues ? 0 : frameRect().width()); +} + +void ScrollView::setVerticalAdjustment(GtkAdjustment* vadj, bool resetValues) +{ + ASSERT(!parent() || !vadj); + if (parent()) + return; + + m_verticalAdjustment = vadj; + + if (!m_verticalAdjustment) { + MainFrameScrollbarGtk* vScrollbar = reinterpret_cast<MainFrameScrollbarGtk*>(verticalScrollbar()); + if (vScrollbar) + vScrollbar->detachAdjustment(); + + return; + } + + // We may be lacking scrollbars when returning to a cached + // page, this kicks the page to recreate the scrollbars. + setHasVerticalScrollbar(true); + + MainFrameScrollbarGtk* vScrollbar = reinterpret_cast<MainFrameScrollbarGtk*>(verticalScrollbar()); + vScrollbar->attachAdjustment(m_verticalAdjustment.get()); + + // We used to reset everything to 0 here, but when page cache + // is enabled we reuse FrameViews that are cached. Since their + // size is not going to change when being restored, (which is + // what would cause the upper limit in the adjusments to be + // set in the normal case), we make sure they are up-to-date + // here. This is needed for the parent scrolling widget to be + // able to report correct values. + int verticalPageStep = max(max<int>(frameRect().width() * Scrollbar::minFractionToStepWhenPaging(), frameRect().width() - Scrollbar::maxOverlapBetweenPages()), 1); + gtk_adjustment_configure(m_verticalAdjustment.get(), + resetValues ? 0 : scrollOffset().height(), 0, + resetValues ? 0 : contentsSize().height(), + resetValues ? 0 : Scrollbar::pixelsPerLineStep(), + resetValues ? 0 : verticalPageStep, + resetValues ? 0 : frameRect().height()); +} + +void ScrollView::setGtkAdjustments(GtkAdjustment* hadj, GtkAdjustment* vadj, bool resetValues) +{ + setHorizontalAdjustment(hadj, resetValues); + setVerticalAdjustment(vadj, resetValues); +} + +void ScrollView::platformAddChild(Widget* child) +{ +} + +void ScrollView::platformRemoveChild(Widget* child) +{ +} + +IntRect ScrollView::visibleContentRect(bool includeScrollbars) const +{ + // If we are an interior frame scrollbar or are in some sort of transition + // state, just calculate our size based on what the GTK+ theme says the + // scrollbar width should be. + if (parent() || !hostWindow() || !hostWindow()->platformPageClient()) { + return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()), + IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)), + max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0)))); + } + + // We don't have a parent, so we are the main frame and thus have + // a parent widget which we can use to measure the visible region. + GtkWidget* measuredWidget = hostWindow()->platformPageClient(); + GtkWidget* parentWidget = gtk_widget_get_parent(measuredWidget); + + // We may not be in a widget that displays scrollbars, but we may + // have other kinds of decoration that make us smaller. + if (parentWidget && includeScrollbars) + measuredWidget = parentWidget; + + GtkAllocation allocation; + gtk_widget_get_allocation(measuredWidget, &allocation); + return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()), + IntSize(allocation.width, allocation.height)); +} + +void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode, bool horizontalLock, bool verticalLock) +{ + // FIXME: Restructure the ScrollView abstraction so that we do not have to + // copy this verbatim from ScrollView.cpp. Until then, we should make sure this + // is kept in sync. + bool needsUpdate = false; + + if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) { + m_horizontalScrollbarMode = horizontalMode; + needsUpdate = true; + } + + if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) { + m_verticalScrollbarMode = verticalMode; + needsUpdate = true; + } + + if (horizontalLock) + setHorizontalScrollbarLock(); + + if (verticalLock) + setVerticalScrollbarLock(); + + if (needsUpdate) + updateScrollbars(scrollOffset()); + + // We don't need to report policy changes on ScrollView's unless this + // one has an adjustment attached and it is a main frame. + if (!m_horizontalAdjustment || parent() || !isFrameView()) + return; + + // For frames that do have adjustments attached, we want to report + // policy changes, so that they may be applied to the widget to + // which the WebView's container (e.g. GtkScrolledWindow). + if (hostWindow()) + hostWindow()->scrollbarsModeDidChange(); +} + +} diff --git a/Source/WebCore/platform/gtk/ScrollbarThemeGtk.cpp b/Source/WebCore/platform/gtk/ScrollbarThemeGtk.cpp new file mode 100644 index 0000000..cb9b0f8 --- /dev/null +++ b/Source/WebCore/platform/gtk/ScrollbarThemeGtk.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ScrollbarThemeGtk.h" + +#include "PlatformMouseEvent.h" +#include "RenderThemeGtk.h" +#include "ScrollView.h" +#include "Scrollbar.h" + +namespace WebCore { + +static HashSet<Scrollbar*>* gScrollbars; + +ScrollbarTheme* ScrollbarTheme::nativeTheme() +{ + static ScrollbarThemeGtk theme; + return &theme; +} + +ScrollbarThemeGtk::~ScrollbarThemeGtk() +{ +} + +void ScrollbarThemeGtk::registerScrollbar(Scrollbar* scrollbar) +{ + if (!gScrollbars) + gScrollbars = new HashSet<Scrollbar*>; + gScrollbars->add(scrollbar); +} + +void ScrollbarThemeGtk::unregisterScrollbar(Scrollbar* scrollbar) +{ + gScrollbars->remove(scrollbar); + if (gScrollbars->isEmpty()) { + delete gScrollbars; + gScrollbars = 0; + } +} + +void ScrollbarThemeGtk::updateScrollbarsFrameThickness() +{ + if (!gScrollbars) + return; + + // Update the thickness of every interior frame scrollbar widget. The + // platform-independent scrollbar them code isn't yet smart enough to get + // this information when it paints. + HashSet<Scrollbar*>::iterator end = gScrollbars->end(); + for (HashSet<Scrollbar*>::iterator it = gScrollbars->begin(); it != end; ++it) { + Scrollbar* scrollbar = (*it); + + // Top-level scrollbar i.e. scrollbars who have a parent ScrollView + // with no parent are native, and thus do not need to be resized. + if (!scrollbar->parent() || !scrollbar->parent()->parent()) + return; + + int thickness = scrollbarThickness(scrollbar->controlSize()); + if (scrollbar->orientation() == HorizontalScrollbar) + scrollbar->setFrameRect(IntRect(0, scrollbar->parent()->height() - thickness, scrollbar->width(), thickness)); + else + scrollbar->setFrameRect(IntRect(scrollbar->parent()->width() - thickness, 0, thickness, scrollbar->height())); + } +} + +bool ScrollbarThemeGtk::hasThumb(Scrollbar* scrollbar) +{ + // This method is just called as a paint-time optimization to see if + // painting the thumb can be skipped. We don't have to be exact here. + return thumbLength(scrollbar) > 0; +} + +IntRect ScrollbarThemeGtk::backButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool) +{ + if (part == BackButtonEndPart && !m_hasBackButtonEndPart) + return IntRect(); + + int x = scrollbar->x() + m_troughBorderWidth; + int y = scrollbar->y() + m_troughBorderWidth; + IntSize size = buttonSize(scrollbar); + if (part == BackButtonStartPart) + return IntRect(x, y, size.width(), size.height()); + + // BackButtonEndPart (alternate button) + if (scrollbar->orientation() == HorizontalScrollbar) + return IntRect(scrollbar->x() + scrollbar->width() - m_troughBorderWidth - (2 * size.width()), y, size.width(), size.height()); + + // VerticalScrollbar alternate button + return IntRect(x, scrollbar->y() + scrollbar->height() - m_troughBorderWidth - (2 * size.height()), size.width(), size.height()); +} + +IntRect ScrollbarThemeGtk::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool) +{ + if (part == ForwardButtonStartPart && !m_hasForwardButtonStartPart) + return IntRect(); + + IntSize size = buttonSize(scrollbar); + if (scrollbar->orientation() == HorizontalScrollbar) { + int y = scrollbar->y() + m_troughBorderWidth; + if (part == ForwardButtonEndPart) + return IntRect(scrollbar->x() + scrollbar->width() - size.width() - m_troughBorderWidth, y, size.width(), size.height()); + + // ForwardButtonStartPart (alternate button) + return IntRect(scrollbar->x() + m_troughBorderWidth + size.width(), y, size.width(), size.height()); + } + + // VerticalScrollbar + int x = scrollbar->x() + m_troughBorderWidth; + if (part == ForwardButtonEndPart) + return IntRect(x, scrollbar->y() + scrollbar->height() - size.height() - m_troughBorderWidth, size.width(), size.height()); + + // ForwardButtonStartPart (alternate button) + return IntRect(x, scrollbar->y() + m_troughBorderWidth + size.height(), size.width(), size.height()); +} + +IntRect ScrollbarThemeGtk::trackRect(Scrollbar* scrollbar, bool) +{ + // The padding along the thumb movement axis (from outside to in) + // is the size of trough border plus the size of the stepper (button) + // plus the size of stepper spacing (the space between the stepper and + // the place where the thumb stops). There is often no stepper spacing. + int movementAxisPadding = m_troughBorderWidth + m_stepperSize + m_stepperSpacing; + + // The fatness of the scrollbar on the non-movement axis. + int thickness = scrollbarThickness(scrollbar->controlSize()); + + int alternateButtonOffset = 0; + int alternateButtonWidth = 0; + if (m_hasForwardButtonStartPart) { + alternateButtonOffset += m_stepperSize; + alternateButtonWidth += m_stepperSize; + } + if (m_hasBackButtonEndPart) + alternateButtonWidth += m_stepperSize; + + if (scrollbar->orientation() == HorizontalScrollbar) { + // Once the scrollbar becomes smaller than the natural size of the + // two buttons, the track disappears. + if (scrollbar->width() < 2 * thickness) + return IntRect(); + return IntRect(scrollbar->x() + movementAxisPadding + alternateButtonOffset, scrollbar->y(), + scrollbar->width() - (2 * movementAxisPadding) - alternateButtonWidth, thickness); + } + + if (scrollbar->height() < 2 * thickness) + return IntRect(); + return IntRect(scrollbar->x(), scrollbar->y() + movementAxisPadding + alternateButtonOffset, + thickness, scrollbar->height() - (2 * movementAxisPadding) - alternateButtonWidth); +} + +IntRect ScrollbarThemeGtk::thumbRect(Scrollbar* scrollbar, const IntRect& unconstrainedTrackRect) +{ + IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect); + int thumbPos = thumbPosition(scrollbar); + if (scrollbar->orientation() == HorizontalScrollbar) + return IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - m_thumbFatness) / 2, thumbLength(scrollbar), m_thumbFatness); + + // VerticalScrollbar + return IntRect(trackRect.x() + (trackRect.width() - m_thumbFatness) / 2, trackRect.y() + thumbPos, m_thumbFatness, thumbLength(scrollbar)); +} + +bool ScrollbarThemeGtk::paint(Scrollbar* scrollbar, GraphicsContext* graphicsContext, const IntRect& damageRect) +{ + if (graphicsContext->paintingDisabled()) + return false; + + // Create the ScrollbarControlPartMask based on the damageRect + ScrollbarControlPartMask scrollMask = NoPart; + + IntRect backButtonStartPaintRect; + IntRect backButtonEndPaintRect; + IntRect forwardButtonStartPaintRect; + IntRect forwardButtonEndPaintRect; + if (hasButtons(scrollbar)) { + backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart, true); + if (damageRect.intersects(backButtonStartPaintRect)) + scrollMask |= BackButtonStartPart; + backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, true); + if (damageRect.intersects(backButtonEndPaintRect)) + scrollMask |= BackButtonEndPart; + forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButtonStartPart, true); + if (damageRect.intersects(forwardButtonStartPaintRect)) + scrollMask |= ForwardButtonStartPart; + forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEndPart, true); + if (damageRect.intersects(forwardButtonEndPaintRect)) + scrollMask |= ForwardButtonEndPart; + } + + IntRect trackPaintRect = trackRect(scrollbar, true); + if (damageRect.intersects(trackPaintRect)) + scrollMask |= TrackBGPart; + + if (m_troughUnderSteppers && (scrollMask & BackButtonStartPart + || scrollMask & BackButtonEndPart + || scrollMask & ForwardButtonStartPart + || scrollMask & ForwardButtonEndPart)) + scrollMask |= TrackBGPart; + + bool thumbPresent = hasThumb(scrollbar); + IntRect currentThumbRect; + if (thumbPresent) { + IntRect track = trackRect(scrollbar, false); + currentThumbRect = thumbRect(scrollbar, track); + if (damageRect.intersects(currentThumbRect)) + scrollMask |= ThumbPart; + } + + paintScrollbarBackground(graphicsContext, scrollbar); + + if (scrollMask & TrackBGPart) + paintTrackBackground(graphicsContext, scrollbar, trackPaintRect); + + // Paint the back and forward buttons. + if (scrollMask & BackButtonStartPart) + paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackButtonStartPart); + if (scrollMask & BackButtonEndPart) + paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButtonEndPart); + if (scrollMask & ForwardButtonStartPart) + paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, ForwardButtonStartPart); + if (scrollMask & ForwardButtonEndPart) + paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, ForwardButtonEndPart); + + // Paint the thumb. + if (scrollMask & ThumbPart) + paintThumb(graphicsContext, scrollbar, currentThumbRect); + + return true; +} + +void ScrollbarThemeGtk::paintScrollCorner(ScrollView* view, GraphicsContext* context, const IntRect& cornerRect) +{ + // ScrollbarThemeComposite::paintScrollCorner incorrectly assumes that the + // ScrollView is a FrameView (see FramelessScrollView), so we cannot let + // that code run. For FrameView's this is correct since we don't do custom + // scrollbar corner rendering, which ScrollbarThemeComposite supports. + ScrollbarTheme::paintScrollCorner(view, context, cornerRect); +} + +bool ScrollbarThemeGtk::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& event) +{ + return (event.shiftKey() && event.button() == LeftButton) || (event.button() == MiddleButton); +} + +int ScrollbarThemeGtk::scrollbarThickness(ScrollbarControlSize) +{ + return m_thumbFatness + (m_troughBorderWidth * 2); +} + +IntSize ScrollbarThemeGtk::buttonSize(Scrollbar* scrollbar) +{ + if (scrollbar->orientation() == VerticalScrollbar) + return IntSize(m_thumbFatness, m_stepperSize); + + // HorizontalScrollbar + return IntSize(m_stepperSize, m_thumbFatness); +} + +int ScrollbarThemeGtk::minimumThumbLength(Scrollbar* scrollbar) +{ + return m_minThumbLength; +} + +} + diff --git a/Source/WebCore/platform/gtk/ScrollbarThemeGtk.h b/Source/WebCore/platform/gtk/ScrollbarThemeGtk.h new file mode 100644 index 0000000..207ec1f --- /dev/null +++ b/Source/WebCore/platform/gtk/ScrollbarThemeGtk.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ScrollbarThemeGtk_h +#define ScrollbarThemeGtk_h + +#include "ScrollbarThemeComposite.h" + +namespace WebCore { + +class Scrollbar; + +class ScrollbarThemeGtk : public ScrollbarThemeComposite { +public: + ScrollbarThemeGtk(); + virtual ~ScrollbarThemeGtk(); + + virtual bool hasButtons(Scrollbar*) { return true; } + virtual bool hasThumb(Scrollbar*); + virtual IntRect backButtonRect(Scrollbar*, ScrollbarPart, bool); + virtual IntRect forwardButtonRect(Scrollbar*, ScrollbarPart, bool); + virtual IntRect trackRect(Scrollbar*, bool); + IntRect thumbRect(Scrollbar*, const IntRect& unconstrainedTrackRect); + bool paint(Scrollbar*, GraphicsContext*, const IntRect& damageRect); + void paintScrollbarBackground(GraphicsContext*, Scrollbar*); + void paintTrackBackground(GraphicsContext*, Scrollbar*, const IntRect&); + void paintThumb(GraphicsContext*, Scrollbar*, const IntRect&); + virtual void paintButton(GraphicsContext*, Scrollbar*, const IntRect&, ScrollbarPart); + virtual void paintScrollCorner(ScrollView*, GraphicsContext*, const IntRect&); + virtual bool shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent&); + virtual int scrollbarThickness(ScrollbarControlSize); + virtual IntSize buttonSize(Scrollbar*); + virtual int minimumThumbLength(Scrollbar*); + + // TODO: These are the default GTK+ values. At some point we should pull these from the theme itself. + virtual double initialAutoscrollTimerDelay() { return 0.20; } + virtual double autoscrollTimerDelay() { return 0.02; } + void updateThemeProperties(); + void updateScrollbarsFrameThickness(); + void registerScrollbar(Scrollbar*); + void unregisterScrollbar(Scrollbar*); + +protected: +#ifndef GTK_API_VERSION_2 + GtkStyleContext* m_context; +#endif + int m_thumbFatness; + int m_troughBorderWidth; + int m_stepperSize; + int m_stepperSpacing; + int m_minThumbLength; + gboolean m_troughUnderSteppers; + gboolean m_hasForwardButtonStartPart; + gboolean m_hasBackButtonEndPart; +}; + +} +#endif diff --git a/Source/WebCore/platform/gtk/ScrollbarThemeGtk2.cpp b/Source/WebCore/platform/gtk/ScrollbarThemeGtk2.cpp new file mode 100644 index 0000000..79295c1 --- /dev/null +++ b/Source/WebCore/platform/gtk/ScrollbarThemeGtk2.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ScrollbarThemeGtk.h" + +#ifdef GTK_API_VERSION_2 + +#include "PlatformMouseEvent.h" +#include "RenderThemeGtk.h" +#include "ScrollView.h" +#include "Scrollbar.h" +#include "WidgetRenderingContext.h" +#include "gtkdrawing.h" +#include <gtk/gtk.h> + +namespace WebCore { + +static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, ScrollbarThemeGtk* scrollbarTheme) +{ + scrollbarTheme->updateThemeProperties(); +} + +ScrollbarThemeGtk::ScrollbarThemeGtk() +{ + updateThemeProperties(); + g_signal_connect(static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->gtkScrollbar(), + "style-set", G_CALLBACK(gtkStyleSetCallback), this); +} + +void ScrollbarThemeGtk::updateThemeProperties() +{ + MozGtkScrollbarMetrics metrics; + moz_gtk_get_scrollbar_metrics(&metrics); + + m_thumbFatness = metrics.slider_width; + m_troughBorderWidth = metrics.trough_border; + m_stepperSize = metrics.stepper_size; + m_stepperSpacing = metrics.stepper_spacing; + m_minThumbLength = metrics.min_slider_size; + m_troughUnderSteppers = metrics.trough_under_steppers; + m_hasForwardButtonStartPart = metrics.has_secondary_forward_stepper; + m_hasBackButtonEndPart = metrics.has_secondary_backward_stepper; + + updateScrollbarsFrameThickness(); +} + +void ScrollbarThemeGtk::paintTrackBackground(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect) +{ + GtkWidgetState state; + state.focused = FALSE; + state.isDefault = FALSE; + state.canDefault = FALSE; + state.disabled = FALSE; + state.active = FALSE; + state.inHover = FALSE; + + // Paint the track background. If the trough-under-steppers property is true, this + // should be the full size of the scrollbar, but if is false, it should only be the + // track rect. + IntRect fullScrollbarRect = rect; + if (m_troughUnderSteppers) + fullScrollbarRect = IntRect(scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height()); + + GtkThemeWidgetType type = scrollbar->orientation() == VerticalScrollbar ? MOZ_GTK_SCROLLBAR_TRACK_VERTICAL : MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL; + WidgetRenderingContext widgetContext(context, fullScrollbarRect); + widgetContext.paintMozillaWidget(type, &state, 0); +} + +void ScrollbarThemeGtk::paintScrollbarBackground(GraphicsContext* context, Scrollbar* scrollbar) +{ + // This is unused by the moz_gtk_scrollecd_window_paint. + GtkWidgetState state; + IntRect fullScrollbarRect = IntRect(scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height()); + WidgetRenderingContext widgetContext(context, fullScrollbarRect); + widgetContext.paintMozillaWidget(MOZ_GTK_SCROLLED_WINDOW, &state, 0); +} + +void ScrollbarThemeGtk::paintThumb(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect) +{ + GtkWidgetState state; + state.focused = FALSE; + state.isDefault = FALSE; + state.canDefault = FALSE; + state.disabled = FALSE; + state.active = scrollbar->pressedPart() == ThumbPart; + state.inHover = scrollbar->hoveredPart() == ThumbPart; + state.maxpos = scrollbar->maximum(); + state.curpos = scrollbar->currentPos(); + + GtkThemeWidgetType type = scrollbar->orientation() == VerticalScrollbar ? MOZ_GTK_SCROLLBAR_THUMB_VERTICAL : MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL; + WidgetRenderingContext widgetContext(context, rect); + widgetContext.paintMozillaWidget(type, &state, 0); +} + +void ScrollbarThemeGtk::paintButton(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part) +{ + int flags = 0; + if (scrollbar->orientation() == VerticalScrollbar) + flags |= MOZ_GTK_STEPPER_VERTICAL; + + if (part == ForwardButtonEndPart) + flags |= (MOZ_GTK_STEPPER_DOWN | MOZ_GTK_STEPPER_BOTTOM); + if (part == ForwardButtonStartPart) + flags |= MOZ_GTK_STEPPER_DOWN; + + GtkWidgetState state; + state.focused = TRUE; + state.isDefault = TRUE; + state.canDefault = TRUE; + state.depressed = FALSE; + + if ((BackButtonStartPart == part && scrollbar->currentPos()) + || (BackButtonEndPart == part && scrollbar->currentPos()) + || (ForwardButtonEndPart == part && scrollbar->currentPos() != scrollbar->maximum()) + || (ForwardButtonStartPart == part && scrollbar->currentPos() != scrollbar->maximum())) { + state.disabled = FALSE; + state.active = part == scrollbar->pressedPart(); + state.inHover = part == scrollbar->hoveredPart(); + } else { + state.disabled = TRUE; + state.active = FALSE; + state.inHover = FALSE; + } + + WidgetRenderingContext widgetContext(context, rect); + widgetContext.paintMozillaWidget(MOZ_GTK_SCROLLBAR_BUTTON, &state, flags); +} + +} // namespace WebCore + +#endif // GTK_API_VERSION_2 diff --git a/Source/WebCore/platform/gtk/ScrollbarThemeGtk3.cpp b/Source/WebCore/platform/gtk/ScrollbarThemeGtk3.cpp new file mode 100644 index 0000000..d000063 --- /dev/null +++ b/Source/WebCore/platform/gtk/ScrollbarThemeGtk3.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ScrollbarThemeGtk.h" + +#ifndef GTK_API_VERSION_2 + +#include "PlatformMouseEvent.h" +#include "RenderThemeGtk.h" +#include "ScrollView.h" +#include "Scrollbar.h" +#include <gtk/gtk.h> + +namespace WebCore { + +static void gtkStyleChangedCallback(GtkWidget*, ScrollbarThemeGtk* scrollbarTheme) +{ + scrollbarTheme->updateThemeProperties(); +} + +ScrollbarThemeGtk::ScrollbarThemeGtk() + : m_context(static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->gtkScrollbarStyle()) +{ + updateThemeProperties(); + g_signal_connect(m_context, "changed", G_CALLBACK(gtkStyleChangedCallback), this); +} + +void ScrollbarThemeGtk::updateThemeProperties() +{ + gtk_style_context_get_style(m_context, + "min-slider-length", &m_minThumbLength, + "slider-width", &m_thumbFatness, + "trough-border", &m_troughBorderWidth, + "stepper-size", &m_stepperSize, + "stepper-spacing", &m_stepperSpacing, + "trough-under-steppers", &m_troughUnderSteppers, + "has-secondary-backward-stepper", &m_hasBackButtonEndPart, + "has-secondary-forward-stepper", &m_hasForwardButtonStartPart, + NULL); + updateScrollbarsFrameThickness(); +} + +void ScrollbarThemeGtk::paintTrackBackground(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect) +{ + // Paint the track background. If the trough-under-steppers property is true, this + // should be the full size of the scrollbar, but if is false, it should only be the + // track rect. + IntRect fullScrollbarRect(rect); + if (m_troughUnderSteppers) + fullScrollbarRect = IntRect(scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height()); + + gtk_style_context_save(m_context); + + gtk_style_context_add_class(m_context, GTK_STYLE_CLASS_SCROLLBAR); + gtk_style_context_add_class(m_context, GTK_STYLE_CLASS_TROUGH); + + gtk_render_background(m_context, context->platformContext(), + fullScrollbarRect.x(), fullScrollbarRect.y(), fullScrollbarRect.width(), fullScrollbarRect.height()); + gtk_render_frame(m_context, context->platformContext(), + fullScrollbarRect.x(), fullScrollbarRect.y(), fullScrollbarRect.width(), fullScrollbarRect.height()); + + gtk_style_context_restore(m_context); +} + +void ScrollbarThemeGtk::paintScrollbarBackground(GraphicsContext* context, Scrollbar* scrollbar) +{ + gtk_style_context_save(m_context); + + gtk_style_context_add_class(m_context, GTK_STYLE_CLASS_SCROLLBAR); + gtk_style_context_add_class(m_context, "scrolled-window"); + gtk_render_frame(m_context, context->platformContext(), scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height()); + + gtk_style_context_restore(m_context); +} + +void ScrollbarThemeGtk::paintThumb(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect) +{ + gtk_style_context_save(m_context); + + gtk_style_context_add_class(m_context, GTK_STYLE_CLASS_SCROLLBAR); + gtk_style_context_add_class(m_context, GTK_STYLE_CLASS_SLIDER); + + guint flags = 0; + if (scrollbar->pressedPart() == ThumbPart) + flags |= GTK_STATE_FLAG_ACTIVE; + if (scrollbar->hoveredPart() == ThumbPart) + flags |= GTK_STATE_FLAG_PRELIGHT; + gtk_style_context_set_state(m_context, static_cast<GtkStateFlags>(flags)); + + gtk_render_slider(m_context, context->platformContext(), rect.x(), rect.y(), rect.width(), rect.height(), + scrollbar->orientation() == VerticalScrollbar ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL); + + gtk_style_context_restore(m_context); +} + +void ScrollbarThemeGtk::paintButton(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part) +{ + gtk_style_context_save(m_context); + + gtk_style_context_add_class(m_context, GTK_STYLE_CLASS_SCROLLBAR); + + guint flags = 0; + if ((BackButtonStartPart == part && scrollbar->currentPos()) + || (BackButtonEndPart == part && scrollbar->currentPos()) + || (ForwardButtonEndPart == part && scrollbar->currentPos() != scrollbar->maximum()) + || (ForwardButtonStartPart == part && scrollbar->currentPos() != scrollbar->maximum())) { + if (part == scrollbar->pressedPart()) + flags |= GTK_STATE_FLAG_ACTIVE; + if (part == scrollbar->hoveredPart()) + flags |= GTK_STATE_FLAG_PRELIGHT; + } else + flags |= GTK_STATE_FLAG_INSENSITIVE; + gtk_style_context_set_state(m_context, static_cast<GtkStateFlags>(flags)); + + guint sides = gtk_style_context_get_junction_sides(m_context); + if (scrollbar->orientation() == VerticalScrollbar) + sides &= ~(GTK_JUNCTION_TOP | GTK_JUNCTION_BOTTOM); + else + sides &= ~(GTK_JUNCTION_LEFT | GTK_JUNCTION_RIGHT); + + switch (part) { + case BackButtonStartPart: + sides |= (scrollbar->orientation() == VerticalScrollbar) ? GTK_JUNCTION_BOTTOM : GTK_JUNCTION_RIGHT; + break; + case BackButtonEndPart: + case ForwardButtonEndPart: + sides |= (scrollbar->orientation() == VerticalScrollbar) ? + GTK_JUNCTION_TOP | GTK_JUNCTION_BOTTOM : GTK_JUNCTION_RIGHT | GTK_JUNCTION_LEFT; + break; + case ForwardButtonStartPart: + sides |= (scrollbar->orientation() == VerticalScrollbar) ? GTK_JUNCTION_TOP : GTK_JUNCTION_LEFT; + break; + default: + ASSERT_NOT_REACHED(); + } + gtk_style_context_set_junction_sides(m_context, static_cast<GtkJunctionSides>(sides)); + gtk_style_context_add_class(m_context, GTK_STYLE_CLASS_BUTTON); + + gtk_render_background(m_context, context->platformContext(), rect.x(), rect.y(), rect.width(), rect.height()); + gtk_render_frame(m_context, context->platformContext(), rect.x(), rect.y(), rect.width(), rect.height()); + + gfloat arrowScaling; + gtk_style_context_get_style(m_context, "arrow-scaling", &arrowScaling, NULL); + + IntSize arrowSize = rect.size(); + arrowSize.scale(arrowScaling); + IntPoint arrowPoint(rect.x() + (rect.width() - arrowSize.width()) / 2, + rect.y() + (rect.height() - arrowSize.height()) / 2); + + if (flags & GTK_STATE_FLAG_ACTIVE) { + gint arrowDisplacementX, arrowDisplacementY; + gtk_style_context_get_style(m_context, + "arrow-displacement-x", &arrowDisplacementX, + "arrow-displacement-y", &arrowDisplacementY, + NULL); + arrowPoint.move(arrowDisplacementX, arrowDisplacementY); + } + + gdouble angle, size; + if (scrollbar->orientation() == VerticalScrollbar) { + size = arrowSize.width(); + angle = (part == ForwardButtonEndPart || part == ForwardButtonStartPart) ? G_PI : 0; + } else { + size = arrowSize.height(); + angle = (part == ForwardButtonEndPart || part == ForwardButtonStartPart) ? G_PI / 2 : 3 * (G_PI / 2); + } + + gtk_render_arrow(m_context, context->platformContext(), angle, arrowPoint.x(), arrowPoint.y(), size); + + gtk_style_context_restore(m_context); +} + +} // namespace WebCore + +#endif // !GTK_API_VERSION_2 diff --git a/Source/WebCore/platform/gtk/SearchPopupMenuGtk.cpp b/Source/WebCore/platform/gtk/SearchPopupMenuGtk.cpp new file mode 100644 index 0000000..2413773 --- /dev/null +++ b/Source/WebCore/platform/gtk/SearchPopupMenuGtk.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "SearchPopupMenuGtk.h" + +#include "NotImplemented.h" + +namespace WebCore { + +SearchPopupMenuGtk::SearchPopupMenuGtk(PopupMenuClient* client) + : m_popup(adoptRef(new PopupMenuGtk(client))) +{ + notImplemented(); +} + +PopupMenu* SearchPopupMenuGtk::popupMenu() +{ + return m_popup.get(); +} + +void SearchPopupMenuGtk::saveRecentSearches(const AtomicString&, const Vector<String>&) +{ + notImplemented(); +} + +void SearchPopupMenuGtk::loadRecentSearches(const AtomicString&, Vector<String>&) +{ + notImplemented(); +} + +bool SearchPopupMenuGtk::enabled() +{ + notImplemented(); + return false; +} + +} diff --git a/Source/WebCore/platform/gtk/SearchPopupMenuGtk.h b/Source/WebCore/platform/gtk/SearchPopupMenuGtk.h new file mode 100644 index 0000000..453c63d --- /dev/null +++ b/Source/WebCore/platform/gtk/SearchPopupMenuGtk.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef SearchPopupMenuGtk_h +#define SearchPopupMenuGtk_h + +#include "PopupMenuGtk.h" +#include "SearchPopupMenu.h" + +namespace WebCore { + +class SearchPopupMenuGtk : public SearchPopupMenu { +public: + SearchPopupMenuGtk(PopupMenuClient*); + + virtual PopupMenu* popupMenu(); + virtual void saveRecentSearches(const AtomicString& name, const Vector<String>& searchItems); + virtual void loadRecentSearches(const AtomicString& name, Vector<String>& searchItems); + virtual bool enabled(); + +private: + RefPtr<PopupMenuGtk> m_popup; +}; + +} + +#endif // SearchPopupMenuGtk_h diff --git a/Source/WebCore/platform/gtk/SharedBufferGtk.cpp b/Source/WebCore/platform/gtk/SharedBufferGtk.cpp new file mode 100644 index 0000000..c7040f2 --- /dev/null +++ b/Source/WebCore/platform/gtk/SharedBufferGtk.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "SharedBuffer.h" + +#include "FileSystem.h" +#include <wtf/text/CString.h> + +#include <glib.h> + + +namespace WebCore { + +PassRefPtr<SharedBuffer> SharedBuffer::createWithContentsOfFile(const String& filePath) +{ + if (filePath.isEmpty()) + return 0; + + CString filename = fileSystemRepresentation(filePath); + gchar* contents; + gsize size; + GError* error = 0; + if (!g_file_get_contents(filename.data(), &contents, &size, &error)) { + LOG_ERROR("Failed to fully read contents of file %s - %s", filenameForDisplay(filePath).utf8().data(), error->message); + g_error_free(error); + return 0; + } + + RefPtr<SharedBuffer> result = SharedBuffer::create(contents, size); + g_free(contents); + + return result.release(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/gtk/SharedTimerGtk.cpp b/Source/WebCore/platform/gtk/SharedTimerGtk.cpp new file mode 100644 index 0000000..ee4d75b --- /dev/null +++ b/Source/WebCore/platform/gtk/SharedTimerGtk.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SharedTimer.h" + +#include <wtf/Assertions.h> +#include <wtf/CurrentTime.h> +#include <gdk/gdk.h> +#include <glib.h> + +namespace WebCore { + +static guint sharedTimer; +static void (*sharedTimerFiredFunction)(); + +void setSharedTimerFiredFunction(void (*f)()) +{ + sharedTimerFiredFunction = f; +} + +static gboolean timeout_cb(gpointer) +{ + if (sharedTimerFiredFunction) + sharedTimerFiredFunction(); + return FALSE; +} + +void setSharedTimerFireTime(double fireTime) +{ + ASSERT(sharedTimerFiredFunction); + + double interval = fireTime - currentTime(); + guint intervalInMS; + if (interval < 0) + intervalInMS = 0; + else { + interval *= 1000; + intervalInMS = (guint)interval; + } + + stopSharedTimer(); + sharedTimer = g_timeout_add_full(GDK_PRIORITY_REDRAW, intervalInMS, timeout_cb, 0, 0); +} + +void stopSharedTimer() +{ + gboolean s = FALSE; + if (sharedTimer == 0) + return; + + s = g_source_remove(sharedTimer); + ASSERT(s); + sharedTimer = 0; +} + +} diff --git a/Source/WebCore/platform/gtk/SoundGtk.cpp b/Source/WebCore/platform/gtk/SoundGtk.cpp new file mode 100644 index 0000000..2f7316c --- /dev/null +++ b/Source/WebCore/platform/gtk/SoundGtk.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "Sound.h" + +#include <gdk/gdk.h> + +namespace WebCore { + +void systemBeep() +{ + gdk_beep(); +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/gtk/TemporaryLinkStubs.cpp b/Source/WebCore/platform/gtk/TemporaryLinkStubs.cpp new file mode 100644 index 0000000..5c27080 --- /dev/null +++ b/Source/WebCore/platform/gtk/TemporaryLinkStubs.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "AXObjectCache.h" +#include "CookieStorage.h" +#include "DNS.h" +#include "Editor.h" +#include "FrameView.h" +#include "FTPDirectoryDocument.h" +#include "NotImplemented.h" +#include "PluginView.h" +#include <float.h> + +using namespace WebCore; + +/********************************************************/ +/* Completely empty stubs (mostly to allow DRT to run): */ +/********************************************************/ + +namespace WebCore { +void getSupportedKeySizes(Vector<String>&) { notImplemented(); } +String signedPublicKeyAndChallengeString(unsigned keySizeIndex, const String &challengeString, const KURL &url) { return String(); } +float userIdleTime() { notImplemented(); return FLT_MAX; } // return an arbitrarily high userIdleTime so that releasing pages from the page cache isn't postponed +void setCookieStoragePrivateBrowsingEnabled(bool) { notImplemented(); } + +} diff --git a/Source/WebCore/platform/gtk/WheelEventGtk.cpp b/Source/WebCore/platform/gtk/WheelEventGtk.cpp new file mode 100644 index 0000000..fc6206f --- /dev/null +++ b/Source/WebCore/platform/gtk/WheelEventGtk.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PlatformWheelEvent.h" +#include "Scrollbar.h" + +#include <gdk/gdk.h> + +namespace WebCore { + +// Keep this in sync with the other platform event constructors +PlatformWheelEvent::PlatformWheelEvent(GdkEventScroll* event) +{ + static const float delta = 1; + + m_deltaX = 0; + m_deltaY = 0; + + // Docs say an upwards scroll (away from the user) has a positive delta + switch (event->direction) { + case GDK_SCROLL_UP: + m_deltaY = delta; + break; + case GDK_SCROLL_DOWN: + m_deltaY = -delta; + break; + case GDK_SCROLL_LEFT: + m_deltaX = delta; + break; + case GDK_SCROLL_RIGHT: + m_deltaX = -delta; + break; + } + m_wheelTicksX = m_deltaX; + m_wheelTicksY = m_deltaY; + + m_position = IntPoint(static_cast<int>(event->x), static_cast<int>(event->y)); + m_globalPosition = IntPoint(static_cast<int>(event->x_root), static_cast<int>(event->y_root)); + m_granularity = ScrollByPixelWheelEvent; + m_isAccepted = false; + m_shiftKey = event->state & GDK_SHIFT_MASK; + m_ctrlKey = event->state & GDK_CONTROL_MASK; + m_altKey = event->state & GDK_MOD1_MASK; + m_metaKey = event->state & GDK_META_MASK; + + // FIXME: retrieve the user setting for the number of lines to scroll on each wheel event + m_deltaX *= static_cast<float>(Scrollbar::pixelsPerLineStep()); + m_deltaY *= static_cast<float>(Scrollbar::pixelsPerLineStep()); +} + +} diff --git a/Source/WebCore/platform/gtk/WidgetGtk.cpp b/Source/WebCore/platform/gtk/WidgetGtk.cpp new file mode 100644 index 0000000..ee1005c --- /dev/null +++ b/Source/WebCore/platform/gtk/WidgetGtk.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007, 2009 Holger Hans Peter Freyther + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Widget.h" + +#include "Cursor.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "GtkVersioning.h" +#include "HostWindow.h" +#include "IntRect.h" +#include "RenderObject.h" + +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +namespace WebCore { + +static GdkCursor* lastSetCursor; + +Widget::Widget(PlatformWidget widget) +{ + init(widget); +} + +Widget::~Widget() +{ + ASSERT(!parent()); + releasePlatformWidget(); +} + +void Widget::setFocus(bool focused) +{ + if (focused) + gtk_widget_grab_focus(platformWidget() ? platformWidget() : GTK_WIDGET(root()->hostWindow()->platformPageClient())); +} + +static GdkWindow* gdkWindow(PlatformWidget widget) +{ + return widget ? gtk_widget_get_window(widget) : 0; +} + +void Widget::setCursor(const Cursor& cursor) +{ + GdkCursor* platformCursor = cursor.platformCursor().get(); + + // http://bugs.webkit.org/show_bug.cgi?id=16388 + // [GTK] Widget::setCursor() gets called frequently + // + // gdk_window_set_cursor() in certain GDK backends seems to be an + // expensive operation, so avoid it if possible. + + if (platformCursor == lastSetCursor) + return; + + gdk_window_set_cursor(gdkWindow(platformWidget()) ? gdkWindow(platformWidget()) : gtk_widget_get_window(GTK_WIDGET(root()->hostWindow()->platformPageClient())), platformCursor); + lastSetCursor = platformCursor; +} + +void Widget::show() +{ + setSelfVisible(true); + + if (isParentVisible() && platformWidget()) + gtk_widget_show(platformWidget()); +} + +void Widget::hide() +{ + setSelfVisible(false); + + if (isParentVisible() && platformWidget()) + gtk_widget_hide(platformWidget()); +} + +void Widget::paint(GraphicsContext* context, const IntRect& rect) +{ +} + +void Widget::setIsSelected(bool isSelected) +{ + if (!platformWidget()) + return; + + // See if the platformWidget has a webkit-widget-is-selected property + // and set it afterwards. + GParamSpec* spec = g_object_class_find_property(G_OBJECT_GET_CLASS(platformWidget()), + "webkit-widget-is-selected"); + if (!spec) + return; + + g_object_set(platformWidget(), "webkit-widget-is-selected", isSelected, NULL); +} + +IntRect Widget::frameRect() const +{ + return m_frame; +} + +void Widget::setFrameRect(const IntRect& rect) +{ + m_frame = rect; +} + +void Widget::releasePlatformWidget() +{ + if (!platformWidget()) + return; + g_object_unref(platformWidget()); +} + +void Widget::retainPlatformWidget() +{ + if (!platformWidget()) + return; + g_object_ref_sink(platformWidget()); +} + +} diff --git a/Source/WebCore/platform/gtk/WidgetRenderingContext.h b/Source/WebCore/platform/gtk/WidgetRenderingContext.h new file mode 100644 index 0000000..7334656 --- /dev/null +++ b/Source/WebCore/platform/gtk/WidgetRenderingContext.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WidgetRenderingContext_h +#define WidgetRenderingContext_h + +#include "IntRect.h" +#include "gtkdrawing.h" + +namespace WebCore { + +class GraphicsContext; +class RenderThemeGtk; + +class WidgetRenderingContext { +public: + WidgetRenderingContext(GraphicsContext*, const IntRect&); + ~WidgetRenderingContext(); + + bool paintMozillaWidget(GtkThemeWidgetType, GtkWidgetState*, int flags, GtkTextDirection = GTK_TEXT_DIR_NONE); + void gtkPaintBox(const IntRect&, GtkWidget*, GtkStateType, GtkShadowType, const gchar*); + void gtkPaintFocus(const IntRect&, GtkWidget*, GtkStateType, const gchar*); + void gtkPaintSlider(const IntRect&, GtkWidget*, GtkStateType, GtkShadowType, const gchar*, GtkOrientation); + +private: + GraphicsContext* m_graphicsContext; + IntRect m_targetRect; + GdkRectangle m_paintRect; + IntSize m_extraSpace; + bool m_hadError; + +#ifdef GTK_API_VERSION_2 + GdkDrawable* m_target; +#else + cairo_t* m_target; +#endif + +}; + +} + +#endif diff --git a/Source/WebCore/platform/gtk/WidgetRenderingContextGtk2.cpp b/Source/WebCore/platform/gtk/WidgetRenderingContextGtk2.cpp new file mode 100644 index 0000000..e85c570 --- /dev/null +++ b/Source/WebCore/platform/gtk/WidgetRenderingContextGtk2.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2010 Sencha, Inc. + * Copyright (C) 2010 Igalia S.L. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef GTK_API_VERSION_2 + +#include "config.h" +#include "WidgetRenderingContext.h" + +#include "GraphicsContext.h" +#include "RefPtrCairo.h" +#include "RenderThemeGtk.h" +#include "Timer.h" +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +namespace WebCore { + +static GdkPixmap* gScratchBuffer = 0; +static void purgeScratchBuffer() +{ + if (gScratchBuffer) + g_object_unref(gScratchBuffer); + gScratchBuffer = 0; +} + +// FIXME: Perhaps we can share some of this code with the ContextShadowCairo. +// Widget rendering needs a scratch image as the buffer for the intermediate +// render. Instead of creating and destroying the buffer for every operation, +// we create a buffer which will be automatically purged via a timer. +class PurgeScratchBufferTimer : public TimerBase { +private: + virtual void fired() { purgeScratchBuffer(); } +}; +static PurgeScratchBufferTimer purgeScratchBufferTimer; +static void scheduleScratchBufferPurge() +{ + if (purgeScratchBufferTimer.isActive()) + purgeScratchBufferTimer.stop(); + purgeScratchBufferTimer.startOneShot(2); +} + +static IntSize getExtraSpaceForWidget(RenderThemeGtk* theme) +{ + // Checkboxes and scrollbar thumbs often draw outside their boundaries. Here we figure out + // the maximum amount of space we need for any type of widget and use that to increase the + // size of the scratch buffer, while preserving the final widget position. + + // The checkbox extra space is calculated by looking at the widget style. + int indicatorSize, indicatorSpacing; + theme->getIndicatorMetrics(CheckboxPart, indicatorSize, indicatorSpacing); + IntSize extraSpace(indicatorSpacing, indicatorSpacing); + + // Scrollbar thumbs need at least an extra pixel along their movement axis. + return extraSpace.expandedTo(IntSize(1, 1)); +} + +WidgetRenderingContext::WidgetRenderingContext(GraphicsContext* graphicsContext, const IntRect& targetRect) + : m_graphicsContext(graphicsContext) + , m_targetRect(targetRect) + , m_hadError(false) +{ + RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get()); + + // Fallback: We failed to create an RGBA colormap earlier, so we cannot properly paint + // to a temporary surface and preserve transparency. To ensure decent widget rendering, just + // paint directly to the target drawable. This will not render CSS rotational transforms properly. + if (!theme->m_themePartsHaveRGBAColormap && graphicsContext->gdkWindow()) { + m_paintRect = graphicsContext->getCTM().mapRect(targetRect); + m_target = graphicsContext->gdkWindow(); + return; + } + + // Some widgets render outside their rectangles. We need to account for this. + m_extraSpace = getExtraSpaceForWidget(theme); + + // Offset the target rectangle so that the extra space is within the boundaries of the scratch buffer. + m_paintRect = IntRect(IntPoint(m_extraSpace.width(), m_extraSpace.height()), + m_targetRect.size()); + + int width = m_targetRect.width() + (m_extraSpace.width() * 2); + int height = m_targetRect.height() + (m_extraSpace.height() * 2); + int scratchWidth = 0; + int scratchHeight = 0; + if (gScratchBuffer) + gdk_drawable_get_size(gScratchBuffer, &scratchWidth, &scratchHeight); + + // We do not need to recreate the buffer if the current buffer is large enough. + if (!gScratchBuffer || scratchWidth < width || scratchHeight < height) { + purgeScratchBuffer(); + // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests. + width = (1 + (width >> 5)) << 5; + height = (1 + (height >> 5)) << 5; + + gScratchBuffer = gdk_pixmap_new(0, width, height, gdk_colormap_get_visual(theme->m_themeParts.colormap)->depth); + gdk_drawable_set_colormap(gScratchBuffer, theme->m_themeParts.colormap); + } + m_target = gScratchBuffer; + + // Clear the scratch buffer. + RefPtr<cairo_t> scratchBufferContext = adoptRef(gdk_cairo_create(gScratchBuffer)); + cairo_set_operator(scratchBufferContext.get(), CAIRO_OPERATOR_CLEAR); + cairo_paint(scratchBufferContext.get()); +} + +WidgetRenderingContext::~WidgetRenderingContext() +{ + // We do not need to blit back to the target in the fallback case. See above. + RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get()); + if (!theme->m_themePartsHaveRGBAColormap && m_graphicsContext->gdkWindow()) + return; + + // Don't paint the results back if there was an error. + if (m_hadError) { + scheduleScratchBufferPurge(); + return; + } + + // FIXME: It's unclear if it is necessary to preserve the current source here. + cairo_t* cairoContext = m_graphicsContext->platformContext(); + RefPtr<cairo_pattern_t> previousSource(cairo_get_source(cairoContext)); + + // The blit rectangle is the original target rectangle adjusted for any extra space. + IntRect fullTargetRect(m_targetRect); + fullTargetRect.inflateX(m_extraSpace.width()); + fullTargetRect.inflateY(m_extraSpace.height()); + + gdk_cairo_set_source_pixmap(cairoContext, gScratchBuffer, fullTargetRect.x(), fullTargetRect.y()); + cairo_rectangle(cairoContext, fullTargetRect.x(), fullTargetRect.y(), fullTargetRect.width(), fullTargetRect.height()); + cairo_fill(cairoContext); + cairo_set_source(cairoContext, previousSource.get()); + scheduleScratchBufferPurge(); +} + +bool WidgetRenderingContext::paintMozillaWidget(GtkThemeWidgetType type, GtkWidgetState* state, int flags, GtkTextDirection textDirection) +{ + // Sometimes moz_gtk_widget_paint modifies the clipping rectangle, so we must use a copy. + GdkRectangle clipRect = m_paintRect; + m_hadError = moz_gtk_widget_paint(type, m_target, &clipRect, &m_paintRect, + state, flags, textDirection) != MOZ_GTK_SUCCESS; + return !m_hadError; +} + +void WidgetRenderingContext::gtkPaintBox(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail) +{ + GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() }; + gtk_paint_box(gtk_widget_get_style(widget), m_target, stateType, shadowType, &m_paintRect, + widget, detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height); +} + +void WidgetRenderingContext::gtkPaintFocus(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, const gchar* detail) +{ + GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() }; + gtk_paint_focus(gtk_widget_get_style(widget), m_target, stateType, &m_paintRect, widget, + detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height); +} + +void WidgetRenderingContext::gtkPaintSlider(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail, GtkOrientation orientation) +{ + GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() }; + gtk_paint_slider(gtk_widget_get_style(widget), m_target, stateType, shadowType, &m_paintRect, widget, + detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height, orientation); +} + +} + +#endif // GTK_API_VERSION_2 diff --git a/Source/WebCore/platform/gtk/WidgetRenderingContextGtk3.cpp b/Source/WebCore/platform/gtk/WidgetRenderingContextGtk3.cpp new file mode 100644 index 0000000..69c4af5 --- /dev/null +++ b/Source/WebCore/platform/gtk/WidgetRenderingContextGtk3.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "WidgetRenderingContext.h" + +#include "GraphicsContext.h" +#include "IntRect.h" + +#ifndef GTK_API_VERSION_2 + +namespace WebCore { + +WidgetRenderingContext::WidgetRenderingContext(GraphicsContext* context, const IntRect& targetRect) + : m_graphicsContext(context) + , m_targetRect(targetRect) + , m_paintRect(targetRect) + , m_hadError(false) + , m_target(context->platformContext()) +{ +} + +WidgetRenderingContext::~WidgetRenderingContext() +{ +} + +bool WidgetRenderingContext::paintMozillaWidget(GtkThemeWidgetType type, GtkWidgetState* state, int flags, GtkTextDirection textDirection) +{ + m_hadError = moz_gtk_widget_paint(type, m_target, &m_paintRect, state, flags, textDirection) != MOZ_GTK_SUCCESS; + return !m_hadError; +} + +void WidgetRenderingContext::gtkPaintBox(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail) +{ + GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() }; + gtk_paint_box(gtk_widget_get_style(widget), m_target, stateType, shadowType, widget, + detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height); +} + +void WidgetRenderingContext::gtkPaintFocus(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, const gchar* detail) +{ + GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() }; + gtk_paint_focus(gtk_widget_get_style(widget), m_target, stateType, widget, + detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height); +} + +void WidgetRenderingContext::gtkPaintSlider(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail, GtkOrientation orientation) +{ + GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() }; + gtk_paint_slider(gtk_widget_get_style(widget), m_target, stateType, shadowType, widget, + detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height, orientation); +} + +} + +#endif // !GTK_API_VERSION_2 diff --git a/Source/WebCore/platform/gtk/gtk2drawing.c b/Source/WebCore/platform/gtk/gtk2drawing.c new file mode 100644 index 0000000..3ebd332 --- /dev/null +++ b/Source/WebCore/platform/gtk/gtk2drawing.c @@ -0,0 +1,1372 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner <bryner@brianryner.com> (Original Author) + * Pierre Chanial <p_ch@verizon.net> + * Michael Ventnor <m.ventnor@gmail.com> + * Alp Toker <alp@nuanti.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * This file contains painting functions for each of the gtk2 widgets. + * Adapted from the gtkdrawing.c, and gtk+2.0 source. + */ + +#ifdef GTK_API_VERSION_2 + +#undef GTK_DISABLE_DEPRECATED +#undef GDK_DISABLE_DEPRECATED + +#include <gdk/gdkprivate.h> +#include "gtkdrawing.h" +#include "GtkVersioning.h" +#include <math.h> +#include <string.h> + +#define XTHICKNESS(style) (style->xthickness) +#define YTHICKNESS(style) (style->ythickness) + +static GtkThemeParts *gParts = NULL; +static style_prop_t style_prop_func; +static gboolean have_arrow_scaling; +static gboolean is_initialized; + +void +moz_gtk_use_theme_parts(GtkThemeParts* parts) +{ + gParts = parts; +} + +/* Because we have such an unconventional way of drawing widgets, signal to the GTK theme engine + that they are drawing for Mozilla instead of a conventional GTK app so they can do any specific + things they may want to do. */ +static void +moz_gtk_set_widget_name(GtkWidget* widget) +{ + gtk_widget_set_name(widget, "MozillaGtkWidget"); +} + +gint +moz_gtk_enable_style_props(style_prop_t styleGetProp) +{ + style_prop_func = styleGetProp; + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_window_widget() +{ + if (!gParts->protoWindow) { + gParts->protoWindow = gtk_window_new(GTK_WINDOW_POPUP); + + if (gParts->colormap) + gtk_widget_set_colormap(gParts->protoWindow, gParts->colormap); + + gtk_widget_realize(gParts->protoWindow); + moz_gtk_set_widget_name(gParts->protoWindow); + } + return MOZ_GTK_SUCCESS; +} + +static gint +setup_widget_prototype(GtkWidget* widget) +{ + ensure_window_widget(); + if (!gParts->protoLayout) { + gParts->protoLayout = gtk_fixed_new(); + gtk_container_add(GTK_CONTAINER(gParts->protoWindow), gParts->protoLayout); + } + + gtk_container_add(GTK_CONTAINER(gParts->protoLayout), widget); + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_button_widget() +{ + if (!gParts->buttonWidget) { + gParts->buttonWidget = gtk_button_new_with_label("M"); + setup_widget_prototype(gParts->buttonWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_toggle_button_widget() +{ + if (!gParts->toggleButtonWidget) { + gParts->toggleButtonWidget = gtk_toggle_button_new(); + setup_widget_prototype(gParts->toggleButtonWidget); + /* toggle button must be set active to get the right style on hover. */ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gParts->toggleButtonWidget), TRUE); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_button_arrow_widget() +{ + if (!gParts->buttonArrowWidget) { + ensure_toggle_button_widget(); + + gParts->buttonArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_container_add(GTK_CONTAINER(gParts->toggleButtonWidget), gParts->buttonArrowWidget); + gtk_widget_realize(gParts->buttonArrowWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_checkbox_widget() +{ + if (!gParts->checkboxWidget) { + gParts->checkboxWidget = gtk_check_button_new_with_label("M"); + setup_widget_prototype(gParts->checkboxWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_radiobutton_widget() +{ + if (!gParts->radiobuttonWidget) { + gParts->radiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M"); + setup_widget_prototype(gParts->radiobuttonWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_scrollbar_widget() +{ + if (!gParts->vertScrollbarWidget) { + gParts->vertScrollbarWidget = gtk_vscrollbar_new(NULL); + setup_widget_prototype(gParts->vertScrollbarWidget); + } + if (!gParts->horizScrollbarWidget) { + gParts->horizScrollbarWidget = gtk_hscrollbar_new(NULL); + setup_widget_prototype(gParts->horizScrollbarWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_entry_widget() +{ + if (!gParts->entryWidget) { + gParts->entryWidget = gtk_entry_new(); + setup_widget_prototype(gParts->entryWidget); + } + return MOZ_GTK_SUCCESS; +} + +/* We need to have pointers to the inner widgets (button, separator, arrow) + * of the ComboBox to get the correct rendering from theme engines which + * special cases their look. Since the inner layout can change, we ask GTK + * to NULL our pointers when they are about to become invalid because the + * corresponding widgets don't exist anymore. It's the role of + * g_object_add_weak_pointer(). + * Note that if we don't find the inner widgets (which shouldn't happen), we + * fallback to use generic "non-inner" widgets, and they don't need that kind + * of weak pointer since they are explicit children of gParts->protoWindow and as + * such GTK holds a strong reference to them. */ +static void +moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data) +{ + if (GTK_IS_TOGGLE_BUTTON(widget)) { + gParts->comboBoxButtonWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gParts->comboBoxButtonWidget); + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + } +} + +static void +moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget, + gpointer client_data) +{ + if (GTK_IS_SEPARATOR(widget)) { + gParts->comboBoxSeparatorWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gParts->comboBoxSeparatorWidget); + } else if (GTK_IS_ARROW(widget)) { + gParts->comboBoxArrowWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gParts->comboBoxArrowWidget); + } else + return; + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); +} + +static gint +ensure_combo_box_widgets() +{ + GtkWidget* buttonChild; + + if (gParts->comboBoxButtonWidget && gParts->comboBoxArrowWidget) + return MOZ_GTK_SUCCESS; + + /* Create a ComboBox if needed */ + if (!gParts->comboBoxWidget) { + gParts->comboBoxWidget = gtk_combo_box_new(); + setup_widget_prototype(gParts->comboBoxWidget); + } + + /* Get its inner Button */ + gtk_container_forall(GTK_CONTAINER(gParts->comboBoxWidget), + moz_gtk_get_combo_box_inner_button, + NULL); + + if (gParts->comboBoxButtonWidget) { + /* Get the widgets inside the Button */ + buttonChild = gtk_bin_get_child(GTK_BIN(gParts->comboBoxButtonWidget)); + if (GTK_IS_HBOX(buttonChild)) { + /* appears-as-list = FALSE, cell-view = TRUE; the button + * contains an hbox. This hbox is there because the ComboBox + * needs to place a cell renderer, a separator, and an arrow in + * the button when appears-as-list is FALSE. */ + gtk_container_forall(GTK_CONTAINER(buttonChild), + moz_gtk_get_combo_box_button_inner_widgets, + NULL); + } else if(GTK_IS_ARROW(buttonChild)) { + /* appears-as-list = TRUE, or cell-view = FALSE; + * the button only contains an arrow */ + gParts->comboBoxArrowWidget = buttonChild; + g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) + &gParts->comboBoxArrowWidget); + gtk_widget_realize(gParts->comboBoxArrowWidget); + g_object_set_data(G_OBJECT(gParts->comboBoxArrowWidget), + "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + } + } else { + /* Shouldn't be reached with current internal gtk implementation; we + * use a generic toggle button as last resort fallback to avoid + * crashing. */ + ensure_toggle_button_widget(); + gParts->comboBoxButtonWidget = gParts->toggleButtonWidget; + } + + if (!gParts->comboBoxArrowWidget) { + /* Shouldn't be reached with current internal gtk implementation; + * we gParts->buttonArrowWidget as last resort fallback to avoid + * crashing. */ + ensure_button_arrow_widget(); + gParts->comboBoxArrowWidget = gParts->buttonArrowWidget; + } + + /* We don't test the validity of gParts->comboBoxSeparatorWidget since there + * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it + * is invalid we just won't paint it. */ + + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_progress_widget() +{ + if (!gParts->progresWidget) { + gParts->progresWidget = gtk_progress_bar_new(); + setup_widget_prototype(gParts->progresWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_scrolled_window_widget() +{ + if (!gParts->scrolledWindowWidget) { + gParts->scrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL); + setup_widget_prototype(gParts->scrolledWindowWidget); + } + return MOZ_GTK_SUCCESS; +} + +static GtkStateType +ConvertGtkState(GtkWidgetState* state) +{ + if (state->disabled) + return GTK_STATE_INSENSITIVE; + else if (state->depressed) + return (state->inHover ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE); + else if (state->inHover) + return (state->active ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT); + else + return GTK_STATE_NORMAL; +} + +static gint +TSOffsetStyleGCArray(GdkGC** gcs, gint xorigin, gint yorigin) +{ + int i; + /* there are 5 gc's in each array, for each of the widget states */ + for (i = 0; i < 5; ++i) + gdk_gc_set_ts_origin(gcs[i], xorigin, yorigin); + return MOZ_GTK_SUCCESS; +} + +static gint +TSOffsetStyleGCs(GtkStyle* style, gint xorigin, gint yorigin) +{ + TSOffsetStyleGCArray(style->fg_gc, xorigin, yorigin); + TSOffsetStyleGCArray(style->bg_gc, xorigin, yorigin); + TSOffsetStyleGCArray(style->light_gc, xorigin, yorigin); + TSOffsetStyleGCArray(style->dark_gc, xorigin, yorigin); + TSOffsetStyleGCArray(style->mid_gc, xorigin, yorigin); + TSOffsetStyleGCArray(style->text_gc, xorigin, yorigin); + TSOffsetStyleGCArray(style->base_gc, xorigin, yorigin); + gdk_gc_set_ts_origin(style->black_gc, xorigin, yorigin); + gdk_gc_set_ts_origin(style->white_gc, xorigin, yorigin); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_button_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkReliefStyle relief, GtkWidget* widget, + GtkTextDirection direction) +{ + GtkShadowType shadow_type; + GtkStyle* style = gtk_widget_get_style(widget); + GtkStateType button_state = ConvertGtkState(state); + gint x = rect->x, y=rect->y, width=rect->width, height=rect->height; + + gboolean interior_focus; + gint focus_width, focus_pad; + + moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad); + + gtk_widget_set_state(widget, button_state); + gtk_widget_set_direction(widget, direction); + + if (state->isDefault) + GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_DEFAULT); + + gtk_button_set_relief(GTK_BUTTON(widget), relief); + + /* Some theme engines love to cause us pain in that gtk_paint_focus is a + no-op on buttons and button-like widgets. They only listen to this flag. */ + if (state->focused && !state->disabled) + GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); + + if (!interior_focus && state->focused) { + x += focus_width + focus_pad; + y += focus_width + focus_pad; + width -= 2 * (focus_width + focus_pad); + height -= 2 * (focus_width + focus_pad); + } + + shadow_type = button_state == GTK_STATE_ACTIVE || + state->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + + if (state->isDefault && relief == GTK_RELIEF_NORMAL) { + gtk_paint_box(style, drawable, button_state, shadow_type, cliprect, + widget, "buttondefault", x, y, width, height); + } + + if (relief != GTK_RELIEF_NONE || state->depressed || + (button_state != GTK_STATE_NORMAL && + button_state != GTK_STATE_INSENSITIVE)) { + TSOffsetStyleGCs(style, x, y); + /* the following line can trigger an assertion (Crux theme) + file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area): + assertion `GDK_IS_WINDOW (window)' failed */ + gtk_paint_box(style, drawable, button_state, shadow_type, cliprect, + widget, "button", x, y, width, height); + } + + if (state->focused) { + if (interior_focus) { + GtkStyle* style = gtk_widget_get_style(widget); + x += style->xthickness + focus_pad; + y += style->ythickness + focus_pad; + width -= 2 * (style->xthickness + focus_pad); + height -= 2 * (style->ythickness + focus_pad); + } else { + x -= focus_width + focus_pad; + y -= focus_width + focus_pad; + width += 2 * (focus_width + focus_pad); + height += 2 * (focus_width + focus_pad); + } + + TSOffsetStyleGCs(style, x, y); + gtk_paint_focus(style, drawable, button_state, cliprect, + widget, "button", x, y, width, height); + } + + GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_DEFAULT); + GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS); + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_init() +{ + GtkWidgetClass *entry_class; + + is_initialized = TRUE; + have_arrow_scaling = (gtk_major_version > 2 || + (gtk_major_version == 2 && gtk_minor_version >= 12)); + + /* Add style property to GtkEntry. + * Adding the style property to the normal GtkEntry class means that it + * will work without issues inside GtkComboBox and for Spinbuttons. */ + entry_class = g_type_class_ref(GTK_TYPE_ENTRY); + gtk_widget_class_install_style_property(entry_class, + g_param_spec_boolean("honors-transparent-bg-hint", + "Transparent BG enabling flag", + "If TRUE, the theme is able to draw the GtkEntry on non-prefilled background.", + FALSE, + G_PARAM_READWRITE)); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing) +{ + ensure_checkbox_widget(); + + gtk_widget_style_get (gParts->checkboxWidget, + "indicator_size", indicator_size, + "indicator_spacing", indicator_spacing, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing) +{ + ensure_radiobutton_widget(); + + gtk_widget_style_get (gParts->radiobuttonWidget, + "indicator_size", indicator_size, + "indicator_spacing", indicator_spacing, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus, + gint* focus_width, gint* focus_pad) +{ + gtk_widget_style_get (widget, + "interior-focus", interior_focus, + "focus-line-width", focus_width, + "focus-padding", focus_pad, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border) +{ + static const GtkBorder default_inner_border = { 1, 1, 1, 1 }; + GtkBorder *tmp_border; + + gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL); + + if (tmp_border) { + *inner_border = *tmp_border; + gtk_border_free(tmp_border); + } + else + *inner_border = default_inner_border; + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + gboolean selected, gboolean inconsistent, + gboolean isradio, GtkTextDirection direction) +{ + GtkStateType state_type = ConvertGtkState(state); + GtkShadowType shadow_type = (selected)?GTK_SHADOW_IN:GTK_SHADOW_OUT; + gint indicator_size, indicator_spacing; + gint x, y, width, height; + gint focus_x, focus_y, focus_width, focus_height; + GtkWidget *w; + GtkStyle *style; + + if (isradio) { + moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing); + w = gParts->radiobuttonWidget; + } else { + moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing); + w = gParts->checkboxWidget; + } + + // "GetMinimumWidgetSize was ignored" + // FIXME: This assert causes a build failure in WebKitGTK+ debug + // builds, because it uses 'false' in its definition. We may want + // to force this file to be built with g++, by renaming it. + // ASSERT(rect->width == indicator_size); + + /* + * vertically center in the box, since XUL sometimes ignores our + * GetMinimumWidgetSize in the vertical dimension + */ + x = rect->x; + y = rect->y + (rect->height - indicator_size) / 2; + width = indicator_size; + height = indicator_size; + + focus_x = x - indicator_spacing; + focus_y = y - indicator_spacing; + focus_width = width + 2 * indicator_spacing; + focus_height = height + 2 * indicator_spacing; + + style = gtk_widget_get_style(w); + TSOffsetStyleGCs(style, x, y); + + gtk_widget_set_sensitive(w, !state->disabled); + gtk_widget_set_direction(w, direction); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), selected); + + if (isradio) { + gtk_paint_option(style, drawable, state_type, shadow_type, cliprect, + gParts->radiobuttonWidget, "radiobutton", x, y, + width, height); + if (state->focused) { + gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect, + gParts->radiobuttonWidget, "radiobutton", focus_x, focus_y, + focus_width, focus_height); + } + } + else { + /* + * 'indeterminate' type on checkboxes. In GTK, the shadow type + * must also be changed for the state to be drawn. + */ + if (inconsistent) { + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gParts->checkboxWidget), TRUE); + shadow_type = GTK_SHADOW_ETCHED_IN; + } else { + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gParts->checkboxWidget), FALSE); + } + + gtk_paint_check(style, drawable, state_type, shadow_type, cliprect, + gParts->checkboxWidget, "checkbutton", x, y, width, height); + if (state->focused) { + gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect, + gParts->checkboxWidget, "checkbutton", focus_x, focus_y, + focus_width, focus_height); + } + } + + return MOZ_GTK_SUCCESS; +} + +static gint +calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect, + GdkRectangle* inner_rect, + GtkTextDirection direction, + gboolean ignore_focus) +{ + GtkBorder inner_border; + gboolean interior_focus; + gint focus_width, focus_pad; + GtkStyle* style; + + style = gtk_widget_get_style(button); + + /* This mirrors gtkbutton's child positioning */ + moz_gtk_button_get_inner_border(button, &inner_border); + moz_gtk_widget_get_focus(button, &interior_focus, + &focus_width, &focus_pad); + + if (ignore_focus) + focus_width = focus_pad = 0; + + inner_rect->x = rect->x + XTHICKNESS(style) + focus_width + focus_pad; + inner_rect->x += direction == GTK_TEXT_DIR_LTR ? + inner_border.left : inner_border.right; + inner_rect->y = rect->y + inner_border.top + YTHICKNESS(style) + + focus_width + focus_pad; + inner_rect->width = MAX(1, rect->width - inner_border.left - + inner_border.right - (XTHICKNESS(style) + focus_pad + focus_width) * 2); + inner_rect->height = MAX(1, rect->height - inner_border.top - + inner_border.bottom - (YTHICKNESS(style) + focus_pad + focus_width) * 2); + + return MOZ_GTK_SUCCESS; +} + + +static gint +calculate_arrow_rect(GtkWidget* arrow, GdkRectangle* rect, + GdkRectangle* arrow_rect, GtkTextDirection direction) +{ + /* defined in gtkarrow.c */ + gfloat arrow_scaling = 0.7; + gfloat xalign, xpad; + gint extent; + GtkMisc* misc = GTK_MISC(arrow); + gfloat misc_xalign, misc_yalign; + gint misc_xpad, misc_ypad; + + if (have_arrow_scaling) + gtk_widget_style_get(arrow, "arrow_scaling", &arrow_scaling, NULL); + + gtk_misc_get_padding(misc, &misc_xpad, &misc_ypad); + gtk_misc_get_alignment(misc, &misc_xalign, &misc_yalign); + + extent = MIN((rect->width - misc_xpad * 2), + (rect->height - misc_ypad * 2)) * arrow_scaling; + + xalign = direction == GTK_TEXT_DIR_LTR ? misc_xalign : 1.0 - misc_xalign; + xpad = misc_xpad + (rect->width - extent) * xalign; + + arrow_rect->x = direction == GTK_TEXT_DIR_LTR ? + floor(rect->x + xpad) : ceil(rect->x + xpad); + arrow_rect->y = floor(rect->y + misc_ypad + + ((rect->height - extent) * misc_yalign)); + + arrow_rect->width = arrow_rect->height = extent; + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrolled_window_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state) +{ + GtkStyle* style; + GtkAllocation allocation; + GtkWidget* widget; + + ensure_scrolled_window_widget(); + widget = gParts->scrolledWindowWidget; + + gtk_widget_get_allocation(widget, &allocation); + allocation.x = rect->x; + allocation.y = rect->y; + allocation.width = rect->width; + allocation.height = rect->height; + gtk_widget_set_allocation(widget, &allocation); + + style = gtk_widget_get_style(widget); + TSOffsetStyleGCs(style, rect->x - 1, rect->y - 1); + gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, + cliprect, gParts->scrolledWindowWidget, "scrolled_window", + rect->x, rect->y, rect->width, rect->height); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrollbar_button_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkScrollbarButtonFlags flags, + GtkTextDirection direction) +{ + GtkStateType state_type = ConvertGtkState(state); + GtkShadowType shadow_type = (state->active) ? + GTK_SHADOW_IN : GTK_SHADOW_OUT; + GdkRectangle arrow_rect; + GtkStyle* style; + GtkWidget *scrollbar; + GtkAllocation allocation; + GtkArrowType arrow_type; + gint arrow_displacement_x, arrow_displacement_y; + const char* detail = (flags & MOZ_GTK_STEPPER_VERTICAL) ? + "vscrollbar" : "hscrollbar"; + + ensure_scrollbar_widget(); + + if (flags & MOZ_GTK_STEPPER_VERTICAL) + scrollbar = gParts->vertScrollbarWidget; + else + scrollbar = gParts->horizScrollbarWidget; + + gtk_widget_set_direction(scrollbar, direction); + + /* Some theme engines (i.e., ClearLooks) check the scrollbar's allocation + to determine where it should paint rounded corners on the buttons. + We need to trick them into drawing the buttons the way we want them. */ + + gtk_widget_get_allocation(scrollbar, &allocation); + allocation.x = rect->x; + allocation.y = rect->y; + allocation.width = rect->width; + allocation.height = rect->height; + + if (flags & MOZ_GTK_STEPPER_VERTICAL) { + allocation.height *= 5; + if (flags & MOZ_GTK_STEPPER_DOWN) { + arrow_type = GTK_ARROW_DOWN; + if (flags & MOZ_GTK_STEPPER_BOTTOM) + allocation.y -= 4 * rect->height; + else + allocation.y -= rect->height; + + } else { + arrow_type = GTK_ARROW_UP; + if (flags & MOZ_GTK_STEPPER_BOTTOM) + allocation.y -= 3 * rect->height; + } + } else { + allocation.width *= 5; + if (flags & MOZ_GTK_STEPPER_DOWN) { + arrow_type = GTK_ARROW_RIGHT; + if (flags & MOZ_GTK_STEPPER_BOTTOM) + allocation.x -= 4 * rect->width; + else + allocation.x -= rect->width; + } else { + arrow_type = GTK_ARROW_LEFT; + if (flags & MOZ_GTK_STEPPER_BOTTOM) + allocation.x -= 3 * rect->width; + } + } + + gtk_widget_set_allocation(scrollbar, &allocation); + style = gtk_widget_get_style(scrollbar); + + TSOffsetStyleGCs(style, rect->x, rect->y); + + gtk_paint_box(style, drawable, state_type, shadow_type, cliprect, + scrollbar, detail, rect->x, rect->y, + rect->width, rect->height); + + arrow_rect.width = rect->width / 2; + arrow_rect.height = rect->height / 2; + arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; + arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; + + if (state_type == GTK_STATE_ACTIVE) { + gtk_widget_style_get(scrollbar, + "arrow-displacement-x", &arrow_displacement_x, + "arrow-displacement-y", &arrow_displacement_y, + NULL); + + arrow_rect.x += arrow_displacement_x; + arrow_rect.y += arrow_displacement_y; + } + + gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect, + scrollbar, detail, arrow_type, TRUE, arrow_rect.x, + arrow_rect.y, arrow_rect.width, arrow_rect.height); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrollbar_trough_paint(GtkThemeWidgetType widget, + GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkTextDirection direction) +{ + GtkStyle* style; + GtkScrollbar *scrollbar; + + ensure_scrollbar_widget(); + + if (widget == MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL) + scrollbar = GTK_SCROLLBAR(gParts->horizScrollbarWidget); + else + scrollbar = GTK_SCROLLBAR(gParts->vertScrollbarWidget); + + gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction); + + style = gtk_widget_get_style(GTK_WIDGET(scrollbar)); + + TSOffsetStyleGCs(style, rect->x, rect->y); + gtk_paint_box(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_IN, cliprect, + GTK_WIDGET(scrollbar), "trough", rect->x, rect->y, + rect->width, rect->height); + + if (state->focused) { + gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect, + GTK_WIDGET(scrollbar), "trough", + rect->x, rect->y, rect->width, rect->height); + } + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, + GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkTextDirection direction) +{ + GtkStateType state_type = (state->inHover || state->active) ? + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; + GtkShadowType shadow_type = GTK_SHADOW_OUT; + GtkStyle* style; + GtkScrollbar *scrollbar; + GtkAdjustment *adj; + gboolean activate_slider; + + ensure_scrollbar_widget(); + + if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) + scrollbar = GTK_SCROLLBAR(gParts->horizScrollbarWidget); + else + scrollbar = GTK_SCROLLBAR(gParts->vertScrollbarWidget); + + gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction); + + /* Make sure to set the scrollbar range before painting so that + everything is drawn properly. At least the bluecurve (and + maybe other) themes don't draw the top or bottom black line + surrounding the scrollbar if the theme thinks that it's butted + up against the scrollbar arrows. Note the increases of the + clip rect below. */ + /* Changing the cliprect is pretty bogus. This lets themes draw + outside the frame, which means we don't invalidate them + correctly. See bug 297508. But some themes do seem to need + it. So we modify the frame's overflow area to account for what + we're doing here; see nsNativeThemeGTK::GetWidgetOverflow. */ + adj = gtk_range_get_adjustment(GTK_RANGE(scrollbar)); + + if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) { + cliprect->x -= 1; + cliprect->width += 2; + gtk_adjustment_set_page_size(adj, rect->width); + } + else { + cliprect->y -= 1; + cliprect->height += 2; + gtk_adjustment_set_page_size(adj, rect->height); + } + +#if GTK_CHECK_VERSION(2, 14, 0) + gtk_adjustment_configure(adj, + state->curpos, + 0, + state->maxpos, + gtk_adjustment_get_step_increment(adj), + gtk_adjustment_get_page_increment(adj), + gtk_adjustment_get_page_size(adj)); +#else + adj->lower = 0; + adj->value = state->curpos; + adj->upper = state->maxpos; + gtk_adjustment_changed(adj); +#endif + + style = gtk_widget_get_style(GTK_WIDGET(scrollbar)); + + gtk_widget_style_get(GTK_WIDGET(scrollbar), "activate-slider", + &activate_slider, NULL); + + if (activate_slider && state->active) { + shadow_type = GTK_SHADOW_IN; + state_type = GTK_STATE_ACTIVE; + } + + TSOffsetStyleGCs(style, rect->x, rect->y); + + gtk_paint_slider(style, drawable, state_type, shadow_type, cliprect, + GTK_WIDGET(scrollbar), "slider", rect->x, rect->y, + rect->width, rect->height, + (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ? + GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkWidget* widget, GtkTextDirection direction) +{ + GtkStateType bg_state = state->disabled ? + GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL; + gint x, y, width = rect->width, height = rect->height; + GtkStyle* style; + gboolean interior_focus; + gboolean theme_honors_transparency = FALSE; + gint focus_width; + + gtk_widget_set_direction(widget, direction); + + style = gtk_widget_get_style(widget); + + gtk_widget_style_get(widget, + "interior-focus", &interior_focus, + "focus-line-width", &focus_width, + "honors-transparent-bg-hint", &theme_honors_transparency, + NULL); + + /* gtkentry.c uses two windows, one for the entire widget and one for the + * text area inside it. The background of both windows is set to the "base" + * color of the new state in gtk_entry_state_changed, but only the inner + * textarea window uses gtk_paint_flat_box when exposed */ + + TSOffsetStyleGCs(style, rect->x, rect->y); + + /* This gets us a lovely greyish disabledish look */ + gtk_widget_set_sensitive(widget, !state->disabled); + + /* GTK fills the outer widget window with the base color before drawing the widget. + * Some older themes rely on this behavior, but many themes nowadays use rounded + * corners on their widgets. While most GTK apps are blissfully unaware of this + * problem due to their use of the default window background, we render widgets on + * many kinds of backgrounds on the web. + * If the theme is able to cope with transparency, then we can skip pre-filling + * and notify the theme it will paint directly on the canvas. */ + if (theme_honors_transparency) { + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + } else { + gdk_draw_rectangle(drawable, style->base_gc[bg_state], TRUE, + cliprect->x, cliprect->y, cliprect->width, cliprect->height); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(FALSE)); + } + + /* Get the position of the inner window, see _gtk_entry_get_borders */ + x = XTHICKNESS(style); + y = YTHICKNESS(style); + + if (!interior_focus) { + x += focus_width; + y += focus_width; + } + + /* Simulate an expose of the inner window */ + gtk_paint_flat_box(style, drawable, bg_state, GTK_SHADOW_NONE, + cliprect, widget, "entry_bg", rect->x + x, + rect->y + y, rect->width - 2*x, rect->height - 2*y); + + /* Now paint the shadow and focus border. + * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad + * smaller when focused if the focus is not interior, then the focus. */ + x = rect->x; + y = rect->y; + + if (state->focused && !state->disabled) { + /* This will get us the lit borders that focused textboxes enjoy on + * some themes. */ + GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); + + if (!interior_focus) { + /* Indent the border a little bit if we have exterior focus + (this is what GTK does to draw native entries) */ + x += focus_width; + y += focus_width; + width -= 2 * focus_width; + height -= 2 * focus_width; + } + } + + gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, + cliprect, widget, "entry", x, y, width, height); + + if (state->focused && !state->disabled) { + if (!interior_focus) { + gtk_paint_focus(style, drawable, GTK_STATE_NORMAL, cliprect, + widget, "entry", + rect->x, rect->y, rect->width, rect->height); + } + + /* Now unset the focus flag. We don't want other entries to look + * like they're focused too! */ + GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS); + } + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_combo_box_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + gboolean ishtml, GtkTextDirection direction) +{ + GdkRectangle arrow_rect, real_arrow_rect; + gint /* arrow_size, */ separator_width; + gboolean wide_separators; + GtkStateType state_type = ConvertGtkState(state); + GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + GtkStyle* style; + GtkRequisition arrow_req; + + ensure_combo_box_widgets(); + + /* Also sets the direction on gParts->comboBoxButtonWidget, which is then + * inherited by the separator and arrow */ + moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL, + gParts->comboBoxButtonWidget, direction); + + calculate_button_inner_rect(gParts->comboBoxButtonWidget, + rect, &arrow_rect, direction, ishtml); + /* Now arrow_rect contains the inner rect ; we want to correct the width + * to what the arrow needs (see gtk_combo_box_size_allocate) */ + gtk_widget_size_request(gParts->comboBoxArrowWidget, &arrow_req); + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x += arrow_rect.width - arrow_req.width; + arrow_rect.width = arrow_req.width; + + calculate_arrow_rect(gParts->comboBoxArrowWidget, + &arrow_rect, &real_arrow_rect, direction); + + style = gtk_widget_get_style(gParts->comboBoxArrowWidget); + TSOffsetStyleGCs(style, rect->x, rect->y); + + gtk_widget_size_allocate(gParts->comboBoxWidget, rect); + + gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect, + gParts->comboBoxArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, + real_arrow_rect.x, real_arrow_rect.y, + real_arrow_rect.width, real_arrow_rect.height); + + + /* If there is no separator in the theme, there's nothing left to do. */ + if (!gParts->comboBoxSeparatorWidget) + return MOZ_GTK_SUCCESS; + + style = gtk_widget_get_style(gParts->comboBoxSeparatorWidget); + TSOffsetStyleGCs(style, rect->x, rect->y); + + gtk_widget_style_get(gParts->comboBoxSeparatorWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (wide_separators) { + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x -= separator_width; + else + arrow_rect.x += arrow_rect.width; + + gtk_paint_box(style, drawable, + GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT, + cliprect, gParts->comboBoxSeparatorWidget, "vseparator", + arrow_rect.x, arrow_rect.y, + separator_width, arrow_rect.height); + } else { + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x -= XTHICKNESS(style); + else + arrow_rect.x += arrow_rect.width; + + gtk_paint_vline(style, drawable, GTK_STATE_NORMAL, cliprect, + gParts->comboBoxSeparatorWidget, "vseparator", + arrow_rect.y, arrow_rect.y + arrow_rect.height, + arrow_rect.x); + } + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_progressbar_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyle* style; + + ensure_progress_widget(); + gtk_widget_set_direction(gParts->progresWidget, direction); + + style = gtk_widget_get_style(gParts->progresWidget); + + TSOffsetStyleGCs(style, rect->x, rect->y); + gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, + cliprect, gParts->progresWidget, "trough", rect->x, rect->y, + rect->width, rect->height); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_progress_chunk_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyle* style; + + ensure_progress_widget(); + gtk_widget_set_direction(gParts->progresWidget, direction); + + style = gtk_widget_get_style(gParts->progresWidget); + + TSOffsetStyleGCs(style, rect->x, rect->y); + gtk_paint_box(style, drawable, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, + cliprect, gParts->progresWidget, "bar", rect->x, rect->y, + rect->width, rect->height); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, + gint* right, gint* bottom, GtkTextDirection direction, + gboolean inhtml) +{ + GtkWidget* w; + GtkStyle *style; + + switch (widget) { + case MOZ_GTK_BUTTON: + { + GtkBorder inner_border; + gboolean interior_focus; + gint focus_width, focus_pad; + GtkStyle *style; + + ensure_button_widget(); + *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gParts->buttonWidget)); + + /* Don't add this padding in HTML, otherwise the buttons will + become too big and stuff the layout. */ + if (!inhtml) { + moz_gtk_widget_get_focus(gParts->buttonWidget, &interior_focus, &focus_width, &focus_pad); + moz_gtk_button_get_inner_border(gParts->buttonWidget, &inner_border); + *left += focus_width + focus_pad + inner_border.left; + *right += focus_width + focus_pad + inner_border.right; + *top += focus_width + focus_pad + inner_border.top; + *bottom += focus_width + focus_pad + inner_border.bottom; + } + + style = gtk_widget_get_style(gParts->buttonWidget); + *left += style->xthickness; + *right += style->xthickness; + *top += style->ythickness; + *bottom += style->ythickness; + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_ENTRY: + ensure_entry_widget(); + w = gParts->entryWidget; + break; + case MOZ_GTK_DROPDOWN: + { + /* We need to account for the arrow on the dropdown, so text + * doesn't come too close to the arrow, or in some cases spill + * into the arrow. */ + gboolean ignored_interior_focus, wide_separators; + gint focus_width, focus_pad, separator_width; + GtkRequisition arrow_req; + GtkStyle* style; + + ensure_combo_box_widgets(); + + *left = gtk_container_get_border_width(GTK_CONTAINER(gParts->comboBoxButtonWidget)); + + if (!inhtml) { + moz_gtk_widget_get_focus(gParts->comboBoxButtonWidget, + &ignored_interior_focus, + &focus_width, &focus_pad); + *left += focus_width + focus_pad; + } + + style = gtk_widget_get_style(gParts->comboBoxButtonWidget); + *top = *left + style->ythickness; + *left += style->xthickness; + + *right = *left; *bottom = *top; + + /* If there is no separator, don't try to count its width. */ + separator_width = 0; + if (gParts->comboBoxSeparatorWidget) { + gtk_widget_style_get(gParts->comboBoxSeparatorWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (!wide_separators) + separator_width = + XTHICKNESS(style); + } + + gtk_widget_size_request(gParts->comboBoxArrowWidget, &arrow_req); + if (direction == GTK_TEXT_DIR_RTL) + *left += separator_width + arrow_req.width; + else + *right += separator_width + arrow_req.width; + + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_PROGRESSBAR: + ensure_progress_widget(); + w = gParts->progresWidget; + break; + /* These widgets have no borders, since they are not containers. */ + case MOZ_GTK_CHECKBUTTON: + case MOZ_GTK_RADIOBUTTON: + case MOZ_GTK_SCROLLBAR_BUTTON: + case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: + case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: + case MOZ_GTK_PROGRESS_CHUNK: + *left = *top = *right = *bottom = 0; + return MOZ_GTK_SUCCESS; + default: + g_warning("Unsupported widget type: %d", widget); + return MOZ_GTK_UNKNOWN_WIDGET; + } + + style = gtk_widget_get_style(w); + *right = *left = XTHICKNESS(style); + *bottom = *top = YTHICKNESS(style); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics) +{ + ensure_scrollbar_widget(); + + gtk_widget_style_get (gParts->horizScrollbarWidget, + "slider_width", &metrics->slider_width, + "trough_border", &metrics->trough_border, + "stepper_size", &metrics->stepper_size, + "stepper_spacing", &metrics->stepper_spacing, + "trough_under_steppers", &metrics->trough_under_steppers, + "has_secondary_forward_stepper", &metrics->has_secondary_forward_stepper, + "has_secondary_backward_stepper", &metrics->has_secondary_backward_stepper, + NULL); + + metrics->min_slider_size = gtk_range_get_min_slider_size(GTK_RANGE(gParts->horizScrollbarWidget)); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, + GdkRectangle* rect, GdkRectangle* cliprect, + GtkWidgetState* state, gint flags, + GtkTextDirection direction) +{ + switch (widget) { + case MOZ_GTK_BUTTON: + if (state->depressed) { + ensure_toggle_button_widget(); + return moz_gtk_button_paint(drawable, rect, cliprect, state, + (GtkReliefStyle) flags, + gParts->toggleButtonWidget, direction); + } + ensure_button_widget(); + return moz_gtk_button_paint(drawable, rect, cliprect, state, + (GtkReliefStyle) flags, gParts->buttonWidget, + direction); + break; + case MOZ_GTK_CHECKBUTTON: + case MOZ_GTK_RADIOBUTTON: + return moz_gtk_toggle_paint(drawable, rect, cliprect, state, + !!(flags & MOZ_GTK_WIDGET_CHECKED), + !!(flags & MOZ_GTK_WIDGET_INCONSISTENT), + (widget == MOZ_GTK_RADIOBUTTON), + direction); + break; + case MOZ_GTK_SCROLLBAR_BUTTON: + return moz_gtk_scrollbar_button_paint(drawable, rect, cliprect, state, + (GtkScrollbarButtonFlags) flags, + direction); + break; + case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: + return moz_gtk_scrollbar_trough_paint(widget, drawable, rect, + cliprect, state, direction); + break; + case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: + return moz_gtk_scrollbar_thumb_paint(widget, drawable, rect, + cliprect, state, direction); + break; + case MOZ_GTK_SCROLLED_WINDOW: + return moz_gtk_scrolled_window_paint(drawable, rect, cliprect, state); + break; + case MOZ_GTK_ENTRY: + ensure_entry_widget(); + return moz_gtk_entry_paint(drawable, rect, cliprect, state, + gParts->entryWidget, direction); + break; + case MOZ_GTK_DROPDOWN: + return moz_gtk_combo_box_paint(drawable, rect, cliprect, state, + (gboolean) flags, direction); + break; + case MOZ_GTK_PROGRESSBAR: + return moz_gtk_progressbar_paint(drawable, rect, cliprect, direction); + break; + case MOZ_GTK_PROGRESS_CHUNK: + return moz_gtk_progress_chunk_paint(drawable, rect, cliprect, + direction); + break; + default: + g_warning("Unknown widget type: %d", widget); + } + + return MOZ_GTK_UNKNOWN_WIDGET; +} + +GtkWidget* moz_gtk_get_scrollbar_widget(void) +{ + if (!is_initialized) + return NULL; + ensure_scrollbar_widget(); + return gParts->horizScrollbarWidget; +} + +gint +moz_gtk_shutdown() +{ + GtkWidgetClass *entry_class; + entry_class = g_type_class_peek(GTK_TYPE_ENTRY); + g_type_class_unref(entry_class); + + is_initialized = FALSE; + + return MOZ_GTK_SUCCESS; +} + +void moz_gtk_destroy_theme_parts_widgets(GtkThemeParts* parts) +{ + if (!parts) + return; + + if (parts->protoWindow) { + gtk_widget_destroy(parts->protoWindow); + parts->protoWindow = NULL; + } +} + +GtkWidget* moz_gtk_get_progress_widget() +{ + if (!is_initialized) + return NULL; + ensure_progress_widget(); + return gParts->progresWidget; +} + +#endif // GTK_API_VERSION_2 diff --git a/Source/WebCore/platform/gtk/gtk3drawing.c b/Source/WebCore/platform/gtk/gtk3drawing.c new file mode 100644 index 0000000..d3bdd56 --- /dev/null +++ b/Source/WebCore/platform/gtk/gtk3drawing.c @@ -0,0 +1,1288 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner <bryner@brianryner.com> (Original Author) + * Pierre Chanial <p_ch@verizon.net> + * Michael Ventnor <m.ventnor@gmail.com> + * Alp Toker <alp@nuanti.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * This file contains painting functions for each of the gtk2 widgets. + * Adapted from the gtkdrawing.c, and gtk+2.0 source. + */ + +#ifndef GTK_API_VERSION_2 + +#include <gdk/gdkprivate.h> +#include "gtkdrawing.h" +#include "GtkVersioning.h" +#include <math.h> +#include <string.h> + +#define XTHICKNESS(style) (style->xthickness) +#define YTHICKNESS(style) (style->ythickness) + +static GtkThemeParts *gParts = NULL; +static style_prop_t style_prop_func; +static gboolean have_arrow_scaling; +static gboolean is_initialized; + +void +moz_gtk_use_theme_parts(GtkThemeParts* parts) +{ + gParts = parts; +} + +/* Because we have such an unconventional way of drawing widgets, signal to the GTK theme engine + that they are drawing for Mozilla instead of a conventional GTK app so they can do any specific + things they may want to do. */ +static void +moz_gtk_set_widget_name(GtkWidget* widget) +{ + gtk_widget_set_name(widget, "MozillaGtkWidget"); +} + +gint +moz_gtk_enable_style_props(style_prop_t styleGetProp) +{ + style_prop_func = styleGetProp; + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_window_widget() +{ + if (!gParts->protoWindow) { + gParts->protoWindow = gtk_window_new(GTK_WINDOW_POPUP); + gtk_widget_realize(gParts->protoWindow); + moz_gtk_set_widget_name(gParts->protoWindow); + } + return MOZ_GTK_SUCCESS; +} + +static gint +setup_widget_prototype(GtkWidget* widget) +{ + ensure_window_widget(); + if (!gParts->protoLayout) { + gParts->protoLayout = gtk_fixed_new(); + gtk_container_add(GTK_CONTAINER(gParts->protoWindow), gParts->protoLayout); + } + + gtk_container_add(GTK_CONTAINER(gParts->protoLayout), widget); + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_button_widget() +{ + if (!gParts->buttonWidget) { + gParts->buttonWidget = gtk_button_new_with_label("M"); + setup_widget_prototype(gParts->buttonWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_toggle_button_widget() +{ + if (!gParts->toggleButtonWidget) { + gParts->toggleButtonWidget = gtk_toggle_button_new(); + setup_widget_prototype(gParts->toggleButtonWidget); + /* toggle button must be set active to get the right style on hover. */ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gParts->toggleButtonWidget), TRUE); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_button_arrow_widget() +{ + if (!gParts->buttonArrowWidget) { + ensure_toggle_button_widget(); + + gParts->buttonArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_container_add(GTK_CONTAINER(gParts->toggleButtonWidget), gParts->buttonArrowWidget); + gtk_widget_realize(gParts->buttonArrowWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_checkbox_widget() +{ + if (!gParts->checkboxWidget) { + gParts->checkboxWidget = gtk_check_button_new_with_label("M"); + setup_widget_prototype(gParts->checkboxWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_radiobutton_widget() +{ + if (!gParts->radiobuttonWidget) { + gParts->radiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M"); + setup_widget_prototype(gParts->radiobuttonWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_scrollbar_widget() +{ + if (!gParts->vertScrollbarWidget) { + gParts->vertScrollbarWidget = gtk_vscrollbar_new(NULL); + setup_widget_prototype(gParts->vertScrollbarWidget); + } + if (!gParts->horizScrollbarWidget) { + gParts->horizScrollbarWidget = gtk_hscrollbar_new(NULL); + setup_widget_prototype(gParts->horizScrollbarWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_entry_widget() +{ + if (!gParts->entryWidget) { + gParts->entryWidget = gtk_entry_new(); + setup_widget_prototype(gParts->entryWidget); + } + return MOZ_GTK_SUCCESS; +} + +/* We need to have pointers to the inner widgets (button, separator, arrow) + * of the ComboBox to get the correct rendering from theme engines which + * special cases their look. Since the inner layout can change, we ask GTK + * to NULL our pointers when they are about to become invalid because the + * corresponding widgets don't exist anymore. It's the role of + * g_object_add_weak_pointer(). + * Note that if we don't find the inner widgets (which shouldn't happen), we + * fallback to use generic "non-inner" widgets, and they don't need that kind + * of weak pointer since they are explicit children of gParts->protoWindow and as + * such GTK holds a strong reference to them. */ +static void +moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data) +{ + if (GTK_IS_TOGGLE_BUTTON(widget)) { + gParts->comboBoxButtonWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gParts->comboBoxButtonWidget); + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + } +} + +static void +moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget, + gpointer client_data) +{ + if (GTK_IS_SEPARATOR(widget)) { + gParts->comboBoxSeparatorWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gParts->comboBoxSeparatorWidget); + } else if (GTK_IS_ARROW(widget)) { + gParts->comboBoxArrowWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gParts->comboBoxArrowWidget); + } else + return; + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); +} + +static gint +ensure_combo_box_widgets() +{ + GtkWidget* buttonChild; + + if (gParts->comboBoxButtonWidget && gParts->comboBoxArrowWidget) + return MOZ_GTK_SUCCESS; + + /* Create a ComboBox if needed */ + if (!gParts->comboBoxWidget) { + gParts->comboBoxWidget = gtk_combo_box_new(); + setup_widget_prototype(gParts->comboBoxWidget); + } + + /* Get its inner Button */ + gtk_container_forall(GTK_CONTAINER(gParts->comboBoxWidget), + moz_gtk_get_combo_box_inner_button, + NULL); + + if (gParts->comboBoxButtonWidget) { + /* Get the widgets inside the Button */ + buttonChild = gtk_bin_get_child(GTK_BIN(gParts->comboBoxButtonWidget)); + if (GTK_IS_HBOX(buttonChild)) { + /* appears-as-list = FALSE, cell-view = TRUE; the button + * contains an hbox. This hbox is there because the ComboBox + * needs to place a cell renderer, a separator, and an arrow in + * the button when appears-as-list is FALSE. */ + gtk_container_forall(GTK_CONTAINER(buttonChild), + moz_gtk_get_combo_box_button_inner_widgets, + NULL); + } else if(GTK_IS_ARROW(buttonChild)) { + /* appears-as-list = TRUE, or cell-view = FALSE; + * the button only contains an arrow */ + gParts->comboBoxArrowWidget = buttonChild; + g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) + &gParts->comboBoxArrowWidget); + gtk_widget_realize(gParts->comboBoxArrowWidget); + g_object_set_data(G_OBJECT(gParts->comboBoxArrowWidget), + "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + } + } else { + /* Shouldn't be reached with current internal gtk implementation; we + * use a generic toggle button as last resort fallback to avoid + * crashing. */ + ensure_toggle_button_widget(); + gParts->comboBoxButtonWidget = gParts->toggleButtonWidget; + } + + if (!gParts->comboBoxArrowWidget) { + /* Shouldn't be reached with current internal gtk implementation; + * we gParts->buttonArrowWidget as last resort fallback to avoid + * crashing. */ + ensure_button_arrow_widget(); + gParts->comboBoxArrowWidget = gParts->buttonArrowWidget; + } + + /* We don't test the validity of gParts->comboBoxSeparatorWidget since there + * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it + * is invalid we just won't paint it. */ + + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_progress_widget() +{ + if (!gParts->progresWidget) { + gParts->progresWidget = gtk_progress_bar_new(); + setup_widget_prototype(gParts->progresWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_scrolled_window_widget() +{ + if (!gParts->scrolledWindowWidget) { + gParts->scrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL); + setup_widget_prototype(gParts->scrolledWindowWidget); + } + return MOZ_GTK_SUCCESS; +} + +static GtkStateType +ConvertGtkState(GtkWidgetState* state) +{ + if (state->disabled) + return GTK_STATE_INSENSITIVE; + else if (state->depressed) + return (state->inHover ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE); + else if (state->inHover) + return (state->active ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT); + else + return GTK_STATE_NORMAL; +} + +static gint +moz_gtk_button_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, GtkReliefStyle relief, + GtkWidget* widget, GtkTextDirection direction) +{ + GtkShadowType shadow_type; + GtkStyle* style = gtk_widget_get_style(widget); + GtkStateType button_state = ConvertGtkState(state); + gint x = rect->x, y=rect->y, width=rect->width, height=rect->height; + GdkWindow* window = gtk_widget_get_window(widget); + + gboolean interior_focus; + gint focus_width, focus_pad; + + moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad); + + if (window && gdk_window_is_visible(window)) { + gdk_window_set_background_pattern(window, NULL); + } + + gtk_widget_set_state(widget, button_state); + gtk_widget_set_direction(widget, direction); + gtk_button_set_relief(GTK_BUTTON(widget), relief); + + if (!interior_focus && state->focused) { + x += focus_width + focus_pad; + y += focus_width + focus_pad; + width -= 2 * (focus_width + focus_pad); + height -= 2 * (focus_width + focus_pad); + } + + shadow_type = button_state == GTK_STATE_ACTIVE || + state->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + + if (state->isDefault && relief == GTK_RELIEF_NORMAL) { + gtk_paint_box(style, cr, button_state, shadow_type, + widget, "buttondefault", x, y, width, height); + } + + if (relief != GTK_RELIEF_NONE || state->depressed || + (button_state != GTK_STATE_NORMAL && + button_state != GTK_STATE_INSENSITIVE)) { + /* the following line can trigger an assertion (Crux theme) + file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area): + assertion `GDK_IS_WINDOW (window)' failed */ + gtk_paint_box(style, cr, button_state, shadow_type, + widget, "button", x, y, width, height); + } + + if (state->focused) { + if (interior_focus) { + GtkStyle* style = gtk_widget_get_style(widget); + x += style->xthickness + focus_pad; + y += style->ythickness + focus_pad; + width -= 2 * (style->xthickness + focus_pad); + height -= 2 * (style->ythickness + focus_pad); + } else { + x -= focus_width + focus_pad; + y -= focus_width + focus_pad; + width += 2 * (focus_width + focus_pad); + height += 2 * (focus_width + focus_pad); + } + + gtk_paint_focus(style, cr, button_state, + widget, "button", x, y, width, height); + } + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_init() +{ + GtkWidgetClass *entry_class; + + is_initialized = TRUE; + have_arrow_scaling = (gtk_major_version > 2 || + (gtk_major_version == 2 && gtk_minor_version >= 12)); + + /* Add style property to GtkEntry. + * Adding the style property to the normal GtkEntry class means that it + * will work without issues inside GtkComboBox and for Spinbuttons. */ + entry_class = g_type_class_ref(GTK_TYPE_ENTRY); + gtk_widget_class_install_style_property(entry_class, + g_param_spec_boolean("honors-transparent-bg-hint", + "Transparent BG enabling flag", + "If TRUE, the theme is able to draw the GtkEntry on non-prefilled background.", + FALSE, + G_PARAM_READWRITE)); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing) +{ + ensure_checkbox_widget(); + + gtk_widget_style_get (gParts->checkboxWidget, + "indicator_size", indicator_size, + "indicator_spacing", indicator_spacing, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing) +{ + ensure_radiobutton_widget(); + + gtk_widget_style_get (gParts->radiobuttonWidget, + "indicator_size", indicator_size, + "indicator_spacing", indicator_spacing, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus, + gint* focus_width, gint* focus_pad) +{ + gtk_widget_style_get (widget, + "interior-focus", interior_focus, + "focus-line-width", focus_width, + "focus-padding", focus_pad, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border) +{ + static const GtkBorder default_inner_border = { 1, 1, 1, 1 }; + GtkBorder *tmp_border; + + gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL); + + if (tmp_border) { + *inner_border = *tmp_border; + gtk_border_free(tmp_border); + } + else + *inner_border = default_inner_border; + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_toggle_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, gboolean selected, + gboolean inconsistent, gboolean isradio, + GtkTextDirection direction) +{ + GtkStateType state_type = ConvertGtkState(state); + GtkShadowType shadow_type = (selected)?GTK_SHADOW_IN:GTK_SHADOW_OUT; + gint indicator_size, indicator_spacing; + gint x, y, width, height; + gint focus_x, focus_y, focus_width, focus_height; + GtkWidget *w; + GtkStyle *style; + + if (isradio) { + moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing); + w = gParts->radiobuttonWidget; + } else { + moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing); + w = gParts->checkboxWidget; + } + + // "GetMinimumWidgetSize was ignored" + // FIXME: This assert causes a build failure in WebKitGTK+ debug + // builds, because it uses 'false' in its definition. We may want + // to force this file to be built with g++, by renaming it. + // ASSERT(rect->width == indicator_size); + + /* + * vertically center in the box, since XUL sometimes ignores our + * GetMinimumWidgetSize in the vertical dimension + */ + x = rect->x; + y = rect->y + (rect->height - indicator_size) / 2; + width = indicator_size; + height = indicator_size; + + focus_x = x - indicator_spacing; + focus_y = y - indicator_spacing; + focus_width = width + 2 * indicator_spacing; + focus_height = height + 2 * indicator_spacing; + + style = gtk_widget_get_style(w); + + gtk_widget_set_sensitive(w, !state->disabled); + gtk_widget_set_direction(w, direction); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), selected); + + if (isradio) { + gtk_paint_option(style, cr, state_type, shadow_type, + gParts->radiobuttonWidget, "radiobutton", x, y, + width, height); + if (state->focused) { + gtk_paint_focus(style, cr, GTK_STATE_ACTIVE, + gParts->radiobuttonWidget, "radiobutton", focus_x, focus_y, + focus_width, focus_height); + } + } + else { + /* + * 'indeterminate' type on checkboxes. In GTK, the shadow type + * must also be changed for the state to be drawn. + */ + if (inconsistent) { + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gParts->checkboxWidget), TRUE); + shadow_type = GTK_SHADOW_ETCHED_IN; + } else { + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gParts->checkboxWidget), FALSE); + } + + gtk_paint_check(style, cr, state_type, shadow_type, + gParts->checkboxWidget, "checkbutton", x, y, width, height); + if (state->focused) { + gtk_paint_focus(style, cr, GTK_STATE_ACTIVE, + gParts->checkboxWidget, "checkbutton", focus_x, focus_y, + focus_width, focus_height); + } + } + + return MOZ_GTK_SUCCESS; +} + +static gint +calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect, + GdkRectangle* inner_rect, + GtkTextDirection direction, + gboolean ignore_focus) +{ + GtkBorder inner_border; + gboolean interior_focus; + gint focus_width, focus_pad; + GtkStyle* style; + + style = gtk_widget_get_style(button); + + /* This mirrors gtkbutton's child positioning */ + moz_gtk_button_get_inner_border(button, &inner_border); + moz_gtk_widget_get_focus(button, &interior_focus, + &focus_width, &focus_pad); + + if (ignore_focus) + focus_width = focus_pad = 0; + + inner_rect->x = rect->x + XTHICKNESS(style) + focus_width + focus_pad; + inner_rect->x += direction == GTK_TEXT_DIR_LTR ? + inner_border.left : inner_border.right; + inner_rect->y = rect->y + inner_border.top + YTHICKNESS(style) + + focus_width + focus_pad; + inner_rect->width = MAX(1, rect->width - inner_border.left - + inner_border.right - (XTHICKNESS(style) + focus_pad + focus_width) * 2); + inner_rect->height = MAX(1, rect->height - inner_border.top - + inner_border.bottom - (YTHICKNESS(style) + focus_pad + focus_width) * 2); + + return MOZ_GTK_SUCCESS; +} + + +static gint +calculate_arrow_rect(GtkWidget* arrow, GdkRectangle* rect, + GdkRectangle* arrow_rect, GtkTextDirection direction) +{ + /* defined in gtkarrow.c */ + gfloat arrow_scaling = 0.7; + gfloat xalign, xpad; + gint extent; + GtkMisc* misc = GTK_MISC(arrow); + gfloat misc_xalign, misc_yalign; + gint misc_xpad, misc_ypad; + + if (have_arrow_scaling) + gtk_widget_style_get(arrow, "arrow_scaling", &arrow_scaling, NULL); + + gtk_misc_get_padding(misc, &misc_xpad, &misc_ypad); + gtk_misc_get_alignment(misc, &misc_xalign, &misc_yalign); + + extent = MIN((rect->width - misc_xpad * 2), + (rect->height - misc_ypad * 2)) * arrow_scaling; + + xalign = direction == GTK_TEXT_DIR_LTR ? misc_xalign : 1.0 - misc_xalign; + xpad = misc_xpad + (rect->width - extent) * xalign; + + arrow_rect->x = direction == GTK_TEXT_DIR_LTR ? + floor(rect->x + xpad) : ceil(rect->x + xpad); + arrow_rect->y = floor(rect->y + misc_ypad + + ((rect->height - extent) * misc_yalign)); + + arrow_rect->width = arrow_rect->height = extent; + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrolled_window_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state) +{ + GtkStyle* style; + GtkAllocation allocation; + GtkWidget* widget; + + ensure_scrolled_window_widget(); + widget = gParts->scrolledWindowWidget; + + gtk_widget_get_allocation(widget, &allocation); + allocation.x = rect->x; + allocation.y = rect->y; + allocation.width = rect->width; + allocation.height = rect->height; + gtk_widget_set_allocation(widget, &allocation); + + style = gtk_widget_get_style(widget); + gtk_paint_shadow(style, cr, GTK_STATE_NORMAL, GTK_SHADOW_IN, + widget, "scrolled_window", rect->x , rect->y, + rect->width, rect->height); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrollbar_button_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, + GtkScrollbarButtonFlags flags, + GtkTextDirection direction) +{ + GtkStateType state_type = ConvertGtkState(state); + GtkShadowType shadow_type = (state->active) ? + GTK_SHADOW_IN : GTK_SHADOW_OUT; + GdkRectangle arrow_rect; + GtkStyle* style; + GtkWidget *scrollbar; + GtkAllocation allocation; + GtkArrowType arrow_type; + gint arrow_displacement_x, arrow_displacement_y; + const char* detail = (flags & MOZ_GTK_STEPPER_VERTICAL) ? + "vscrollbar" : "hscrollbar"; + + ensure_scrollbar_widget(); + + if (flags & MOZ_GTK_STEPPER_VERTICAL) + scrollbar = gParts->vertScrollbarWidget; + else + scrollbar = gParts->horizScrollbarWidget; + + gtk_widget_set_direction(scrollbar, direction); + + /* Some theme engines (i.e., ClearLooks) check the scrollbar's allocation + to determine where it should paint rounded corners on the buttons. + We need to trick them into drawing the buttons the way we want them. */ + + gtk_widget_get_allocation(scrollbar, &allocation); + allocation.x = rect->x; + allocation.y = rect->y; + allocation.width = rect->width; + allocation.height = rect->height; + + if (flags & MOZ_GTK_STEPPER_VERTICAL) { + allocation.height *= 5; + if (flags & MOZ_GTK_STEPPER_DOWN) { + arrow_type = GTK_ARROW_DOWN; + if (flags & MOZ_GTK_STEPPER_BOTTOM) + allocation.y -= 4 * rect->height; + else + allocation.y -= rect->height; + + } else { + arrow_type = GTK_ARROW_UP; + if (flags & MOZ_GTK_STEPPER_BOTTOM) + allocation.y -= 3 * rect->height; + } + } else { + allocation.width *= 5; + if (flags & MOZ_GTK_STEPPER_DOWN) { + arrow_type = GTK_ARROW_RIGHT; + if (flags & MOZ_GTK_STEPPER_BOTTOM) + allocation.x -= 4 * rect->width; + else + allocation.x -= rect->width; + } else { + arrow_type = GTK_ARROW_LEFT; + if (flags & MOZ_GTK_STEPPER_BOTTOM) + allocation.x -= 3 * rect->width; + } + } + + gtk_widget_set_allocation(scrollbar, &allocation); + style = gtk_widget_get_style(scrollbar); + + gtk_paint_box(style, cr, state_type, shadow_type, + scrollbar, detail, rect->x, rect->y, + rect->width, rect->height); + + arrow_rect.width = rect->width / 2; + arrow_rect.height = rect->height / 2; + arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; + arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; + + if (state_type == GTK_STATE_ACTIVE) { + gtk_widget_style_get(scrollbar, + "arrow-displacement-x", &arrow_displacement_x, + "arrow-displacement-y", &arrow_displacement_y, + NULL); + + arrow_rect.x += arrow_displacement_x; + arrow_rect.y += arrow_displacement_y; + } + + gtk_paint_arrow(style, cr, state_type, shadow_type, + scrollbar, detail, arrow_type, TRUE, arrow_rect.x, + arrow_rect.y, arrow_rect.width, arrow_rect.height); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrollbar_trough_paint(GtkThemeWidgetType widget, + cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, + GtkTextDirection direction) +{ + GtkStyle* style; + GtkScrollbar *scrollbar; + + ensure_scrollbar_widget(); + + if (widget == MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL) + scrollbar = GTK_SCROLLBAR(gParts->horizScrollbarWidget); + else + scrollbar = GTK_SCROLLBAR(gParts->vertScrollbarWidget); + + gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction); + + style = gtk_widget_get_style(GTK_WIDGET(scrollbar)); + + gtk_paint_box(style, cr, GTK_STATE_ACTIVE, GTK_SHADOW_IN, + GTK_WIDGET(scrollbar), "trough", rect->x, rect->y, + rect->width, rect->height); + + if (state->focused) { + gtk_paint_focus(style, cr, GTK_STATE_ACTIVE, + GTK_WIDGET(scrollbar), "trough", + rect->x, rect->y, rect->width, rect->height); + } + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, + cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, + GtkTextDirection direction) +{ + GtkStateType state_type = (state->inHover || state->active) ? + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; + GtkShadowType shadow_type = GTK_SHADOW_OUT; + GtkStyle* style; + GtkScrollbar *scrollbar; + GtkAdjustment *adj; + + ensure_scrollbar_widget(); + + if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) + scrollbar = GTK_SCROLLBAR(gParts->horizScrollbarWidget); + else + scrollbar = GTK_SCROLLBAR(gParts->vertScrollbarWidget); + + gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction); + + /* Make sure to set the scrollbar range before painting so that + everything is drawn properly. At least the bluecurve (and + maybe other) themes don't draw the top or bottom black line + surrounding the scrollbar if the theme thinks that it's butted + up against the scrollbar arrows. Note the increases of the + clip rect below. */ + adj = gtk_range_get_adjustment(GTK_RANGE(scrollbar)); + + if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) { + gtk_adjustment_set_page_size(adj, rect->width); + } + else { + gtk_adjustment_set_page_size(adj, rect->height); + } + + gtk_adjustment_configure(adj, + state->curpos, + 0, + state->maxpos, + gtk_adjustment_get_step_increment(adj), + gtk_adjustment_get_page_increment(adj), + gtk_adjustment_get_page_size(adj)); + + style = gtk_widget_get_style(GTK_WIDGET(scrollbar)); + + gtk_paint_slider(style, cr, state_type, shadow_type, + GTK_WIDGET(scrollbar), "slider", rect->x, rect->y, + rect->width, rect->height, + (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ? + GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_entry_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, GtkWidget* widget, + GtkTextDirection direction) +{ + GtkStateType bg_state = state->disabled ? + GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL; + gint x, y, width = rect->width, height = rect->height; + GtkStyle* style; + gboolean interior_focus; + gboolean theme_honors_transparency = FALSE; + gint focus_width; + + gtk_widget_set_direction(widget, direction); + + style = gtk_widget_get_style(widget); + + gtk_widget_style_get(widget, + "interior-focus", &interior_focus, + "focus-line-width", &focus_width, + "honors-transparent-bg-hint", &theme_honors_transparency, + NULL); + + /* gtkentry.c uses two windows, one for the entire widget and one for the + * text area inside it. The background of both windows is set to the "base" + * color of the new state in gtk_entry_state_changed, but only the inner + * textarea window uses gtk_paint_flat_box when exposed */ + + /* This gets us a lovely greyish disabledish look */ + gtk_widget_set_sensitive(widget, !state->disabled); + + /* GTK fills the outer widget window with the base color before drawing the widget. + * Some older themes rely on this behavior, but many themes nowadays use rounded + * corners on their widgets. While most GTK apps are blissfully unaware of this + * problem due to their use of the default window background, we render widgets on + * many kinds of backgrounds on the web. + * If the theme is able to cope with transparency, then we can skip pre-filling + * and notify the theme it will paint directly on the canvas. */ + if (theme_honors_transparency) { + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + } else { + cairo_save(cr); + gdk_cairo_set_source_color(cr, (const GdkColor*)&style->base[bg_state]); + cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); + cairo_fill(cr); + cairo_restore(cr); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(FALSE)); + } + + /* Get the position of the inner window, see _gtk_entry_get_borders */ + x = XTHICKNESS(style); + y = YTHICKNESS(style); + + if (!interior_focus) { + x += focus_width; + y += focus_width; + } + + /* Simulate an expose of the inner window */ + gtk_paint_flat_box(style, cr, bg_state, GTK_SHADOW_NONE, + widget, "entry_bg", rect->x + x, + rect->y + y, rect->width - 2*x, rect->height - 2*y); + + /* Now paint the shadow and focus border. + * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad + * smaller when focused if the focus is not interior, then the focus. */ + x = rect->x; + y = rect->y; + + if (state->focused && !state->disabled) { + /* This will get us the lit borders that focused textboxes enjoy on + * some themes. */ + if (!interior_focus) { + /* Indent the border a little bit if we have exterior focus + (this is what GTK does to draw native entries) */ + x += focus_width; + y += focus_width; + width -= 2 * focus_width; + height -= 2 * focus_width; + } + } + + gtk_paint_shadow(style, cr, GTK_STATE_NORMAL, GTK_SHADOW_IN, + widget, "entry", x, y, width, height); + + if (state->focused && !state->disabled) { + if (!interior_focus) { + gtk_paint_focus(style, cr, GTK_STATE_NORMAL, + widget, "entry", + rect->x, rect->y, rect->width, rect->height); + } + } + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_combo_box_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, gboolean ishtml, + GtkTextDirection direction) +{ + GdkRectangle arrow_rect, real_arrow_rect; + gint /* arrow_size, */ separator_width; + gboolean wide_separators; + GtkStateType state_type = ConvertGtkState(state); + GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + GtkStyle* style; + GtkRequisition arrow_req; + + ensure_combo_box_widgets(); + + /* Also sets the direction on gParts->comboBoxButtonWidget, which is then + * inherited by the separator and arrow */ + moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, + gParts->comboBoxButtonWidget, direction); + + calculate_button_inner_rect(gParts->comboBoxButtonWidget, + rect, &arrow_rect, direction, ishtml); + /* Now arrow_rect contains the inner rect ; we want to correct the width + * to what the arrow needs (see gtk_combo_box_size_allocate) */ + gtk_widget_get_preferred_size(gParts->comboBoxArrowWidget, &arrow_req, NULL); + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x += arrow_rect.width - arrow_req.width; + arrow_rect.width = arrow_req.width; + + calculate_arrow_rect(gParts->comboBoxArrowWidget, + &arrow_rect, &real_arrow_rect, direction); + + style = gtk_widget_get_style(gParts->comboBoxArrowWidget); + + gtk_widget_size_allocate(gParts->comboBoxWidget, rect); + + gtk_paint_arrow(style, cr, state_type, shadow_type, + gParts->comboBoxArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, + real_arrow_rect.x, real_arrow_rect.y, + real_arrow_rect.width, real_arrow_rect.height); + + + /* If there is no separator in the theme, there's nothing left to do. */ + if (!gParts->comboBoxSeparatorWidget) + return MOZ_GTK_SUCCESS; + + style = gtk_widget_get_style(gParts->comboBoxSeparatorWidget); + + gtk_widget_style_get(gParts->comboBoxSeparatorWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (wide_separators) { + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x -= separator_width; + else + arrow_rect.x += arrow_rect.width; + + gtk_paint_box(style, cr, + GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT, + gParts->comboBoxSeparatorWidget, "vseparator", + arrow_rect.x, arrow_rect.y, + separator_width, arrow_rect.height); + } else { + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x -= XTHICKNESS(style); + else + arrow_rect.x += arrow_rect.width; + + gtk_paint_vline(style, cr, GTK_STATE_NORMAL, + gParts->comboBoxSeparatorWidget, "vseparator", + arrow_rect.y, arrow_rect.y + arrow_rect.height, + arrow_rect.x); + } + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_progressbar_paint(cairo_t* cr, GdkRectangle* rect, + GtkTextDirection direction) +{ + GtkStyle* style; + + ensure_progress_widget(); + gtk_widget_set_direction(gParts->progresWidget, direction); + + style = gtk_widget_get_style(gParts->progresWidget); + + gtk_paint_box(style, cr, GTK_STATE_NORMAL, GTK_SHADOW_IN, + gParts->progresWidget, "trough", rect->x, rect->y, + rect->width, rect->height); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_progress_chunk_paint(cairo_t* cr, GdkRectangle* rect, + GtkTextDirection direction) +{ + GtkStyle* style; + + ensure_progress_widget(); + gtk_widget_set_direction(gParts->progresWidget, direction); + + style = gtk_widget_get_style(gParts->progresWidget); + + gtk_paint_box(style, cr, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, + gParts->progresWidget, "bar", rect->x, rect->y, + rect->width, rect->height); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, + gint* right, gint* bottom, GtkTextDirection direction, + gboolean inhtml) +{ + GtkWidget* w; + GtkStyle *style; + + switch (widget) { + case MOZ_GTK_BUTTON: + { + GtkBorder inner_border; + gboolean interior_focus; + gint focus_width, focus_pad; + GtkStyle *style; + + ensure_button_widget(); + *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gParts->buttonWidget)); + + /* Don't add this padding in HTML, otherwise the buttons will + become too big and stuff the layout. */ + if (!inhtml) { + moz_gtk_widget_get_focus(gParts->buttonWidget, &interior_focus, &focus_width, &focus_pad); + moz_gtk_button_get_inner_border(gParts->buttonWidget, &inner_border); + *left += focus_width + focus_pad + inner_border.left; + *right += focus_width + focus_pad + inner_border.right; + *top += focus_width + focus_pad + inner_border.top; + *bottom += focus_width + focus_pad + inner_border.bottom; + } + + style = gtk_widget_get_style(gParts->buttonWidget); + *left += style->xthickness; + *right += style->xthickness; + *top += style->ythickness; + *bottom += style->ythickness; + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_ENTRY: + ensure_entry_widget(); + w = gParts->entryWidget; + break; + case MOZ_GTK_DROPDOWN: + { + /* We need to account for the arrow on the dropdown, so text + * doesn't come too close to the arrow, or in some cases spill + * into the arrow. */ + gboolean ignored_interior_focus, wide_separators; + gint focus_width, focus_pad, separator_width; + GtkRequisition arrow_req; + GtkStyle* style; + + ensure_combo_box_widgets(); + + *left = gtk_container_get_border_width(GTK_CONTAINER(gParts->comboBoxButtonWidget)); + + if (!inhtml) { + moz_gtk_widget_get_focus(gParts->comboBoxButtonWidget, + &ignored_interior_focus, + &focus_width, &focus_pad); + *left += focus_width + focus_pad; + } + + style = gtk_widget_get_style(gParts->comboBoxButtonWidget); + *top = *left + style->ythickness; + *left += style->xthickness; + + *right = *left; *bottom = *top; + + /* If there is no separator, don't try to count its width. */ + separator_width = 0; + if (gParts->comboBoxSeparatorWidget) { + gtk_widget_style_get(gParts->comboBoxSeparatorWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (!wide_separators) + separator_width = + XTHICKNESS(style); + } + + gtk_widget_get_preferred_size(gParts->comboBoxArrowWidget, &arrow_req, NULL); + if (direction == GTK_TEXT_DIR_RTL) + *left += separator_width + arrow_req.width; + else + *right += separator_width + arrow_req.width; + + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_PROGRESSBAR: + ensure_progress_widget(); + w = gParts->progresWidget; + break; + /* These widgets have no borders, since they are not containers. */ + case MOZ_GTK_CHECKBUTTON: + case MOZ_GTK_RADIOBUTTON: + case MOZ_GTK_SCROLLBAR_BUTTON: + case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: + case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: + case MOZ_GTK_PROGRESS_CHUNK: + *left = *top = *right = *bottom = 0; + return MOZ_GTK_SUCCESS; + default: + g_warning("Unsupported widget type: %d", widget); + return MOZ_GTK_UNKNOWN_WIDGET; + } + + style = gtk_widget_get_style(w); + *right = *left = XTHICKNESS(style); + *bottom = *top = YTHICKNESS(style); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics) +{ + ensure_scrollbar_widget(); + + gtk_widget_style_get (gParts->horizScrollbarWidget, + "slider_width", &metrics->slider_width, + "trough_border", &metrics->trough_border, + "stepper_size", &metrics->stepper_size, + "stepper_spacing", &metrics->stepper_spacing, + "trough_under_steppers", &metrics->trough_under_steppers, + "has_secondary_forward_stepper", &metrics->has_secondary_forward_stepper, + "has_secondary_backward_stepper", &metrics->has_secondary_backward_stepper, + NULL); + + metrics->min_slider_size = gtk_range_get_min_slider_size(GTK_RANGE(gParts->horizScrollbarWidget)); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_widget_paint(GtkThemeWidgetType widget, cairo_t* cr, + GdkRectangle* rect, GtkWidgetState* state, + gint flags, GtkTextDirection direction) +{ + switch (widget) { + case MOZ_GTK_BUTTON: + if (state->depressed) { + ensure_toggle_button_widget(); + return moz_gtk_button_paint(cr, rect, state, + (GtkReliefStyle) flags, + gParts->toggleButtonWidget, direction); + } + ensure_button_widget(); + return moz_gtk_button_paint(cr, rect, state, + (GtkReliefStyle) flags, gParts->buttonWidget, + direction); + break; + case MOZ_GTK_CHECKBUTTON: + case MOZ_GTK_RADIOBUTTON: + return moz_gtk_toggle_paint(cr, rect, state, + !!(flags & MOZ_GTK_WIDGET_CHECKED), + !!(flags & MOZ_GTK_WIDGET_INCONSISTENT), + (widget == MOZ_GTK_RADIOBUTTON), + direction); + break; + case MOZ_GTK_SCROLLBAR_BUTTON: + return moz_gtk_scrollbar_button_paint(cr, rect, state, + (GtkScrollbarButtonFlags) flags, + direction); + break; + case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: + return moz_gtk_scrollbar_trough_paint(widget, cr, rect, + state, direction); + break; + case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: + return moz_gtk_scrollbar_thumb_paint(widget, cr, rect, + state, direction); + break; + case MOZ_GTK_SCROLLED_WINDOW: + return moz_gtk_scrolled_window_paint(cr, rect, state); + break; + case MOZ_GTK_ENTRY: + ensure_entry_widget(); + return moz_gtk_entry_paint(cr, rect, state, + gParts->entryWidget, direction); + break; + case MOZ_GTK_DROPDOWN: + return moz_gtk_combo_box_paint(cr, rect, state, + (gboolean) flags, direction); + break; + case MOZ_GTK_PROGRESSBAR: + return moz_gtk_progressbar_paint(cr, rect, direction); + break; + case MOZ_GTK_PROGRESS_CHUNK: + return moz_gtk_progress_chunk_paint(cr, rect, direction); + break; + default: + g_warning("Unknown widget type: %d", widget); + } + + return MOZ_GTK_UNKNOWN_WIDGET; +} + +GtkWidget* moz_gtk_get_scrollbar_widget(void) +{ + if (!is_initialized) + return NULL; + ensure_scrollbar_widget(); + return gParts->horizScrollbarWidget; +} + +gint +moz_gtk_shutdown() +{ + GtkWidgetClass *entry_class; + entry_class = g_type_class_peek(GTK_TYPE_ENTRY); + g_type_class_unref(entry_class); + + is_initialized = FALSE; + + return MOZ_GTK_SUCCESS; +} + +void moz_gtk_destroy_theme_parts_widgets(GtkThemeParts* parts) +{ + if (!parts) + return; + + if (parts->protoWindow) { + gtk_widget_destroy(parts->protoWindow); + parts->protoWindow = NULL; + } +} + +GtkWidget* moz_gtk_get_progress_widget() +{ + if (!is_initialized) + return NULL; + ensure_progress_widget(); + return gParts->progresWidget; +} + +#endif // GTK_API_VERSION_2 diff --git a/Source/WebCore/platform/gtk/gtkdrawing.h b/Source/WebCore/platform/gtk/gtkdrawing.h new file mode 100644 index 0000000..9d13a07 --- /dev/null +++ b/Source/WebCore/platform/gtk/gtkdrawing.h @@ -0,0 +1,296 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner <bryner@brianryner.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * gtkdrawing.h: GTK widget rendering utilities + * + * gtkdrawing provides an API for rendering GTK widgets in the + * current theme to a pixmap or window, without requiring an actual + * widget instantiation, similar to the Macintosh Appearance Manager + * or Windows XP's DrawThemeBackground() API. + */ + +#ifndef _GTK_DRAWING_H_ +#define _GTK_DRAWING_H_ + +#include <gtk/gtk.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*** type definitions ***/ +typedef struct { + guint8 active; + guint8 focused; + guint8 inHover; + guint8 disabled; + guint8 isDefault; + guint8 canDefault; + /* The depressed state is for buttons which remain active for a longer period: + * activated toggle buttons or buttons showing a popup menu. */ + guint8 depressed; + gint32 curpos; /* curpos and maxpos are used for scrollbars */ + gint32 maxpos; +} GtkWidgetState; + +typedef struct { + gint slider_width; + gint trough_border; + gint stepper_size; + gint stepper_spacing; + gint min_slider_size; + gboolean trough_under_steppers; + gboolean has_secondary_forward_stepper; + gboolean has_secondary_backward_stepper; +} MozGtkScrollbarMetrics; + +typedef struct _GtkThemeParts { +#ifdef GTK_API_VERSION_2 + GdkColormap* colormap; +#endif // GTK_API_VERSION_2 + GtkWidget* protoWindow; + GtkWidget* protoLayout; + GtkWidget* buttonWidget; + GtkWidget* toggleButtonWidget; + GtkWidget* buttonArrowWidget; + GtkWidget* checkboxWidget; + GtkWidget* radiobuttonWidget; + GtkWidget* horizScrollbarWidget; + GtkWidget* vertScrollbarWidget; + GtkWidget* entryWidget; + GtkWidget* comboBoxWidget; + GtkWidget* comboBoxButtonWidget; + GtkWidget* comboBoxArrowWidget; + GtkWidget* comboBoxSeparatorWidget; + GtkWidget* comboBoxEntryWidget; + GtkWidget* comboBoxEntryTextareaWidget; + GtkWidget* comboBoxEntryButtonWidget; + GtkWidget* comboBoxEntryArrowWidget; + GtkWidget* progresWidget; + GtkWidget* scrolledWindowWidget; +} GtkThemeParts; + +typedef enum { + MOZ_GTK_STEPPER_DOWN = 1 << 0, + MOZ_GTK_STEPPER_BOTTOM = 1 << 1, + MOZ_GTK_STEPPER_VERTICAL = 1 << 2 +} GtkScrollbarButtonFlags; + +/* function type for moz_gtk_enable_style_props */ +typedef gint (*style_prop_t)(GtkStyle*, const gchar*, gint); + +/*** result/error codes ***/ +#define MOZ_GTK_SUCCESS 0 +#define MOZ_GTK_UNKNOWN_WIDGET -1 +#define MOZ_GTK_UNSAFE_THEME -2 + +/*** checkbox/radio flags ***/ +#define MOZ_GTK_WIDGET_CHECKED 1 +#define MOZ_GTK_WIDGET_INCONSISTENT (1 << 1) + +/*** widget type constants ***/ +typedef enum { + /* Paints a GtkButton. flags is a GtkReliefStyle. */ + MOZ_GTK_BUTTON, + /* Paints a GtkCheckButton. flags is a boolean, 1=checked, 0=not checked. */ + MOZ_GTK_CHECKBUTTON, + /* Paints a GtkRadioButton. flags is a boolean, 1=checked, 0=not checked. */ + MOZ_GTK_RADIOBUTTON, + /** + * Paints the button of a GtkScrollbar. flags is a GtkArrowType giving + * the arrow direction. + */ + MOZ_GTK_SCROLLBAR_BUTTON, + /* Paints the trough (track) of a GtkScrollbar. */ + MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL, + MOZ_GTK_SCROLLBAR_TRACK_VERTICAL, + /* Paints the slider (thumb) of a GtkScrollbar. */ + MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL, + MOZ_GTK_SCROLLBAR_THUMB_VERTICAL, + /* Paints the background of a scrolled window */ + MOZ_GTK_SCROLLED_WINDOW, + MOZ_GTK_ENTRY, + /* Paints a GtkOptionMenu. */ + MOZ_GTK_DROPDOWN, + /* Paints a GtkProgressBar. */ + MOZ_GTK_PROGRESSBAR, + /* Paints a progress chunk of a GtkProgressBar. */ + MOZ_GTK_PROGRESS_CHUNK +} GtkThemeWidgetType; + +/*** General library functions ***/ +/** + * Initializes the drawing library. You must call this function + * prior to using any other functionality. + * returns: MOZ_GTK_SUCCESS if there were no errors + * MOZ_GTK_UNSAFE_THEME if the current theme engine is known + * to crash with gtkdrawing. + */ +gint moz_gtk_init(); + +/** + * Instruct the drawing library to do all rendering based on + * the given collection of theme parts. If any members of the + * GtkThemeParts struct are NULL, they will be created lazily. + */ +void +moz_gtk_use_theme_parts(GtkThemeParts* parts); + +/** + * Enable GTK+ 1.2.9+ theme enhancements. You must provide a pointer + * to the GTK+ 1.2.9+ function "gtk_style_get_prop_experimental". + * styleGetProp: pointer to gtk_style_get_prop_experimental + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ +gint moz_gtk_enable_style_props(style_prop_t styleGetProp); + +/** + * Perform cleanup of the drawing library. You should call this function + * when your program exits, or you no longer need the library. + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ +gint moz_gtk_shutdown(); + +/** + * Destroy the widgets in the given GtkThemeParts, which should + * be destroyed before the GtkThemeParts can be freed. + */ +void moz_gtk_destroy_theme_parts_widgets(GtkThemeParts* parts); + +/*** Widget drawing ***/ +/** + * Paint a widget in the current theme. + * widget: a constant giving the widget to paint + * rect: the bounding rectangle for the widget + * cliprect: a clipprect rectangle for this painting operation + * state: the state of the widget. ignored for some widgets. + * flags: widget-dependant flags; see the GtkThemeWidgetType definition. + * direction: the text direction, to draw the widget correctly LTR and RTL. + */ +#ifdef GTK_API_VERSION_2 +gint +moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, + GdkRectangle* rect, GdkRectangle* cliprect, + GtkWidgetState* state, gint flags, + GtkTextDirection direction); +#else +gint +moz_gtk_widget_paint(GtkThemeWidgetType widget, cairo_t* cr, + GdkRectangle* rect, GtkWidgetState* state, + gint flags, GtkTextDirection direction); +#endif + +/*** Widget metrics ***/ +/** + * Get the border size of a widget + * left/right: [OUT] the widget's left/right border + * top/bottom: [OUT] the widget's top/bottom border + * direction: the text direction for the widget + * inhtml: boolean indicating whether this widget will be drawn as a HTML form control, + * in order to workaround a size issue (MOZ_GTK_BUTTON only, ignored otherwise) + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ +gint moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, + gint* right, gint* bottom, GtkTextDirection direction, + gboolean inhtml); + +/** + * Get the desired size of a GtkCheckButton + * indicator_size: [OUT] the indicator size + * indicator_spacing: [OUT] the spacing between the indicator and its + * container + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ +gint +moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing); + +/** + * Get the desired size of a GtkRadioButton + * indicator_size: [OUT] the indicator size + * indicator_spacing: [OUT] the spacing between the indicator and its + * container + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ +gint +moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing); + +/** Get the focus metrics for a treeheadercell, button, checkbox, or radio button. + * widget: [IN] the widget to get the focus metrics for + * interior_focus: [OUT] whether the focus is drawn around the + * label (TRUE) or around the whole container (FALSE) + * focus_width: [OUT] the width of the focus line + * focus_pad: [OUT] the padding between the focus line and children + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ +gint +moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus, + gint* focus_width, gint* focus_pad); + +/** + * Get the desired metrics for a GtkScrollbar + * metrics: [IN] struct which will contain the metrics + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ +gint +moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics* metrics); + +/** + * Retrieve an actual GTK scrollbar widget for style analysis. It will not + * be modified. + */ +GtkWidget* moz_gtk_get_scrollbar_widget(void); + +/** + * Retrieve an actual GTK progress bar widget for style analysis. It will not + * be modified. + */ +GtkWidget* moz_gtk_get_progress_widget(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif |