diff options
Diffstat (limited to 'WebCore/platform/gtk')
34 files changed, 2046 insertions, 1695 deletions
diff --git a/WebCore/platform/gtk/ClipboardGtk.cpp b/WebCore/platform/gtk/ClipboardGtk.cpp index 12e3a01..44aa7f7 100644 --- a/WebCore/platform/gtk/ClipboardGtk.cpp +++ b/WebCore/platform/gtk/ClipboardGtk.cpp @@ -23,9 +23,10 @@ #include "Editor.h" namespace WebCore { + PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy) { - return new ClipboardGtk(policy, false); + return ClipboardGtk::create(policy, false); } ClipboardGtk::ClipboardGtk(ClipboardAccessPolicy policy, bool forDragging) diff --git a/WebCore/platform/gtk/ClipboardGtk.h b/WebCore/platform/gtk/ClipboardGtk.h index 3ad1049..b8b4ddf 100644 --- a/WebCore/platform/gtk/ClipboardGtk.h +++ b/WebCore/platform/gtk/ClipboardGtk.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * 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 @@ -36,9 +36,11 @@ namespace WebCore { // Created from the EventHandlerGtk to be used by the dom class ClipboardGtk : public Clipboard { public: - ClipboardGtk(ClipboardAccessPolicy, bool); - ~ClipboardGtk(); - + static PassRefPtr<ClipboardGtk> create(ClipboardAccessPolicy policy, bool isForDragging) + { + return adoptRef(new ClipboardGtk(policy, isForDragging)); + } + virtual ~ClipboardGtk(); void clearData(const String&); void clearAllData(); @@ -58,6 +60,9 @@ namespace WebCore { virtual void writeRange(Range*, Frame*); virtual bool hasData(); + + private: + ClipboardGtk(ClipboardAccessPolicy, bool); }; } diff --git a/WebCore/platform/gtk/CursorGtk.cpp b/WebCore/platform/gtk/CursorGtk.cpp index 94c6975..76f6d00 100644 --- a/WebCore/platform/gtk/CursorGtk.cpp +++ b/WebCore/platform/gtk/CursorGtk.cpp @@ -28,7 +28,6 @@ #include "config.h" #include "CursorGtk.h" -#include "DeprecatedString.h" #include "NotImplemented.h" #include <wtf/Assertions.h> @@ -214,6 +213,52 @@ const Cursor& rowResizeCursor() static Cursor c = gdk_cursor_new(GDK_DOUBLE_ARROW); return c; } + +const Cursor& middlePanningCursor() +{ + return moveCursor(); +} + +const Cursor& eastPanningCursor() +{ + return eastResizeCursor(); +} + +const Cursor& northPanningCursor() +{ + return northResizeCursor(); +} + +const Cursor& northEastPanningCursor() +{ + return northEastResizeCursor(); +} + +const Cursor& northWestPanningCursor() +{ + return northWestResizeCursor(); +} + +const Cursor& southPanningCursor() +{ + return southResizeCursor(); +} + +const Cursor& southEastPanningCursor() +{ + return southEastResizeCursor(); +} + +const Cursor& southWestPanningCursor() +{ + return southWestResizeCursor(); +} + +const Cursor& westPanningCursor() +{ + return westResizeCursor(); +} + const Cursor& verticalTextCursor() { @@ -223,7 +268,7 @@ const Cursor& verticalTextCursor() const Cursor& cellCursor() { - // TODO: Find a suitable cursor + notImplemented(); return pointerCursor(); } @@ -235,7 +280,7 @@ const Cursor& contextMenuCursor() const Cursor& noDropCursor() { - // TODO: Find a suitable cursor + notImplemented(); return pointerCursor(); } @@ -247,7 +292,7 @@ const Cursor& copyCursor() const Cursor& progressCursor() { - // TODO: Find a suitable cursor + notImplemented(); return pointerCursor(); } @@ -259,13 +304,13 @@ const Cursor& aliasCursor() const Cursor& noneCursor() { - // TODO: Find a suitable cursor + notImplemented(); return pointerCursor(); } const Cursor& notAllowedCursor() { - // TODO: Find a suitable cursor + notImplemented(); return pointerCursor(); } @@ -281,4 +326,16 @@ const Cursor& zoomOutCursor() return c; } +const Cursor& grabCursor() +{ + notImplemented(); + return pointerCursor(); +} + +const Cursor& grabbingCursor() +{ + notImplemented(); + return pointerCursor(); +} + } diff --git a/WebCore/platform/gtk/DragDataGtk.cpp b/WebCore/platform/gtk/DragDataGtk.cpp index e20a1bf..69b3161 100644 --- a/WebCore/platform/gtk/DragDataGtk.cpp +++ b/WebCore/platform/gtk/DragDataGtk.cpp @@ -17,6 +17,7 @@ #include "config.h" #include "DragData.h" +#include "Clipboard.h" #include "Document.h" #include "DocumentFragment.h" @@ -56,7 +57,7 @@ Color DragData::asColor() const return Color(); } -Clipboard* DragData::createClipboard(ClipboardAccessPolicy) const +PassRefPtr<Clipboard> DragData::createClipboard(ClipboardAccessPolicy) const { return 0; } diff --git a/WebCore/platform/gtk/EventLoopGtk.cpp b/WebCore/platform/gtk/EventLoopGtk.cpp new file mode 100644 index 0000000..4ef7b5c --- /dev/null +++ b/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/WebCore/platform/gtk/FileChooserGtk.cpp b/WebCore/platform/gtk/FileChooserGtk.cpp index c41a693..e984718 100644 --- a/WebCore/platform/gtk/FileChooserGtk.cpp +++ b/WebCore/platform/gtk/FileChooserGtk.cpp @@ -28,8 +28,7 @@ #include "FileChooser.h" #include "CString.h" -#include "Document.h" -#include "FrameView.h" +#include "FileSystem.h" #include "Icon.h" #include "LocalizedStrings.h" #include "StringTruncator.h" @@ -45,52 +44,12 @@ static bool stringByAdoptingFileSystemRepresentation(gchar* systemFilename, Stri if (!systemFilename) return false; - gchar* filename = g_filename_to_utf8(systemFilename, -1, 0, 0, 0); + result = filenameToString(systemFilename); g_free(systemFilename); - if (!filename) - return false; - - result = String::fromUTF8(filename); - g_free(filename); return true; } -FileChooser::FileChooser(FileChooserClient* client, const String& filename) - : m_client(client) - , m_filename(filename) - , m_icon(chooseIcon(filename)) -{ -} - -FileChooser::~FileChooser() -{ -} - -void FileChooser::openFileChooser(Document* document) -{ - FrameView* view = document->view(); - if (!view) - return; - - GtkWidget* dialog = gtk_file_chooser_dialog_new(_("Upload File"), - GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view->containingWindow()))), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); - - // We need this protector because otherwise we can be deleted if the file upload control is detached while - // we're within the gtk_run_dialog call. - RefPtr<FileChooser> protector(this); - String result; - - const bool acceptedDialog = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; - if (acceptedDialog && stringByAdoptingFileSystemRepresentation(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)), result)) - chooseFile(result); - gtk_widget_destroy(dialog); -} - String FileChooser::basenameForWidth(const Font& font, int width) const { if (width <= 0) @@ -98,15 +57,13 @@ String FileChooser::basenameForWidth(const Font& font, int width) const String string = fileButtonNoFileSelectedLabel(); - if (!m_filename.isEmpty()) { - gchar* systemFilename = g_filename_from_utf8(m_filename.utf8().data(), -1, 0, 0, 0); - if (systemFilename) { - gchar* systemBasename = g_path_get_basename(systemFilename); - g_free(systemFilename); - - stringByAdoptingFileSystemRepresentation(systemBasename, string); - } - } + if (m_filenames.size() == 1) { + gchar* systemFilename = filenameFromString(m_filenames[0]); + gchar* systemBasename = g_path_get_basename(systemFilename); + g_free(systemFilename); + 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/WebCore/platform/gtk/FileSystemGtk.cpp b/WebCore/platform/gtk/FileSystemGtk.cpp index 904fe9f..965cea9 100644 --- a/WebCore/platform/gtk/FileSystemGtk.cpp +++ b/WebCore/platform/gtk/FileSystemGtk.cpp @@ -22,6 +22,7 @@ #include "config.h" #include "FileSystem.h" +#include "guriescape.h" #include "NotImplemented.h" #include "PlatformString.h" #include "CString.h" @@ -30,12 +31,61 @@ #include <glib/gstdio.h> #include <glib/gutils.h> +#include <unistd.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 PLATFORM(WIN_OS) + return String::fromUTF8(filename); +#else + gchar* escapedString = g_uri_escape_string(filename, "/:", false); + String string(escapedString); + g_free(escapedString); + return string; +#endif +} + +char* filenameFromString(const String& string) +{ +#if PLATFORM(WIN_OS) + return g_strdup(string.utf8().data()); +#else + return g_uri_unescape_string(string.utf8().data(), 0); +#endif +} + +// Converts a string to something suitable to be displayed to the user. +String filenameForDisplay(const String& string) +{ +#if PLATFORM(WIN_OS) + return string; +#else + gchar* filename = filenameFromString(string); + gchar* display = g_filename_to_utf8(filename, 0, 0, 0, 0); + g_free(filename); + if (!display) + return string; + + String displayString = String::fromUTF8(display); + g_free(display); + + return displayString; +#endif +} + bool fileExists(const String& path) { bool result = false; - gchar* filename = g_filename_from_utf8(path.utf8().data(), -1, 0, 0, 0); + gchar* filename = filenameFromString(path); if (filename) { result = g_file_test(filename, G_FILE_TEST_EXISTS); @@ -48,7 +98,7 @@ bool fileExists(const String& path) bool deleteFile(const String& path) { bool result = false; - gchar* filename = g_filename_from_utf8(path.utf8().data(), -1, 0, 0, 0); + gchar* filename = filenameFromString(path); if (filename) { result = g_remove(filename) == 0; @@ -61,7 +111,7 @@ bool deleteFile(const String& path) bool deleteEmptyDirectory(const String& path) { bool result = false; - gchar* filename = g_filename_from_utf8(path.utf8().data(), -1, 0, 0, 0); + gchar* filename = filenameFromString(path); if (filename) { result = g_rmdir(filename) == 0; @@ -73,7 +123,7 @@ bool deleteEmptyDirectory(const String& path) bool getFileSize(const String& path, long long& resultSize) { - gchar* filename = g_filename_from_utf8(path.utf8().data(), -1, 0, 0, 0); + gchar* filename = filenameFromString(path); if (!filename) return false; @@ -87,10 +137,21 @@ bool getFileSize(const String& path, long long& resultSize) return true; } -bool getFileModificationTime(const String&, time_t&) +bool getFileModificationTime(const String& path, time_t& modifiedTime) { - notImplemented(); - return false; + gchar* filename = filenameFromString(path); + if (!filename) + return false; + + struct stat statResult; + gint result = g_stat(filename, &statResult); + g_free(filename); + if (result != 0) + return false; + + modifiedTime = statResult.st_mtime; + return true; + } String pathByAppendingComponent(const String& path, const String& component) @@ -103,7 +164,7 @@ String pathByAppendingComponent(const String& path, const String& component) bool makeAllDirectories(const String& path) { - gchar* filename = g_filename_from_utf8(path.utf8().data(), -1, 0, 0, 0); + gchar* filename = filenameFromString(path); if (!filename) return false; @@ -113,6 +174,52 @@ bool makeAllDirectories(const String& path) return result == 0; } +String homeDirectoryPath() +{ + return filenameToString(g_get_home_dir()); +} + +String pathGetFileName(const String& pathName) +{ + char* tmpFilename = filenameFromString(pathName); + char* baseName = g_path_get_basename(tmpFilename); + String fileName = String::fromUTF8(baseName); + g_free(baseName); + g_free(tmpFilename); + + return fileName; +} + +String directoryName(const String& path) +{ + notImplemented(); + return String(); +} + +Vector<String> listDirectory(const String& path, const String& filter) +{ + Vector<String> entries; + + gchar* filename = filenameFromString(path); + GDir* dir = g_dir_open(filename, 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; + + gchar* entry = g_build_filename(filename, name, NULL); + entries.append(filenameToString(entry)); + g_free(entry); + } + g_dir_close(dir); + g_free(filename); + + return entries; +} + CString openTemporaryFile(const char* prefix, PlatformFileHandle& handle) { gchar* filename = g_strdup_printf("%sXXXXXX", prefix); @@ -152,4 +259,9 @@ int writeToFile(PlatformFileHandle handle, const char* data, int length) return totalBytesWritten; } + +bool unloadModule(PlatformModule module) +{ + return g_module_close(module); +} } diff --git a/WebCore/platform/gtk/CookieJarGtk.cpp b/WebCore/platform/gtk/KURLGtk.cpp index 2813a2e..4858d3e 100644 --- a/WebCore/platform/gtk/CookieJarGtk.cpp +++ b/WebCore/platform/gtk/KURLGtk.cpp @@ -1,4 +1,6 @@ /* + * 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 @@ -15,32 +17,24 @@ */ #include "config.h" -#include "CookieJar.h" - -#include "DeprecatedString.h" #include "KURL.h" -#include "PlatformString.h" -#include "StringHash.h" -#include <wtf/HashMap.h> +#include "CString.h" +#include "FileSystem.h" -namespace WebCore { +#include <glib.h> -static HashMap<String, String> cookieJar; - -void setCookies(Document* /*document*/, const KURL& url, const KURL& /*policyURL*/, const String& value) -{ - cookieJar.set(url.string(), value); -} +namespace WebCore { -String cookies(const Document* /*document*/, const KURL& url) +String KURL::fileSystemPath() const { - return cookieJar.get(url.string()); -} + gchar* filename = g_filename_from_uri(m_string.utf8().data(), 0, 0); + if (!filename) + return String(); -bool cookiesEnabled(const Document* /*document*/) -{ - return true; + String path = filenameToString(filename); + g_free(filename); + return path; } -} +} // namespace WebCore diff --git a/WebCore/platform/gtk/KeyEventGtk.cpp b/WebCore/platform/gtk/KeyEventGtk.cpp index 5d2efcf..153ef19 100644 --- a/WebCore/platform/gtk/KeyEventGtk.cpp +++ b/WebCore/platform/gtk/KeyEventGtk.cpp @@ -2,6 +2,7 @@ * 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 @@ -29,13 +30,13 @@ #include "config.h" #include "PlatformKeyboardEvent.h" -#include "DeprecatedString.h" #include "KeyboardCodes.h" #include "NotImplemented.h" #include "TextEncoding.h" #include <gdk/gdk.h> #include <gdk/gdkkeysyms.h> +#include <gtk/gtkversion.h> namespace WebCore { @@ -187,6 +188,8 @@ static int windowsKeyCodeForKeyEvent(unsigned int keycode) 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: @@ -469,21 +472,30 @@ static int windowsKeyCodeForKeyEvent(unsigned int keycode) static String singleCharacterString(guint val) { - glong nwc; - String retVal; - gunichar c = gdk_keyval_to_unicode(val); - gunichar2* uchar16 = g_ucs4_to_utf16(&c, 1, 0, &nwc, 0); + switch (val) { + case GDK_ISO_Enter: + case GDK_KP_Enter: + case GDK_Return: + return String("\r"); + default: + gunichar c = gdk_keyval_to_unicode(val); + glong nwc; + gunichar2* uchar16 = g_ucs4_to_utf16(&c, 1, 0, &nwc, 0); - if (uchar16) - retVal = String((UChar*)uchar16, nwc); - else - retVal = String(); + String retVal; + if (uchar16) + retVal = String((UChar*)uchar16, nwc); + else + retVal = String(); - g_free(uchar16); + g_free(uchar16); - return retVal; + 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)) @@ -491,20 +503,30 @@ PlatformKeyboardEvent::PlatformKeyboardEvent(GdkEventKey* event) , m_keyIdentifier(keyIdentifierForGdkKeyCode(event->keyval)) , m_autoRepeat(false) , m_windowsVirtualKeyCode(windowsKeyCodeForKeyEvent(event->keyval)) - , m_isKeypad(false) + , 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_MOD2_MASK) +#if GTK_CHECK_VERSION(2,10,0) + , m_metaKey(event->state & GDK_META_MASK) +#else + // GDK_MOD2_MASK doesn't always mean meta so we can't use it + , m_metaKey(false) +#endif + , m_gdkEventKey(event) { } -void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool) +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(); @@ -520,4 +542,9 @@ bool PlatformKeyboardEvent::currentCapsLockState() return false; } +GdkEventKey* PlatformKeyboardEvent::gdkEventKey() const +{ + return m_gdkEventKey; +} + } diff --git a/WebCore/platform/gtk/Language.cpp b/WebCore/platform/gtk/Language.cpp index 171cd84..6ae7305 100644 --- a/WebCore/platform/gtk/Language.cpp +++ b/WebCore/platform/gtk/Language.cpp @@ -26,6 +26,30 @@ #include <gtk/gtk.h> #include <pango/pango.h> +#if !defined(PANGO_VERSION_CHECK) +// PANGO_VERSION_CHECK() and pango_language_get_default() appeared in 1.5.2 +#include <locale.h> + +static gchar * +_pango_get_lc_ctype (void) +{ + return g_strdup (setlocale (LC_CTYPE, NULL)); +} + +static PangoLanguage * +pango_language_get_default (void) +{ + static PangoLanguage *result = NULL; + if (G_UNLIKELY (!result)) + { + gchar *lang = _pango_get_lc_ctype (); + result = pango_language_from_string (lang); + g_free (lang); + } + return result; +} +#endif + namespace WebCore { String defaultLanguage() diff --git a/WebCore/platform/gtk/LocalizedStringsGtk.cpp b/WebCore/platform/gtk/LocalizedStringsGtk.cpp index 041cd83..52d4f5f 100644 --- a/WebCore/platform/gtk/LocalizedStringsGtk.cpp +++ b/WebCore/platform/gtk/LocalizedStringsGtk.cpp @@ -3,7 +3,7 @@ * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007 Holger Hans Peter Freyther * Copyright (C) 2008 Christian Dywan <christian@imendio.com> - * All rights reserved. + * Copyright (C) 2008 Nuanti Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,6 +30,7 @@ #include "config.h" #include "LocalizedStrings.h" +#include "NotImplemented.h" #include "PlatformString.h" #include <gtk/gtk.h> @@ -124,7 +125,7 @@ String contextMenuItemTagDelete() String contextMenuItemTagSelectAll() { -#if GLIB_CHECK_VERSION(2,10,0) +#if GTK_CHECK_VERSION(2,10,0) static String stockLabel = String::fromUTF8(gtkStockLabel(GTK_STOCK_SELECT_ALL)); #else static String stockLabel = String::fromUTF8(_("Select _All")); @@ -285,9 +286,61 @@ 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 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) +{ + notImplemented(); + return String(); +} + } diff --git a/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp b/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp index c7c90ad..20fe0cb 100644 --- a/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp +++ b/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2006 Zack Rusin <zack@kde.org> * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2007 Trolltech ASA + * 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 @@ -37,6 +37,7 @@ struct ExtensionMap { static const ExtensionMap extensionMap [] = { { "bmp", "image/bmp" }, + { "css", "text/css" }, { "gif", "image/gif" }, { "html", "text/html" }, { "ico", "image/x-icon" }, diff --git a/WebCore/platform/gtk/MouseEventGtk.cpp b/WebCore/platform/gtk/MouseEventGtk.cpp index 29ea371..f441f00 100644 --- a/WebCore/platform/gtk/MouseEventGtk.cpp +++ b/WebCore/platform/gtk/MouseEventGtk.cpp @@ -31,11 +31,13 @@ #include "Assertions.h" #include <gdk/gdk.h> +#include <gtk/gtkversion.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; @@ -44,7 +46,12 @@ PlatformMouseEvent::PlatformMouseEvent(GdkEventButton* event) 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_MOD2_MASK; +#if GTK_CHECK_VERSION(2,10,0) + m_metaKey = event->state & GDK_META_MASK; +#else + // GDK_MOD2_MASK doesn't always mean meta so we can't use it + m_metaKey = false; +#endif switch (event->type) { case GDK_BUTTON_PRESS: diff --git a/WebCore/platform/gtk/PasteboardGtk.cpp b/WebCore/platform/gtk/PasteboardGtk.cpp index 745728c..15a7e64 100644 --- a/WebCore/platform/gtk/PasteboardGtk.cpp +++ b/WebCore/platform/gtk/PasteboardGtk.cpp @@ -102,16 +102,20 @@ void Pasteboard::setHelper(PasteboardHelper* helper) void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame) { + GtkClipboard* clipboard = m_helper->getClipboard(frame); +#if GTK_CHECK_VERSION(2,10,0) gchar* text = g_strdup(frame->selectedText().utf8().data()); gchar* markup = g_strdup(createMarkup(selectedRange, 0, AnnotateForInterchange).utf8().data()); PasteboardSelectionData* data = new PasteboardSelectionData(text, markup); gint n_targets; GtkTargetEntry* targets = gtk_target_table_new_from_list(m_helper->getCopyTargetList(frame), &n_targets); - GtkClipboard* clipboard = m_helper->getClipboard(frame); gtk_clipboard_set_with_data(clipboard, targets, n_targets, clipboard_get_contents_cb, clipboard_clear_contents_cb, data); gtk_target_table_free(targets, n_targets); +#else + gtk_clipboard_set_text(clipboard, frame->selectedText().utf8().data(), frame->selectedText().utf8().length()); +#endif } void Pasteboard::writeURL(const KURL& url, const String&, Frame* frame) diff --git a/WebCore/platform/gtk/PlatformScreenGtk.cpp b/WebCore/platform/gtk/PlatformScreenGtk.cpp index 74c785f..9788253 100644 --- a/WebCore/platform/gtk/PlatformScreenGtk.cpp +++ b/WebCore/platform/gtk/PlatformScreenGtk.cpp @@ -1,6 +1,8 @@ /* * 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. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,44 +30,104 @@ #include "config.h" #include "PlatformScreen.h" +#include "HostWindow.h" #include "NotImplemented.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 { int screenDepth(Widget* widget) { - ASSERT(widget->containingWindow() && GTK_WIDGET(widget->containingWindow())->window); + GtkWidget* container = GTK_WIDGET(widget->root()->hostWindow()->platformWindow()); + if (!container) + return 24; - gint dummy, depth; - gdk_window_get_geometry(GTK_WIDGET(widget->containingWindow())->window, &dummy, &dummy, &dummy, &dummy, &depth); - return depth; + GdkVisual* visual = gdk_drawable_get_visual(GDK_DRAWABLE(GTK_WIDGET(widget->root()->hostWindow()->platformWindow())->window)); + return visual->depth; } -int screenDepthPerComponent(Widget*) +int screenDepthPerComponent(Widget* widget) { - notImplemented(); - return 8; + GtkWidget* container = GTK_WIDGET(widget->root()->hostWindow()->platformWindow()); + if (!container) + return 8; + + GdkVisual* visual = gdk_drawable_get_visual(GDK_DRAWABLE(GTK_WIDGET(widget->root()->hostWindow()->platformWindow())->window)); + return visual->bits_per_rgb; } -bool screenIsMonochrome(Widget*) +bool screenIsMonochrome(Widget* widget) { - notImplemented(); - return false; + GtkWidget* container = GTK_WIDGET(widget->root()->hostWindow()->platformWindow()); + if (!container) + return false; + + return screenDepth(widget) < 2; } -FloatRect screenRect(Widget*) +FloatRect screenRect(Widget* widget) { - notImplemented(); - return FloatRect(); + GtkWidget* container = GTK_WIDGET(widget->root()->hostWindow()->platformWindow()); + if (!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(container)->window); + GdkRectangle geometry; + gdk_screen_get_monitor_geometry(screen, monitor, &geometry); + + return FloatRect(geometry.x, geometry.y, geometry.width, geometry.height); } -FloatRect screenAvailableRect(Widget*) +FloatRect screenAvailableRect(Widget* widget) { - notImplemented(); - return FloatRect(); +#if PLATFORM(X11) + GtkWidget* container = GTK_WIDGET(widget->root()->hostWindow()->platformWindow()); + if (!container) + return FloatRect(); + + if (!GTK_WIDGET_REALIZED(container)) + return screenRect(widget); + + GdkDrawable* rootWindow = GDK_DRAWABLE(gtk_widget_get_root_window(container)); + GdkDisplay* display = gdk_drawable_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/WebCore/platform/gtk/PopupMenuGtk.cpp b/WebCore/platform/gtk/PopupMenuGtk.cpp index b4689f6..85c5aa0 100644 --- a/WebCore/platform/gtk/PopupMenuGtk.cpp +++ b/WebCore/platform/gtk/PopupMenuGtk.cpp @@ -1,8 +1,9 @@ /* * This file is part of the popup menu implementation for <select> elements in WebCore. * - * Copyright (C) 2006, 2007 Apple Inc. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,6 +27,7 @@ #include "CString.h" #include "FrameView.h" +#include "HostWindow.h" #include "NotImplemented.h" #include "PlatformString.h" #include <gtk/gtk.h> @@ -61,7 +63,7 @@ void PopupMenu::show(const IntRect& rect, FrameView* view, int index) gtk_container_foreach(GTK_CONTAINER(m_popup), reinterpret_cast<GtkCallback>(menuRemoveItem), this); int x, y; - gdk_window_get_origin(GTK_WIDGET(view->containingWindow())->window, &x, &y); + gdk_window_get_origin(GTK_WIDGET(view->hostWindow()->platformWindow())->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(); @@ -77,7 +79,7 @@ void PopupMenu::show(const IntRect& rect, FrameView* view, int index) m_indexMap.add(item, i); g_signal_connect(item, "activate", G_CALLBACK(menuItemActivated), this); - // FIXME: Apply the RenderStyle from client()->itemStyle(i) + // 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), item); gtk_widget_show(item); @@ -91,6 +93,24 @@ void PopupMenu::show(const IntRect& rect, FrameView* view, int index) gtk_widget_set_size_request(GTK_WIDGET(m_popup), -1, -1); gtk_widget_size_request(GTK_WIDGET(m_popup), &requisition); gtk_widget_set_size_request(GTK_WIDGET(m_popup), MAX(rect.width(), requisition.width), -1); + + GList* children = GTK_MENU_SHELL(m_popup)->children; + if (size) + for (int i = 0; i < size; i++) { + if (i > index) + break; + + GtkWidget* item = reinterpret_cast<GtkWidget*>(children->data); + GtkRequisition itemRequisition; + gtk_widget_get_child_requisition(item, &itemRequisition); + m_menuPosition.setY(m_menuPosition.y() - itemRequisition.height); + + children = g_list_next(children); + } + else + // Center vertically the empty popup in the combo box area + m_menuPosition.setY(m_menuPosition.y() - rect.height() / 2); + gtk_menu_popup(m_popup, NULL, NULL, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, 0, gtk_get_current_event_time()); } @@ -127,7 +147,7 @@ void PopupMenu::menuPositionFunction(GtkMenu*, gint* x, gint* y, gboolean* pushI { *x = that->m_menuPosition.x(); *y = that->m_menuPosition.y(); - pushIn = false; + *pushIn = true; } void PopupMenu::menuRemoveItem(GtkWidget* widget, PopupMenu* that) diff --git a/WebCore/platform/gtk/RenderThemeGtk.cpp b/WebCore/platform/gtk/RenderThemeGtk.cpp index 0113b9e..bf8b8d7 100644 --- a/WebCore/platform/gtk/RenderThemeGtk.cpp +++ b/WebCore/platform/gtk/RenderThemeGtk.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2007 Apple Inc. * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2008 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,9 +21,12 @@ */ #include "config.h" +#include "RenderThemeGtk.h" + +#include "AffineTransform.h" +#include "GraphicsContext.h" #include "NotImplemented.h" #include "RenderObject.h" -#include "RenderThemeGtk.h" #include "gtkdrawing.h" #include <gdk/gdk.h> @@ -35,25 +39,31 @@ RenderTheme* theme() return >kTheme; } +static bool mozGtkInitialized = false; + RenderThemeGtk::RenderThemeGtk() : m_gtkWindow(0) , m_gtkContainer(0) , m_gtkEntry(0) , m_gtkTreeView(0) { + if (!mozGtkInitialized) { + mozGtkInitialized = true; + moz_gtk_init(); + } } -static bool supportsFocus(EAppearance appearance) +static bool supportsFocus(ControlPart appearance) { switch (appearance) { - case PushButtonAppearance: - case ButtonAppearance: - case TextFieldAppearance: - case TextAreaAppearance: - case SearchFieldAppearance: - case MenulistAppearance: - case RadioAppearance: - case CheckboxAppearance: + case PushButtonPart: + case ButtonPart: + case TextFieldPart: + case TextAreaPart: + case SearchFieldPart: + case MenulistPart: + case RadioPart: + case CheckboxPart: return true; default: return false; @@ -70,11 +80,11 @@ bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const return isEnabled(o); } -short RenderThemeGtk::baselinePosition(const RenderObject* o) const +int RenderThemeGtk::baselinePosition(const RenderObject* o) const { // FIXME: This strategy is possibly incorrect for the GTK+ port. - if (o->style()->appearance() == CheckboxAppearance || - o->style()->appearance() == RadioAppearance) + if (o->style()->appearance() == CheckboxPart || + o->style()->appearance() == RadioPart) return o->marginTop() + o->height() - 2; return RenderTheme::baselinePosition(o); } @@ -151,12 +161,31 @@ static bool paintMozWidget(RenderTheme* theme, GtkThemeWidgetType type, RenderOb break; } - IntPoint pos = i.context->translatePoint(rect.location()); + AffineTransform ctm = i.context->getCTM(); + + IntPoint pos = ctm.mapPoint(rect.location()); GdkRectangle gdkRect = IntRect(pos.x(), pos.y(), rect.width(), rect.height()); GtkTextDirection direction = gtkTextDirection(o->style()->direction()); - // FIXME: Pass the real clip region. - return moz_gtk_widget_paint(type, i.context->gdkDrawable(), &gdkRect, &gdkRect, &mozState, flags, direction) != MOZ_GTK_SUCCESS; +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,4,0) + // Find the clip rectangle + cairo_t *cr = i.context->platformContext(); + double clipX1, clipX2, clipY1, clipY2; + cairo_clip_extents(cr, &clipX1, &clipY1, &clipX2, &clipY2); + + GdkRectangle gdkClipRect; + gdkClipRect.width = clipX2 - clipX1; + gdkClipRect.height = clipY2 - clipY1; + IntPoint clipPos = ctm.mapPoint(IntPoint(clipX1, clipY1)); + gdkClipRect.x = clipPos.x(); + gdkClipRect.y = clipPos.y(); + + gdk_rectangle_intersect(&gdkRect, &gdkClipRect, &gdkClipRect); +#else + GdkRectangle gdkClipRect = gdkRect; +#endif + + return moz_gtk_widget_paint(type, i.context->gdkDrawable(), &gdkRect, &gdkClipRect, &mozState, flags, direction) != MOZ_GTK_SUCCESS; } static void setButtonPadding(RenderStyle* style) @@ -169,7 +198,7 @@ static void setButtonPadding(RenderStyle* style) style->setPaddingBottom(Length(padding / 2, Fixed)); } -static void setToggleSize(RenderStyle* style, EAppearance appearance) +static void setToggleSize(RenderStyle* style, ControlPart appearance) { // The width and height are both specified, so we shouldn't change them. if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) @@ -179,11 +208,11 @@ static void setToggleSize(RenderStyle* style, EAppearance appearance) gint indicator_size, indicator_spacing; switch (appearance) { - case CheckboxAppearance: + case CheckboxPart: if (moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing) != MOZ_GTK_SUCCESS) return; break; - case RadioAppearance: + case RadioPart: if (moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing) != MOZ_GTK_SUCCESS) return; break; @@ -203,7 +232,7 @@ static void setToggleSize(RenderStyle* style, EAppearance appearance) void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const { - setToggleSize(style, RadioAppearance); + setToggleSize(style, RadioPart); } bool RenderThemeGtk::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) @@ -213,7 +242,7 @@ bool RenderThemeGtk::paintCheckbox(RenderObject* o, const RenderObject::PaintInf void RenderThemeGtk::setRadioSize(RenderStyle* style) const { - setToggleSize(style, RadioAppearance); + setToggleSize(style, RadioPart); } bool RenderThemeGtk::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) @@ -224,7 +253,7 @@ bool RenderThemeGtk::paintRadio(RenderObject* o, const RenderObject::PaintInfo& void RenderThemeGtk::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const { // FIXME: Is this condition necessary? - if (style->appearance() == PushButtonAppearance) { + if (style->appearance() == PushButtonPart) { style->resetBorder(); style->setHeight(Length(Auto)); style->setWhiteSpace(PRE); diff --git a/WebCore/platform/gtk/RenderThemeGtk.h b/WebCore/platform/gtk/RenderThemeGtk.h index b908793..dda8bc8 100644 --- a/WebCore/platform/gtk/RenderThemeGtk.h +++ b/WebCore/platform/gtk/RenderThemeGtk.h @@ -28,7 +28,6 @@ #define RenderThemeGdk_h #include "RenderTheme.h" -#include "GraphicsContext.h" #include <gtk/gtk.h> @@ -53,7 +52,7 @@ public: // 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 short baselinePosition(const RenderObject*) const; + virtual int baselinePosition(const RenderObject*) const; // The platform selection color. virtual Color platformActiveSelectionBackgroundColor() const; diff --git a/WebCore/platform/gtk/ScrollViewGtk.cpp b/WebCore/platform/gtk/ScrollViewGtk.cpp index ec43128..e1316ee 100644 --- a/WebCore/platform/gtk/ScrollViewGtk.cpp +++ b/WebCore/platform/gtk/ScrollViewGtk.cpp @@ -1,7 +1,8 @@ /* - * Copyright (C) 2006, 2007 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Computer, 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. * @@ -31,17 +32,14 @@ #include "ScrollView.h" #include "FloatRect.h" -#include "FocusController.h" -#include "Frame.h" -#include "FrameView.h" #include "GraphicsContext.h" +#include "HostWindow.h" #include "IntRect.h" #include "NotImplemented.h" #include "PlatformMouseEvent.h" #include "PlatformWheelEvent.h" -#include "PlatformScrollBar.h" -#include "Page.h" -#include "RenderLayer.h" +#include "ScrollbarGtk.h" +#include "ScrollbarTheme.h" #include <gtk/gtk.h> @@ -49,213 +47,45 @@ using namespace std; namespace WebCore { -class ScrollViewScrollbar : public PlatformScrollbar { -public: - ScrollViewScrollbar(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize); - -protected: - void geometryChanged(); -}; - -class ScrollView::ScrollViewPrivate : public ScrollbarClient -{ -public: - ScrollViewPrivate(ScrollView* _view) - : view(_view) - , hasStaticBackground(false) - , scrollbarsSuppressed(false) - , vScrollbarMode(ScrollbarAuto) - , hScrollbarMode(ScrollbarAuto) - , inUpdateScrollbars(false) - , horizontalAdjustment(0) - , verticalAdjustment(0) - {} - - ~ScrollViewPrivate() - { - setHasHorizontalScrollbar(false); - setHasVerticalScrollbar(false); - - if (horizontalAdjustment) { - g_signal_handlers_disconnect_by_func(G_OBJECT(horizontalAdjustment), (gpointer)ScrollViewPrivate::adjustmentChanged, this); - g_object_unref(horizontalAdjustment); - } - - if (verticalAdjustment) { - g_signal_handlers_disconnect_by_func(G_OBJECT(verticalAdjustment), (gpointer)ScrollViewPrivate::adjustmentChanged, this); - g_object_unref(verticalAdjustment); - } - } - - void scrollBackingStore(const IntSize& scrollDelta); - - void setHasHorizontalScrollbar(bool hasBar); - void setHasVerticalScrollbar(bool hasBar); - - virtual void valueChanged(Scrollbar*); - virtual IntRect windowClipRect() const; - virtual bool isActive() const; - - static void adjustmentChanged(GtkAdjustment*, gpointer); - - ScrollView* view; - bool hasStaticBackground; - bool scrollbarsSuppressed; - ScrollbarMode vScrollbarMode; - ScrollbarMode hScrollbarMode; - RefPtr<ScrollViewScrollbar> vBar; - RefPtr<ScrollViewScrollbar> hBar; - IntSize scrollOffset; - IntSize contentsSize; - IntSize viewPortSize; - bool inUpdateScrollbars; - HashSet<Widget*> children; - GtkAdjustment* horizontalAdjustment; - GtkAdjustment* verticalAdjustment; -}; - -ScrollViewScrollbar::ScrollViewScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize size) - : PlatformScrollbar(client, orientation, size) -{ -} - -void ScrollViewScrollbar::geometryChanged() -{ - if (!parent()) - return; - - ASSERT(parent()->isFrameView()); - - FrameView* frameView = static_cast<FrameView*>(parent()); - IntPoint loc = frameView->convertToContainingWindow(frameGeometry().location()); - - // Don't allow the allocation size to be negative - IntSize sz = frameGeometry().size(); - sz.clampNegativeToZero(); - - GtkAllocation allocation = { loc.x(), loc.y(), sz.width(), sz.height() }; - gtk_widget_size_allocate(gtkWidget(), &allocation); -} - -void ScrollView::ScrollViewPrivate::setHasHorizontalScrollbar(bool hasBar) -{ - if (Scrollbar::hasPlatformScrollbars()) { - if (hasBar && !hBar && !horizontalAdjustment) { - hBar = new ScrollViewScrollbar(this, HorizontalScrollbar, RegularScrollbar); - view->addChild(hBar.get()); - } else if (!hasBar && hBar) { - view->removeChild(hBar.get()); - hBar = 0; - } - } -} - -void ScrollView::ScrollViewPrivate::setHasVerticalScrollbar(bool hasBar) -{ - if (Scrollbar::hasPlatformScrollbars()) { - if (hasBar && !vBar && !verticalAdjustment) { - vBar = new ScrollViewScrollbar(this, VerticalScrollbar, RegularScrollbar); - view->addChild(vBar.get()); - } else if (!hasBar && vBar) { - view->removeChild(vBar.get()); - vBar = 0; - } - } -} - - -void ScrollView::ScrollViewPrivate::scrollBackingStore(const IntSize& scrollDelta) -{ - // Since scrolling is double buffered, we will be blitting the scroll view's intersection - // with the clip rect every time to keep it smooth. - IntRect clipRect = view->windowClipRect(); - IntRect scrollViewRect = view->convertToContainingWindow(IntRect(0, 0, view->visibleWidth(), view->visibleHeight())); - - IntRect updateRect = clipRect; - updateRect.intersect(scrollViewRect); - - //FIXME update here? - - if (!hasStaticBackground) // The main frame can just blit the WebView window - // FIXME: Find a way to blit subframes without blitting overlapping content - view->scrollBackingStore(-scrollDelta.width(), -scrollDelta.height(), scrollViewRect, clipRect); - else { - // We need to go ahead and repaint the entire backing store. Do it now before moving the - // plugins. - view->addToDirtyRegion(updateRect); - view->updateBackingStore(); - } - - view->geometryChanged(); - - // Now update the window (which should do nothing but a blit of the backing store's updateRect and so should - // be very fast). - view->update(); -} - -void ScrollView::ScrollViewPrivate::adjustmentChanged(GtkAdjustment* adjustment, gpointer _that) +static void adjustmentChanged(GtkAdjustment* adjustment, gpointer _that) { - ScrollViewPrivate* that = reinterpret_cast<ScrollViewPrivate*>(_that); + ScrollView* that = reinterpret_cast<ScrollView*>(_that); // Figure out if we really moved. - IntSize newOffset = that->scrollOffset; - if (adjustment == that->horizontalAdjustment) + IntSize newOffset = that->scrollOffset(); + if (adjustment == that->m_horizontalAdjustment) newOffset.setWidth(static_cast<int>(gtk_adjustment_get_value(adjustment))); - else if (adjustment == that->verticalAdjustment) + else if (adjustment == that->m_verticalAdjustment) newOffset.setHeight(static_cast<int>(gtk_adjustment_get_value(adjustment))); - IntSize scrollDelta = newOffset - that->scrollOffset; + IntSize scrollDelta = newOffset - that->scrollOffset(); if (scrollDelta == IntSize()) return; - that->scrollOffset = newOffset; + that->setScrollOffset(newOffset); - if (that->scrollbarsSuppressed) + if (that->scrollbarsSuppressed()) return; - that->scrollBackingStore(scrollDelta); - static_cast<FrameView*>(that->view)->frame()->sendScrollEvent(); + that->scrollContents(scrollDelta); } -void ScrollView::ScrollViewPrivate::valueChanged(Scrollbar* bar) +void ScrollView::platformInit() { - // Figure out if we really moved. - IntSize newOffset = scrollOffset; - if (bar) { - if (bar == hBar) - newOffset.setWidth(bar->value()); - else if (bar == vBar) - newOffset.setHeight(bar->value()); - } - IntSize scrollDelta = newOffset - scrollOffset; - if (scrollDelta == IntSize()) - return; - scrollOffset = newOffset; - - if (scrollbarsSuppressed) - return; - - scrollBackingStore(scrollDelta); - static_cast<FrameView*>(view)->frame()->sendScrollEvent(); + m_horizontalAdjustment = 0; + m_verticalAdjustment = 0; } -IntRect ScrollView::ScrollViewPrivate::windowClipRect() const +void ScrollView::platformDestroy() { - return static_cast<const FrameView*>(view)->windowClipRect(false); -} - -bool ScrollView::ScrollViewPrivate::isActive() const -{ - Page* page = static_cast<const FrameView*>(view)->frame()->page(); - return page && page->focusController()->isActive(); -} - -ScrollView::ScrollView() - : m_data(new ScrollViewPrivate(this)) -{} + if (m_horizontalAdjustment) { + g_signal_handlers_disconnect_by_func(G_OBJECT(m_horizontalAdjustment), (gpointer)adjustmentChanged, this); + g_object_unref(m_horizontalAdjustment); + } -ScrollView::~ScrollView() -{ - delete m_data; + if (m_verticalAdjustment) { + g_signal_handlers_disconnect_by_func(G_OBJECT(m_verticalAdjustment), (gpointer)adjustmentChanged, this); + g_object_unref(m_verticalAdjustment); + } } /* @@ -266,567 +96,100 @@ void ScrollView::setGtkAdjustments(GtkAdjustment* hadj, GtkAdjustment* vadj) { ASSERT(!hadj == !vadj); - if (m_data->horizontalAdjustment) { - g_signal_handlers_disconnect_by_func(G_OBJECT(m_data->horizontalAdjustment), (gpointer)ScrollViewPrivate::adjustmentChanged, m_data); - g_signal_handlers_disconnect_by_func(G_OBJECT(m_data->verticalAdjustment), (gpointer)ScrollViewPrivate::adjustmentChanged, m_data); - g_object_unref(m_data->horizontalAdjustment); - g_object_unref(m_data->verticalAdjustment); + if (m_horizontalAdjustment) { + g_signal_handlers_disconnect_by_func(G_OBJECT(m_horizontalAdjustment), (gpointer)adjustmentChanged, this); + g_signal_handlers_disconnect_by_func(G_OBJECT(m_verticalAdjustment), (gpointer)adjustmentChanged, this); + g_object_unref(m_horizontalAdjustment); + g_object_unref(m_verticalAdjustment); } - m_data->horizontalAdjustment = hadj; - m_data->verticalAdjustment = vadj; + m_horizontalAdjustment = hadj; + m_verticalAdjustment = vadj; - if (m_data->horizontalAdjustment) { - g_signal_connect(m_data->horizontalAdjustment, "value-changed", G_CALLBACK(ScrollViewPrivate::adjustmentChanged), m_data); - g_signal_connect(m_data->verticalAdjustment, "value-changed", G_CALLBACK(ScrollViewPrivate::adjustmentChanged), m_data); + if (m_horizontalAdjustment) { + g_signal_connect(m_horizontalAdjustment, "value-changed", G_CALLBACK(adjustmentChanged), this); + g_signal_connect(m_verticalAdjustment, "value-changed", G_CALLBACK(adjustmentChanged), this); /* * disable the scrollbars (if we have any) as the GtkAdjustment over */ - m_data->setHasVerticalScrollbar(false); - m_data->setHasHorizontalScrollbar(false); - -#if GLIB_CHECK_VERSION(2,10,0) - g_object_ref_sink(m_data->horizontalAdjustment); - g_object_ref_sink(m_data->verticalAdjustment); -#else - g_object_ref(m_data->horizontalAdjustment); - gtk_object_sink(GTK_OBJECT(m_data->horizontalAdjustment)); - g_object_ref(m_data->verticalAdjustment); - gtk_object_sink(GTK_OBJECT(m_data->verticalAdjustment)); -#endif - } - - updateScrollbars(m_data->scrollOffset); -} - -void ScrollView::updateContents(const IntRect& updateRect, bool now) -{ - if (updateRect.isEmpty()) - return; - - IntPoint windowPoint = contentsToWindow(updateRect.location()); - IntRect containingWindowRect = updateRect; - containingWindowRect.setLocation(windowPoint); - - GdkRectangle rect = containingWindowRect; - GdkWindow* window = GTK_WIDGET(containingWindow())->window; - - if (window) - gdk_window_invalidate_rect(window, &rect, true); - - // Cache the dirty spot. - addToDirtyRegion(containingWindowRect); - - if (now && window) - gdk_window_process_updates(window, true); -} - -void ScrollView::update() -{ - ASSERT(containingWindow()); - - GdkRectangle rect = frameGeometry(); - gdk_window_invalidate_rect(GTK_WIDGET(containingWindow())->window, &rect, true); -} - -int ScrollView::visibleWidth() const -{ - return width() - (m_data->vBar ? m_data->vBar->width() : 0); -} - -int ScrollView::visibleHeight() const -{ - return height() - (m_data->hBar ? m_data->hBar->height() : 0); -} - -// Region of the content currently visible in the viewport in the content view's coordinate system. -FloatRect ScrollView::visibleContentRect() const -{ - return FloatRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); -} - -FloatRect ScrollView::visibleContentRectConsideringExternalScrollers() const -{ - // external scrollers not supported for now - return visibleContentRect(); -} - -void ScrollView::setContentsPos(int newX, int newY) -{ - int dx = newX - contentsX(); - int dy = newY - contentsY(); - scrollBy(dx, dy); -} - -void ScrollView::resizeContents(int w, int h) -{ - IntSize newSize(w, h); - if (m_data->contentsSize == newSize) - return; - - m_data->contentsSize = newSize; - updateScrollbars(m_data->scrollOffset); -} - -int ScrollView::contentsX() const -{ - return scrollOffset().width(); -} - -int ScrollView::contentsY() const -{ - return scrollOffset().height(); -} - -int ScrollView::contentsWidth() const -{ - return m_data->contentsSize.width(); -} - -int ScrollView::contentsHeight() const -{ - return m_data->contentsSize.height(); -} - -IntSize ScrollView::scrollOffset() const -{ - return m_data->scrollOffset; -} - -IntSize ScrollView::maximumScroll() const -{ - IntSize delta = (m_data->contentsSize - IntSize(visibleWidth(), visibleHeight())) - scrollOffset(); - delta.clampNegativeToZero(); - return delta; -} - -void ScrollView::scrollBy(int dx, int dy) -{ - IntSize scrollOffset = m_data->scrollOffset; - IntSize newScrollOffset = scrollOffset + IntSize(dx, dy).shrunkTo(maximumScroll()); - newScrollOffset.clampNegativeToZero(); - - if (newScrollOffset == scrollOffset) - return; - - updateScrollbars(newScrollOffset); -} - -ScrollbarMode ScrollView::hScrollbarMode() const -{ - return m_data->hScrollbarMode; -} - -ScrollbarMode ScrollView::vScrollbarMode() const -{ - return m_data->vScrollbarMode; -} - -void ScrollView::suppressScrollbars(bool suppressed, bool repaintOnSuppress) -{ - m_data->scrollbarsSuppressed = suppressed; - if (repaintOnSuppress) - updateScrollbars(m_data->scrollOffset); -} - -void ScrollView::setHScrollbarMode(ScrollbarMode newMode) -{ - if (m_data->hScrollbarMode != newMode) { - m_data->hScrollbarMode = newMode; - updateScrollbars(m_data->scrollOffset); - } -} - -void ScrollView::setVScrollbarMode(ScrollbarMode newMode) -{ - if (m_data->vScrollbarMode != newMode) { - m_data->vScrollbarMode = newMode; - updateScrollbars(m_data->scrollOffset); - } -} - -void ScrollView::setScrollbarsMode(ScrollbarMode newMode) -{ - m_data->hScrollbarMode = m_data->vScrollbarMode = newMode; - updateScrollbars(m_data->scrollOffset); -} - -void ScrollView::setStaticBackground(bool flag) -{ - m_data->hasStaticBackground = flag; -} - -void ScrollView::setFrameGeometry(const IntRect& newGeometry) -{ - ASSERT(isFrameView()); - IntRect oldGeometry = frameGeometry(); - Widget::setFrameGeometry(newGeometry); + setHasVerticalScrollbar(false); + setHasHorizontalScrollbar(false); - if (newGeometry == oldGeometry) - return; - if (newGeometry.width() != oldGeometry.width() || newGeometry.height() != oldGeometry.height()) { - updateScrollbars(m_data->scrollOffset); - static_cast<FrameView*>(this)->setNeedsLayout(); + g_object_ref(m_horizontalAdjustment); + g_object_ref(m_verticalAdjustment); } - geometryChanged(); + updateScrollbars(m_scrollOffset); } -void ScrollView::addChild(Widget* child) +void ScrollView::platformAddChild(Widget* child) { - child->setParent(this); - child->setContainingWindow(containingWindow()); - m_data->children.add(child); - - if (child->gtkWidget()) - gtk_container_add(GTK_CONTAINER(containingWindow()), child->gtkWidget()); + if (!GTK_IS_SOCKET(child->platformWidget())) + gtk_container_add(GTK_CONTAINER(hostWindow()->platformWindow()), child->platformWidget()); } -void ScrollView::removeChild(Widget* child) +void ScrollView::platformRemoveChild(Widget* child) { - child->setParent(0); - m_data->children.remove(child); + GtkWidget* parent; - if (child->gtkWidget() && GTK_WIDGET(containingWindow()) == GTK_WIDGET(child->gtkWidget())->parent) - gtk_container_remove(GTK_CONTAINER(containingWindow()), child->gtkWidget()); -} + // HostWindow can be NULL here. If that's the case + // let's grab the child's parent instead. + if (hostWindow()) + parent = GTK_WIDGET(hostWindow()->platformWindow()); + else + parent = GTK_WIDGET(child->platformWidget()->parent); -void ScrollView::scrollRectIntoViewRecursively(const IntRect& r) -{ - IntPoint p(max(0, r.x()), max(0, r.y())); - ScrollView* view = this; - while (view) { - view->setContentsPos(p.x(), p.y()); - p.move(view->x() - view->scrollOffset().width(), view->y() - view->scrollOffset().height()); - view = static_cast<ScrollView*>(view->parent()); - } + if (GTK_IS_CONTAINER(parent) && parent == child->platformWidget()->parent) + gtk_container_remove(GTK_CONTAINER(parent), child->platformWidget()); } -bool ScrollView::inWindow() const +bool ScrollView::platformHandleHorizontalAdjustment(const IntSize& scroll) { - notImplemented(); - return true; -} - -void ScrollView::wheelEvent(PlatformWheelEvent& e) -{ - // Determine how much we want to scroll. If we can move at all, we will accept the event. - IntSize maxScrollDelta = maximumScroll(); - if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) || - (e.deltaX() > 0 && scrollOffset().width() > 0) || - (e.deltaY() < 0 && maxScrollDelta.height() > 0) || - (e.deltaY() > 0 && scrollOffset().height() > 0)) - e.accept(); - - scrollBy(-e.deltaX() * LINE_STEP, -e.deltaY() * LINE_STEP); -} - -void ScrollView::updateScrollbars(const IntSize& desiredOffset) -{ - // Don't allow re-entrancy into this function. - if (m_data->inUpdateScrollbars) - return; - - // FIXME: This code is here so we don't have to fork FrameView.h/.cpp. - // In the end, FrameView should just merge with ScrollView. - if (static_cast<const FrameView*>(this)->frame()->prohibitsScrolling()) - return; - - m_data->inUpdateScrollbars = true; - - bool hasVerticalScrollbar = m_data->vBar; - bool hasHorizontalScrollbar = m_data->hBar; - bool oldHasVertical = hasVerticalScrollbar; - bool oldHasHorizontal = hasHorizontalScrollbar; - ScrollbarMode hScroll = m_data->hScrollbarMode; - ScrollbarMode vScroll = m_data->vScrollbarMode; - - const int cVerticalWidth = PlatformScrollbar::verticalScrollbarWidth(); - const int cHorizontalHeight = PlatformScrollbar::horizontalScrollbarHeight(); + if (m_horizontalAdjustment) { + m_horizontalAdjustment->page_size = visibleWidth(); + m_horizontalAdjustment->step_increment = visibleWidth() / 10.0; + m_horizontalAdjustment->page_increment = visibleWidth() * 0.9; + m_horizontalAdjustment->lower = 0; + m_horizontalAdjustment->upper = contentsWidth(); + gtk_adjustment_changed(m_horizontalAdjustment); - for (int pass = 0; pass < 2; pass++) { - bool scrollsVertically; - bool scrollsHorizontally; - - if (!m_data->scrollbarsSuppressed && (hScroll == ScrollbarAuto || vScroll == ScrollbarAuto)) { - // Do a layout if pending before checking if scrollbars are needed. - if (hasVerticalScrollbar != oldHasVertical || hasHorizontalScrollbar != oldHasHorizontal) - static_cast<FrameView*>(this)->layout(); - - scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == ScrollbarAuto && contentsHeight() > height()); - if (scrollsVertically) - scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll == ScrollbarAuto && contentsWidth() + cVerticalWidth > width()); - else { - scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll == ScrollbarAuto && contentsWidth() > width()); - if (scrollsHorizontally) - scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == ScrollbarAuto && contentsHeight() + cHorizontalHeight > height()); - } - } - else { - scrollsHorizontally = (hScroll == ScrollbarAuto) ? hasHorizontalScrollbar : (hScroll == ScrollbarAlwaysOn); - scrollsVertically = (vScroll == ScrollbarAuto) ? hasVerticalScrollbar : (vScroll == ScrollbarAlwaysOn); - } - - if (hasVerticalScrollbar != scrollsVertically) { - m_data->setHasVerticalScrollbar(scrollsVertically); - hasVerticalScrollbar = scrollsVertically; - } - - if (hasHorizontalScrollbar != scrollsHorizontally) { - m_data->setHasHorizontalScrollbar(scrollsHorizontally); - hasHorizontalScrollbar = scrollsHorizontally; + if (m_scrollOffset.width() != scroll.width()) { + m_horizontalAdjustment->value = scroll.width(); + gtk_adjustment_value_changed(m_horizontalAdjustment); } + return true; } - - // Set up the range (and page step/line step). - IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight()); - IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition); - scroll.clampNegativeToZero(); - - if (m_data->horizontalAdjustment) { - m_data->horizontalAdjustment->page_size = visibleWidth(); - m_data->horizontalAdjustment->step_increment = visibleWidth() / 10.0; - m_data->horizontalAdjustment->page_increment = visibleWidth() * 0.9; - m_data->horizontalAdjustment->lower = 0; - m_data->horizontalAdjustment->upper = contentsWidth(); - gtk_adjustment_changed(m_data->horizontalAdjustment); - - if (m_data->scrollOffset.width() != scroll.width()) { - m_data->horizontalAdjustment->value = scroll.width(); - gtk_adjustment_value_changed(m_data->horizontalAdjustment); - } - } else if (m_data->hBar) { - int clientWidth = visibleWidth(); - m_data->hBar->setEnabled(contentsWidth() > clientWidth); - int pageStep = (clientWidth - PAGE_KEEP); - if (pageStep < 0) pageStep = clientWidth; - IntRect oldRect(m_data->hBar->frameGeometry()); - IntRect hBarRect = IntRect(0, - height() - m_data->hBar->height(), - width() - (m_data->vBar ? m_data->vBar->width() : 0), - m_data->hBar->height()); - m_data->hBar->setRect(hBarRect); - if (!m_data->scrollbarsSuppressed && oldRect != m_data->hBar->frameGeometry()) - m_data->hBar->invalidate(); - - if (m_data->scrollbarsSuppressed) - m_data->hBar->setSuppressInvalidation(true); - m_data->hBar->setSteps(LINE_STEP, pageStep); - m_data->hBar->setProportion(clientWidth, contentsWidth()); - m_data->hBar->setValue(scroll.width()); - if (m_data->scrollbarsSuppressed) - m_data->hBar->setSuppressInvalidation(false); - } - - if (m_data->verticalAdjustment) { - m_data->verticalAdjustment->page_size = visibleHeight(); - m_data->verticalAdjustment->step_increment = visibleHeight() / 10.0; - m_data->verticalAdjustment->page_increment = visibleHeight() * 0.9; - m_data->verticalAdjustment->lower = 0; - m_data->verticalAdjustment->upper = contentsHeight(); - gtk_adjustment_changed(m_data->verticalAdjustment); - - if (m_data->scrollOffset.height() != scroll.height()) { - m_data->verticalAdjustment->value = scroll.height(); - gtk_adjustment_value_changed(m_data->verticalAdjustment); - } - } else if (m_data->vBar) { - int clientHeight = visibleHeight(); - m_data->vBar->setEnabled(contentsHeight() > clientHeight); - int pageStep = (clientHeight - PAGE_KEEP); - if (pageStep < 0) pageStep = clientHeight; - IntRect oldRect(m_data->vBar->frameGeometry()); - IntRect vBarRect = IntRect(width() - m_data->vBar->width(), - 0, - m_data->vBar->width(), - height() - (m_data->hBar ? m_data->hBar->height() : 0)); - m_data->vBar->setRect(vBarRect); - if (!m_data->scrollbarsSuppressed && oldRect != m_data->vBar->frameGeometry()) - m_data->vBar->invalidate(); - - if (m_data->scrollbarsSuppressed) - m_data->vBar->setSuppressInvalidation(true); - m_data->vBar->setSteps(LINE_STEP, pageStep); - m_data->vBar->setProportion(clientHeight, contentsHeight()); - m_data->vBar->setValue(scroll.height()); - if (m_data->scrollbarsSuppressed) - m_data->vBar->setSuppressInvalidation(false); - } - - if (oldHasVertical != (m_data->vBar != 0) || oldHasHorizontal != (m_data->hBar != 0)) - geometryChanged(); - - // See if our offset has changed in a situation where we might not have scrollbars. - // This can happen when editing a body with overflow:hidden and scrolling to reveal selection. - // It can also happen when maximizing a window that has scrollbars (but the new maximized result - // does not). - IntSize scrollDelta = scroll - m_data->scrollOffset; - if (scrollDelta != IntSize()) { - m_data->scrollOffset = scroll; - m_data->scrollBackingStore(scrollDelta); - } - - m_data->inUpdateScrollbars = false; -} - -IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const -{ - IntPoint viewPoint = convertFromContainingWindow(windowPoint); - return viewPoint + scrollOffset(); -} - -IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const -{ - IntPoint viewPoint = contentsPoint - scrollOffset(); - return convertToContainingWindow(viewPoint); -} - -PlatformScrollbar* ScrollView::scrollbarUnderMouse(const PlatformMouseEvent& mouseEvent) -{ - IntPoint viewPoint = convertFromContainingWindow(mouseEvent.pos()); - if (m_data->hBar && m_data->hBar->frameGeometry().contains(viewPoint)) - return m_data->hBar.get(); - if (m_data->vBar && m_data->vBar->frameGeometry().contains(viewPoint)) - return m_data->vBar.get(); - return 0; -} - -IntPoint ScrollView::convertChildToSelf(const Widget* child, const IntPoint& point) const -{ - IntPoint newPoint = point; - if (child != m_data->hBar && child != m_data->vBar) - newPoint = point - scrollOffset(); - return Widget::convertChildToSelf(child, newPoint); -} - -IntPoint ScrollView::convertSelfToChild(const Widget* child, const IntPoint& point) const -{ - IntPoint newPoint = point; - if (child != m_data->hBar && child != m_data->vBar) - newPoint = point + scrollOffset(); - return Widget::convertSelfToChild(child, newPoint); + return false; } -void ScrollView::paint(GraphicsContext* context, const IntRect& rect) +bool ScrollView::platformHandleVerticalAdjustment(const IntSize& scroll) { - // FIXME: This code is here so we don't have to fork FrameView.h/.cpp. - // In the end, FrameView should just merge with ScrollView. - ASSERT(isFrameView()); - - if (context->paintingDisabled()) - return; - - IntRect documentDirtyRect = rect; - documentDirtyRect.intersect(frameGeometry()); + if (m_verticalAdjustment) { + m_verticalAdjustment->page_size = visibleHeight(); + m_verticalAdjustment->step_increment = visibleHeight() / 10.0; + m_verticalAdjustment->page_increment = visibleHeight() * 0.9; + m_verticalAdjustment->lower = 0; + m_verticalAdjustment->upper = contentsHeight(); + gtk_adjustment_changed(m_verticalAdjustment); - context->save(); - - context->translate(x(), y()); - documentDirtyRect.move(-x(), -y()); - - context->translate(-contentsX(), -contentsY()); - documentDirtyRect.move(contentsX(), contentsY()); - - context->clip(enclosingIntRect(visibleContentRect())); - static_cast<const FrameView*>(this)->frame()->paint(context, documentDirtyRect); - context->restore(); - - // Now paint the scrollbars. - if (!m_data->scrollbarsSuppressed && (m_data->hBar || m_data->vBar)) { - context->save(); - IntRect scrollViewDirtyRect = rect; - scrollViewDirtyRect.intersect(frameGeometry()); - context->translate(x(), y()); - scrollViewDirtyRect.move(-x(), -y()); - if (m_data->hBar) - m_data->hBar->paint(context, scrollViewDirtyRect); - if (m_data->vBar) - m_data->vBar->paint(context, scrollViewDirtyRect); - - /* - * FIXME: TODO: Check if that works with RTL - */ - // Fill the scroll corner with white. - IntRect hCorner; - if (m_data->hBar && width() - m_data->hBar->width() > 0) { - hCorner = IntRect(m_data->hBar->width(), - height() - m_data->hBar->height(), - width() - m_data->hBar->width(), - m_data->hBar->height()); - if (hCorner.intersects(scrollViewDirtyRect)) - context->fillRect(hCorner, Color::white); + if (m_scrollOffset.height() != scroll.height()) { + m_verticalAdjustment->value = scroll.height(); + gtk_adjustment_value_changed(m_verticalAdjustment); } - - if (m_data->vBar && height() - m_data->vBar->height() > 0) { - IntRect vCorner(width() - m_data->vBar->width(), - m_data->vBar->height(), - m_data->vBar->width(), - height() - m_data->vBar->height()); - if (vCorner != hCorner && vCorner.intersects(scrollViewDirtyRect)) - context->fillRect(vCorner, Color::white); - } - - context->restore(); - } -} - -/* - * update children but nor our scrollbars. They should not scroll when - * we scroll our content. - */ -void ScrollView::geometryChanged() const -{ - HashSet<Widget*>::const_iterator end = m_data->children.end(); - for (HashSet<Widget*>::const_iterator current = m_data->children.begin(); current != end; ++current) - (*current)->geometryChanged(); -} - -bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity) -{ - if (direction == ScrollUp || direction == ScrollDown) { - if (m_data->vBar) - return m_data->vBar->scroll(direction, granularity); - } else { - if (m_data->hBar) - return m_data->hBar->scroll(direction, granularity); - } + return true; + } return false; } -void ScrollView::addToDirtyRegion(const IntRect& containingWindowRect) -{ - ASSERT(isFrameView()); - const FrameView* frameView = static_cast<const FrameView*>(this); - Page* page = frameView->frame() ? frameView->frame()->page() : 0; - if (!page) - return; - page->chrome()->addToDirtyRegion(containingWindowRect); -} - -void ScrollView::scrollBackingStore(int dx, int dy, const IntRect& scrollViewRect, const IntRect& clipRect) +bool ScrollView::platformHasHorizontalAdjustment() const { - ASSERT(isFrameView()); - const FrameView* frameView = static_cast<const FrameView*>(this); - Page* page = frameView->frame() ? frameView->frame()->page() : 0; - if (!page) - return; - page->chrome()->scrollBackingStore(dx, dy, scrollViewRect, clipRect); -} - -void ScrollView::updateBackingStore() -{ - ASSERT(isFrameView()); - const FrameView* frameView = static_cast<const FrameView*>(this); - Page* page = frameView->frame() ? frameView->frame()->page() : 0; - if (!page) - return; - page->chrome()->updateBackingStore(); + return m_horizontalAdjustment != 0; } -HashSet<Widget*>* ScrollView::children() const +bool ScrollView::platformHasVerticalAdjustment() const { - return &m_data->children; + return m_verticalAdjustment != 0; } } diff --git a/WebCore/platform/gtk/PlatformScrollBarGtk.cpp b/WebCore/platform/gtk/ScrollbarGtk.cpp index c675ba0..099895d 100644 --- a/WebCore/platform/gtk/PlatformScrollBarGtk.cpp +++ b/WebCore/platform/gtk/ScrollbarGtk.cpp @@ -17,19 +17,25 @@ */ #include "config.h" -#include "PlatformScrollBar.h" +#include "ScrollbarGtk.h" #include "IntRect.h" #include "GraphicsContext.h" #include "FrameView.h" #include "NotImplemented.h" +#include "ScrollbarTheme.h" #include "gtkdrawing.h" #include <gtk/gtk.h> using namespace WebCore; -static gboolean gtkScrollEventCallback(GtkWidget* widget, GdkEventScroll* event, PlatformScrollbar*) +PassRefPtr<Scrollbar> Scrollbar::createNativeScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize size) +{ + return adoptRef(new ScrollbarGtk(client, orientation, size)); +} + +static gboolean gtkScrollEventCallback(GtkWidget* widget, GdkEventScroll* event, ScrollbarGtk*) { /* Scroll only if our parent rejects the scroll event. The rationale for * this is that we want the main frame to scroll when we move the mouse @@ -37,7 +43,7 @@ static gboolean gtkScrollEventCallback(GtkWidget* widget, GdkEventScroll* event, return gtk_widget_event(gtk_widget_get_parent(widget), reinterpret_cast<GdkEvent*>(event)); } -PlatformScrollbar::PlatformScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, +ScrollbarGtk::ScrollbarGtk(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) : Scrollbar(client, orientation, controlSize) , m_adjustment(GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0))) @@ -47,50 +53,45 @@ PlatformScrollbar::PlatformScrollbar(ScrollbarClient* client, ScrollbarOrientati GTK_SCROLLBAR(::gtk_vscrollbar_new(m_adjustment)); gtk_widget_show(GTK_WIDGET(scrollBar)); g_object_ref(G_OBJECT(scrollBar)); - g_signal_connect(G_OBJECT(scrollBar), "value-changed", G_CALLBACK(PlatformScrollbar::gtkValueChanged), this); + g_signal_connect(G_OBJECT(scrollBar), "value-changed", G_CALLBACK(ScrollbarGtk::gtkValueChanged), this); g_signal_connect(G_OBJECT(scrollBar), "scroll-event", G_CALLBACK(gtkScrollEventCallback), this); - setGtkWidget(GTK_WIDGET(scrollBar)); + setPlatformWidget(GTK_WIDGET(scrollBar)); /* - * assign a sane default width and height to the ScrollBar, otherwise + * assign a sane default width and height to the Scrollbar, otherwise * we will end up with a 0 width scrollbar. */ - resize(PlatformScrollbar::horizontalScrollbarHeight(), - PlatformScrollbar::verticalScrollbarWidth()); + resize(ScrollbarTheme::nativeTheme()->scrollbarThickness(), + ScrollbarTheme::nativeTheme()->scrollbarThickness()); } -PlatformScrollbar::~PlatformScrollbar() +ScrollbarGtk::~ScrollbarGtk() { /* * the Widget does not take over ownership. */ - g_signal_handlers_disconnect_by_func(G_OBJECT(gtkWidget()), (gpointer)PlatformScrollbar::gtkValueChanged, this); - g_signal_handlers_disconnect_by_func(G_OBJECT(gtkWidget()), (gpointer)gtkScrollEventCallback, this); - g_object_unref(G_OBJECT(gtkWidget())); + g_signal_handlers_disconnect_by_func(G_OBJECT(platformWidget()), (gpointer)ScrollbarGtk::gtkValueChanged, this); + g_signal_handlers_disconnect_by_func(G_OBJECT(platformWidget()), (gpointer)gtkScrollEventCallback, this); + g_object_unref(G_OBJECT(platformWidget())); } -int PlatformScrollbar::width() const +void ScrollbarGtk::frameRectsChanged() const { - return Widget::width(); -} + if (!parent() || !parent()->isScrollViewScrollbar(this)) + return; -int PlatformScrollbar::height() const -{ - return Widget::height(); -} + IntPoint loc = parent()->convertToContainingWindow(frameRect().location()); -void PlatformScrollbar::setEnabled(bool enabled) -{ - Widget::setEnabled(enabled); -} + // Don't allow the allocation size to be negative + IntSize sz = frameRect().size(); + sz.clampNegativeToZero(); -void PlatformScrollbar::paint(GraphicsContext* graphicsContext, const IntRect& damageRect) -{ - Widget::paint(graphicsContext, damageRect); + GtkAllocation allocation = { loc.x(), loc.y(), sz.width(), sz.height() }; + gtk_widget_size_allocate(platformWidget(), &allocation); } -void PlatformScrollbar::updateThumbPosition() +void ScrollbarGtk::updateThumbPosition() { if (m_adjustment->value != m_currentPos) { m_adjustment->value = m_currentPos; @@ -98,7 +99,7 @@ void PlatformScrollbar::updateThumbPosition() } } -void PlatformScrollbar::updateThumbProportion() +void ScrollbarGtk::updateThumbProportion() { m_adjustment->step_increment = m_lineStep; m_adjustment->page_increment = m_pageStep; @@ -107,13 +108,13 @@ void PlatformScrollbar::updateThumbProportion() gtk_adjustment_changed(m_adjustment); } -void PlatformScrollbar::setRect(const IntRect& rect) +void ScrollbarGtk::setFrameRect(const IntRect& rect) { - setFrameGeometry(rect); - geometryChanged(); + Widget::setFrameRect(rect); + frameRectsChanged(); } -void PlatformScrollbar::geometryChanged() +void ScrollbarGtk::frameRectsChanged() { if (!parent()) return; @@ -121,36 +122,29 @@ void PlatformScrollbar::geometryChanged() ASSERT(parent()->isFrameView()); FrameView* frameView = static_cast<FrameView*>(parent()); - IntRect windowRect = IntRect(frameView->contentsToWindow(frameGeometry().location()), frameGeometry().size()); - GtkAllocation allocation = { windowRect.x(), windowRect.y(), windowRect.width(), windowRect.height() }; - gtk_widget_size_allocate(gtkWidget(), &allocation); + IntRect windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size()); + + IntSize sz = frameRect().size(); + sz.clampNegativeToZero(); + + GtkAllocation allocation = { windowRect.x(), windowRect.y(), sz.width(), sz.height() }; + gtk_widget_size_allocate(platformWidget(), &allocation); } -void PlatformScrollbar::gtkValueChanged(GtkAdjustment*, PlatformScrollbar* that) +void ScrollbarGtk::gtkValueChanged(GtkAdjustment*, ScrollbarGtk* that) { that->setValue(static_cast<int>(gtk_adjustment_get_value(that->m_adjustment))); } -static int scrollbarSize() +void ScrollbarGtk::setEnabled(bool shouldEnable) { - static int size = 0; - - if (size) - return size; - - MozGtkScrollbarMetrics metrics; - moz_gtk_get_scrollbar_metrics(&metrics); - size = metrics.slider_width; - - return size; + if (enabled() == shouldEnable) + return; + + Scrollbar::setEnabled(shouldEnable); + if (platformWidget()) + gtk_widget_set_sensitive(platformWidget(), shouldEnable); } -int PlatformScrollbar::horizontalScrollbarHeight() -{ - return scrollbarSize(); -} -int PlatformScrollbar::verticalScrollbarWidth() -{ - return scrollbarSize(); -} + diff --git a/WebCore/platform/gtk/PlatformScrollBar.h b/WebCore/platform/gtk/ScrollbarGtk.h index 88bbfa4..50e9184 100644 --- a/WebCore/platform/gtk/PlatformScrollBar.h +++ b/WebCore/platform/gtk/ScrollbarGtk.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2006, 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 @@ -23,42 +23,45 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PlatformScrollBar_h -#define PlatformScrollBar_h +#ifndef ScrollbarGtk_h +#define ScrollbarGtk_h -#include "Widget.h" -#include "ScrollBar.h" +#include "Scrollbar.h" +#include <wtf/PassRefPtr.h> typedef struct _GtkAdjustment GtkAdjustment; namespace WebCore { -class PlatformScrollbar : public Widget, public Scrollbar { +class ScrollbarGtk : public Scrollbar { public: - PlatformScrollbar(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize); - virtual ~PlatformScrollbar(); + friend class Scrollbar; - virtual bool isWidget() const { return true; } + virtual ~ScrollbarGtk(); + + virtual void setFrameRect(const IntRect&); + + virtual bool handleMouseMoveEvent(const PlatformMouseEvent&) { return false; } + virtual bool handleMouseOutEvent(const PlatformMouseEvent&) { return false; } + virtual bool handleMousePressEvent(const PlatformMouseEvent&) { return false; } + virtual bool handleMouseReleaseEvent(const PlatformMouseEvent&) { return false; } - virtual int width() const; - virtual int height() const; - virtual void setRect(const IntRect&); virtual void setEnabled(bool); - virtual void paint(GraphicsContext*, const IntRect& damageRect); - static int horizontalScrollbarHeight(); - static int verticalScrollbarWidth(); + virtual void frameRectsChanged() const; protected: + ScrollbarGtk(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize); + virtual void updateThumbPosition(); virtual void updateThumbProportion(); - virtual void geometryChanged(); - + virtual void frameRectsChanged(); + private: - static void gtkValueChanged(GtkAdjustment*, PlatformScrollbar*); + static void gtkValueChanged(GtkAdjustment*, ScrollbarGtk*); GtkAdjustment* m_adjustment; }; } -#endif // PlatformScrollBar_h +#endif // ScrollbarGtk_h diff --git a/WebCore/platform/gtk/ScrollbarThemeGtk.cpp b/WebCore/platform/gtk/ScrollbarThemeGtk.cpp new file mode 100644 index 0000000..fee2c70 --- /dev/null +++ b/WebCore/platform/gtk/ScrollbarThemeGtk.cpp @@ -0,0 +1,56 @@ +/* + * 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 "gtkdrawing.h" +#include <gtk/gtk.h> + +namespace WebCore { + +ScrollbarTheme* ScrollbarTheme::nativeTheme() +{ + static ScrollbarThemeGtk theme; + return &theme; +} + +ScrollbarThemeGtk::~ScrollbarThemeGtk() +{ +} + +int ScrollbarThemeGtk::scrollbarThickness(ScrollbarControlSize controlSize) +{ + static int size; + if (!size) { + MozGtkScrollbarMetrics metrics; + moz_gtk_get_scrollbar_metrics(&metrics); + size = metrics.slider_width; + } + return size; +} + +} + diff --git a/WebCore/platform/gtk/ScrollbarThemeGtk.h b/WebCore/platform/gtk/ScrollbarThemeGtk.h new file mode 100644 index 0000000..21ccb43 --- /dev/null +++ b/WebCore/platform/gtk/ScrollbarThemeGtk.h @@ -0,0 +1,41 @@ +/* + * 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 "ScrollbarTheme.h" + +namespace WebCore { + +class ScrollbarThemeGtk : public ScrollbarTheme { +public: + virtual ~ScrollbarThemeGtk(); + + virtual int scrollbarThickness(ScrollbarControlSize = RegularScrollbar); +}; + +} +#endif diff --git a/WebCore/platform/gtk/SearchPopupMenuGtk.cpp b/WebCore/platform/gtk/SearchPopupMenuGtk.cpp index e7469c8..fbaa527 100644 --- a/WebCore/platform/gtk/SearchPopupMenuGtk.cpp +++ b/WebCore/platform/gtk/SearchPopupMenuGtk.cpp @@ -40,7 +40,7 @@ void SearchPopupMenu::loadRecentSearches(const AtomicString&, Vector<String>&) bool SearchPopupMenu::enabled() { notImplemented(); - return true; + return false; } } diff --git a/WebCore/platform/gtk/SharedBufferGtk.cpp b/WebCore/platform/gtk/SharedBufferGtk.cpp new file mode 100644 index 0000000..783fec6 --- /dev/null +++ b/WebCore/platform/gtk/SharedBufferGtk.cpp @@ -0,0 +1,53 @@ +/* + * 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 "CString.h" +#include "FileSystem.h" + +#include <glib.h> + + +namespace WebCore { + +PassRefPtr<SharedBuffer> SharedBuffer::createWithContentsOfFile(const String& filePath) +{ + if (filePath.isEmpty()) + return 0; + + gchar* filename = filenameFromString(filePath); + gchar* contents; + gsize size; + GError* error = 0; + if (!g_file_get_contents(filename, &contents, &size, &error)) { + LOG_ERROR("Failed to fully read contents of file %s - %s", filenameForDisplay(filePath).utf8().data(), error->message); + g_error_free(error); + g_free(filename); + return 0; + } + + RefPtr<SharedBuffer> result = SharedBuffer::create(contents, size); + g_free(filename); + g_free(contents); + + return result.release(); +} + +} // namespace WebCore diff --git a/WebCore/platform/gtk/SharedTimerGtk.cpp b/WebCore/platform/gtk/SharedTimerGtk.cpp index a537ffb..e0a5b60 100644 --- a/WebCore/platform/gtk/SharedTimerGtk.cpp +++ b/WebCore/platform/gtk/SharedTimerGtk.cpp @@ -64,7 +64,7 @@ void setSharedTimerFireTime(double fireTime) stopSharedTimer(); if (intervalInMS == 0) - sharedTimer = g_idle_add_full(G_PRIORITY_DEFAULT, timeout_cb, NULL, NULL); + sharedTimer = g_idle_add(timeout_cb, NULL); else sharedTimer = g_timeout_add_full(G_PRIORITY_DEFAULT, intervalInMS, timeout_cb, NULL, NULL); } diff --git a/WebCore/platform/gtk/TemporaryLinkStubs.cpp b/WebCore/platform/gtk/TemporaryLinkStubs.cpp index 525a6f8..5093794 100644 --- a/WebCore/platform/gtk/TemporaryLinkStubs.cpp +++ b/WebCore/platform/gtk/TemporaryLinkStubs.cpp @@ -28,14 +28,13 @@ #include "config.h" #include "AXObjectCache.h" +#include "DNS.h" #include "Editor.h" #include "FrameView.h" #include "FTPDirectoryDocument.h" -#include "GlobalHistory.h" -#include "KURL.h" #include "NotImplemented.h" -#include "PluginInfoStore.h" -#include "SharedBuffer.h" +#include "PluginView.h" +#include <float.h> using namespace WebCore; @@ -57,25 +56,13 @@ Vector<char> loadResourceIntoArray(const char* resourceName) /* Completely empty stubs (mostly to allow DRT to run): */ /********************************************************/ -bool WebCore::historyContains(const UChar*, unsigned) { return false; } +void PluginView::invalidateRegion(NPRegion) { notImplemented(); } -PluginInfo* PluginInfoStore::createPluginInfoForPluginAtIndex(unsigned) { notImplemented(); return 0;} -unsigned PluginInfoStore::pluginCount() const { notImplemented(); return 0; } -String PluginInfoStore::pluginNameForMIMEType(const String& mimeType) { notImplemented(); return String(); } -bool WebCore::PluginInfoStore::supportsMIMEType(const WebCore::String&) { notImplemented(); return false; } -void WebCore::refreshPlugins(bool) { notImplemented(); } - - -Color WebCore::focusRingColor() { return 0xFF0000FF; } -void WebCore::setFocusRingColorChangeFunction(void (*)()) { } +Color WebCore::focusRingColor() { return Color(); } namespace WebCore { -Vector<String> supportedKeySizes() { notImplemented(); return Vector<String>(); } +void getSupportedKeySizes(Vector<String>&) { notImplemented(); } String signedPublicKeyAndChallengeString(unsigned keySizeIndex, const String &challengeString, const KURL &url) { return String(); } -float userIdleTime() { notImplemented(); return 0.0; } +float userIdleTime() { notImplemented(); return FLT_MAX; } // return an arbitrarily high userIdleTime so that releasing pages from the page cache isn't postponed -String KURL::fileSystemPath() const { notImplemented(); return String(); } - -PassRefPtr<SharedBuffer> SharedBuffer::createWithContentsOfFile(const String&) { notImplemented(); return 0; } } - diff --git a/WebCore/platform/gtk/ThreadingGtk.cpp b/WebCore/platform/gtk/ThreadingGtk.cpp deleted file mode 100644 index 5ce6ba2..0000000 --- a/WebCore/platform/gtk/ThreadingGtk.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) - * - * 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. - * 3. Neither the name of Apple Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "Threading.h" - -#include "HashMap.h" -#include "Logging.h" - -#include <glib.h> - -namespace WebCore { - -struct FunctionWithContext { - MainThreadFunction* function; - void* context; -}; - -static gboolean callFunctionOnMainThread(gpointer data) -{ - FunctionWithContext* functionWithContext = static_cast<FunctionWithContext*>(data); - functionWithContext->function(functionWithContext->context); - delete functionWithContext; - return FALSE; -} - -void callOnMainThread(MainThreadFunction* function, void* context) -{ - ASSERT(function); - FunctionWithContext* functionWithContext = new FunctionWithContext; - functionWithContext->function = function; - functionWithContext->context = context; - g_timeout_add(0, callFunctionOnMainThread, functionWithContext); -} - -void initializeThreading() -{ - if (!g_thread_supported()) - g_thread_init(NULL); - ASSERT(g_thread_supported()); -} - -static Mutex& threadMapMutex() -{ - static Mutex mutex; - return mutex; -} - -static HashMap<ThreadIdentifier, GThread*>& threadMap() -{ - static HashMap<ThreadIdentifier, GThread*> map; - return map; -} - -static ThreadIdentifier establishIdentifierForThread(GThread*& thread) -{ - MutexLocker locker(threadMapMutex()); - - static ThreadIdentifier identifierCount = 1; - - threadMap().add(identifierCount, thread); - - return identifierCount++; -} - -static ThreadIdentifier identifierByGthreadHandle(GThread*& thread) -{ - MutexLocker locker(threadMapMutex()); - - HashMap<ThreadIdentifier, GThread*>::iterator i = threadMap().begin(); - for (; i != threadMap().end(); ++i) { - if (i->second == thread) - return i->first; - } - - return 0; -} - -static GThread* threadForIdentifier(ThreadIdentifier id) -{ - MutexLocker locker(threadMapMutex()); - - return threadMap().get(id); -} - -static void clearThreadForIdentifier(ThreadIdentifier id) -{ - MutexLocker locker(threadMapMutex()); - - ASSERT(threadMap().contains(id)); - - threadMap().remove(id); -} - -ThreadIdentifier createThread(ThreadFunction entryPoint, void* data) -{ - GThread* thread; - if (!(thread = g_thread_create(entryPoint, data, TRUE, 0))) { - LOG_ERROR("Failed to create thread at entry point %p with data %p", entryPoint, data); - return 0; - } - - ThreadIdentifier threadID = establishIdentifierForThread(thread); - LOG(Threading, "Created thread with thread id %u", threadID); - return threadID; -} - -int waitForThreadCompletion(ThreadIdentifier threadID, void** result) -{ - ASSERT(threadID); - - GThread* thread = threadForIdentifier(threadID); - - *result = g_thread_join(thread); - - clearThreadForIdentifier(threadID); - return 0; -} - -void detachThread(ThreadIdentifier) -{ -} - -ThreadIdentifier currentThread() -{ - GThread* currentThread = g_thread_self(); - if (ThreadIdentifier id = identifierByGthreadHandle(currentThread)) - return id; - return establishIdentifierForThread(currentThread); -} - -Mutex::Mutex() - : m_mutex(g_mutex_new()) -{ -} - -Mutex::~Mutex() -{ - g_mutex_free(m_mutex); -} - -void Mutex::lock() -{ - g_mutex_lock(m_mutex); -} - -bool Mutex::tryLock() -{ - return g_mutex_trylock(m_mutex); -} - -void Mutex::unlock() -{ - g_mutex_unlock(m_mutex); -} - -ThreadCondition::ThreadCondition() - : m_condition(g_cond_new()) -{ -} - -ThreadCondition::~ThreadCondition() -{ - g_cond_free(m_condition); -} - -void ThreadCondition::wait(Mutex& mutex) -{ - g_cond_wait(m_condition, mutex.impl()); -} - -void ThreadCondition::signal() -{ - g_cond_signal(m_condition); -} - -void ThreadCondition::broadcast() -{ - g_cond_broadcast(m_condition); -} - - -} diff --git a/WebCore/platform/gtk/WheelEventGtk.cpp b/WebCore/platform/gtk/WheelEventGtk.cpp index 3368f25..fbe31f7 100644 --- a/WebCore/platform/gtk/WheelEventGtk.cpp +++ b/WebCore/platform/gtk/WheelEventGtk.cpp @@ -29,9 +29,11 @@ #include "PlatformWheelEvent.h" #include <gdk/gdk.h> +#include <gtk/gtkversion.h> namespace WebCore { +// Keep this in sync with the other platform event constructors PlatformWheelEvent::PlatformWheelEvent(GdkEventScroll* event) { static const float delta = 1; @@ -57,12 +59,21 @@ PlatformWheelEvent::PlatformWheelEvent(GdkEventScroll* event) m_position = IntPoint((int)event->x, (int)event->y); m_globalPosition = IntPoint((int)event->x_root, (int)event->y_root); + m_granularity = ScrollByLineWheelEvent; 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_MOD2_MASK; - m_isContinuous = false; +#if GTK_CHECK_VERSION(2,10,0) + m_metaKey = event->state & GDK_META_MASK; +#else + // GDK_MOD2_MASK doesn't always mean meta so we can't use it + m_metaKey = false; +#endif + + // FIXME: retrieve the user setting for the number of lines to scroll on each wheel event + m_deltaX *= horizontalLineMultiplier(); + m_deltaY *= verticalLineMultiplier(); } } diff --git a/WebCore/platform/gtk/WidgetGtk.cpp b/WebCore/platform/gtk/WidgetGtk.cpp index 1d5f021..82fed74 100644 --- a/WebCore/platform/gtk/WidgetGtk.cpp +++ b/WebCore/platform/gtk/WidgetGtk.cpp @@ -31,6 +31,7 @@ #include "Cursor.h" #include "FrameView.h" #include "GraphicsContext.h" +#include "HostWindow.h" #include "IntRect.h" #include "NotImplemented.h" #include "RenderObject.h" @@ -42,97 +43,38 @@ namespace WebCore { class WidgetPrivate { public: - GtkWidget* widget; - WidgetClient* client; - IntRect frameRect; - - ScrollView* parent; - GtkContainer* containingWindow; - bool suppressInvalidation; GdkCursor* cursor; - - GdkDrawable* gdkDrawable() const - { - return widget ? widget->window : 0; - } }; -Widget::Widget() - : data(new WidgetPrivate) -{ - data->widget = 0; - data->parent = 0; - data->containingWindow = 0; - data->suppressInvalidation = false; - data->cursor = 0; -} - -GtkWidget* Widget::gtkWidget() const -{ - return data->widget; -} - -void Widget::setGtkWidget(GtkWidget* widget) +Widget::Widget(PlatformWidget widget) + : m_data(new WidgetPrivate) { - data->widget = widget; + init(widget); + m_data->cursor = 0; } Widget::~Widget() { ASSERT(!parent()); - delete data; -} - -void Widget::setContainingWindow(GtkContainer* containingWindow) -{ - data->containingWindow = containingWindow; -} - -GtkContainer* Widget::containingWindow() const -{ - return data->containingWindow; -} - -void Widget::setClient(WidgetClient* c) -{ - data->client = c; -} - -WidgetClient* Widget::client() const -{ - return data->client; -} - -IntRect Widget::frameGeometry() const -{ - return data->frameRect; -} - -void Widget::setFrameGeometry(const IntRect& r) -{ - data->frameRect = r; -} - -void Widget::setParent(ScrollView* v) -{ - data->parent = v; -} - -ScrollView* Widget::parent() const -{ - return data->parent; + releasePlatformWidget(); + delete m_data; } void Widget::setFocus() { - gtk_widget_grab_focus(gtkWidget() ? gtkWidget() : GTK_WIDGET(containingWindow())); + gtk_widget_grab_focus(platformWidget() ? platformWidget() : GTK_WIDGET(root()->hostWindow()->platformWindow())); } Cursor Widget::cursor() { - return Cursor(data->cursor); + return Cursor(m_data->cursor); } +static GdkDrawable* gdkDrawable(PlatformWidget widget) +{ + return widget ? widget->window : 0; +} + void Widget::setCursor(const Cursor& cursor) { GdkCursor* pcur = cursor.impl(); @@ -143,62 +85,42 @@ void Widget::setCursor(const Cursor& cursor) // gdk_window_set_cursor() in certain GDK backends seems to be an // expensive operation, so avoid it if possible. - if (pcur == data->cursor) + if (pcur == m_data->cursor) return; - gdk_window_set_cursor(data->gdkDrawable() ? GDK_WINDOW(data->gdkDrawable()) : GTK_WIDGET(containingWindow())->window, pcur); - data->cursor = pcur; + gdk_window_set_cursor(gdkDrawable(platformWidget()) ? GDK_WINDOW(gdkDrawable(platformWidget())) : GTK_WIDGET(root()->hostWindow()->platformWindow())->window, pcur); + m_data->cursor = pcur; } void Widget::show() { - if (!gtkWidget()) + if (!platformWidget()) return; - gtk_widget_show(gtkWidget()); + gtk_widget_show(platformWidget()); } void Widget::hide() { - if (!gtkWidget()) + if (!platformWidget()) return; - gtk_widget_hide(gtkWidget()); -} - -void Widget::setEnabled(bool shouldEnable) -{ - if (!gtkWidget()) - return; - gtk_widget_set_sensitive(gtkWidget(), shouldEnable); -} - -bool Widget::isEnabled() const -{ - if (!gtkWidget()) - return false; - return GTK_WIDGET_IS_SENSITIVE(gtkWidget()); -} - -void Widget::removeFromParent() -{ - if (parent()) - parent()->removeChild(this); + gtk_widget_hide(platformWidget()); } /* * Strategy to painting a Widget: * 1.) do not paint if there is no GtkWidget set - * 2.) We assume that GTK_NO_WINDOW is set and that geometryChanged positioned + * 2.) We assume that GTK_NO_WINDOW is set and that frameRectsChanged positioned * the widget correctly. ATM we do not honor the GraphicsContext translation. */ void Widget::paint(GraphicsContext* context, const IntRect&) { - if (!gtkWidget()) + if (!platformWidget()) return; if (!context->gdkExposeEvent()) return; - GtkWidget* widget = gtkWidget(); + GtkWidget* widget = platformWidget(); ASSERT(GTK_WIDGET_NO_WINDOW(widget)); GdkEvent* event = gdk_event_new(GDK_EXPOSE); @@ -226,85 +148,33 @@ void Widget::setIsSelected(bool) notImplemented(); } -void Widget::invalidate() +IntRect Widget::frameRect() const { - invalidateRect(IntRect(0, 0, width(), height())); + return m_frame; } -void Widget::invalidateRect(const IntRect& rect) +void Widget::setFrameRect(const IntRect& rect) { - if (data->suppressInvalidation) - return; - - if (!parent()) { - gtk_widget_queue_draw_area(GTK_WIDGET(containingWindow()), rect.x(), rect.y(), - rect.width(), rect.height()); - if (isFrameView()) - static_cast<FrameView*>(this)->addToDirtyRegion(rect); - return; - } - - // Get the root widget. - ScrollView* outermostView = parent(); - while (outermostView && outermostView->parent()) - outermostView = outermostView->parent(); - if (!outermostView) - return; - - IntRect windowRect = convertToContainingWindow(rect); - gtk_widget_queue_draw_area(GTK_WIDGET(containingWindow()), windowRect.x(), windowRect.y(), - windowRect.width(), windowRect.height()); - outermostView->addToDirtyRegion(windowRect); + m_frame = rect; } -IntPoint Widget::convertToContainingWindow(const IntPoint& point) const +void Widget::releasePlatformWidget() { - IntPoint windowPoint = point; - for (const Widget *parentWidget = parent(), *childWidget = this; - parentWidget; - childWidget = parentWidget, parentWidget = parentWidget->parent()) - windowPoint = parentWidget->convertChildToSelf(childWidget, windowPoint); - return windowPoint; -} - -IntPoint Widget::convertFromContainingWindow(const IntPoint& point) const -{ - IntPoint widgetPoint = point; - for (const Widget *parentWidget = parent(), *childWidget = this; - parentWidget; - childWidget = parentWidget, parentWidget = parentWidget->parent()) - widgetPoint = parentWidget->convertSelfToChild(childWidget, widgetPoint); - return widgetPoint; -} - -IntRect Widget::convertToContainingWindow(const IntRect& rect) const -{ - IntRect convertedRect = rect; - convertedRect.setLocation(convertToContainingWindow(convertedRect.location())); - return convertedRect; -} - -IntPoint Widget::convertChildToSelf(const Widget* child, const IntPoint& point) const -{ - return IntPoint(point.x() + child->x(), point.y() + child->y()); -} - -IntPoint Widget::convertSelfToChild(const Widget* child, const IntPoint& point) const -{ - return IntPoint(point.x() - child->x(), point.y() - child->y()); -} - -bool Widget::suppressInvalidation() const -{ - return data->suppressInvalidation; + if (!platformWidget()) + return; + g_object_unref(platformWidget()); } -void Widget::setSuppressInvalidation(bool suppress) +void Widget::retainPlatformWidget() { - data->suppressInvalidation = suppress; + if (!platformWidget()) + return; +#if GLIB_CHECK_VERSION(2,10,0) + g_object_ref_sink(platformWidget()); +#else + g_object_ref(platformWidget()); + gtk_object_sink(GTK_OBJECT(platformWidget())); +#endif } -void Widget::geometryChanged() const -{ -} } diff --git a/WebCore/platform/gtk/gtk2drawing.c b/WebCore/platform/gtk/gtk2drawing.c index 475e9fd..83b4cb4 100644 --- a/WebCore/platform/gtk/gtk2drawing.c +++ b/WebCore/platform/gtk/gtk2drawing.c @@ -23,6 +23,7 @@ * 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 @@ -57,6 +58,7 @@ static GtkWidget* gProtoWindow; static GtkWidget* gButtonWidget; static GtkWidget* gToggleButtonWidget; +static GtkWidget* gButtonArrowWidget; static GtkWidget* gCheckboxWidget; static GtkWidget* gRadiobuttonWidget; static GtkWidget* gHorizScrollbarWidget; @@ -65,11 +67,14 @@ static GtkWidget* gSpinWidget; static GtkWidget* gHScaleWidget; static GtkWidget* gVScaleWidget; static GtkWidget* gEntryWidget; -static GtkWidget* gArrowWidget; -static GtkWidget* gOptionMenuWidget; +static GtkWidget* gComboBoxWidget; +static GtkWidget* gComboBoxButtonWidget; +static GtkWidget* gComboBoxArrowWidget; +static GtkWidget* gComboBoxSeparatorWidget; static GtkWidget* gComboBoxEntryWidget; -static GtkWidget* gDropdownEntryWidget; -static GtkWidget* gDropdownButtonWidget; +static GtkWidget* gComboBoxEntryTextareaWidget; +static GtkWidget* gComboBoxEntryButtonWidget; +static GtkWidget* gComboBoxEntryArrowWidget; static GtkWidget* gHandleBoxWidget; static GtkWidget* gToolbarWidget; static GtkWidget* gFrameWidget; @@ -81,8 +86,10 @@ static GtkWidget* gMenuBarWidget; static GtkWidget* gMenuBarItemWidget; static GtkWidget* gMenuPopupWidget; static GtkWidget* gMenuItemWidget; +static GtkWidget* gImageMenuItemWidget; static GtkWidget* gCheckMenuItemWidget; static GtkWidget* gTreeViewWidget; +static GtkWidget* gMiddleTreeViewColumn; static GtkWidget* gTreeHeaderCellWidget; static GtkWidget* gTreeHeaderSortArrowWidget; static GtkWidget* gExpanderWidget; @@ -90,14 +97,22 @@ static GtkWidget* gToolbarSeparatorWidget; static GtkWidget* gMenuSeparatorWidget; static GtkWidget* gHPanedWidget; static GtkWidget* gVPanedWidget; - -static GtkShadowType gMenuBarShadowType; -static GtkShadowType gToolbarShadowType; +static GtkWidget* gScrolledWindowWidget; static style_prop_t style_prop_func; -static gboolean have_menu_shadow_type; +static gboolean have_arrow_scaling; +static gboolean have_2_10; static gboolean is_initialized; +/* 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) { @@ -111,6 +126,7 @@ ensure_window_widget() if (!gProtoWindow) { gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP); gtk_widget_realize(gProtoWindow); + moz_gtk_set_widget_name(gProtoWindow); } return MOZ_GTK_SUCCESS; } @@ -127,6 +143,7 @@ setup_widget_prototype(GtkWidget* widget) gtk_container_add(GTK_CONTAINER(protoLayout), widget); gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", TRUE); return MOZ_GTK_SUCCESS; } @@ -173,6 +190,19 @@ ensure_toggle_button_widget() } static gint +ensure_button_arrow_widget() +{ + if (!gButtonArrowWidget) { + ensure_toggle_button_widget(); + + gButtonArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_container_add(GTK_CONTAINER(gToggleButtonWidget), gButtonArrowWidget); + gtk_widget_realize(gButtonArrowWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint ensure_checkbox_widget() { if (!gCheckboxWidget) { @@ -240,64 +270,219 @@ ensure_entry_widget() return MOZ_GTK_SUCCESS; } -static gint -ensure_option_menu_widget() -{ - if (!gOptionMenuWidget) { - gOptionMenuWidget = gtk_option_menu_new(); - setup_widget_prototype(gOptionMenuWidget); +/* 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 gProtoWindow 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)) { + gComboBoxButtonWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxButtonWidget); + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", TRUE); } - return MOZ_GTK_SUCCESS; } -static gint -ensure_combo_box_entry_widget() -{ - if (!gComboBoxEntryWidget) { - gComboBoxEntryWidget = gtk_combo_box_entry_new(); - setup_widget_prototype(gComboBoxEntryWidget); - } - return MOZ_GTK_SUCCESS; +static void +moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget, + gpointer client_data) +{ + if (GTK_IS_SEPARATOR(widget)) { + gComboBoxSeparatorWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxSeparatorWidget); + } else if (GTK_IS_ARROW(widget)) { + gComboBoxArrowWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxArrowWidget); + } else + return; + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", TRUE); } static gint -ensure_dropdown_entry_widget() +ensure_combo_box_widgets() { - if (!gDropdownEntryWidget) { - ensure_combo_box_entry_widget(); + GtkWidget* buttonChild; + + if (gComboBoxButtonWidget && gComboBoxArrowWidget) + return MOZ_GTK_SUCCESS; + + /* Create a ComboBox if needed */ + if (!gComboBoxWidget) { + gComboBoxWidget = gtk_combo_box_new(); + setup_widget_prototype(gComboBoxWidget); + } + + /* Get its inner Button */ + gtk_container_forall(GTK_CONTAINER(gComboBoxWidget), + moz_gtk_get_combo_box_inner_button, + NULL); + + if (gComboBoxButtonWidget) { + /* Get the widgets inside the Button */ + buttonChild = GTK_BIN(gComboBoxButtonWidget)->child; + 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 */ + gComboBoxArrowWidget = buttonChild; + g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) + &gComboBoxArrowWidget); + gtk_widget_realize(gComboBoxArrowWidget); + g_object_set_data(G_OBJECT(gComboBoxArrowWidget), + "transparent-bg-hint", 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(); + gComboBoxButtonWidget = gToggleButtonWidget; + } - gDropdownEntryWidget = GTK_BIN(gComboBoxEntryWidget)->child; - gtk_widget_realize(gDropdownEntryWidget); + if (!gComboBoxArrowWidget) { + /* Shouldn't be reached with current internal gtk implementation; + * we gButtonArrowWidget as last resort fallback to avoid + * crashing. */ + ensure_button_arrow_widget(); + gComboBoxArrowWidget = gButtonArrowWidget; } + + /* We don't test the validity of gComboBoxSeparatorWidget 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; } +/* We need to have pointers to the inner widgets (entry, button, arrow) of + * the ComboBoxEntry 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 gProtoWindow and as + * such GTK holds a strong reference to them. */ static void -moz_gtk_get_dropdown_button(GtkWidget *widget, - gpointer client_data) -{ - if (GTK_IS_TOGGLE_BUTTON(widget)) - gDropdownButtonWidget = widget; +moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget, + gpointer client_data) +{ + if (GTK_IS_TOGGLE_BUTTON(widget)) { + gComboBoxEntryButtonWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxEntryButtonWidget); + } else if (GTK_IS_ENTRY(widget)) { + gComboBoxEntryTextareaWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxEntryTextareaWidget); + } else + return; + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", TRUE); +} + +static void +moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data) +{ + if (GTK_IS_ARROW(widget)) { + gComboBoxEntryArrowWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxEntryArrowWidget); + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", TRUE); + } } static gint -ensure_arrow_widget() +ensure_combo_box_entry_widgets() { - if (!gArrowWidget) { - ensure_combo_box_entry_widget(); + GtkWidget* buttonChild; - gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget), - moz_gtk_get_dropdown_button, - NULL); + if (gComboBoxEntryTextareaWidget && + gComboBoxEntryButtonWidget && + gComboBoxEntryArrowWidget) + return MOZ_GTK_SUCCESS; + + /* Create a ComboBoxEntry if needed */ + if (!gComboBoxEntryWidget) { + gComboBoxEntryWidget = gtk_combo_box_entry_new(); + setup_widget_prototype(gComboBoxEntryWidget); + } - gArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); - gtk_container_add(GTK_CONTAINER(GTK_BIN(gDropdownButtonWidget)->child), - gArrowWidget); - gtk_widget_realize(gArrowWidget); + /* Get its inner Entry and Button */ + gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget), + moz_gtk_get_combo_box_entry_inner_widgets, + NULL); + + if (!gComboBoxEntryTextareaWidget) { + ensure_entry_widget(); + gComboBoxEntryTextareaWidget = gEntryWidget; + } + + if (gComboBoxEntryButtonWidget) { + /* Get the Arrow inside the Button */ + buttonChild = GTK_BIN(gComboBoxEntryButtonWidget)->child; + if (GTK_IS_HBOX(buttonChild)) { + /* appears-as-list = FALSE, cell-view = TRUE; the button + * contains an hbox. This hbox is there because ComboBoxEntry + * inherits from ComboBox which needs to place a cell renderer, + * a separator, and an arrow in the button when appears-as-list + * is FALSE. Here the hbox should only contain an arrow, since + * a ComboBoxEntry doesn't need all those widgets in the + * button. */ + gtk_container_forall(GTK_CONTAINER(buttonChild), + moz_gtk_get_combo_box_entry_arrow, + NULL); + } else if(GTK_IS_ARROW(buttonChild)) { + /* appears-as-list = TRUE, or cell-view = FALSE; + * the button only contains an arrow */ + gComboBoxEntryArrowWidget = buttonChild; + g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) + &gComboBoxEntryArrowWidget); + gtk_widget_realize(gComboBoxEntryArrowWidget); + g_object_set_data(G_OBJECT(gComboBoxEntryArrowWidget), + "transparent-bg-hint", 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(); + gComboBoxEntryButtonWidget = gToggleButtonWidget; } + + if (!gComboBoxEntryArrowWidget) { + /* Shouldn't be reached with current internal gtk implementation; + * we gButtonArrowWidget as last resort fallback to avoid + * crashing. */ + ensure_button_arrow_widget(); + gComboBoxEntryArrowWidget = gButtonArrowWidget; + } + return MOZ_GTK_SUCCESS; } + static gint ensure_handlebox_widget() { @@ -316,8 +501,7 @@ ensure_toolbar_widget() gToolbarWidget = gtk_toolbar_new(); gtk_container_add(GTK_CONTAINER(gHandleBoxWidget), gToolbarWidget); gtk_widget_realize(gToolbarWidget); - gtk_widget_style_get(gToolbarWidget, "shadow_type", &gToolbarShadowType, - NULL); + g_object_set_data(G_OBJECT(gToolbarWidget), "transparent-bg-hint", TRUE); } return MOZ_GTK_SUCCESS; } @@ -339,6 +523,7 @@ ensure_tooltip_widget() if (!gTooltipWidget) { gTooltipWidget = gtk_window_new(GTK_WINDOW_POPUP); gtk_widget_realize(gTooltipWidget); + moz_gtk_set_widget_name(gTooltipWidget); } return MOZ_GTK_SUCCESS; } @@ -391,8 +576,6 @@ ensure_menu_bar_widget() if (!gMenuBarWidget) { gMenuBarWidget = gtk_menu_bar_new(); setup_widget_prototype(gMenuBarWidget); - gtk_widget_style_get(gMenuBarWidget, "shadow_type", &gMenuBarShadowType, - NULL); } return MOZ_GTK_SUCCESS; } @@ -406,6 +589,8 @@ ensure_menu_bar_item_widget() gtk_menu_shell_append(GTK_MENU_SHELL(gMenuBarWidget), gMenuBarItemWidget); gtk_widget_realize(gMenuBarItemWidget); + g_object_set_data(G_OBJECT(gMenuBarItemWidget), + "transparent-bg-hint", TRUE); } return MOZ_GTK_SUCCESS; } @@ -419,6 +604,8 @@ ensure_menu_popup_widget() gtk_menu_item_set_submenu(GTK_MENU_ITEM(gMenuBarItemWidget), gMenuPopupWidget); gtk_widget_realize(gMenuPopupWidget); + g_object_set_data(G_OBJECT(gMenuPopupWidget), + "transparent-bg-hint", TRUE); } return MOZ_GTK_SUCCESS; } @@ -432,6 +619,23 @@ ensure_menu_item_widget() gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), gMenuItemWidget); gtk_widget_realize(gMenuItemWidget); + g_object_set_data(G_OBJECT(gMenuItemWidget), + "transparent-bg-hint", TRUE); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_image_menu_item_widget() +{ + if (!gImageMenuItemWidget) { + ensure_menu_popup_widget(); + gImageMenuItemWidget = gtk_image_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), + gImageMenuItemWidget); + gtk_widget_realize(gImageMenuItemWidget); + g_object_set_data(G_OBJECT(gImageMenuItemWidget), + "transparent-bg-hint", TRUE); } return MOZ_GTK_SUCCESS; } @@ -445,6 +649,8 @@ ensure_menu_separator_widget() gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), gMenuSeparatorWidget); gtk_widget_realize(gMenuSeparatorWidget); + g_object_set_data(G_OBJECT(gMenuSeparatorWidget), + "transparent-bg-hint", TRUE); } return MOZ_GTK_SUCCESS; } @@ -458,6 +664,8 @@ ensure_check_menu_item_widget() gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), gCheckMenuItemWidget); gtk_widget_realize(gCheckMenuItemWidget); + g_object_set_data(G_OBJECT(gCheckMenuItemWidget), + "transparent-bg-hint", TRUE); } return MOZ_GTK_SUCCESS; } @@ -476,16 +684,45 @@ static gint ensure_tree_header_cell_widget() { if(!gTreeHeaderCellWidget) { - GtkTreeViewColumn* treeViewColumn; - ensure_tree_view_widget(); + /* + * Some GTK engines paint the first and last cell + * of a TreeView header with a highlight. + * Since we do not know where our widget will be relative + * to the other buttons in the TreeView header, we must + * paint it as a button that is between two others, + * thus ensuring it is neither the first or last button + * in the header. + * GTK doesn't give us a way to do this explicitly, + * so we must paint with a button that is between two + * others. + */ + + GtkTreeViewColumn* firstTreeViewColumn; + GtkTreeViewColumn* lastTreeViewColumn; - treeViewColumn = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(treeViewColumn, "M"); + ensure_tree_view_widget(); - gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), treeViewColumn); - gTreeHeaderCellWidget = treeViewColumn->button; - gtk_tree_view_column_set_sort_indicator(treeViewColumn, TRUE); - gTreeHeaderSortArrowWidget = treeViewColumn->arrow; + /* Create and append our three columns */ + firstTreeViewColumn = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(firstTreeViewColumn, "M"); + gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), firstTreeViewColumn); + + gMiddleTreeViewColumn = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(GTK_TREE_VIEW_COLUMN(gMiddleTreeViewColumn), "M"); + gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), + GTK_TREE_VIEW_COLUMN(gMiddleTreeViewColumn)); + + lastTreeViewColumn = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(lastTreeViewColumn, "M"); + gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn); + + /* Use the middle column's header for our button */ + gTreeHeaderCellWidget = GTK_TREE_VIEW_COLUMN(gMiddleTreeViewColumn)->button; + gTreeHeaderSortArrowWidget = GTK_TREE_VIEW_COLUMN(gMiddleTreeViewColumn)->arrow; + g_object_set_data(G_OBJECT(gTreeHeaderCellWidget), + "transparent-bg-hint", TRUE); + g_object_set_data(G_OBJECT(gTreeHeaderSortArrowWidget), + "transparent-bg-hint", TRUE); } return MOZ_GTK_SUCCESS; } @@ -500,6 +737,16 @@ ensure_expander_widget() return MOZ_GTK_SUCCESS; } +static gint +ensure_scrolled_window_widget() +{ + if (!gScrolledWindowWidget) { + gScrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL); + setup_widget_prototype(gScrolledWindowWidget); + } + return MOZ_GTK_SUCCESS; +} + static GtkStateType ConvertGtkState(GtkWidgetState* state) { @@ -566,6 +813,13 @@ moz_gtk_button_paint(GdkDrawable* drawable, GdkRectangle* rect, if (state->isDefault) GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_DEFAULT); + GTK_BUTTON(widget)->relief = 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; @@ -576,7 +830,7 @@ moz_gtk_button_paint(GdkDrawable* drawable, GdkRectangle* rect, shadow_type = button_state == GTK_STATE_ACTIVE || state->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT; - if (state->isDefault && GTK_BUTTON(widget)->relief == GTK_RELIEF_NORMAL) { + if (state->isDefault && relief == GTK_RELIEF_NORMAL) { gtk_paint_box(style, drawable, button_state, shadow_type, cliprect, widget, "buttondefault", x, y, width, height); } @@ -611,16 +865,32 @@ moz_gtk_button_paint(GdkDrawable* drawable, GdkRectangle* rect, } 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_menu_shadow_type = - (gtk_major_version > 2 || - (gtk_major_version == 2 && gtk_minor_version >= 1)); + have_arrow_scaling = (gtk_major_version > 2 || + (gtk_major_version == 2 && gtk_minor_version >= 12)); + + have_2_10 = (gtk_major_version > 2 || + (gtk_major_version == 2 && gtk_minor_version >= 10)); + + /* 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; } @@ -664,45 +934,6 @@ moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus, return MOZ_GTK_SUCCESS; } -static gint -moz_gtk_option_menu_get_metrics(gboolean* interior_focus, - GtkRequisition* indicator_size, - GtkBorder* indicator_spacing, - gint* focus_width, - gint* focus_pad) -{ - static const GtkRequisition default_indicator_size = { 7, 13 }; - static const GtkBorder default_indicator_spacing = { 7, 5, 2, 2 }; - /* these default values are not used in gtkoptionmenu.c - static const gboolean default_interior_focus = TRUE; - static const gint default_focus_width = 1; - static const gint default_focus_pad = 0; */ - GtkRequisition *tmp_indicator_size; - GtkBorder *tmp_indicator_spacing; - - gtk_widget_style_get(gOptionMenuWidget, - "interior_focus", interior_focus, - "indicator_size", &tmp_indicator_size, - "indicator_spacing", &tmp_indicator_spacing, - "focus_line_width", focus_width, - "focus_padding", focus_pad, - NULL); - - if (tmp_indicator_size) - *indicator_size = *tmp_indicator_size; - else - *indicator_size = default_indicator_size; - if (tmp_indicator_spacing) - *indicator_spacing = *tmp_indicator_spacing; - else - *indicator_spacing = default_indicator_spacing; - - gtk_requisition_free(tmp_indicator_size); - gtk_border_free(tmp_indicator_spacing); - - return MOZ_GTK_SUCCESS; -} - gint moz_gtk_splitter_get_metrics(gint orientation, gint* size) { @@ -716,6 +947,25 @@ moz_gtk_splitter_get_metrics(gint orientation, gint* size) 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 = NULL; + + if (have_2_10) + 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, @@ -726,6 +976,7 @@ moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect, 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; @@ -737,11 +988,19 @@ moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect, w = gCheckboxWidget; } - /* offset by indicator_spacing, and centered vertically within the rect */ - x = rect->x + indicator_spacing; + /* + * 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 = w->style; TSOffsetStyleGCs(style, x, y); @@ -756,8 +1015,8 @@ moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect, width, height); if (state->focused) { gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect, - gRadiobuttonWidget, "radiobutton", rect->x, rect->y, - rect->width, rect->height); + gRadiobuttonWidget, "radiobutton", focus_x, focus_y, + focus_width, focus_height); } } else { @@ -765,8 +1024,8 @@ moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect, gCheckboxWidget, "checkbutton", x, y, width, height); if (state->focused) { gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect, - gCheckboxWidget, "checkbutton", rect->x, rect->y, - rect->width, rect->height); + gCheckboxWidget, "checkbutton", focus_x, focus_y, + focus_width, focus_height); } } @@ -774,20 +1033,63 @@ moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect, } static gint -calculate_arrow_dimensions(GdkRectangle* rect, GdkRectangle* arrow_rect) +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 = button->style; + + /* 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) { - GtkMisc* misc = GTK_MISC(gArrowWidget); + /* defined in gtkarrow.c */ + gfloat arrow_scaling = 0.7; + gfloat xalign, xpad; + gint extent; + GtkMisc* misc = GTK_MISC(arrow); - gint extent = MIN(rect->width - misc->xpad * 2, - rect->height - misc->ypad * 2); + if (have_arrow_scaling) + gtk_widget_style_get(arrow, "arrow_scaling", &arrow_scaling, NULL); - arrow_rect->x = ((rect->x + misc->xpad) * (1.0 - misc->xalign) + - (rect->x + rect->width - extent - misc->xpad) * - misc->xalign); + extent = MIN((rect->width - misc->xpad * 2), + (rect->height - misc->ypad * 2)) * arrow_scaling; - arrow_rect->y = ((rect->y + misc->ypad) * (1.0 - misc->yalign) + - (rect->y + rect->height - extent - misc->ypad) * - misc->yalign); + 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; @@ -803,11 +1105,11 @@ moz_gtk_scrollbar_button_paint(GdkDrawable* drawable, GdkRectangle* rect, GtkStateType state_type = ConvertGtkState(state); GtkShadowType shadow_type = (state->active) ? GTK_SHADOW_IN : GTK_SHADOW_OUT; - GdkRectangle button_rect; GdkRectangle arrow_rect; GtkStyle* style; GtkWidget *scrollbar; GtkArrowType arrow_type; + gint arrow_displacement_x, arrow_displacement_y; const char* detail = (flags & MOZ_GTK_STEPPER_VERTICAL) ? "vscrollbar" : "hscrollbar"; @@ -860,20 +1162,26 @@ moz_gtk_scrollbar_button_paint(GdkDrawable* drawable, GdkRectangle* rect, style = scrollbar->style; - ensure_arrow_widget(); - - calculate_arrow_dimensions(rect, &button_rect); - TSOffsetStyleGCs(style, button_rect.x, button_rect.y); + TSOffsetStyleGCs(style, rect->x, rect->y); gtk_paint_box(style, drawable, state_type, shadow_type, cliprect, - scrollbar, detail, button_rect.x, button_rect.y, - button_rect.width, button_rect.height); + 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; - arrow_rect.width = button_rect.width / 2; - arrow_rect.height = button_rect.height / 2; - arrow_rect.x = button_rect.x + (button_rect.width - arrow_rect.width) / 2; - arrow_rect.y = button_rect.y + - (button_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, @@ -928,9 +1236,11 @@ moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, { 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(); @@ -971,10 +1281,18 @@ moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, gtk_adjustment_changed(adj); style = GTK_WIDGET(scrollbar)->style; + + 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, GTK_SHADOW_OUT, cliprect, + 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) ? @@ -1167,52 +1485,87 @@ moz_gtk_vpaned_paint(GdkDrawable* drawable, GdkRectangle* rect, } static gint +moz_gtk_caret_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + ensure_entry_widget(); + gtk_draw_insertion_cursor(gEntryWidget, drawable, cliprect, + rect, TRUE, direction, FALSE); + + 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 = widget->style; - /* paint the background first */ - x = XTHICKNESS(style); - y = YTHICKNESS(style); + 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); - TSOffsetStyleGCs(style, rect->x + x, rect->y + y); - gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE, - cliprect, widget, "entry_bg", rect->x + x, - rect->y + y, rect->width - 2*x, rect->height - 2*y); + /* 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", 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", FALSE); + } - gtk_widget_style_get(widget, - "interior-focus", &interior_focus, - "focus-line-width", &focus_width, - NULL); + /* Get the position of the inner window, see _gtk_entry_get_borders */ + x = XTHICKNESS(style); + y = YTHICKNESS(style); - /* - * Now paint the shadow and focus border. - * - * gtk+ is able to draw over top of the entry when it gains focus, - * so the non-focused text border is implicitly already drawn when - * the entry is drawn in a focused state. - * - * Gecko doesn't quite work this way, so always draw the non-focused - * shadow, then draw the shadow again, inset, if we're focused. - */ + 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. */ + /* 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) { @@ -1225,19 +1578,18 @@ moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect, } } - TSOffsetStyleGCs(style, x, y); 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) { - TSOffsetStyleGCs(style, rect->x, rect->y); 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! */ + /* 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); } @@ -1255,7 +1607,10 @@ moz_gtk_treeview_paint(GdkDrawable* drawable, GdkRectangle* rect, GtkStateType state_type; ensure_tree_view_widget(); + ensure_scrolled_window_widget(); + gtk_widget_set_direction(gTreeViewWidget, direction); + gtk_widget_set_direction(gScrolledWindowWidget, direction); /* only handle disabled and normal states, otherwise the whole background * area will be painted differently with other states */ @@ -1267,20 +1622,21 @@ moz_gtk_treeview_paint(GdkDrawable* drawable, GdkRectangle* rect, gtk_widget_modify_bg(gTreeViewWidget, state_type, &gTreeViewWidget->style->base[state_type]); - style = gTreeViewWidget->style; + style = gScrolledWindowWidget->style; xthickness = XTHICKNESS(style); ythickness = YTHICKNESS(style); + TSOffsetStyleGCs(gTreeViewWidget->style, rect->x, rect->y); TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_flat_box(style, drawable, state_type, GTK_SHADOW_NONE, - cliprect, gTreeViewWidget, "treeview", + gtk_paint_flat_box(gTreeViewWidget->style, drawable, state_type, + GTK_SHADOW_NONE, cliprect, gTreeViewWidget, "treeview", rect->x + xthickness, rect->y + ythickness, rect->width - 2 * xthickness, rect->height - 2 * ythickness); gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, - cliprect, gTreeViewWidget, "scrolled_window", + cliprect, gScrolledWindowWidget, "scrolled_window", rect->x, rect->y, rect->width, rect->height); return MOZ_GTK_SUCCESS; @@ -1289,8 +1645,11 @@ moz_gtk_treeview_paint(GdkDrawable* drawable, GdkRectangle* rect, static gint moz_gtk_tree_header_cell_paint(GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, GtkWidgetState* state, - GtkTextDirection direction) + gboolean isSorted, GtkTextDirection direction) { + gtk_tree_view_column_set_sort_indicator(GTK_TREE_VIEW_COLUMN(gMiddleTreeViewColumn), + isSorted); + moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL, gTreeHeaderCellWidget, direction); return MOZ_GTK_SUCCESS; @@ -1375,73 +1734,84 @@ moz_gtk_expander_paint(GdkDrawable* drawable, GdkRectangle* rect, } static gint -moz_gtk_option_menu_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkTextDirection direction) +moz_gtk_combo_box_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + gboolean ishtml, GtkTextDirection direction) { - GtkStyle* style; + GdkRectangle arrow_rect, real_arrow_rect; + gint arrow_size, separator_width = 0; + gboolean wide_separators = FALSE; GtkStateType state_type = ConvertGtkState(state); - gint x = rect->x, y=rect->y, width=rect->width, height=rect->height; - gint tab_x, tab_y; - gboolean interior_focus; - GtkRequisition indicator_size; - GtkBorder indicator_spacing; - gint focus_width; - gint focus_pad; + GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + GtkStyle* style; + GtkRequisition arrow_req; - ensure_option_menu_widget(); - gtk_widget_set_direction(gOptionMenuWidget, direction); - moz_gtk_option_menu_get_metrics(&interior_focus, &indicator_size, - &indicator_spacing, &focus_width, - &focus_pad); + ensure_combo_box_widgets(); - style = gOptionMenuWidget->style; + /* Also sets the direction on gComboBoxButtonWidget, which is then + * inherited by the separator and arrow */ + moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL, + gComboBoxButtonWidget, direction); - 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); - } + calculate_button_inner_rect(gComboBoxButtonWidget, + 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(gComboBoxArrowWidget, &arrow_req); + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x += arrow_rect.width - arrow_req.width; + arrow_rect.width = arrow_req.width; - TSOffsetStyleGCs(style, x, y); - gtk_paint_box(style, drawable, state_type, GTK_SHADOW_OUT, - cliprect, gOptionMenuWidget, "optionmenu", - x, y, width, height); - - if (direction == GTK_TEXT_DIR_RTL) { - tab_x = x + indicator_spacing.right + XTHICKNESS(style); + calculate_arrow_rect(gComboBoxArrowWidget, + &arrow_rect, &real_arrow_rect, direction); + + style = gComboBoxArrowWidget->style; + TSOffsetStyleGCs(style, rect->x, rect->y); + + gtk_widget_size_allocate(gComboBoxWidget, rect); + + gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect, + gComboBoxArrowWidget, "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 (!gComboBoxSeparatorWidget) + return MOZ_GTK_SUCCESS; + + style = gComboBoxSeparatorWidget->style; + TSOffsetStyleGCs(style, rect->x, rect->y); + + if (have_2_10) + gtk_widget_style_get(gComboBoxSeparatorWidget, + "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, gComboBoxSeparatorWidget, "vseparator", + arrow_rect.x, arrow_rect.y, + separator_width, arrow_rect.height); } else { - tab_x = x + width - indicator_size.width - indicator_spacing.right - - XTHICKNESS(style); + 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, + gComboBoxSeparatorWidget, "vseparator", + arrow_rect.y, arrow_rect.y + arrow_rect.height, + arrow_rect.x); } - tab_y = y + (height - indicator_size.height) / 2; - TSOffsetStyleGCs(style, tab_x, tab_y); - gtk_paint_tab(style, drawable, state_type, GTK_SHADOW_OUT, cliprect, - gOptionMenuWidget, "optionmenutab", tab_x, tab_y, - indicator_size.width, indicator_size.height); - - if (state->focused) { - if (interior_focus) { - x += XTHICKNESS(style) + focus_pad; - y += YTHICKNESS(style) + focus_pad; - /* Standard GTK combos have their focus ring around the entire - control, not just the text bit */ - width -= 2 * (XTHICKNESS(style) + focus_pad); - height -= 2 * (YTHICKNESS(style) + 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, state_type, cliprect, gOptionMenuWidget, - "button", x, y, width, height); - } - return MOZ_GTK_SUCCESS; } @@ -1454,63 +1824,66 @@ moz_gtk_downarrow_paint(GdkDrawable* drawable, GdkRectangle* rect, GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; GdkRectangle arrow_rect; - ensure_arrow_widget(); - style = gArrowWidget->style; + ensure_button_arrow_widget(); + style = gButtonArrowWidget->style; - arrow_rect.x = rect->x + 1 + XTHICKNESS(style); - arrow_rect.y = rect->y + 1 + YTHICKNESS(style); - arrow_rect.width = MAX(1, rect->width - (arrow_rect.x - rect->x) * 2); - arrow_rect.height = MAX(1, rect->height - (arrow_rect.y - rect->y) * 2); + calculate_arrow_rect(gButtonArrowWidget, rect, &arrow_rect, + GTK_TEXT_DIR_LTR); TSOffsetStyleGCs(style, arrow_rect.x, arrow_rect.y); gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect, - gArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, + gButtonArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, arrow_rect.x, arrow_rect.y, arrow_rect.width, arrow_rect.height); return MOZ_GTK_SUCCESS; } static gint -moz_gtk_dropdown_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkTextDirection direction) +moz_gtk_combo_box_entry_button_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, + GtkWidgetState* state, + gboolean input_focus, + GtkTextDirection direction) { - static gfloat arrow_scaling = 0.7; - gint real_arrow_padding; + gint x_displacement, y_displacement; GdkRectangle arrow_rect, real_arrow_rect; GtkStateType state_type = ConvertGtkState(state); GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; GtkStyle* style; - ensure_arrow_widget(); - gtk_widget_set_direction(gDropdownButtonWidget, direction); + ensure_combo_box_entry_widgets(); + + if (input_focus) { + /* Some themes draw a complementary focus ring for the dropdown button + * when the dropdown entry has focus */ + GTK_WIDGET_SET_FLAGS(gComboBoxEntryTextareaWidget, GTK_HAS_FOCUS); + } moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL, - gDropdownButtonWidget, direction); + gComboBoxEntryButtonWidget, direction); - /* This mirrors gtkbutton's child positioning */ - style = gDropdownButtonWidget->style; - arrow_rect.x = rect->x + 1 + XTHICKNESS(style); - arrow_rect.y = rect->y + 1 + YTHICKNESS(style); - arrow_rect.width = MAX(1, rect->width - (arrow_rect.x - rect->x) * 2); - arrow_rect.height = MAX(1, rect->height - (arrow_rect.y - rect->y) * 2); - - calculate_arrow_dimensions(&arrow_rect, &real_arrow_rect); - style = gArrowWidget->style; - TSOffsetStyleGCs(style, real_arrow_rect.x, real_arrow_rect.y); + if (input_focus) + GTK_WIDGET_UNSET_FLAGS(gComboBoxEntryTextareaWidget, GTK_HAS_FOCUS); + + calculate_button_inner_rect(gComboBoxEntryButtonWidget, + rect, &arrow_rect, direction, FALSE); + if (state_type == GTK_STATE_ACTIVE) { + gtk_widget_style_get(gComboBoxEntryButtonWidget, + "child-displacement-x", &x_displacement, + "child-displacement-y", &y_displacement, + NULL); + arrow_rect.x += x_displacement; + arrow_rect.y += y_displacement; + } - real_arrow_rect.width = real_arrow_rect.height = - MIN (real_arrow_rect.width, real_arrow_rect.height) * arrow_scaling; + calculate_arrow_rect(gComboBoxEntryArrowWidget, + &arrow_rect, &real_arrow_rect, direction); - real_arrow_padding = floor((arrow_rect.width - real_arrow_rect.width) / 2 + 0.5); - real_arrow_rect.x = arrow_rect.x + real_arrow_padding; - if (direction == GTK_TEXT_DIR_RTL) - real_arrow_rect.x = arrow_rect.x + arrow_rect.width - - real_arrow_rect.width - real_arrow_padding; - real_arrow_rect.y = floor (arrow_rect.y + ((arrow_rect.height - real_arrow_rect.height) / 2) + 0.5); + style = gComboBoxEntryArrowWidget->style; + TSOffsetStyleGCs(style, real_arrow_rect.x, real_arrow_rect.y); gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect, - gDropdownButtonWidget, "arrow", GTK_ARROW_DOWN, TRUE, + gComboBoxEntryArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, real_arrow_rect.x, real_arrow_rect.y, real_arrow_rect.width, real_arrow_rect.height); @@ -1624,7 +1997,9 @@ moz_gtk_toolbar_paint(GdkDrawable* drawable, GdkRectangle* rect, cliprect, rect->x, rect->y, rect->width, rect->height); - gtk_paint_box (style, drawable, GTK_STATE_NORMAL, gToolbarShadowType, + gtk_widget_style_get(gToolbarWidget, "shadow-type", &shadow_type, NULL); + + gtk_paint_box (style, drawable, GTK_STATE_NORMAL, shadow_type, cliprect, gToolbarWidget, "toolbar", rect->x, rect->y, rect->width, rect->height); @@ -1637,9 +2012,9 @@ moz_gtk_toolbar_separator_paint(GdkDrawable* drawable, GdkRectangle* rect, GtkTextDirection direction) { GtkStyle* style; - gint separator_width; + gint separator_width = 0; gint paint_width; - gboolean wide_separators; + gboolean wide_separators = FALSE; /* Defined as constants in GTK+ 2.10.14 */ const double start_fraction = 0.2; @@ -1650,10 +2025,11 @@ moz_gtk_toolbar_separator_paint(GdkDrawable* drawable, GdkRectangle* rect, style = gToolbarSeparatorWidget->style; - gtk_widget_style_get(gToolbarWidget, - "wide-separators", &wide_separators, - "separator-width", &separator_width, - NULL); + if (have_2_10) + gtk_widget_style_get(gToolbarWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); TSOffsetStyleGCs(style, rect->x, rect->y); @@ -1965,6 +2341,36 @@ moz_gtk_tabpanels_paint(GdkDrawable* drawable, GdkRectangle* rect, } static gint +moz_gtk_tab_scroll_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkArrowType arrow_type, + GtkTextDirection direction) +{ + GtkStateType state_type = ConvertGtkState(state); + GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + GtkStyle* style; + gint arrow_size = MIN(rect->width, rect->height); + gint x = rect->x + (rect->width - arrow_size) / 2; + gint y = rect->y + (rect->height - arrow_size) / 2; + + ensure_tab_widget(); + + style = gTabWidget->style; + TSOffsetStyleGCs(style, rect->x, rect->y); + + if (direction == GTK_TEXT_DIR_RTL) { + arrow_type = (arrow_type == GTK_ARROW_LEFT) ? + GTK_ARROW_RIGHT : GTK_ARROW_LEFT; + } + + gtk_paint_arrow(style, drawable, state_type, shadow_type, NULL, + gTabWidget, "notebook", arrow_type, TRUE, + x, y, arrow_size, arrow_size); + + return MOZ_GTK_SUCCESS; +} + +static gint moz_gtk_menu_bar_paint(GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, GtkTextDirection direction) { @@ -1973,13 +2379,16 @@ moz_gtk_menu_bar_paint(GdkDrawable* drawable, GdkRectangle* rect, ensure_menu_bar_widget(); gtk_widget_set_direction(gMenuBarWidget, direction); + gtk_widget_style_get(gMenuBarWidget, "shadow-type", &shadow_type, NULL); + style = gMenuBarWidget->style; TSOffsetStyleGCs(style, rect->x, rect->y); gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_NORMAL, cliprect, rect->x, rect->y, rect->width, rect->height); - gtk_paint_box(style, drawable, GTK_STATE_NORMAL, gMenuBarShadowType, + + gtk_paint_box(style, drawable, GTK_STATE_NORMAL, shadow_type, cliprect, gMenuBarWidget, "menubar", rect->x, rect->y, rect->width, rect->height); return MOZ_GTK_SUCCESS; @@ -2011,9 +2420,9 @@ moz_gtk_menu_separator_paint(GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, GtkTextDirection direction) { GtkStyle* style; - gboolean wide_separators; - gint separator_height; - guint horizontal_padding; + gboolean wide_separators = FALSE; + gint separator_height = 0; + guint horizontal_padding = 0; gint paint_height; ensure_menu_separator_widget(); @@ -2021,9 +2430,13 @@ moz_gtk_menu_separator_paint(GdkDrawable* drawable, GdkRectangle* rect, style = gMenuSeparatorWidget->style; + if (have_2_10) + gtk_widget_style_get(gMenuSeparatorWidget, + "wide-separators", &wide_separators, + "separator-height", &separator_height, + NULL); + gtk_widget_style_get(gMenuSeparatorWidget, - "wide-separators", &wide_separators, - "separator-height", &separator_height, "horizontal-padding", &horizontal_padding, NULL); @@ -2077,12 +2490,9 @@ moz_gtk_menu_item_paint(GdkDrawable* drawable, GdkRectangle* rect, style = item_widget->style; TSOffsetStyleGCs(style, rect->x, rect->y); - if (have_menu_shadow_type) { - gtk_widget_style_get(item_widget, "selected_shadow_type", - &shadow_type, NULL); - } else { - shadow_type = GTK_SHADOW_OUT; - } + + gtk_widget_style_get(item_widget, "selected-shadow-type", + &shadow_type, NULL); gtk_paint_box(style, drawable, GTK_STATE_PRELIGHT, shadow_type, cliprect, item_widget, "menuitem", rect->x, rect->y, @@ -2197,8 +2607,7 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, switch (widget) { case MOZ_GTK_BUTTON: { - /* Constant in gtkbutton.c */ - static const gint child_spacing = 1; + GtkBorder inner_border; gboolean interior_focus; gint focus_width, focus_pad; @@ -2209,10 +2618,11 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, become too big and stuff the layout. */ if (!inhtml) { moz_gtk_widget_get_focus(gButtonWidget, &interior_focus, &focus_width, &focus_pad); - *left += focus_width + focus_pad + child_spacing; - *right += focus_width + focus_pad + child_spacing; - *top += focus_width + focus_pad + child_spacing; - *bottom += focus_width + focus_pad + child_spacing; + moz_gtk_button_get_inner_border(gButtonWidget, &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; } *left += gButtonWidget->style->xthickness; @@ -2221,11 +2631,6 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, *bottom += gButtonWidget->style->ythickness; return MOZ_GTK_SUCCESS; } - - case MOZ_GTK_TOOLBAR: - ensure_toolbar_widget(); - w = gToolbarWidget; - break; case MOZ_GTK_ENTRY: ensure_entry_widget(); w = gEntryWidget; @@ -2242,19 +2647,19 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, * That is why the following code is the same as for MOZ_GTK_BUTTON. * */ - /* Constant in gtkbutton.c */ - static const gint child_spacing = 1; + GtkBorder inner_border; gboolean interior_focus; gint focus_width, focus_pad; ensure_tree_header_cell_widget(); *left = *top = *right = *bottom = GTK_CONTAINER(gTreeHeaderCellWidget)->border_width; - moz_gtk_widget_get_focus(gTreeHeaderCellWidget, &interior_focus, &focus_width, &focus_pad); - *left += focus_width + focus_pad; - *right += focus_width + focus_pad; - *top += focus_width + focus_pad + child_spacing; - *bottom += focus_width + focus_pad + child_spacing; + moz_gtk_widget_get_focus(gTreeHeaderCellWidget, &interior_focus, &focus_width, &focus_pad); + moz_gtk_button_get_inner_border(gTreeHeaderCellWidget, &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; *left += gTreeHeaderCellWidget->style->xthickness; *right += gTreeHeaderCellWidget->style->xthickness; @@ -2267,32 +2672,59 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, w = gTreeHeaderSortArrowWidget; break; case MOZ_GTK_DROPDOWN_ENTRY: - ensure_dropdown_entry_widget(); - w = gDropdownEntryWidget; + ensure_combo_box_entry_widgets(); + w = gComboBoxEntryTextareaWidget; break; case MOZ_GTK_DROPDOWN_ARROW: - ensure_arrow_widget(); - w = gDropdownButtonWidget; + ensure_combo_box_entry_widgets(); + w = gComboBoxEntryButtonWidget; 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 interior_focus; - GtkRequisition indicator_size; - GtkBorder indicator_spacing; - gint focus_width, focus_pad; + /* 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 = FALSE; + gint focus_width, focus_pad, separator_width; + GtkRequisition arrow_req; + + ensure_combo_box_widgets(); + + *left = GTK_CONTAINER(gComboBoxButtonWidget)->border_width; + + if (!inhtml) { + moz_gtk_widget_get_focus(gComboBoxButtonWidget, + &ignored_interior_focus, + &focus_width, &focus_pad); + *left += focus_width + focus_pad; + } + + *top = *left + gComboBoxButtonWidget->style->ythickness; + *left += gComboBoxButtonWidget->style->xthickness; + + *right = *left; *bottom = *top; + + /* If there is no separator, don't try to count its width. */ + separator_width = 0; + if (gComboBoxSeparatorWidget) { + if (have_2_10) + gtk_widget_style_get(gComboBoxSeparatorWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (!wide_separators) + separator_width = + XTHICKNESS(gComboBoxSeparatorWidget->style); + } - ensure_option_menu_widget(); - *right = *left = gOptionMenuWidget->style->xthickness; - *bottom = *top = gOptionMenuWidget->style->ythickness; - moz_gtk_option_menu_get_metrics(&interior_focus, &indicator_size, - &indicator_spacing, &focus_width, &focus_pad); + gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req); if (direction == GTK_TEXT_DIR_RTL) - *left += indicator_spacing.left + indicator_size.width + indicator_spacing.right; + *left += separator_width + arrow_req.width; else - *right += indicator_spacing.left + indicator_size.width + indicator_spacing.right; + *right += separator_width + arrow_req.width; + return MOZ_GTK_SUCCESS; } case MOZ_GTK_TABPANELS: @@ -2379,10 +2811,6 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, return MOZ_GTK_SUCCESS; } - case MOZ_GTK_MENUBAR: - ensure_menu_bar_widget(); - w = gMenuBarWidget; - break; case MOZ_GTK_MENUPOPUP: ensure_menu_popup_widget(); w = gMenuPopupWidget; @@ -2416,6 +2844,7 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, case MOZ_GTK_GRIPPER: case MOZ_GTK_PROGRESS_CHUNK: case MOZ_GTK_EXPANDER: + case MOZ_GTK_TREEVIEW_EXPANDER: case MOZ_GTK_TOOLBAR_SEPARATOR: case MOZ_GTK_MENUSEPARATOR: /* These widgets have no borders.*/ @@ -2425,6 +2854,10 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, case MOZ_GTK_RESIZER: case MOZ_GTK_MENUARROW: case MOZ_GTK_TOOLBARBUTTON_ARROW: + case MOZ_GTK_TOOLBAR: + case MOZ_GTK_MENUBAR: + case MOZ_GTK_TAB_SCROLLARROW: + case MOZ_GTK_ENTRY_CARET: *left = *top = *right = *bottom = 0; return MOZ_GTK_SUCCESS; default: @@ -2439,22 +2872,48 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, } gint -moz_gtk_get_dropdown_arrow_size(gint* width, gint* height) +moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height) { - const gint min_arrow_size = 15; - ensure_arrow_widget(); - /* - * First get the border of the dropdown arrow, then add in the requested - * size of the arrow. Note that the minimum arrow size is fixed at - * 15 pixels. - */ + * We get the requisition of the drop down button, which includes + * all padding, border and focus line widths the button uses, + * as well as the minimum arrow size and its padding + * */ + GtkRequisition requisition; + ensure_combo_box_entry_widgets(); + + gtk_widget_size_request(gComboBoxEntryButtonWidget, &requisition); + *width = requisition.width; + *height = requisition.height; + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height) +{ + gint arrow_size = 16; + + ensure_tab_widget(); + if (have_2_10) + gtk_widget_style_get(gTabWidget, + "scroll-arrow-hlength", &arrow_size, + NULL); + + *height = *width = arrow_size; - *width = 2 * (1 + XTHICKNESS(gDropdownButtonWidget->style)); - *width += min_arrow_size + GTK_MISC(gArrowWidget)->xpad * 2; + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_downarrow_size(gint* width, gint* height) +{ + GtkRequisition requisition; + ensure_button_arrow_widget(); - *height = 2 * (1 + YTHICKNESS(gDropdownButtonWidget->style)); - *height += min_arrow_size + GTK_MISC(gArrowWidget)->ypad * 2; + gtk_widget_size_request(gButtonArrowWidget, &requisition); + *width = requisition.width; + *height = requisition.height; return MOZ_GTK_SUCCESS; } @@ -2462,18 +2921,22 @@ moz_gtk_get_dropdown_arrow_size(gint* width, gint* height) gint moz_gtk_get_toolbar_separator_width(gint* size) { - gboolean wide_separators; - gint separator_width; + gboolean wide_separators = FALSE; + gint separator_width = 0; GtkStyle* style; ensure_toolbar_widget(); style = gToolbarWidget->style; + if (have_2_10) + gtk_widget_style_get(gToolbarWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + gtk_widget_style_get(gToolbarWidget, "space-size", size, - "wide-separators", &wide_separators, - "separator-width", &separator_width, NULL); /* Just in case... */ @@ -2507,15 +2970,16 @@ moz_gtk_get_treeview_expander_size(gint* size) gint moz_gtk_get_menu_separator_height(gint *size) { - gboolean wide_separators; - gint separator_height; + gboolean wide_separators = FALSE; + gint separator_height = 0; ensure_menu_separator_widget(); - gtk_widget_style_get(gMenuSeparatorWidget, - "wide-separators", &wide_separators, - "separator-height", &separator_height, - NULL); + if (have_2_10) + gtk_widget_style_get(gMenuSeparatorWidget, + "wide-separators", &wide_separators, + "separator-height", &separator_height, + NULL); if (wide_separators) *size = separator_height + gMenuSeparatorWidget->style->ythickness; @@ -2559,6 +3023,19 @@ moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics) return MOZ_GTK_SUCCESS; } +gboolean +moz_gtk_images_in_menus() +{ + gboolean result; + GtkSettings* settings; + + ensure_image_menu_item_widget(); + settings = gtk_widget_get_settings(gImageMenuItemWidget); + + g_object_get(settings, "gtk-menu-images", &result, NULL); + return result; +} + gint moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, @@ -2634,7 +3111,7 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, break; case MOZ_GTK_TREE_HEADER_CELL: return moz_gtk_tree_header_cell_paint(drawable, rect, cliprect, state, - direction); + flags, direction); break; case MOZ_GTK_TREE_HEADER_SORTARROW: return moz_gtk_tree_header_sort_arrow_paint(drawable, rect, cliprect, @@ -2655,18 +3132,21 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, return moz_gtk_entry_paint(drawable, rect, cliprect, state, gEntryWidget, direction); break; + case MOZ_GTK_ENTRY_CARET: + return moz_gtk_caret_paint(drawable, rect, cliprect, direction); + break; case MOZ_GTK_DROPDOWN: - return moz_gtk_option_menu_paint(drawable, rect, cliprect, state, - direction); + return moz_gtk_combo_box_paint(drawable, rect, cliprect, state, + (gboolean) flags, direction); break; case MOZ_GTK_DROPDOWN_ARROW: - return moz_gtk_dropdown_arrow_paint(drawable, rect, cliprect, state, - direction); + return moz_gtk_combo_box_entry_button_paint(drawable, rect, cliprect, + state, flags, direction); break; case MOZ_GTK_DROPDOWN_ENTRY: - ensure_dropdown_entry_widget(); + ensure_combo_box_entry_widgets(); return moz_gtk_entry_paint(drawable, rect, cliprect, state, - gDropdownEntryWidget, direction); + gComboBoxEntryTextareaWidget, direction); break; case MOZ_GTK_CHECKBUTTON_CONTAINER: case MOZ_GTK_RADIOBUTTON_CONTAINER: @@ -2711,6 +3191,10 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, case MOZ_GTK_TABPANELS: return moz_gtk_tabpanels_paint(drawable, rect, cliprect, direction); break; + case MOZ_GTK_TAB_SCROLLARROW: + return moz_gtk_tab_scroll_arrow_paint(drawable, rect, cliprect, state, + (GtkArrowType) flags, direction); + break; case MOZ_GTK_MENUBAR: return moz_gtk_menu_bar_paint(drawable, rect, cliprect, direction); break; @@ -2766,6 +3250,8 @@ GtkWidget* moz_gtk_get_scrollbar_widget(void) gint moz_gtk_shutdown() { + GtkWidgetClass *entry_class; + if (gTooltipWidget) gtk_widget_destroy(gTooltipWidget); /* This will destroy all of our widgets */ @@ -2775,6 +3261,7 @@ moz_gtk_shutdown() gProtoWindow = NULL; gButtonWidget = NULL; gToggleButtonWidget = NULL; + gButtonArrowWidget = NULL; gCheckboxWidget = NULL; gRadiobuttonWidget = NULL; gHorizScrollbarWidget = NULL; @@ -2783,11 +3270,14 @@ moz_gtk_shutdown() gHScaleWidget = NULL; gVScaleWidget = NULL; gEntryWidget = NULL; - gArrowWidget = NULL; - gOptionMenuWidget = NULL; - gDropdownButtonWidget = NULL; - gDropdownEntryWidget = NULL; + gComboBoxWidget = NULL; + gComboBoxButtonWidget = NULL; + gComboBoxSeparatorWidget = NULL; + gComboBoxArrowWidget = NULL; gComboBoxEntryWidget = NULL; + gComboBoxEntryButtonWidget = NULL; + gComboBoxEntryArrowWidget = NULL; + gComboBoxEntryTextareaWidget = NULL; gHandleBoxWidget = NULL; gToolbarWidget = NULL; gStatusbarWidget = NULL; @@ -2799,8 +3289,10 @@ moz_gtk_shutdown() gMenuBarItemWidget = NULL; gMenuPopupWidget = NULL; gMenuItemWidget = NULL; + gImageMenuItemWidget = NULL; gCheckMenuItemWidget = NULL; gTreeViewWidget = NULL; + gMiddleTreeViewColumn = NULL; gTreeHeaderCellWidget = NULL; gTreeHeaderSortArrowWidget = NULL; gExpanderWidget = NULL; @@ -2808,6 +3300,10 @@ moz_gtk_shutdown() gMenuSeparatorWidget = NULL; gHPanedWidget = NULL; gVPanedWidget = NULL; + gScrolledWindowWidget = NULL; + + entry_class = g_type_class_peek(GTK_TYPE_ENTRY); + g_type_class_unref(entry_class); is_initialized = FALSE; diff --git a/WebCore/platform/gtk/gtkdrawing.h b/WebCore/platform/gtk/gtkdrawing.h index ee79746..6e44d4a 100644 --- a/WebCore/platform/gtk/gtkdrawing.h +++ b/WebCore/platform/gtk/gtkdrawing.h @@ -144,6 +144,8 @@ typedef enum { MOZ_GTK_GRIPPER, /* Paints a GtkEntry. */ MOZ_GTK_ENTRY, + /* Paints the native caret (or in GTK-speak: insertion cursor) */ + MOZ_GTK_ENTRY_CARET, /* Paints a GtkOptionMenu. */ MOZ_GTK_DROPDOWN, /* Paints a dropdown arrow (a GtkButton containing a down GtkArrow). */ @@ -176,6 +178,8 @@ typedef enum { MOZ_GTK_TAB, /* Paints the background and border of a GtkNotebook. */ MOZ_GTK_TABPANELS, + /* Paints a GtkArrow for a GtkNotebook. flags is a GtkArrowType. */ + MOZ_GTK_TAB_SCROLLARROW, /* Paints the background and border of a GtkTreeView */ MOZ_GTK_TREEVIEW, /* Paints treeheader cells */ @@ -289,6 +293,16 @@ moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing); gint moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing); +/** + * Get the inner-border value for a GtkButton widget (button or tree header) + * widget: [IN] the widget to get the border value for + * inner_border: [OUT] the inner border + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ +gint +moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border); + /** 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 @@ -329,7 +343,25 @@ moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics* metrics); * * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise */ -gint moz_gtk_get_dropdown_arrow_size(gint* width, gint* height); +gint moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height); + +/** + * Get the desired size of a scroll arrow widget + * width: [OUT] the desired width + * height: [OUT] the desired height + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ +gint moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height); + +/** + * Get the desired size of a toolbar button dropdown arrow + * width: [OUT] the desired width + * height: [OUT] the desired height + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ +gint moz_gtk_get_downarrow_size(gint* width, gint* height); /** * Get the desired size of a toolbar separator @@ -383,6 +415,12 @@ GtkWidget* moz_gtk_get_scrollbar_widget(void); */ gint moz_gtk_get_tab_thickness(void); +/** + * Get a boolean which indicates whether or not to use images in menus. + * If TRUE, use images in menus. + */ +gboolean moz_gtk_images_in_menus(void); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/WebCore/platform/gtk/guriescape.c b/WebCore/platform/gtk/guriescape.c new file mode 100644 index 0000000..0792587 --- /dev/null +++ b/WebCore/platform/gtk/guriescape.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2008 Collabora, Ltd. + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * Copyright (C) 1997-2000 The GLib Team + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * 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 "guriescape.h" + +#include <string.h> + +#if !PLATFORM(WIN_OS) && !GLIB_CHECK_VERSION(2,16,0) + +/* is_valid, gunichar_ok and g_string_append_uri_escaped were copied for glib/gstring.c + * in the glib package. + * + * Original copyright: + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + * + * Please don't change the indentation so it's easier to update these functions + * if they are changed in glib. + */ +static gboolean +is_valid (char c, const char *reserved_chars_allowed) +{ + if (g_ascii_isalnum (c) || + c == '-' || + c == '.' || + c == '_' || + c == '~') + return TRUE; + + if (reserved_chars_allowed && + strchr (reserved_chars_allowed, c) != NULL) + return TRUE; + + return FALSE; +} + +static gboolean +gunichar_ok (gunichar c) +{ + return + (c != (gunichar) -2) && + (c != (gunichar) -1); +} + +static GString * +_webcore_g_string_append_uri_escaped (GString *string, + const char *unescaped, + const char *reserved_chars_allowed, + gboolean allow_utf8) +{ + unsigned char c; + const char *end; + static const gchar hex[16] = "0123456789ABCDEF"; + + g_return_val_if_fail (string != NULL, NULL); + g_return_val_if_fail (unescaped != NULL, NULL); + + end = unescaped + strlen (unescaped); + + while ((c = *unescaped) != 0) + { + if (c >= 0x80 && allow_utf8 && + gunichar_ok (g_utf8_get_char_validated (unescaped, end - unescaped))) + { + int len = g_utf8_skip [c]; + g_string_append_len (string, unescaped, len); + unescaped += len; + } + else if (is_valid (c, reserved_chars_allowed)) + { + g_string_append_c (string, c); + unescaped++; + } + else + { + g_string_append_c (string, '%'); + g_string_append_c (string, hex[((guchar)c) >> 4]); + g_string_append_c (string, hex[((guchar)c) & 0xf]); + unescaped++; + } + } + + return string; +} + +/* g_uri_escape_string, unescape_character, g_uri_unescape_segment and + * g_uri_unescape_string were copied for glib/gurifuncs.c in the glib package + * and prefixed with _webcore (if necessary) to avoid exporting a symbol with + * the "g_" prefix. + * + * Original copyright: + * Copyright (C) 2006-2007 Red Hat, Inc. + * Author: Alexander Larsson <alexl@redhat.com> + * + * Please don't change the indentation so it's easier to update this function + * if it's changed in glib. + */ +char * +_webcore_g_uri_escape_string (const char *unescaped, + const char *reserved_chars_allowed, + gboolean allow_utf8) +{ + GString *s; + + g_return_val_if_fail (unescaped != NULL, NULL); + + s = g_string_sized_new (strlen (unescaped) + 10); + + _webcore_g_string_append_uri_escaped (s, unescaped, reserved_chars_allowed, allow_utf8); + + return g_string_free (s, FALSE); +} + +static int +unescape_character (const char *scanner) +{ + int first_digit; + int second_digit; + + first_digit = g_ascii_xdigit_value (*scanner++); + if (first_digit < 0) + return -1; + + second_digit = g_ascii_xdigit_value (*scanner++); + if (second_digit < 0) + return -1; + + return (first_digit << 4) | second_digit; +} + + + +static char * +_webcore_g_uri_unescape_segment (const char *escaped_string, + const char *escaped_string_end, + const char *illegal_characters) +{ + const char *in; + char *out, *result; + gint character; + + if (escaped_string == NULL) + return NULL; + + if (escaped_string_end == NULL) + escaped_string_end = escaped_string + strlen (escaped_string); + + result = g_malloc (escaped_string_end - escaped_string + 1); + + out = result; + for (in = escaped_string; in < escaped_string_end; in++) + { + character = *in; + + if (*in == '%') + { + in++; + + if (escaped_string_end - in < 2) + { + /* Invalid escaped char (to short) */ + g_free (result); + return NULL; + } + + character = unescape_character (in); + + /* Check for an illegal character. We consider '\0' illegal here. */ + if (character <= 0 || + (illegal_characters != NULL && + strchr (illegal_characters, (char)character) != NULL)) + { + g_free (result); + return NULL; + } + + in++; /* The other char will be eaten in the loop header */ + } + *out++ = (char)character; + } + + *out = '\0'; + + return result; +} + + +char * +_webcore_g_uri_unescape_string (const char *escaped_string, + const char *illegal_characters) +{ + return _webcore_g_uri_unescape_segment (escaped_string, NULL, illegal_characters); +} + +#endif /* #if !PLATFORM(WIN_OS) && !GLIB_CHECK_VERSION(2,16,0) */ diff --git a/WebCore/platform/gtk/guriescape.h b/WebCore/platform/gtk/guriescape.h new file mode 100644 index 0000000..8c6662a --- /dev/null +++ b/WebCore/platform/gtk/guriescape.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Collabora, 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. + */ + +#ifndef guriescape_h +#define guriescape_h + +#include <glib.h> +#include <wtf/Platform.h> + +G_BEGIN_DECLS + +#if !PLATFORM(WIN_OS) && !GLIB_CHECK_VERSION(2,16,0) + +#define g_uri_escape_string _webcore_g_uri_escape_string +#define g_uri_unescape_string _webcore_g_uri_unescape_string + +char *_webcore_g_uri_escape_string (const char *unescaped, + const char *reserved_chars_allowed, + gboolean allow_utf8); + +char *_webcore_g_uri_unescape_string (const char *escaped_string, + const char *illegal_characters); + +#endif + +G_END_DECLS + +#endif /* guriescape_h */ |