diff options
author | Pellegrino Prevete | 2021-08-11 00:16:51 +0200 |
---|---|---|
committer | Pellegrino Prevete | 2021-08-11 00:16:51 +0200 |
commit | cc35f686f58ba61522c193cc5eca74e4bce5124b (patch) | |
tree | 25cba63a595897806aac10a60edd7fbc3c816456 | |
download | aur-cc35f686f58ba61522c193cc5eca74e4bce5124b.tar.gz |
aur/gtk2-maemo: new package
-rw-r--r-- | .SRCINFO | 66 | ||||
-rw-r--r-- | PKGBUILD | 114 | ||||
-rw-r--r-- | gtk-query-immodules-2.0.hook | 11 | ||||
-rw-r--r-- | gtk2.install | 3 | ||||
-rw-r--r-- | gtkrc | 3 | ||||
-rw-r--r-- | hildonize-gdk-window.patch | 45 | ||||
-rw-r--r-- | hildonize-gtk-container.patch | 276 | ||||
-rw-r--r-- | hildonize-gtk-dialog.patch | 642 | ||||
-rw-r--r-- | hildonize-gtk-entry.patch | 958 | ||||
-rw-r--r-- | hildonize-gtk-enums.patch | 92 | ||||
-rw-r--r-- | hildonize-gtk-iconview.patch | 1311 | ||||
-rw-r--r-- | hildonize-gtk-imcontext.patch | 486 | ||||
-rw-r--r-- | hildonize-gtk-menu.patch | 755 | ||||
-rw-r--r-- | hildonize-gtk-rbtree.patch | 77 | ||||
-rw-r--r-- | hildonize-gtk-textview.patch | 811 | ||||
-rw-r--r-- | hildonize-gtk-widget.patch | 932 | ||||
-rw-r--r-- | hildonize-gtk-window.patch | 278 | ||||
-rw-r--r-- | xid-collision-debug.patch | 20 |
18 files changed, 6880 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO new file mode 100644 index 000000000000..9e1b56de8b45 --- /dev/null +++ b/.SRCINFO @@ -0,0 +1,66 @@ +pkgbase = gtk2-maemo + pkgdesc = GObject-based multi-platform GUI toolkit (legacy) + pkgver = 2.24.33 + pkgrel = 2 + url = https://www.gtk.org/ + install = gtk2.install + arch = x86_64 + license = LGPL + makedepends = gobject-introspection + makedepends = git + makedepends = gtk-doc + makedepends = hicolor-icon-theme + depends = atk + depends = pango + depends = libxcursor + depends = libxinerama + depends = libxrandr + depends = libxi + depends = libxcomposite + depends = libxdamage + depends = shared-mime-info + depends = cairo + depends = libcups + depends = gtk-update-icon-cache + depends = librsvg + depends = desktop-file-utils + optdepends = gnome-themes-standard: Default widget theme + optdepends = adwaita-icon-theme: Default icon theme + optdepends = python: gtk-builder-convert + provides = libgailutil.so + provides = libgdk-x11-2.0.so + provides = libgtk-x11-2.0.so + source = gtk2::git+https://gitlab.gnome.org/GNOME/gtk.git#commit=68631945733158f164427db84f01301d7e875763 + source = hildonize-gdk-window.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gdk-window.diff + source = hildonize-gtk-container.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-container.diff + source = hildonize-gtk-dialog.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-dialog.diff + source = hildonize-gtk-entry.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-entry.diff + source = hildonize-gtk-enums.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-enums.diff + source = hildonize-gtk-iconview.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-iconview.diff + source = hildonize-gtk-imcontext.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-imcontext.diff + source = hildonize-gtk-menu.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-menu.diff + source = hildonize-gtk-rbtree.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-rbtree.diff + source = hildonize-gtk-textview.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-textview.diff + source = hildonize-gtk-widget.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-widget.diff + source = hildonize-gtk-window.patch::https://raw.githubusercontent.com/maemo-leste/gtk/2.24.25/debian/patches/hildonize-gtk-window.diff + source = gtkrc + source = gtk-query-immodules-2.0.hook + source = xid-collision-debug.patch + sha512sums = SKIP + sha512sums = f838b74958b4f5f1f37fb2831c07dfb7ea051fd62c74f17d8848736e1bbd5671b9a8a66e5a34ce131669a05b2e99312fa43a91ef1d456a914f2d308c09412681 + sha512sums = 809135f8921bf42c0684be6183eb626ec21db87ead3cb3efbfb59d1b6c57f6d2bc61f23cdf54ed6521de1a74c606ed69cb9991332edb938b076baecd805154b2 + sha512sums = 77c37f91e989b9e6326b9b2c1bf32f5feaa7b81ec5742f5fb1c5b6db07de4efcc9753beadb9517fe8e04983e11d122b5dfd94897bc8bdb2d68cf7dfffdb1b46e + sha512sums = 29ea967378c84e907f7a102bcf6cd893c12c25586c95b2a2994e92dd1f1e82996bfb1416cef47a17ff6b8a225a222cd545b2080d45d190d9061a0f1ecf2baf37 + sha512sums = a65dc9a3f5e2967d382d8f13781bf446441bea23d84740eaf43e4ce7ee14b3592b7f105f4b89b7246c42f5be9a9e1e9ae93ac23bc6b62ae9c09b45a0eb6a273d + sha512sums = 44ba4adcd50c589990af195d3b25baed8098ef8f7f191d841b9ce86bf7e65a48c4e8222a755eca1373eaabe6a79db9c3721f0fa690cca5e5e31fd9b4c3fc69bb + sha512sums = ece16de21b19ccb7b08bad78880fa83765bb562973cf73249a9b2b617e0918e231d54bf64ee82648901c7f6c4af4220b06547dc9a17bbb77fab9e83c08cdcfe7 + sha512sums = 44cd0f71316f38da52ad0e9db3681bf4fbf83c9175b98bb9241d0bedf3f66ed6923c8aa6c9c26264ee4d7b5f8abb15f2ab7cd9f1eaf037273bf94ec50ff4ea41 + sha512sums = d664555b7e7a98a567548d403e3df0d0534312ba6947d422a0d68dddbaa786806a842a25dcb5f4a0e1235a2f64d6920f67bf93b4574540d4af2337e1d5b68f2c + sha512sums = f4c1978b1cbd00cc28779f1cda99434f2f5aec53fd6564282d7d2c5ac5d4869207d38a31ad971bfa09eefe5a78030b3c4c169cbdbd7f5e3b2f7c42bed7f58e56 + sha512sums = aa3100404b4fe4509ec52c79384e5e59585d482947e394ad26f9c83ad11c9a19f2b0f66112ba9c74feb7a0c7d1b5ec54c64fac34ec0c03666b3ecb62160db8b6 + sha512sums = 7622b5ff6cd1c0705eee7ad5789c675055a79a4b0aaf2f25fdab4a6fdf883e6fbdef22145d2f2ce4ada3015bd7a8424f61a325a8d00c80e26bf1b507c7949318 + sha512sums = b124433dd4b20d1d62f073df87e253ca23b3b51625cce55f29a220a4369eda5108c0de07fdc686f570232322c3ff04f7758383f2be5aeace40f843907aa3696d + sha512sums = 5e99c5558bf48dc251134869c6310bd9e4bce3775a93547f62028fe32b415c18079da89fe46c85d80b54c4810732acbd6b88ec9946962d02fafc46ed7f672cf2 + sha512sums = c473ac89fab47cc79e912695aa7408c22c4bcd998e00f9b00d46374d4a961d41ffaa1f885bf2f9d9b68a401e16f64c617f0dfb058a98469dbe16beb37229b9bc + +pkgname = gtk2-maemo diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 000000000000..dd5884074cc2 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,114 @@ +# Maintainer: Pellegrino Prevete <pellegrinoprevete@gmail.com> +# Contributor: Jan Alexander Steffens (heftig) <heftig@archlinux.org> +# Contributor: Jan de Groot <jgc@archlinux.org> + +_pkgname=gtk2 +pkgname=$_pkgname-maemo +pkgver=2.24.33 +_tag=2.24.25 +pkgrel=2 +pkgdesc="GObject-based multi-platform GUI toolkit (legacy)" +arch=(x86_64) +url="https://www.gtk.org/" +depends=(atk pango libxcursor libxinerama libxrandr libxi libxcomposite libxdamage + shared-mime-info cairo libcups gtk-update-icon-cache librsvg desktop-file-utils) +makedepends=(gobject-introspection git gtk-doc hicolor-icon-theme) +optdepends=('gnome-themes-standard: Default widget theme' + 'adwaita-icon-theme: Default icon theme' + 'python: gtk-builder-convert') +provides=(libgailutil.so libg{d,t}k-x11-2.0.so) +license=(LGPL) +install=gtk2.install +_commit=68631945733158f164427db84f01301d7e875763 # tags/2.24.33^0 +_raw_url="https://raw.githubusercontent.com/maemo-leste/gtk" +source=("$_pkgname::git+https://gitlab.gnome.org/GNOME/gtk.git#commit=$_commit" + hildonize-gdk-window.patch::$_raw_url/$_tag/debian/patches/hildonize-gdk-window.diff + hildonize-gtk-container.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-container.diff + hildonize-gtk-dialog.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-dialog.diff + hildonize-gtk-entry.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-entry.diff + hildonize-gtk-enums.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-enums.diff + hildonize-gtk-iconview.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-iconview.diff + hildonize-gtk-imcontext.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-imcontext.diff + hildonize-gtk-menu.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-menu.diff + hildonize-gtk-rbtree.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-rbtree.diff + hildonize-gtk-textview.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-textview.diff + hildonize-gtk-widget.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-widget.diff + hildonize-gtk-window.patch::$_raw_url/$_tag/debian/patches/hildonize-gtk-window.diff + gtkrc + gtk-query-immodules-2.0.hook + xid-collision-debug.patch) +sha512sums=('SKIP' + 'f838b74958b4f5f1f37fb2831c07dfb7ea051fd62c74f17d8848736e1bbd5671b9a8a66e5a34ce131669a05b2e99312fa43a91ef1d456a914f2d308c09412681' + '809135f8921bf42c0684be6183eb626ec21db87ead3cb3efbfb59d1b6c57f6d2bc61f23cdf54ed6521de1a74c606ed69cb9991332edb938b076baecd805154b2' + '77c37f91e989b9e6326b9b2c1bf32f5feaa7b81ec5742f5fb1c5b6db07de4efcc9753beadb9517fe8e04983e11d122b5dfd94897bc8bdb2d68cf7dfffdb1b46e' + '29ea967378c84e907f7a102bcf6cd893c12c25586c95b2a2994e92dd1f1e82996bfb1416cef47a17ff6b8a225a222cd545b2080d45d190d9061a0f1ecf2baf37' + 'a65dc9a3f5e2967d382d8f13781bf446441bea23d84740eaf43e4ce7ee14b3592b7f105f4b89b7246c42f5be9a9e1e9ae93ac23bc6b62ae9c09b45a0eb6a273d' + '44ba4adcd50c589990af195d3b25baed8098ef8f7f191d841b9ce86bf7e65a48c4e8222a755eca1373eaabe6a79db9c3721f0fa690cca5e5e31fd9b4c3fc69bb' + 'ece16de21b19ccb7b08bad78880fa83765bb562973cf73249a9b2b617e0918e231d54bf64ee82648901c7f6c4af4220b06547dc9a17bbb77fab9e83c08cdcfe7' + '44cd0f71316f38da52ad0e9db3681bf4fbf83c9175b98bb9241d0bedf3f66ed6923c8aa6c9c26264ee4d7b5f8abb15f2ab7cd9f1eaf037273bf94ec50ff4ea41' + 'd664555b7e7a98a567548d403e3df0d0534312ba6947d422a0d68dddbaa786806a842a25dcb5f4a0e1235a2f64d6920f67bf93b4574540d4af2337e1d5b68f2c' + 'f4c1978b1cbd00cc28779f1cda99434f2f5aec53fd6564282d7d2c5ac5d4869207d38a31ad971bfa09eefe5a78030b3c4c169cbdbd7f5e3b2f7c42bed7f58e56' + 'aa3100404b4fe4509ec52c79384e5e59585d482947e394ad26f9c83ad11c9a19f2b0f66112ba9c74feb7a0c7d1b5ec54c64fac34ec0c03666b3ecb62160db8b6' + '7622b5ff6cd1c0705eee7ad5789c675055a79a4b0aaf2f25fdab4a6fdf883e6fbdef22145d2f2ce4ada3015bd7a8424f61a325a8d00c80e26bf1b507c7949318' + 'b124433dd4b20d1d62f073df87e253ca23b3b51625cce55f29a220a4369eda5108c0de07fdc686f570232322c3ff04f7758383f2be5aeace40f843907aa3696d' + '5e99c5558bf48dc251134869c6310bd9e4bce3775a93547f62028fe32b415c18079da89fe46c85d80b54c4810732acbd6b88ec9946962d02fafc46ed7f672cf2' + 'c473ac89fab47cc79e912695aa7408c22c4bcd998e00f9b00d46374d4a961d41ffaa1f885bf2f9d9b68a401e16f64c617f0dfb058a98469dbe16beb37229b9bc') + +pkgver() { + cd $_pkgname + git describe --tags | sed 's/-/+/g' +} + +prepare() { + cd $_pkgname + git apply -3 ../xid-collision-debug.patch + patch -p1 < ../hildonize-gdk-window.patch + patch -p1 < ../hildonize-gtk-container.patch + patch -p1 < ../hildonize-gtk-dialog.patch + patch -p1 < ../hildonize-gtk-entry.patch + patch -p1 < ../hildonize-gtk-enums.patch + patch -p1 < ../hildonize-gtk-iconview.patch + patch -p1 < ../hildonize-gtk-imcontext.patch + patch -p1 < ../hildonize-gtk-menu.patch + patch -p1 < ../hildonize-gtk-rbtree.patch + patch -p1 < ../hildonize-gtk-textview.patch + patch -p1 < ../hildonize-gtk-widget.patch + patch -p1 < ../hildonize-gtk-window.patch + libtoolize --force + aclocal -I m4 + autoconf + automake --add-missing + NOCONFIGURE=1 ./autogen.sh +} + +build() { + cd $_pkgname + + ./configure \ + --build=$CBUILD \ + --host=$CHOST \ + --prefix=/usr \ + --sysconfdir=/etc \ + --localstatedir=/var \ + --with-xinput=yes \ + --without-libjasper \ # try without + --with-included-loaders=png + --disable-gtk-doc + + # https://bugzilla.gnome.org/show_bug.cgi?id=655517 + sed -i -e 's/ -shared / -Wl,-O1,--as-needed\0/g' libtool + + make +} + +package() { + cd _pkgname + make DESTDIR="$pkgdir" install + + install -Dt "$pkgdir/usr/share/gtk-2.0" -m644 ../gtkrc + install -Dt "$pkgdir/usr/share/libalpm/hooks" -m644 ../gtk-query-immodules-2.0.hook + + rm "$pkgdir/usr/bin/gtk-update-icon-cache" +} + +# vim:set et sw=2: diff --git a/gtk-query-immodules-2.0.hook b/gtk-query-immodules-2.0.hook new file mode 100644 index 000000000000..51986eb2856e --- /dev/null +++ b/gtk-query-immodules-2.0.hook @@ -0,0 +1,11 @@ +[Trigger] +Type = Path +Operation = Install +Operation = Upgrade +Operation = Remove +Target = usr/lib/gtk-2.0/2.10.0/immodules/*.so + +[Action] +Description = Probing GTK2 input method modules... +When = PostTransaction +Exec = /usr/bin/gtk-query-immodules-2.0 --update-cache diff --git a/gtk2.install b/gtk2.install new file mode 100644 index 000000000000..1fb17159a6c2 --- /dev/null +++ b/gtk2.install @@ -0,0 +1,3 @@ +pre_remove() { + rm -f /usr/lib/gtk-2.0/2.10.0/immodules.cache +} diff --git a/gtkrc b/gtkrc new file mode 100644 index 000000000000..1ee9497d946f --- /dev/null +++ b/gtkrc @@ -0,0 +1,3 @@ +gtk-icon-theme-name = "Adwaita" +gtk-theme-name = "Adwaita" +gtk-font-name = "Cantarell 11" diff --git a/hildonize-gdk-window.patch b/hildonize-gdk-window.patch new file mode 100644 index 000000000000..7dbce537d54a --- /dev/null +++ b/hildonize-gdk-window.patch @@ -0,0 +1,45 @@ +--- a/gdk/gdkwindow.c ++++ b/gdk/gdkwindow.c +@@ -6372,6 +6372,32 @@ + } + + /** ++ * gdk_window_reset_toplevel_updates_libgtk_only: ++ * @window: a #GdkWindow ++ * ++ * Thaws all pending freezes (if any) made with ++ * gdk_window_freeze_toplevel_updates_libgtk_only() ++ * ++ * This function is not part of the GDK public API and is only ++ * for use by GTK+. ++ **/ ++void ++gdk_window_reset_toplevel_updates_libgtk_only (GdkWindow *window) ++{ ++ GdkWindowObject *private = (GdkWindowObject *)window; ++ ++ g_return_if_fail (GDK_IS_WINDOW (window)); ++ g_return_if_fail (private->window_type != GDK_WINDOW_CHILD); ++ ++ if (private->update_and_descendants_freeze_count > 0) ++ { ++ private->update_and_descendants_freeze_count = 0; ++ ++ gdk_window_schedule_update (window); ++ } ++} ++ ++/** + * gdk_window_set_debug_updates: + * @setting: %TRUE to turn on update debugging + * +--- a/gdk/gdkwindow.h ++++ b/gdk/gdkwindow.h +@@ -679,6 +679,7 @@ + + void gdk_window_freeze_toplevel_updates_libgtk_only (GdkWindow *window); + void gdk_window_thaw_toplevel_updates_libgtk_only (GdkWindow *window); ++void gdk_window_reset_toplevel_updates_libgtk_only (GdkWindow *window); + + void gdk_window_process_all_updates (void); + void gdk_window_process_updates (GdkWindow *window, diff --git a/hildonize-gtk-container.patch b/hildonize-gtk-container.patch new file mode 100644 index 000000000000..e634b7904d2d --- /dev/null +++ b/hildonize-gtk-container.patch @@ -0,0 +1,276 @@ +--- a/gtk/gtkcontainer.c ++++ b/gtk/gtkcontainer.c +@@ -41,7 +41,7 @@ + #include <gobject/gobjectnotifyqueue.c> + #include <gobject/gvaluecollector.h> + #include "gtkalias.h" +- ++#include "gtkmenu.h" + + enum { + ADD, +@@ -58,6 +58,13 @@ + PROP_CHILD + }; + ++typedef struct ++{ ++ GtkWidget *menu; ++ void *func; ++ GtkWidgetTapAndHoldFlags flags; ++} GtkContainerTAH; ++ + #define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id) + #define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id)) + +@@ -100,6 +107,12 @@ + + static gchar* gtk_container_child_default_composite_name (GtkContainer *container, + GtkWidget *child); ++static void gtk_container_tap_and_hold_setup (GtkWidget *widget, ++ GtkWidget *menu, ++ GtkCallback func, ++ GtkWidgetTapAndHoldFlags flags); ++static void gtk_container_tap_and_hold_setup_forall (GtkWidget *widget, ++ GtkContainerTAH *tah); + + /* GtkBuildable */ + static void gtk_container_buildable_init (GtkBuildableIface *iface); +@@ -224,6 +237,12 @@ + widget_class->unmap = gtk_container_unmap; + widget_class->focus = gtk_container_focus; + ++ g_signal_override_class_closure ( ++ g_signal_lookup ("tap-and-hold-setup", GTK_TYPE_WIDGET), ++ GTK_TYPE_CONTAINER, ++ g_cclosure_new (G_CALLBACK (gtk_container_tap_and_hold_setup), ++ NULL, NULL)); ++ + class->add = gtk_container_add_unimplemented; + class->remove = gtk_container_remove_unimplemented; + class->check_resize = gtk_container_real_check_resize; +@@ -1050,6 +1069,9 @@ + container->reallocate_redraws = FALSE; + } + ++static GSList *size_allocated_containers = NULL; ++static guint collect_size_allocated_containers = 0; ++ + static void + gtk_container_destroy (GtkObject *object) + { +@@ -1064,6 +1086,9 @@ + container->focus_child = NULL; + } + ++ size_allocated_containers = ++ g_slist_remove_all (size_allocated_containers, container); ++ + /* do this before walking child widgets, to avoid + * removing children from focus chain one by one. + */ +@@ -1334,6 +1359,17 @@ + return GTK_IS_RESIZE_CONTAINER (widget) ? (GtkContainer*) widget : NULL; + } + ++void ++_gtk_container_post_size_allocate (GtkContainer *container) ++{ ++ size_allocated_containers = ++ g_slist_prepend (size_allocated_containers, container); ++} ++ ++static void ++container_scroll_focus_adjustments (GtkContainer *container, ++ gboolean resize_update); ++ + static gboolean + gtk_container_idle_sizer (gpointer data) + { +@@ -1354,7 +1390,49 @@ + g_slist_free_1 (slist); + + GTK_PRIVATE_UNSET_FLAG (widget, GTK_RESIZE_PENDING); ++ collect_size_allocated_containers++; + gtk_container_check_resize (GTK_CONTAINER (widget)); ++ collect_size_allocated_containers--; ++ } ++ ++ /* adjust scroll position in all windows that recently resized a container */ ++ if (collect_size_allocated_containers == 0) ++ { ++ GtkWidget *last = NULL; ++ GSList *current; ++ ++ /* sort toplevels to allow deduping */ ++ size_allocated_containers = ++ g_slist_sort (size_allocated_containers, g_direct_equal); ++ /* adjust focus position on toplevels */ ++ for (current = size_allocated_containers; current; ++ current = current->next) ++ { ++ if (last != current->data) /* dedup toplevels */ ++ { ++ last = current->data; ++ ++ if (GTK_IS_WINDOW (current->data)) ++ { ++ GtkWidget *focus = GTK_WINDOW (current->data)->focus_widget; ++ ++ if (focus && !GTK_IS_CONTAINER (focus)) ++ focus = focus->parent; ++ ++ while (focus) ++ { ++ /* adjust all focus widget parents that could possibly ++ * scroll ++ */ ++ container_scroll_focus_adjustments (GTK_CONTAINER (focus), ++ TRUE); ++ focus = focus->parent; ++ } ++ } ++ } ++ } ++ g_slist_free (size_allocated_containers); ++ size_allocated_containers = NULL; + } + + gdk_window_process_all_updates (); +@@ -1753,7 +1831,12 @@ + g_object_ref (container->focus_child); + } + ++ container_scroll_focus_adjustments (container, FALSE); ++} + ++static void ++container_scroll_focus_adjustments (GtkContainer *container, gboolean resize_update) ++{ + /* check for h/v adjustments + */ + if (container->focus_child) +@@ -1767,6 +1850,7 @@ + vadj = g_object_get_qdata (G_OBJECT (container), vadjustment_key_id); + if (hadj || vadj) + { ++ gboolean valid_coordinates = FALSE; + + focus_child = container->focus_child; + while (GTK_IS_CONTAINER (focus_child) && +@@ -1775,17 +1859,39 @@ + focus_child = GTK_CONTAINER (focus_child)->focus_child; + } + +- gtk_widget_translate_coordinates (focus_child, container->focus_child, +- 0, 0, &x, &y); ++ valid_coordinates = ++ gtk_widget_translate_coordinates (focus_child, ++ container->focus_child, ++ 0, 0, &x, &y); + + x += container->focus_child->allocation.x; + y += container->focus_child->allocation.y; + + if (vadj) +- gtk_adjustment_clamp_page (vadj, y, y + focus_child->allocation.height); ++ { ++ /* When updating the adjustments as a result of container resize ++ * (resize_update=TRUE) force the focused widget visible only if ++ * it is smaller than the viewport. Otherwise the updates starts ++ * to oscillate between two values (possibly HildonScrollArea is ++ * causing that.) That should be enough for vkb resized dialogs. ++ */ ++ if (valid_coordinates && !resize_update || ++ focus_child->allocation.height < vadj->page_size) ++ { ++ gtk_adjustment_clamp_page ( ++ vadj, y, y + focus_child->allocation.height); ++ } ++ } + + if (hadj) +- gtk_adjustment_clamp_page (hadj, x, x + focus_child->allocation.width); ++ { ++ if (valid_coordinates && !resize_update || ++ focus_child->allocation.width < hadj->page_size) ++ { ++ gtk_adjustment_clamp_page ( ++ hadj, x, x + focus_child->allocation.width); ++ } ++ } + } + } + } +@@ -2760,5 +2866,61 @@ + } + } + ++static void ++gtk_container_tap_and_hold_setup_forall (GtkWidget *widget, ++ GtkContainerTAH *tah) ++{ ++ gtk_widget_tap_and_hold_setup (widget, tah->menu, tah->func, tah->flags); ++} ++ ++static void ++gtk_container_tap_and_hold_setup (GtkWidget *widget, ++ GtkWidget *menu, ++ GtkCallback func, ++ GtkWidgetTapAndHoldFlags flags) ++{ ++ GtkContainerTAH tah; ++ GValue instance_and_params[4] = { { 0, }, { 0, }, { 0, }, { 0, } }; ++ ++ g_return_if_fail (GTK_IS_WIDGET (widget)); ++ g_return_if_fail (menu == NULL || GTK_IS_MENU (menu)); ++ ++ tah.menu = menu; ++ tah.func = func; ++ tah.flags = flags; ++ ++ if (flags & GTK_TAP_AND_HOLD_NO_INTERNALS) ++ { ++ gtk_container_foreach ( ++ GTK_CONTAINER (widget), ++ (GtkCallback)gtk_container_tap_and_hold_setup_forall, &tah); ++ } ++ else ++ { ++ gtk_container_forall ( ++ GTK_CONTAINER (widget), ++ (GtkCallback)gtk_container_tap_and_hold_setup_forall, &tah); ++ } ++ ++ g_value_init (&instance_and_params[0], GTK_TYPE_WIDGET); ++ g_value_set_object (&instance_and_params[0], widget); ++ ++ g_value_init (&instance_and_params[1], GTK_TYPE_OBJECT); ++ g_value_set_object (&instance_and_params[1], menu); ++ ++ g_value_init (&instance_and_params[2], G_TYPE_POINTER); ++ g_value_set_pointer (&instance_and_params[2], func); ++ ++ g_value_init (&instance_and_params[3], G_TYPE_UINT); ++ g_value_set_uint (&instance_and_params[3], flags); ++ ++ g_signal_chain_from_overridden (instance_and_params, NULL); ++ ++ g_value_unset (&instance_and_params[0]); ++ g_value_unset (&instance_and_params[1]); ++ g_value_unset (&instance_and_params[2]); ++ g_value_unset (&instance_and_params[3]); ++} ++ + #define __GTK_CONTAINER_C__ + #include "gtkaliasdef.c" +--- a/gtk/gtkcontainer.h ++++ b/gtk/gtkcontainer.h +@@ -219,6 +219,7 @@ + GList *children, + GtkDirectionType direction, + GtkWidget *old_focus); ++void _gtk_container_post_size_allocate (GtkContainer *container); + + #ifndef GTK_DISABLE_DEPRECATED + #define gtk_container_border_width gtk_container_set_border_width diff --git a/hildonize-gtk-dialog.patch b/hildonize-gtk-dialog.patch new file mode 100644 index 000000000000..fb0b17b85626 --- /dev/null +++ b/hildonize-gtk-dialog.patch @@ -0,0 +1,642 @@ +--- a/gtk/gtkdialog.c ++++ b/gtk/gtkdialog.c +@@ -29,13 +29,20 @@ + #include <stdlib.h> + #include <string.h> + #include "config.h" ++#include "gtkalignment.h" ++#include <X11/Xlib.h> ++#include <X11/Xatom.h> ++#include "x11/gdkx.h" + #include "gtkbutton.h" + #include "gtkdialog.h" + #include "gtkhbbox.h" ++#include "gtkvbbox.h" + #include "gtklabel.h" + #include "gtkhseparator.h" ++#include "gtkvseparator.h" + #include "gtkmarshalers.h" + #include "gtkvbox.h" ++#include "gtkhbox.h" + #include "gdkkeysyms.h" + #include "gtkmain.h" + #include "gtkintl.h" +@@ -46,8 +53,13 @@ + + #define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_DIALOG, GtkDialogPrivate)) + ++/* Buttons on a HildonDialog have fixed size */ ++#define HILDON_DIALOG_BUTTON_WIDTH 174 ++ + typedef struct { + guint ignore_separator : 1; ++ GtkWidget *hbox; ++ GtkWidget *alignment; + } GtkDialogPrivate; + + typedef struct _ResponseData ResponseData; +@@ -75,8 +87,11 @@ + GParamSpec *pspec); + static void gtk_dialog_style_set (GtkWidget *widget, + GtkStyle *prev_style); ++static void gtk_dialog_size_request (GtkWidget *widget, ++ GtkRequisition *requisition); ++static void gtk_dialog_realize (GtkWidget *widget); ++static void gtk_dialog_unrealize (GtkWidget *widget); + static void gtk_dialog_map (GtkWidget *widget); +- + static void gtk_dialog_close (GtkDialog *dialog); + + static ResponseData * get_response_data (GtkWidget *widget, +@@ -101,7 +116,12 @@ + + enum { + PROP_0, +- PROP_HAS_SEPARATOR ++ PROP_HAS_SEPARATOR, ++ PROP_TOP_PADDING, ++ PROP_BOTTOM_PADDING, ++ PROP_LEFT_PADDING, ++ PROP_RIGHT_PADDING, ++ PROP_INNER_SPACING, + }; + + enum { +@@ -131,6 +151,9 @@ + + widget_class->map = gtk_dialog_map; + widget_class->style_set = gtk_dialog_style_set; ++ widget_class->size_request = gtk_dialog_size_request; ++ widget_class->realize = gtk_dialog_realize; ++ widget_class->unrealize = gtk_dialog_unrealize; + + class->close = gtk_dialog_close; + +@@ -151,6 +174,56 @@ + FALSE, + GTK_PARAM_READWRITE | G_PARAM_DEPRECATED)); + ++ g_object_class_install_property (gobject_class, ++ PROP_TOP_PADDING, ++ g_param_spec_uint ("top-padding", ++ P_("Top Padding"), ++ P_("The padding to insert at the top of the dialog."), ++ 0, ++ G_MAXUINT, ++ 0, ++ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ g_object_class_install_property (gobject_class, ++ PROP_BOTTOM_PADDING, ++ g_param_spec_uint ("bottom-padding", ++ P_("Bottom Padding"), ++ P_("The padding to insert at the bottom of the dialog."), ++ 0, ++ G_MAXUINT, ++ 8, ++ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ g_object_class_install_property (gobject_class, ++ PROP_LEFT_PADDING, ++ g_param_spec_uint ("left-padding", ++ P_("Left Padding"), ++ P_("The padding to insert at the left of the dialog."), ++ 0, ++ G_MAXUINT, ++ 16, ++ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ g_object_class_install_property (gobject_class, ++ PROP_RIGHT_PADDING, ++ g_param_spec_uint ("right-padding", ++ P_("Right Padding"), ++ P_("The padding to insert at the right of the dialog."), ++ 0, ++ G_MAXUINT, ++ 16, ++ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ g_object_class_install_property (gobject_class, ++ PROP_INNER_SPACING, ++ g_param_spec_int ("inner-spacing", ++ P_("Inner Spacing"), ++ P_("The spacing between content area and action area."), ++ 0, ++ G_MAXINT, ++ 16, ++ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ + /** + * GtkDialog::response: + * @dialog: the object on which the signal is emitted +@@ -246,6 +319,11 @@ + gint content_area_spacing; + gint button_spacing; + gint action_area_border; ++ GtkDialogPrivate *priv = GET_PRIVATE (dialog); ++ guint top_padding; ++ guint bottom_padding; ++ guint left_padding; ++ guint right_padding; + + gtk_widget_style_get (GTK_WIDGET (dialog), + "content-area-border", &content_area_border, +@@ -253,7 +331,12 @@ + "button-spacing", &button_spacing, + "action-area-border", &action_area_border, + NULL); +- ++ gtk_dialog_get_padding (dialog, &top_padding, &bottom_padding, &left_padding, ++ &right_padding); ++ gtk_alignment_set_padding (GTK_ALIGNMENT (priv->alignment), top_padding, ++ bottom_padding, left_padding, right_padding); ++ gtk_box_set_spacing (GTK_BOX (priv->hbox), ++ gtk_dialog_get_inner_spacing (dialog)); + gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), + content_area_border); + if (!_gtk_box_get_spacing_set (GTK_BOX (dialog->vbox))) +@@ -274,6 +357,8 @@ + + priv = GET_PRIVATE (dialog); + priv->ignore_separator = FALSE; ++ priv->hbox = gtk_hbox_new (FALSE, 0); ++ priv->alignment = gtk_alignment_new (0.0, 0.0, 1.0, 1.0); + + /* To avoid breaking old code that prevents destroy on delete event + * by connecting a handler, we have to have the FIRST signal +@@ -286,19 +371,19 @@ + + dialog->vbox = gtk_vbox_new (FALSE, 0); + +- gtk_container_add (GTK_CONTAINER (dialog), dialog->vbox); ++ gtk_container_add (GTK_CONTAINER (priv->alignment), priv->hbox); ++ gtk_container_add (GTK_CONTAINER (dialog), priv->alignment); ++ gtk_widget_show (priv->hbox); ++ gtk_widget_show (priv->alignment); + gtk_widget_show (dialog->vbox); + +- dialog->action_area = gtk_hbutton_box_new (); +- ++ dialog->action_area = gtk_vbutton_box_new (); ++ gtk_widget_set_name (dialog->action_area, "hildon-dialog-action-area"); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog->action_area), + GTK_BUTTONBOX_END); +- +- gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->action_area, +- FALSE, TRUE, 0); ++ gtk_box_pack_end (GTK_BOX (priv->hbox), dialog->action_area, FALSE, TRUE, 0); + gtk_widget_show (dialog->action_area); +- +- dialog->separator = NULL; ++ gtk_box_pack_end (GTK_BOX (priv->hbox), dialog->vbox, TRUE, TRUE, 0); + + gtk_window_set_type_hint (GTK_WINDOW (dialog), + GDK_WINDOW_TYPE_HINT_DIALOG); +@@ -338,14 +423,52 @@ + GParamSpec *pspec) + { + GtkDialog *dialog; +- ++ guint padding_top; ++ guint padding_bottom; ++ guint padding_left; ++ guint padding_right; ++ + dialog = GTK_DIALOG (object); + ++ gtk_dialog_get_padding (dialog, &padding_top, &padding_bottom, &padding_left, ++ &padding_right); ++ + switch (prop_id) + { + case PROP_HAS_SEPARATOR: + gtk_dialog_set_has_separator (dialog, g_value_get_boolean (value)); + break; ++ case PROP_TOP_PADDING: ++ gtk_dialog_set_padding (dialog, ++ g_value_get_uint (value), ++ padding_bottom, ++ padding_left, ++ padding_right); ++ break; ++ case PROP_BOTTOM_PADDING: ++ gtk_dialog_set_padding (dialog, ++ padding_top, ++ g_value_get_uint (value), ++ padding_left, ++ padding_right); ++ break; ++ case PROP_LEFT_PADDING: ++ gtk_dialog_set_padding (dialog, ++ padding_top, ++ padding_bottom, ++ g_value_get_uint (value), ++ padding_right); ++ break; ++ case PROP_RIGHT_PADDING: ++ gtk_dialog_set_padding (dialog, ++ padding_top, ++ padding_bottom, ++ padding_left, ++ g_value_get_uint (value)); ++ break; ++ case PROP_INNER_SPACING: ++ gtk_dialog_set_inner_spacing (dialog, g_value_get_int (value)); ++ break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +@@ -360,14 +483,36 @@ + GParamSpec *pspec) + { + GtkDialog *dialog; +- ++ guint padding_top; ++ guint padding_bottom; ++ guint padding_left; ++ guint padding_right; ++ + dialog = GTK_DIALOG (object); +- ++ ++ gtk_dialog_get_padding (dialog, &padding_top, &padding_bottom, &padding_left, ++ &padding_right); ++ + switch (prop_id) + { + case PROP_HAS_SEPARATOR: + g_value_set_boolean (value, dialog->separator != NULL); + break; ++ case PROP_TOP_PADDING: ++ g_value_set_uint (value, padding_top); ++ break; ++ case PROP_BOTTOM_PADDING: ++ g_value_set_uint (value, padding_bottom); ++ break; ++ case PROP_LEFT_PADDING: ++ g_value_set_uint (value, padding_left); ++ break; ++ case PROP_RIGHT_PADDING: ++ g_value_set_uint (value, padding_right); ++ break; ++ case PROP_INNER_SPACING: ++ g_value_set_int (value, gtk_dialog_get_inner_spacing (dialog)); ++ break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +@@ -453,6 +598,189 @@ + update_spacings (GTK_DIALOG (widget)); + } + ++static G_GNUC_CONST GQuark ++auto_resize_quark (void) ++{ ++ static GQuark quark = 0; ++ ++ if (G_UNLIKELY (quark == 0)) ++ quark = g_quark_from_static_string ("gtk-dialog-widget-auto-resize"); ++ ++ return quark; ++} ++ ++static void ++gtk_dialog_resize_button (GtkWidget *button, ++ gpointer *data) ++{ ++ /* Only resize widgets added with gtk_dialog_add_button () */ ++ if (g_object_get_qdata (G_OBJECT (button), auto_resize_quark ())) ++ { ++ gint width = GPOINTER_TO_INT (data); ++ g_object_set (button, "width-request", width, NULL); ++ } ++} ++ ++static gboolean ++gtk_dialog_get_disable_portrait(GtkDialog *dialog) ++{ ++ Atom actual_type; ++ int actual_format; ++ unsigned long num_items, bytes_left; ++ unsigned char *ret_data_ptr = 0,*leader_data; ++ Display * dpy = GDK_WINDOW_XDISPLAY (GTK_WIDGET(dialog)->window); ++ int error,result; ++ gboolean disable_portrait=False; ++ ++ gdk_error_trap_push (); ++ ++ ++ result = XGetWindowProperty (dpy, ++ GDK_WINDOW_XWINDOW (GTK_WIDGET(dialog)->window), ++ XInternAtom(dpy,"WM_CLIENT_LEADER",False), ++ 0L, (~0L), False, ++ XA_WINDOW, &actual_type, &actual_format, &num_items, ++ &bytes_left, &leader_data); ++ error = gdk_error_trap_pop (); ++ ++ if (error || (result != Success) || !leader_data) ++ { ++ g_warning("gtk_dialog_get_disable_portrait - unable to get window leader."); ++ return False; ++ } ++ ++ gdk_error_trap_push (); ++ ++ result = XGetWindowProperty( ++ dpy, ++ *(Window*)leader_data, ++ XInternAtom(dpy, "_HILDON_PORTRAIT_MODE_TASKNAV_DISABLE", False), ++ 0,1/*= one 32 bits item */ ,False, ++ XA_CARDINAL, &actual_type, &actual_format, &num_items, ++ &bytes_left, &ret_data_ptr); ++ ++ error = gdk_error_trap_pop (); ++ ++ XFree(leader_data); ++ ++ if (!error && ret_data_ptr && (result == Success)) ++ { ++ disable_portrait = *((int*)ret_data_ptr); ++ XFree(ret_data_ptr); ++ return disable_portrait; ++ } ++ ++ return disable_portrait; ++} ++ ++static void ++gtk_dialog_screen_size_changed_cb (GdkScreen *screen, ++ GtkDialog *dialog) ++{ ++ GtkDialogPrivate *priv = GET_PRIVATE (dialog); ++ GtkWidget *parent = gtk_widget_get_parent (dialog->action_area); ++ gint width = gdk_screen_get_width (screen); ++ gboolean portrait = width < gdk_screen_get_height (screen); ++ gint button_width, padding; ++ ++ if(portrait) ++ { ++ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (dialog)); ++ ++ if (GTK_WIDGET_TOPLEVEL (toplevel) && ++ gtk_dialog_get_disable_portrait(GTK_DIALOG(toplevel))) ++ { ++ /* we are stuck in landscape, skip the circus */ ++ return; ++ } ++ } ++ ++ g_object_ref (dialog->action_area); ++ gtk_container_remove (GTK_CONTAINER (parent), dialog->action_area); ++ ++ if (portrait) ++ { ++ parent = dialog->vbox; ++ button_width = width - 16 /* HILDON_MARGIN_DOUBLE */ * 2; ++ padding = 16 /* HILDON_MARGIN_DOUBLE */; ++ } ++ else ++ { ++ parent = gtk_widget_get_parent (dialog->vbox); ++ button_width = 174 /* HILDON_DIALOG_BUTTON_WIDTH */; ++ padding = 0; ++ } ++ ++ gtk_box_pack_end (GTK_BOX (parent), dialog->action_area, FALSE, TRUE, ++ padding); ++ gtk_box_reorder_child (GTK_BOX (parent), dialog->action_area, 0); ++ gtk_container_foreach (GTK_CONTAINER (dialog->action_area), ++ (GtkCallback) gtk_dialog_resize_button, ++ GINT_TO_POINTER (button_width)); ++ g_object_unref (dialog->action_area); ++ ++ if (portrait) ++ gtk_box_set_spacing (GTK_BOX (priv->hbox), padding); ++ else ++ update_spacings (dialog); ++ ++ gtk_window_resize (GTK_WINDOW (dialog), 1, 1); ++} ++ ++static void ++gtk_dialog_realize (GtkWidget *widget) ++{ ++ GdkScreen *screen = gtk_widget_get_screen (widget); ++ ++ GTK_WIDGET_CLASS (gtk_dialog_parent_class)->realize (widget); ++ ++ if (gdk_screen_get_width (screen) < gdk_screen_get_height (screen)) ++ gtk_dialog_screen_size_changed_cb (screen, GTK_DIALOG (widget)); ++ ++ g_signal_connect (screen, "size-changed", ++ G_CALLBACK (gtk_dialog_screen_size_changed_cb), widget); ++} ++ ++static void ++gtk_dialog_unrealize (GtkWidget *widget) ++{ ++ GdkScreen *screen = gtk_widget_get_screen (widget); ++ ++ g_signal_handlers_disconnect_by_func ( ++ screen, gtk_dialog_screen_size_changed_cb, widget); ++ ++ GTK_WIDGET_CLASS (gtk_dialog_parent_class)->unrealize (widget); ++} ++ ++static void ++gtk_dialog_size_request (GtkWidget *widget, ++ GtkRequisition *requisition) ++{ ++ GtkWindow *window; ++ GdkWindowTypeHint type_hint; ++ ++ GTK_WIDGET_CLASS (gtk_dialog_parent_class)->size_request (widget, ++ requisition); ++ ++ window = GTK_WINDOW (widget); ++ type_hint = gtk_window_get_type_hint (window); ++ ++ if (window->type == GTK_WINDOW_TOPLEVEL && ++ (type_hint == GDK_WINDOW_TYPE_HINT_NORMAL || ++ type_hint == GDK_WINDOW_TYPE_HINT_DIALOG)) ++ { ++ gint width; ++ ++ gtk_widget_get_size_request (widget, &width, NULL); ++ ++ if (width == -1) ++ { ++ GdkScreen *screen = gtk_widget_get_screen (widget); ++ requisition->width = gdk_screen_get_width (screen); ++ } ++ } ++} ++ + static GtkWidget * + dialog_find_button (GtkDialog *dialog, + gint response_id) +@@ -711,9 +1039,22 @@ + button = gtk_button_new_from_stock (button_text); + + gtk_widget_set_can_default (button, TRUE); +- +- gtk_widget_show (button); +- ++ ++ if (response_id != GTK_RESPONSE_CANCEL && ++ response_id != GTK_RESPONSE_REJECT && ++ response_id != GTK_RESPONSE_CLOSE) ++ { ++ gtk_widget_show (button); ++ } ++ else ++ gtk_widget_set_no_show_all (button, TRUE); ++ ++ hildon_gtk_widget_set_theme_size ( ++ button, HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT); ++ g_object_set (button, "width-request", HILDON_DIALOG_BUTTON_WIDTH, NULL); ++ g_object_set_qdata (G_OBJECT (button), auto_resize_quark (), ++ GINT_TO_POINTER (TRUE)); ++ + gtk_dialog_add_action_widget (dialog, + button, + response_id); +@@ -877,13 +1218,14 @@ + + if (setting && dialog->separator == NULL) + { +- dialog->separator = gtk_hseparator_new (); +- gtk_box_pack_end (GTK_BOX (dialog->vbox), dialog->separator, FALSE, TRUE, 0); ++ dialog->separator = gtk_vseparator_new (); ++ gtk_box_pack_end ( ++ GTK_BOX (priv->hbox), dialog->separator, FALSE, TRUE, 0); + + /* The app programmer could screw this up, but, their own fault. + * Moves the separator just above the action area. + */ +- gtk_box_reorder_child (GTK_BOX (dialog->vbox), dialog->separator, 1); ++ gtk_box_reorder_child (GTK_BOX (priv->hbox), dialog->separator, 1); + gtk_widget_show (dialog->separator); + } + else if (!setting && dialog->separator != NULL) +@@ -914,6 +1256,96 @@ + } + + /** ++ * gtk_dialog_set_padding: ++ * @dialog: a #GtkDialog ++ * @top_padding: Padding to add at the top of the dialog. ++ * @bottom_padding: Padding to add at the bottom of the dialog. ++ * @left_padding: Padding to add at the left of the dialog. ++ * @right_padding: Padding to add at the right of the dialog. ++ * ++ * Sets additional padding around the dialog. ++ * ++ * Since: maemo 5.0 ++ **/ ++void ++gtk_dialog_set_padding (GtkDialog *dialog, ++ guint top_padding, ++ guint bottom_padding, ++ guint left_padding, ++ guint right_padding) ++{ ++ GtkDialogPrivate *priv; ++ ++ g_return_if_fail (GTK_IS_DIALOG (dialog)); ++ ++ priv = GET_PRIVATE (dialog); ++ ++ gtk_alignment_set_padding (GTK_ALIGNMENT (priv->alignment), top_padding, bottom_padding, left_padding, right_padding); ++} ++ ++/** ++ * gtk_dialog_get_padding: ++ * @dialog: a #GtkDialog ++ * @padding_top: location to store the padding for the top of the dialog, or %NULL ++ * @padding_bottom: location to store the padding for the bottom of the dialog, or %NULL ++ * @padding_left: location to store the padding for the left of the dialog, or %NULL ++ * @padding_right: location to store the padding for the right of the dialog, or %NULL ++ * ++ * Gets the padding on the different sides of the dialog. ++ * ++ * Since: maemo 5.0 ++ **/ ++void ++gtk_dialog_get_padding (GtkDialog *dialog, ++ guint *top_padding, ++ guint *bottom_padding, ++ guint *left_padding, ++ guint *right_padding) ++{ ++ GtkDialogPrivate *priv; ++ ++ g_return_if_fail (GTK_IS_DIALOG (dialog)); ++ ++ priv = GET_PRIVATE (dialog); ++ ++ guint t, b, l, r; ++ ++ gtk_alignment_get_padding (GTK_ALIGNMENT (priv->alignment), &t, &b, &l, &r); ++ ++ if (top_padding) ++ *top_padding = t; ++ if (bottom_padding) ++ *bottom_padding = b; ++ if (left_padding) ++ *left_padding = l; ++ if (right_padding) ++ *right_padding = r; ++} ++ ++void ++gtk_dialog_set_inner_spacing (GtkDialog *dialog, ++ guint inner_spacing) ++{ ++ GtkDialogPrivate *priv; ++ g_return_if_fail (GTK_IS_DIALOG (dialog)); ++ ++ priv = GET_PRIVATE (dialog); ++ ++ gtk_box_set_spacing (GTK_BOX (priv->hbox), inner_spacing); ++} ++ ++guint ++gtk_dialog_get_inner_spacing (GtkDialog *dialog) ++{ ++ GtkDialogPrivate *priv; ++ g_return_val_if_fail (GTK_IS_DIALOG (dialog), 0); ++ ++ priv = GET_PRIVATE (dialog); ++ ++ return gtk_box_get_spacing (GTK_BOX (priv->hbox)); ++} ++ ++/** + * gtk_dialog_response: + * @dialog: a #GtkDialog + * @response_id: response ID +--- a/gtk/gtkdialog.h ++++ b/gtk/gtkdialog.h +@@ -159,6 +159,21 @@ + gboolean gtk_dialog_get_has_separator (GtkDialog *dialog); + #endif + ++void gtk_dialog_set_padding (GtkDialog *dialog, ++ guint padding_top, ++ guint padding_bottom, ++ guint padding_left, ++ guint padding_right); ++void gtk_dialog_get_padding (GtkDialog *dialog, ++ guint *padding_top, ++ guint *padding_bottom, ++ guint *padding_left, ++ guint *padding_right); ++ ++void gtk_dialog_set_inner_spacing (GtkDialog *dialog, ++ guint inner_spacing); ++guint gtk_dialog_get_inner_spacing (GtkDialog *dialog); ++ + gboolean gtk_alternative_dialog_button_order (GdkScreen *screen); + void gtk_dialog_set_alternative_button_order (GtkDialog *dialog, + gint first_response_id, diff --git a/hildonize-gtk-entry.patch b/hildonize-gtk-entry.patch new file mode 100644 index 000000000000..266b114b0c2a --- /dev/null +++ b/hildonize-gtk-entry.patch @@ -0,0 +1,958 @@ +--- a/gtk/gtkentry.c ++++ b/gtk/gtkentry.c +@@ -139,6 +139,9 @@ + gint start_y; + + gchar *im_module; ++ ++ gchar *placeholder_text; ++ PangoLayout *placeholder_layout; + }; + + typedef struct _GtkEntryPasswordHint GtkEntryPasswordHint; +@@ -172,6 +175,8 @@ + ICON_PRESS, + ICON_RELEASE, + PREEDIT_CHANGED, ++ INVALID_INPUT, ++ SELECT_ALL, + LAST_SIGNAL + }; + +@@ -218,7 +223,10 @@ + PROP_TOOLTIP_MARKUP_PRIMARY, + PROP_TOOLTIP_MARKUP_SECONDARY, + PROP_IM_MODULE, +- PROP_EDITING_CANCELED ++ PROP_EDITING_CANCELED, ++ PROP_HILDON_PLACEHOLDER_TEXT, ++ PROP_HILDON_INPUT_MODE, ++ PROP_HILDON_INPUT_DEFAULT + }; + + static guint signals[LAST_SIGNAL] = { 0 }; +@@ -406,6 +414,13 @@ + gint offset, + gint n_chars, + GtkEntry *entry); ++static gboolean gtk_entry_has_selection_cb (GtkIMContext *context, ++ GtkEntry *entry); ++static void gtk_entry_clipboard_operation_cb (GtkIMContext *context, ++ GtkIMContextClipboardOperation op, ++ GtkEntry *entry); ++ ++static PangoLayout * gtk_entry_create_placeholder_layout (GtkEntry *entry); + + /* Internal routines + */ +@@ -770,7 +785,7 @@ + g_param_spec_boolean ("truncate-multiline", + P_("Truncate multiline"), + P_("Whether to truncate multiline pastes to one line."), +- FALSE, ++ TRUE, + GTK_PARAM_READWRITE)); + + /** +@@ -1222,6 +1237,59 @@ + GTK_PARAM_READWRITE)); + + /** ++ * GtkEntry:hildon-placeholder-text: ++ * ++ * Text to be displayed in the #GtkEntry when it is empty. ++ * ++ * Since: maemo 5 ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_HILDON_PLACEHOLDER_TEXT, ++ g_param_spec_string ("hildon-placeholder-text", ++ P_("Hildon Placeholder text"), ++ P_("Text to be displayed when the entry is empty"), ++ "", ++ G_PARAM_READWRITE)); ++ ++ /** ++ * GtkEntry:hildon-input-mode: ++ * ++ * Allowed characters and input mode for the entry. See #HildonGtkInputMode. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_HILDON_INPUT_MODE, ++ g_param_spec_flags ("hildon-input-mode", ++ P_("Hildon input mode"), ++ P_("Define widget's input mode"), ++ HILDON_TYPE_GTK_INPUT_MODE, ++ HILDON_GTK_INPUT_MODE_FULL | ++ HILDON_GTK_INPUT_MODE_AUTOCAP | ++ HILDON_GTK_INPUT_MODE_DICTIONARY, ++ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GtkEntry:hildon-input-default: ++ * ++ * Default input mode for this IM context. See #HildonGtkInputMode. ++ * The default setting for this property is %HILDON_GTK_INPUT_MODE_FULL, ++ * which means that the default input mode to be used is up to the ++ * implementation of the IM context. ++ * ++ * Since: maemo 5 ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_HILDON_INPUT_DEFAULT, ++ g_param_spec_flags ("hildon-input-default", ++ P_("Hildon input default"), ++ P_("Define widget's default input mode"), ++ HILDON_TYPE_GTK_INPUT_MODE, ++ HILDON_GTK_INPUT_MODE_FULL, ++ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** + * GtkEntry:icon-prelight: + * + * The prelight style property determines whether activatable +@@ -1572,6 +1640,46 @@ + G_TYPE_NONE, 1, + G_TYPE_STRING); + ++ /** ++ * GtkEntry::invalid-input: ++ * ++ * Emitted when the users enters a character that does not belong to the ++ * #HildonGtkInputMode of the entry, or the maximum number of characters ++ * has been reached. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ */ ++ signals[INVALID_INPUT] = ++ g_signal_new ("invalid_input", ++ G_OBJECT_CLASS_TYPE (gobject_class), ++ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, ++ G_STRUCT_OFFSET (GtkEntryClass, invalid_input), ++ NULL, NULL, ++ _gtk_marshal_VOID__ENUM, ++ G_TYPE_NONE, 1, ++ GTK_TYPE_INVALID_INPUT_TYPE); ++ ++ /** ++ * GtkEntry::select-all: ++ * @entry: the object which received the signal ++ * ++ * The ::select-all signal is a ++ * <link linkend="keybinding-signals">keybinding signal</link> ++ * which gets emitted to select the complete contents of the entry. ++ * ++ * The default bindings for this signal are Ctrl-a and Ctrl-/. ++ * ++ * Since: 2.20 ++ */ ++ signals[SELECT_ALL] = ++ g_signal_new_class_handler (I_("select-all"), ++ G_OBJECT_CLASS_TYPE (gobject_class), ++ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, ++ G_CALLBACK (gtk_entry_select_all), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0, G_TYPE_NONE); + + /* + * Key bindings +@@ -1631,26 +1739,9 @@ + /* Select all + */ + gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK, +- "move-cursor", 3, +- GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, +- G_TYPE_INT, -1, +- G_TYPE_BOOLEAN, FALSE); +- gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK, +- "move-cursor", 3, +- GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, +- G_TYPE_INT, 1, +- G_TYPE_BOOLEAN, TRUE); +- +- gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK, +- "move-cursor", 3, +- GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, +- G_TYPE_INT, -1, +- G_TYPE_BOOLEAN, FALSE); ++ "select-all", 0); + gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK, +- "move-cursor", 3, +- GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, +- G_TYPE_INT, 1, +- G_TYPE_BOOLEAN, TRUE); ++ "select-all", 0); + /* Unselect all + */ + gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_CONTROL_MASK, +@@ -1830,7 +1921,20 @@ + break; + + case PROP_VISIBILITY: +- gtk_entry_set_visibility (entry, g_value_get_boolean (value)); ++ { ++ /* converting to hildon input mode first then through ++ * that mode changing function to reach compatible with ++ * the gtk original visibility changing ++ */ ++ HildonGtkInputMode mode = hildon_gtk_entry_get_input_mode (entry); ++ ++ if (g_value_get_boolean (value)) ++ mode &= ~HILDON_GTK_INPUT_MODE_INVISIBLE; ++ else ++ mode |= HILDON_GTK_INPUT_MODE_INVISIBLE; ++ ++ hildon_gtk_entry_set_input_mode (entry, mode); ++ } + break; + + case PROP_HAS_FRAME: +@@ -1999,6 +2103,18 @@ + entry->editing_canceled = g_value_get_boolean (value); + break; + ++ case PROP_HILDON_PLACEHOLDER_TEXT: ++ hildon_gtk_entry_set_placeholder_text (entry, g_value_get_string (value)); ++ break; ++ ++ case PROP_HILDON_INPUT_MODE: ++ hildon_gtk_entry_set_input_mode (entry, g_value_get_flags (value)); ++ break; ++ ++ case PROP_HILDON_INPUT_DEFAULT: ++ hildon_gtk_entry_set_input_default (entry, g_value_get_flags (value)); ++ break; ++ + case PROP_SCROLL_OFFSET: + case PROP_CURSOR_POSITION: + default: +@@ -2215,6 +2331,18 @@ + entry->editing_canceled); + break; + ++ case PROP_HILDON_PLACEHOLDER_TEXT: ++ g_value_set_string (value, hildon_gtk_entry_get_placeholder_text (entry)); ++ break; ++ ++ case PROP_HILDON_INPUT_MODE: ++ g_value_set_flags (value, hildon_gtk_entry_get_input_mode (entry)); ++ break; ++ ++ case PROP_HILDON_INPUT_DEFAULT: ++ g_value_set_flags (value, hildon_gtk_entry_get_input_default (entry)); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -2285,13 +2413,15 @@ + entry->is_cell_renderer = FALSE; + entry->editing_canceled = FALSE; + entry->has_frame = TRUE; +- entry->truncate_multiline = FALSE; ++ entry->truncate_multiline = TRUE; + priv->shadow_type = GTK_SHADOW_IN; + priv->xalign = 0.0; + priv->caps_lock_warning = TRUE; + priv->caps_lock_warning_shown = FALSE; + priv->progress_fraction = 0.0; + priv->progress_pulse_fraction = 0.1; ++ priv->placeholder_text = NULL; ++ priv->placeholder_layout = NULL; + + gtk_drag_dest_set (GTK_WIDGET (entry), + GTK_DEST_DEFAULT_HIGHLIGHT, +@@ -2312,7 +2442,10 @@ + G_CALLBACK (gtk_entry_retrieve_surrounding_cb), entry); + g_signal_connect (entry->im_context, "delete-surrounding", + G_CALLBACK (gtk_entry_delete_surrounding_cb), entry); +- ++ g_signal_connect (entry->im_context, "has_selection", ++ G_CALLBACK (gtk_entry_has_selection_cb), entry); ++ g_signal_connect (entry->im_context, "clipboard_operation", ++ G_CALLBACK (gtk_entry_clipboard_operation_cb), entry); + } + + static gint +@@ -2498,6 +2631,18 @@ + + g_free (priv->im_module); + ++ if (priv->placeholder_text) ++ { ++ g_free (priv->placeholder_text); ++ priv->placeholder_text = NULL; ++ } ++ ++ if (priv->placeholder_layout) ++ { ++ g_object_unref (priv->placeholder_layout); ++ priv->placeholder_layout = NULL; ++ } ++ + G_OBJECT_CLASS (gtk_entry_parent_class)->finalize (object); + } + +@@ -3656,6 +3801,16 @@ + + gtk_entry_reset_blink_time (entry); + ++ if (entry->editable) ++ { ++ if (hildon_gtk_im_context_filter_event (entry->im_context, ++ (GdkEvent *) event)) ++ { ++ entry->need_im_reset = TRUE; ++ return TRUE; ++ } ++ } ++ + entry->button = event->button; + + if (!gtk_widget_has_focus (widget)) +@@ -3665,6 +3820,9 @@ + entry->in_click = FALSE; + } + ++ /* we need to reset IM context so pre-edit string can be committed */ ++ _gtk_entry_reset_im_context (entry); ++ + tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset); + + if (_gtk_button_event_triggers_context_menu (event)) +@@ -3683,8 +3841,6 @@ + + if (event->state & GTK_EXTEND_SELECTION_MOD_MASK) + { +- _gtk_entry_reset_im_context (entry); +- + if (!have_selection) /* select from the current position to the clicked position */ + sel_start = sel_end = entry->current_pos; + +@@ -3842,6 +3998,16 @@ + if (event->window != entry->text_area || entry->button != event->button) + return FALSE; + ++ if (entry->editable) ++ { ++ if (hildon_gtk_im_context_filter_event (entry->im_context, ++ (GdkEvent *) event)) ++ { ++ entry->need_im_reset = TRUE; ++ return TRUE; ++ } ++ } ++ + if (entry->in_drag) + { + gint tmp_pos = gtk_entry_find_position (entry, entry->drag_start_x); +@@ -4349,6 +4515,14 @@ + { + GtkEntry *entry = GTK_ENTRY (editable); + guint length; ++ GtkWidget *widget = GTK_WIDGET (editable); ++ gboolean flip = FALSE; ++ ++ if (start == 0 && end == -1 && gtk_widget_has_screen (widget)) ++ { ++ GtkSettings *settings = gtk_widget_get_settings (widget); ++ g_object_get (settings, "gtk-touchscreen-mode", &flip, NULL); ++ } + + length = gtk_entry_buffer_get_length (get_buffer (entry)); + if (start < 0) +@@ -4356,11 +4530,14 @@ + if (end < 0) + end = length; + +- _gtk_entry_reset_im_context (entry); +- +- gtk_entry_set_positions (entry, +- MIN (end, length), +- MIN (start, length)); ++ if (flip) ++ gtk_entry_set_positions (entry, ++ MIN (start, entry->text_length), ++ MIN (end, entry->text_length)); ++ else ++ gtk_entry_set_positions (entry, ++ MIN (end, entry->text_length), ++ MIN (start, entry->text_length)); + + gtk_entry_update_primary_selection (entry); + } +@@ -4435,6 +4612,12 @@ + + gtk_entry_recompute (entry); + ++ if (priv->placeholder_text) ++ { ++ g_object_unref (priv->placeholder_layout); ++ priv->placeholder_layout = gtk_entry_create_placeholder_layout (entry); ++ } ++ + if (previous_style && gtk_widget_get_realized (widget)) + { + gdk_window_set_background (widget->window, &widget->style->base[gtk_widget_get_state (widget)]); +@@ -4507,7 +4690,6 @@ + g_slice_free (GtkEntryPasswordHint, password_hint); + } + +- + static gboolean + gtk_entry_remove_password_hint (gpointer data) + { +@@ -4519,6 +4701,113 @@ + return FALSE; + } + ++/* Returns TRUE if chr is valid in given input mode. Probably should ++ * be made public, but there's no good place for it and the input mode ++ * design might change, so for now we'll keep this here. ++ */ ++static gboolean ++hildon_gtk_input_mode_is_valid_char (HildonGtkInputMode mode, ++ gunichar chr, ++ gunichar *chr_) ++{ ++ static const char *tele_chars_ascii = "p#*+"; ++ ++ if (g_unichar_isalpha (chr) || chr == ' ') ++ { ++ if ((mode & HILDON_GTK_INPUT_MODE_ALPHA) != 0) ++ return TRUE; ++ if ((mode & HILDON_GTK_INPUT_MODE_HEXA) != 0 && g_unichar_isxdigit(chr)) ++ return TRUE; ++ } ++ else if (g_unichar_isdigit (chr)) ++ { ++ if ((mode & (HILDON_GTK_INPUT_MODE_NUMERIC | ++ HILDON_GTK_INPUT_MODE_HEXA | ++ HILDON_GTK_INPUT_MODE_TELE)) != 0) ++ { ++ gchar* number = g_strdup_printf ("%d", g_unichar_digit_value (chr)); ++ *chr_ = g_utf8_get_char (number); ++ g_free (number); ++ return chr == *chr_; ++ } ++ } ++ else ++ { ++ /* special = anything else than alphanumeric/space */ ++ if ((mode & HILDON_GTK_INPUT_MODE_SPECIAL) != 0) ++ return TRUE; ++ ++ /* numeric also contains '-', but hexa doesn't */ ++ if ((mode & HILDON_GTK_INPUT_MODE_NUMERIC) != 0 && chr == '-') ++ return TRUE; ++ } ++ ++ /* check special tele chars last */ ++ if ((mode & HILDON_GTK_INPUT_MODE_TELE) != 0 && ++ strchr(tele_chars_ascii, chr) != NULL) ++ return TRUE; ++ ++ return FALSE; ++} ++ ++static gboolean ++gtk_entry_filter_text (GtkEntry *entry, ++ const gchar *str, ++ gint length, ++ gint nbytes, ++ gchar **filtered) ++{ ++ HildonGtkInputMode input_mode; ++ ++ g_assert (GTK_IS_ENTRY (entry)); ++ ++ if (!length || !str) ++ return FALSE; ++ ++ if (!g_utf8_validate (str, nbytes, NULL)) ++ return FALSE; ++ ++ input_mode = hildon_gtk_entry_get_input_mode (entry); ++ ++ if ((input_mode & HILDON_GTK_INPUT_MODE_TELE) != 0) ++ { ++ gboolean valid = TRUE; ++ GString *result = g_string_sized_new (nbytes); ++ ++ while(length) ++ { ++ gunichar chr = g_utf8_get_char (str); ++ gunichar chr_ = chr; ++ gboolean valid_char; ++ ++ valid_char = hildon_gtk_input_mode_is_valid_char (input_mode, chr, &chr_); ++ if (valid_char || chr != chr_) ++ g_string_append_unichar (result, chr_); ++ if (!valid_char) ++ valid = FALSE; ++ ++ str = g_utf8_next_char (str); ++ length--; ++ } ++ ++ *filtered = g_string_free (result, FALSE); ++ return valid; ++ } ++ ++ while(length) ++ { ++ gunichar chr = g_utf8_get_char (str); ++ ++ if (!hildon_gtk_input_mode_is_valid_char (input_mode, chr, &chr)) ++ return FALSE; ++ ++ str = g_utf8_next_char (str); ++ length--; ++ } ++ ++ return TRUE; ++} ++ + /* Default signal handlers + */ + static void +@@ -4529,9 +4818,28 @@ + { + guint n_inserted; + gint n_chars; ++ gchar *filtered = NULL; ++ GtkEntry *entry = GTK_ENTRY (editable); + + n_chars = g_utf8_strlen (new_text, new_text_length); + ++ if (!gtk_entry_filter_text (entry, new_text, n_chars, ++ new_text_length, &filtered)) ++ { ++ if (filtered) ++ { ++ new_text = filtered; ++ new_text_length = strlen (filtered); ++ n_chars = g_utf8_strlen (filtered, new_text_length); ++ } ++ else ++ { ++ g_signal_emit (entry, signals[INVALID_INPUT], 0, ++ GTK_INVALID_INPUT_MODE_RESTRICTION); ++ return; ++ } ++ } ++ + /* + * The actual insertion into the buffer. This will end up firing the + * following signal handlers: buffer_inserted_text(), buffer_notify_display_text(), +@@ -4540,9 +4848,13 @@ + n_inserted = gtk_entry_buffer_insert_text (get_buffer (GTK_ENTRY (editable)), *position, new_text, n_chars); + + if (n_inserted != n_chars) +- gtk_widget_error_bell (GTK_WIDGET (editable)); ++ { ++ g_signal_emit (entry, signals[INVALID_INPUT], 0, ++ GTK_INVALID_INPUT_MAX_CHARS_REACHED); ++ } + + *position += n_inserted; ++ g_free (filtered); + } + + static void +@@ -4871,8 +5183,6 @@ + gint end_pos = entry->current_pos; + gint old_n_bytes = gtk_entry_buffer_get_bytes (get_buffer (entry)); + +- _gtk_entry_reset_im_context (entry); +- + if (!entry->editable) + { + gtk_widget_error_bell (GTK_WIDGET (entry)); +@@ -4947,8 +5257,6 @@ + GtkEditable *editable = GTK_EDITABLE (entry); + gint prev_pos; + +- _gtk_entry_reset_im_context (entry); +- + if (!entry->editable) + { + gtk_widget_error_bell (GTK_WIDGET (entry)); +@@ -5193,6 +5501,36 @@ + return TRUE; + } + ++static gboolean ++gtk_entry_has_selection_cb (GtkIMContext *context, ++ GtkEntry *entry) ++{ ++ return gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), NULL, NULL); ++} ++ ++static void ++gtk_entry_clipboard_operation_cb (GtkIMContext *context, ++ GtkIMContextClipboardOperation op, ++ GtkEntry *entry) ++{ ++ /* Similar to gtk_editable_*_clipboard(), handle these by sending ++ * signals instead of directly calling our internal functions. That ++ * way the application can hook into them if needed. ++ */ ++ switch (op) ++ { ++ case GTK_IM_CONTEXT_CLIPBOARD_OP_COPY: ++ g_signal_emit_by_name (entry, "copy_clipboard"); ++ break; ++ case GTK_IM_CONTEXT_CLIPBOARD_OP_CUT: ++ g_signal_emit_by_name (entry, "cut_clipboard"); ++ break; ++ case GTK_IM_CONTEXT_CLIPBOARD_OP_PASTE: ++ g_signal_emit_by_name (entry, "paste_clipboard"); ++ break; ++ } ++} ++ + /* Internal functions + */ + +@@ -5334,6 +5672,58 @@ + } + + static PangoLayout * ++gtk_entry_create_placeholder_layout (GtkEntry *entry) ++{ ++ GtkWidget *widget = GTK_WIDGET (entry); ++ PangoLayout *layout = gtk_widget_create_pango_layout (widget, NULL); ++ GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); ++ PangoDirection pango_dir; ++ GdkColor font_color; ++ ++ pango_layout_set_single_paragraph_mode (layout, TRUE); ++ ++ pango_dir = pango_find_base_dir (priv->placeholder_text, ++ strlen (priv->placeholder_text)); ++ ++ if (pango_dir == PANGO_DIRECTION_NEUTRAL) ++ { ++ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ++ pango_dir = PANGO_DIRECTION_RTL; ++ else ++ pango_dir = PANGO_DIRECTION_LTR; ++ } ++ ++ pango_context_set_base_dir (gtk_widget_get_pango_context (widget), ++ pango_dir); ++ ++ pango_layout_set_alignment (layout, pango_dir); ++ ++ pango_layout_set_text (layout, priv->placeholder_text, ++ strlen (priv->placeholder_text)); ++ ++ if (gtk_style_lookup_color (widget->style, "ReversedSecondaryTextColor", ++ &font_color)) ++ { ++ PangoAttrList *list; ++ PangoAttribute *attr; ++ ++ list = pango_attr_list_new (); ++ attr = pango_attr_foreground_new (font_color.red, ++ font_color.green, ++ font_color.blue); ++ attr->start_index = 0; ++ attr->end_index = G_MAXINT; ++ pango_attr_list_insert (list, attr); ++ ++ pango_layout_set_attributes (layout, list); ++ ++ pango_attr_list_unref (list); ++ } ++ ++ return layout; ++} ++ ++static PangoLayout * + gtk_entry_create_layout (GtkEntry *entry, + gboolean include_preedit) + { +@@ -5441,6 +5831,19 @@ + return entry->cached_layout; + } + ++static inline gboolean ++show_placeholder (GtkEntry *entry) ++{ ++ GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); ++ ++ if (!gtk_widget_has_focus (GTK_WIDGET (entry)) ++ && gtk_entry_buffer_get_bytes (get_buffer (entry)) == 0 ++ && priv->placeholder_text) ++ return TRUE; ++ ++ return FALSE; ++} ++ + static void + get_layout_position (GtkEntry *entry, + gint *x, +@@ -5452,8 +5855,12 @@ + GtkBorder inner_border; + gint y_pos; + PangoLayoutLine *line; ++ GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); + +- layout = gtk_entry_ensure_layout (entry, TRUE); ++ if (show_placeholder (entry)) ++ layout = priv->placeholder_layout; ++ else ++ layout = gtk_entry_ensure_layout (entry, TRUE); + + gtk_entry_get_text_area_size (entry, NULL, NULL, &area_width, &area_height); + _gtk_entry_effective_inner_border (entry, &inner_border); +@@ -5487,13 +5894,19 @@ + static void + draw_text_with_color (GtkEntry *entry, cairo_t *cr, GdkColor *default_color) + { +- PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE); ++ GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); ++ PangoLayout *layout; + GtkWidget *widget; + gint x, y; + gint start_pos, end_pos; + + widget = GTK_WIDGET (entry); + ++ if (show_placeholder (entry)) ++ layout = priv->placeholder_layout; ++ else ++ layout = gtk_entry_ensure_layout (entry, TRUE); ++ + cairo_save (cr); + + get_layout_position (entry, &x, &y); +@@ -5772,11 +6185,7 @@ + void + _gtk_entry_reset_im_context (GtkEntry *entry) + { +- if (entry->need_im_reset) +- { +- entry->need_im_reset = FALSE; +- gtk_im_context_reset (entry->im_context); +- } ++ gtk_im_context_reset (entry->im_context); + } + + /** +@@ -6898,15 +7307,19 @@ + { + g_return_if_fail (GTK_IS_ENTRY (entry)); + +- visible = visible != FALSE; ++ g_object_set (G_OBJECT (entry), "visibility", visible, NULL); ++} + +- if (entry->visible != visible) +- { +- entry->visible = visible; ++static void ++gtk_entry_set_real_visibility (GtkEntry *entry, ++ gboolean visible) ++{ ++ g_return_if_fail (GTK_IS_ENTRY (entry)); + +- g_object_notify (G_OBJECT (entry), "visibility"); +- gtk_entry_recompute (entry); +- } ++ entry->visible = visible ? TRUE : FALSE; ++ g_object_notify (G_OBJECT (entry), "visibility"); ++ ++ gtk_entry_recompute (entry); + } + + /** +@@ -10129,5 +10542,150 @@ + remove_capslock_feedback (entry); + } + ++/** ++ * hildon_gtk_entry_set_placeholder_text: ++ * @entry: a #GtkEntry ++ * @placeholder_text: a string to be displayed when @entry is empty ++ * and unfocused or %NULL to remove current placeholder text. ++ * ++ * Sets a text string to be displayed when @entry is empty and unfocused. ++ * This can be provided to give a visual hint of the expected contents ++ * of the #GtkEntry. ++ * ++ * Since: maemo 5 ++ **/ ++void ++hildon_gtk_entry_set_placeholder_text (GtkEntry *entry, ++ const gchar *placeholder_text) ++{ ++ GtkEntryPrivate *priv; ++ ++ g_return_if_fail (GTK_IS_ENTRY (entry)); ++ ++ priv = GTK_ENTRY_GET_PRIVATE (entry); ++ ++ if (priv->placeholder_text) ++ { ++ g_free (priv->placeholder_text); ++ g_object_unref (priv->placeholder_layout); ++ } ++ ++ if (placeholder_text) ++ { ++ priv->placeholder_text = g_strdup (placeholder_text); ++ priv->placeholder_layout = gtk_entry_create_placeholder_layout (entry); ++ } ++ else ++ { ++ priv->placeholder_text = NULL; ++ priv->placeholder_layout = NULL; ++ } ++ ++ if (show_placeholder (entry)) ++ { ++ gtk_widget_queue_draw (GTK_WIDGET (entry)); ++ } ++ ++ g_object_notify (G_OBJECT (entry), "hildon-placeholder-text"); ++} ++ ++/** ++ * hildon_gtk_entry_get_placeholder_text: ++ * @entry: a #GtkEntry ++ * ++ * Gets the text to be displayed if @entry is empty and unfocused. ++ * ++ * Returns: a string or %NULL if no placeholder text is set ++ * ++ * Since: maemo 5 ++ **/ ++const gchar * ++hildon_gtk_entry_get_placeholder_text (GtkEntry *entry) ++{ ++ GtkEntryPrivate *priv; ++ ++ g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL); ++ ++ priv = GTK_ENTRY_GET_PRIVATE (entry); ++ ++ return priv->placeholder_text; ++} ++ ++void ++hildon_gtk_entry_set_input_mode (GtkEntry *entry, ++ HildonGtkInputMode mode) ++{ ++ g_return_if_fail (GTK_IS_ENTRY (entry)); ++ ++ if (hildon_gtk_entry_get_input_mode (entry) != mode) ++ { ++ gtk_entry_set_real_visibility (entry, ++ mode & HILDON_GTK_INPUT_MODE_INVISIBLE ++ ? FALSE : TRUE); ++ g_object_set (G_OBJECT (entry->im_context), ++ "hildon-input-mode", mode, NULL); ++ g_object_notify (G_OBJECT (entry), "hildon-input-mode"); ++ } ++} ++ ++HildonGtkInputMode ++hildon_gtk_entry_get_input_mode (GtkEntry *entry) ++{ ++ HildonGtkInputMode mode; ++ ++ g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE); ++ ++ g_object_get (G_OBJECT (entry->im_context), ++ "hildon-input-mode", &mode, NULL); ++ ++ return mode; ++} ++ ++/** ++ * hildon_gtk_entry_set_input_default: ++ * @entry: a #GtkEntry ++ * @mode: a #HildonGtkInputMode ++ * ++ * Sets the default input mode of the widget. ++ * ++ * Since: maemo 5.0 ++ */ ++void ++hildon_gtk_entry_set_input_default (GtkEntry *entry, ++ HildonGtkInputMode mode) ++{ ++ g_return_if_fail (GTK_IS_ENTRY (entry)); ++ ++ if (hildon_gtk_entry_get_input_default (entry) != mode) ++ { ++ g_object_set (G_OBJECT (entry->im_context), ++ "hildon-input-default", mode, NULL); ++ g_object_notify (G_OBJECT (entry), "hildon-input-default"); ++ } ++} ++ ++/** ++ * hildon_gtk_entry_get_input_default: ++ * @entry: a #GtkEntry ++ * ++ * Gets the default input mode of the widget. ++ * ++ * Return value: the default input mode of the widget. ++ * ++ * Since: maemo 5.0 ++ */ ++HildonGtkInputMode ++hildon_gtk_entry_get_input_default (GtkEntry *entry) ++{ ++ HildonGtkInputMode mode; ++ ++ g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE); ++ ++ g_object_get (G_OBJECT (entry->im_context), ++ "hildon-input-default", &mode, NULL); ++ ++ return mode; ++} ++ + #define __GTK_ENTRY_C__ + #include "gtkaliasdef.c" +--- a/gtk/gtkentry.h ++++ b/gtk/gtkentry.h +@@ -160,6 +160,8 @@ + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); ++ void (* invalid_input) (GtkEntry *entry, ++ GtkInvalidInputType invalid_input_type); + void (*_gtk_reserved2) (void); + }; + +@@ -329,6 +331,19 @@ + gboolean editable); + #endif /* GTK_DISABLE_DEPRECATED */ + ++const gchar * hildon_gtk_entry_get_placeholder_text (GtkEntry *entry); ++ ++void hildon_gtk_entry_set_placeholder_text (GtkEntry *entry, ++ const gchar *placeholder_text); ++ ++void hildon_gtk_entry_set_input_mode (GtkEntry *entry, ++ HildonGtkInputMode input_mode); ++HildonGtkInputMode hildon_gtk_entry_get_input_mode (GtkEntry *entry); ++ ++void hildon_gtk_entry_set_input_default (GtkEntry *entry, ++ HildonGtkInputMode input_mode); ++HildonGtkInputMode hildon_gtk_entry_get_input_default (GtkEntry *entry); ++ + G_END_DECLS + + #endif /* __GTK_ENTRY_H__ */ diff --git a/hildonize-gtk-enums.patch b/hildonize-gtk-enums.patch new file mode 100644 index 000000000000..3d1bfda3f061 --- /dev/null +++ b/hildonize-gtk-enums.patch @@ -0,0 +1,92 @@ +--- a/gtk/gtkenums.h ++++ b/gtk/gtkenums.h +@@ -579,6 +579,52 @@ + GTK_DRAG_RESULT_ERROR + } GtkDragResult; + ++typedef enum ++{ ++ GTK_INVALID_INPUT_MAX_CHARS_REACHED, ++ GTK_INVALID_INPUT_MODE_RESTRICTION ++} GtkInvalidInputType; ++ ++typedef enum ++{ ++ HILDON_GTK_INPUT_MODE_ALPHA = 1 << 0, ++ HILDON_GTK_INPUT_MODE_NUMERIC = 1 << 1, ++ HILDON_GTK_INPUT_MODE_SPECIAL = 1 << 2, ++ HILDON_GTK_INPUT_MODE_HEXA = 1 << 3, ++ HILDON_GTK_INPUT_MODE_TELE = 1 << 4, ++ HILDON_GTK_INPUT_MODE_FULL = (HILDON_GTK_INPUT_MODE_ALPHA | HILDON_GTK_INPUT_MODE_NUMERIC | HILDON_GTK_INPUT_MODE_SPECIAL), ++ HILDON_GTK_INPUT_MODE_NO_SCREEN_PLUGINS = 1 << 27, ++ HILDON_GTK_INPUT_MODE_MULTILINE = 1 << 28, ++ HILDON_GTK_INPUT_MODE_INVISIBLE = 1 << 29, ++ HILDON_GTK_INPUT_MODE_AUTOCAP = 1 << 30, ++ HILDON_GTK_INPUT_MODE_DICTIONARY = 1 << 31 ++} HildonGtkInputMode; ++ ++/* Temporary compatibility define */ ++#define GTK_TYPE_GTK_INPUT_MODE HILDON_TYPE_GTK_INPUT_MODE ++ ++typedef enum ++{ ++ HILDON_DIABLO, ++ HILDON_FREMANTLE ++} HildonMode; ++ ++typedef enum ++{ ++ HILDON_UI_MODE_NORMAL, ++ HILDON_UI_MODE_EDIT ++} HildonUIMode; ++ ++typedef enum { ++ HILDON_SIZE_AUTO_WIDTH = 0 << 0, /* set to automatic width */ ++ HILDON_SIZE_HALFSCREEN_WIDTH = 1 << 0, /* set to 50% screen width */ ++ HILDON_SIZE_FULLSCREEN_WIDTH = 2 << 0, /* set to 100% screen width */ ++ HILDON_SIZE_AUTO_HEIGHT = 0 << 2, /* set to automatic height */ ++ HILDON_SIZE_FINGER_HEIGHT = 1 << 2, /* set to finger height */ ++ HILDON_SIZE_THUMB_HEIGHT = 2 << 2, /* set to thumb height */ ++ HILDON_SIZE_AUTO = (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_AUTO_HEIGHT) ++} HildonSizeType; ++ + G_END_DECLS + + #endif /* __GTK_ENUMS_H__ */ +--- a/gtk/Makefile.am ++++ b/gtk/Makefile.am +@@ -837,6 +837,7 @@ + # versions in the build dir. thus a development setup requires + # srcdir to be writable, passing --disable-rebuilds to + # ../configure will supress all autogeneration rules. ++ + gtkmarshalers.h: stamp-gtkmarshalers.h + @true + stamp-gtkmarshalers.h: @REBUILD@ gtkmarshalers.list +@@ -844,7 +845,7 @@ + && (cmp -s xgen-gmlh gtkmarshalers.h || cp xgen-gmlh gtkmarshalers.h) \ + && rm -f xgen-gmlh \ + && echo timestamp > $(@F) +-gtkmarshalers.c: @REBUILD@ gtkmarshalers.list ++gtkmarshalers.c: gtkmarshalers.h @REBUILD@ gtkmarshalers.list + (echo "#include \"gtkmarshalers.h\""; \ + echo "#include \"gtkalias.h\""; \ + $(GLIB_GENMARSHAL) --prefix=_gtk_marshal $(srcdir)/gtkmarshalers.list --body) >> xgen-gmlc \ +@@ -860,7 +861,7 @@ + && (cmp -s xgen-gmh gtkmarshal.h || cp xgen-gmh gtkmarshal.h) \ + && rm -f xgen-gmh \ + && echo timestamp > $(@F) +-gtkmarshal.c: @REBUILD@ gtkmarshal.list ++gtkmarshal.c: gtkmarshal.h @REBUILD@ gtkmarshal.list + (echo "#include \"gtkmarshal.h\""; \ + echo "#include \"gtkalias.h\""; \ + $(GLIB_GENMARSHAL) --prefix=gtk_marshal $(srcdir)/gtkmarshal.list --body; \ +@@ -877,7 +878,7 @@ + && (cmp -s xgen-gtbh gtktypebuiltins.h || cp xgen-gtbh gtktypebuiltins.h ) \ + && rm -f xgen-gtbh \ + && echo timestamp > $(@F) +-gtktypebuiltins.c: @REBUILD@ $(gtk_public_h_sources) gtktypebuiltins.c.template ++gtktypebuiltins.c: gtktypebuiltins.h @REBUILD@ $(gtk_public_h_sources) gtktypebuiltins.c.template + ( cd $(srcdir) && $(GLIB_MKENUMS) --template gtktypebuiltins.c.template \ + $(gtk_public_h_sources) ) > xgen-gtbc \ + && cp xgen-gtbc gtktypebuiltins.c \ diff --git a/hildonize-gtk-iconview.patch b/hildonize-gtk-iconview.patch new file mode 100644 index 000000000000..9fb2465f2eec --- /dev/null +++ b/hildonize-gtk-iconview.patch @@ -0,0 +1,1311 @@ +--- a/gtk/gtkiconview.c ++++ b/gtk/gtkiconview.c +@@ -40,12 +40,15 @@ + #include "gtkcombobox.h" + #include "gtktextbuffer.h" + #include "gtktreednd.h" ++#include "gtkicontheme.h" + #include "gtkprivate.h" + #include "gtkalias.h" + + #undef DEBUG_ICON_VIEW + + #define SCROLL_EDGE_SIZE 15 ++#define HILDON_TICK_MARK_SIZE 48 ++#define HILDON_ROW_HEADER_HEIGHT 35 + + #define GTK_ICON_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ICON_VIEW, GtkIconViewPrivate)) + +@@ -73,7 +76,7 @@ + + guint selected : 1; + guint selected_before_rubberbanding : 1; +- ++ guint is_header : 1; + }; + + typedef struct _GtkIconViewCellInfo GtkIconViewCellInfo; +@@ -187,6 +190,20 @@ + guint extend_selection_pressed : 1; + + guint draw_focus : 1; ++ ++ guint queued_select_was_selected : 1; ++ ++ HildonUIMode hildon_ui_mode; ++ ++ GtkIconViewItem *queued_activate_item; ++ GtkIconViewItem *queued_select_item; ++ ++ HildonIconViewRowHeaderFunc row_header_func; ++ gpointer row_header_data; ++ GDestroyNotify row_header_destroy; ++ PangoLayout *row_header_layout; ++ ++ GdkPixbuf *tickmark_icon; + }; + + /* Signals */ +@@ -222,7 +239,8 @@ + PROP_MARGIN, + PROP_REORDERABLE, + PROP_TOOLTIP_COLUMN, +- PROP_ITEM_PADDING ++ PROP_ITEM_PADDING, ++ PROP_HILDON_UI_MODE + }; + + /* GObject vfuncs */ +@@ -453,6 +471,9 @@ + + static void adjust_wrap_width (GtkIconView *icon_view, + GtkIconViewItem *item); ++static void free_queued_activate_item (GtkIconView *icon_view); ++static void free_queued_select_item (GtkIconView *icon_view); ++static void hildon_icon_view_setup_row_header_layout (GtkIconView *icon_view); + + /* GtkBuildable */ + static GtkBuildableIface *parent_buildable_iface; +@@ -794,7 +815,29 @@ + 0, G_MAXINT, 6, + GTK_PARAM_READWRITE)); + +- ++ /** ++ * GtkIconView::hildon-ui-mode ++ * ++ * Specifies which UI mode to use. A setting of #HILDON_UI_MODE_NORMAL ++ * will cause the icon view to disable selections and emit item-activated ++ * as soon as an item is pressed. When #HILDON_UI_MODE_EDIT is set, ++ * selections can be made according to the setting of the mode in ++ * GtkIconView::selection-mode. ++ * ++ * Toggling this property will cause the icon view to select an ++ * appropriate selection mode if not already done. ++ * ++ * Since: maemo 5.0 ++ * Stability: unstable. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_HILDON_UI_MODE, ++ g_param_spec_enum ("hildon-ui-mode", ++ P_("Hildon UI Mode"), ++ P_("The mode according to which the icon view should behave"), ++ HILDON_TYPE_UI_MODE, ++ HILDON_UI_MODE_NORMAL, ++ GTK_PARAM_READWRITE)); + + /* Style properties */ + gtk_widget_class_install_style_property (widget_class, +@@ -1145,6 +1188,11 @@ + icon_view->priv->item_padding = 6; + + icon_view->priv->draw_focus = TRUE; ++ ++ icon_view->priv->hildon_ui_mode = HILDON_UI_MODE_NORMAL; ++ ++ icon_view->priv->queued_activate_item = NULL; ++ icon_view->priv->queued_select_item = NULL; + } + + static void +@@ -1184,6 +1232,24 @@ + icon_view->priv->vadjustment = NULL; + } + ++ if (icon_view->priv->row_header_destroy && icon_view->priv->row_header_data) ++ { ++ (* icon_view->priv->row_header_destroy) (icon_view->priv->row_header_data); ++ icon_view->priv->row_header_data = NULL; ++ } ++ ++ if (icon_view->priv->row_header_layout != NULL) ++ { ++ g_object_unref (icon_view->priv->row_header_layout); ++ icon_view->priv->row_header_layout = NULL; ++ } ++ ++ if (icon_view->priv->tickmark_icon) ++ { ++ g_object_unref (icon_view->priv->tickmark_icon); ++ icon_view->priv->tickmark_icon = NULL; ++ } ++ + GTK_OBJECT_CLASS (gtk_icon_view_parent_class)->destroy (object); + } + +@@ -1258,6 +1324,10 @@ + gtk_icon_view_set_item_padding (icon_view, g_value_get_int (value)); + break; + ++ case PROP_HILDON_UI_MODE: ++ hildon_icon_view_set_hildon_ui_mode (icon_view, g_value_get_enum (value)); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -1324,6 +1394,10 @@ + g_value_set_int (value, icon_view->priv->item_padding); + break; + ++ case PROP_HILDON_UI_MODE: ++ g_value_set_enum (value, icon_view->priv->hildon_ui_mode); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -1425,6 +1499,21 @@ + gdk_window_set_background (icon_view->priv->bin_window, &widget->style->base[widget->state]); + } + ++ /* Reset the UI mode */ ++ hildon_icon_view_set_hildon_ui_mode (icon_view, icon_view->priv->hildon_ui_mode); ++ ++ if (icon_view->priv->row_header_layout) ++ hildon_icon_view_setup_row_header_layout (icon_view); ++ ++ if (icon_view->priv->tickmark_icon) ++ g_object_unref (icon_view->priv->tickmark_icon); ++ ++ icon_view->priv->tickmark_icon = ++ gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), ++ "widgets_tickmark_grid", ++ HILDON_TICK_MARK_SIZE, ++ 0, NULL); ++ + gtk_widget_queue_resize (widget); + } + +@@ -1561,12 +1650,15 @@ + gint dest_index; + GtkIconViewDropPosition dest_pos; + GtkIconViewItem *dest_item = NULL; ++ HildonMode mode; + + icon_view = GTK_ICON_VIEW (widget); + + if (expose->window != icon_view->priv->bin_window) + return FALSE; + ++ gtk_widget_style_get (widget, "hildon-mode", &mode, NULL); ++ + /* If a layout has been scheduled, do it now so that all + * cell view items have valid sizes before we proceed. */ + if (icon_view->priv->layout_idle_id != 0) +@@ -1601,8 +1693,25 @@ + gtk_icon_view_paint_item (icon_view, cr, item, &expose->area, + icon_view->priv->bin_window, + item->x, item->y, +- icon_view->priv->draw_focus); +- ++ icon_view->priv->draw_focus); ++ ++ if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE ++ && mode == HILDON_FREMANTLE ++ && icon_view->priv->hildon_ui_mode == HILDON_UI_MODE_EDIT ++ && item->selected) ++ { ++ gdk_draw_pixbuf (icon_view->priv->bin_window, ++ NULL, ++ icon_view->priv->tickmark_icon, ++ 0, 0, ++ item->x + (item->width - HILDON_TICK_MARK_SIZE) / 2, ++ item->y + (item->height - HILDON_TICK_MARK_SIZE) / 2, ++ HILDON_TICK_MARK_SIZE, ++ HILDON_TICK_MARK_SIZE, ++ GDK_RGB_DITHER_MAX, ++ 0, 0); ++ } ++ + if (dest_index == item->index) + dest_item = item; + } +@@ -1713,10 +1822,28 @@ + { + GtkIconView *icon_view; + gint abs_y; ++ HildonMode mode; + + icon_view = GTK_ICON_VIEW (widget); + +- gtk_icon_view_maybe_begin_drag (icon_view, event); ++ gtk_widget_style_get (widget, ++ "hildon-mode", &mode, ++ NULL); ++ ++ if (mode == HILDON_FREMANTLE ++ && gtk_drag_check_threshold (widget, ++ icon_view->priv->press_start_x, ++ icon_view->priv->press_start_y, ++ event->x, event->y)) ++ { ++ if (icon_view->priv->hildon_ui_mode == HILDON_UI_MODE_NORMAL) ++ free_queued_activate_item (icon_view); ++ else if (icon_view->priv->hildon_ui_mode == HILDON_UI_MODE_EDIT) ++ free_queued_select_item (icon_view); ++ } ++ ++ if (icon_view->priv->pressed_button >= 0 && mode == HILDON_DIABLO) ++ gtk_icon_view_maybe_begin_drag (icon_view, event); + + if (icon_view->priv->doing_rubberband) + { +@@ -2138,6 +2265,7 @@ + gboolean dirty = FALSE; + GtkCellRendererMode mode; + gint cursor_cell = -1; ++ HildonMode hildon_mode; + + icon_view = GTK_ICON_VIEW (widget); + +@@ -2154,12 +2282,14 @@ + FALSE, + &info); + ++ gtk_widget_style_get (widget, "hildon-mode", &hildon_mode, NULL); ++ + /* + * We consider only the the cells' area as the item area if the + * item is not selected, but if it *is* selected, the complete + * selection rectangle is considered to be part of the item. + */ +- if (item != NULL && (info != NULL || item->selected)) ++ if (item != NULL && (info != NULL || item->selected) && !item->is_header) + { + if (info != NULL) + { +@@ -2172,44 +2302,65 @@ + + gtk_icon_view_scroll_to_item (icon_view, item); + +- if (icon_view->priv->selection_mode == GTK_SELECTION_NONE) ++ if (hildon_mode == HILDON_DIABLO) + { +- gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); +- } +- else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE && +- (event->state & GTK_EXTEND_SELECTION_MOD_MASK)) +- { +- gtk_icon_view_unselect_all_internal (icon_view); ++ if (icon_view->priv->selection_mode == GTK_SELECTION_NONE) ++ { ++ gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); ++ } ++ else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE && ++ (event->state & GTK_EXTEND_SELECTION_MOD_MASK)) ++ { ++ gtk_icon_view_unselect_all_internal (icon_view); + +- gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); +- if (!icon_view->priv->anchor_item) +- icon_view->priv->anchor_item = item; +- else +- gtk_icon_view_select_all_between (icon_view, +- icon_view->priv->anchor_item, +- item); +- dirty = TRUE; ++ gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); ++ if (!icon_view->priv->anchor_item) ++ icon_view->priv->anchor_item = item; ++ else ++ gtk_icon_view_select_all_between (icon_view, ++ icon_view->priv->anchor_item, ++ item); ++ dirty = TRUE; ++ } ++ else ++ { ++ if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE || ++ ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) && ++ (event->state & GTK_MODIFY_SELECTION_MOD_MASK)) ++ { ++ item->selected = !item->selected; ++ gtk_icon_view_queue_draw_item (icon_view, item); ++ dirty = TRUE; ++ } ++ else ++ { ++ gtk_icon_view_unselect_all_internal (icon_view); ++ ++ item->selected = TRUE; ++ gtk_icon_view_queue_draw_item (icon_view, item); ++ dirty = TRUE; ++ } ++ gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); ++ icon_view->priv->anchor_item = item; ++ } + } +- else ++ else + { +- if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE || +- ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) && +- (event->state & GTK_MODIFY_SELECTION_MOD_MASK)) ++ if (icon_view->priv->hildon_ui_mode == HILDON_UI_MODE_NORMAL) + { +- item->selected = !item->selected; ++ icon_view->priv->queued_activate_item = item; ++ ++ /* Queue a draw so it will appear highlighted */ + gtk_icon_view_queue_draw_item (icon_view, item); +- dirty = TRUE; + } +- else ++ else if (icon_view->priv->hildon_ui_mode == HILDON_UI_MODE_EDIT) + { +- gtk_icon_view_unselect_all_internal (icon_view); ++ icon_view->priv->queued_select_item = item; + +- item->selected = TRUE; +- gtk_icon_view_queue_draw_item (icon_view, item); +- dirty = TRUE; ++ /* Queue a draw so it will appear highlighted */ ++ if (!item->selected) ++ gtk_icon_view_queue_draw_item (icon_view, item); + } +- gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); +- icon_view->priv->anchor_item = item; + } + + /* Save press to possibly begin a drag */ +@@ -2236,7 +2387,7 @@ + (GdkEvent *)event); + } + } +- else ++ else if (hildon_mode == HILDON_DIABLO) + { + if (icon_view->priv->selection_mode != GTK_SELECTION_BROWSE && + !(event->state & GTK_MODIFY_SELECTION_MOD_MASK)) +@@ -2252,26 +2403,6 @@ + icon_view->priv->draw_focus = FALSE; + } + +- if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) +- { +- item = gtk_icon_view_get_item_at_coords (icon_view, +- event->x, event->y, +- FALSE, +- NULL); +- +- if (item && item == icon_view->priv->last_single_clicked) +- { +- GtkTreePath *path; +- +- path = gtk_tree_path_new_from_indices (item->index, -1); +- gtk_icon_view_item_activated (icon_view, path); +- gtk_tree_path_free (path); +- } +- +- icon_view->priv->last_single_clicked = NULL; +- icon_view->priv->pressed_button = -1; +- } +- + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); + +@@ -2283,12 +2414,79 @@ + GdkEventButton *event) + { + GtkIconView *icon_view; ++ HildonMode mode; ++ GtkIconViewItem *item = NULL; + + icon_view = GTK_ICON_VIEW (widget); + + if (icon_view->priv->pressed_button == event->button) + icon_view->priv->pressed_button = -1; + ++ gtk_widget_style_get (widget, ++ "hildon-mode", &mode, ++ NULL); ++ ++ if (mode == HILDON_FREMANTLE) ++ item = gtk_icon_view_get_item_at_coords (icon_view, ++ event->x, event->y, ++ FALSE, ++ NULL); ++ ++ if (icon_view->priv->queued_activate_item ++ && mode == HILDON_FREMANTLE ++ && icon_view->priv->hildon_ui_mode == HILDON_UI_MODE_NORMAL) ++ { ++ GtkTreePath *path; ++ ++ gtk_icon_view_queue_draw_item (icon_view, ++ icon_view->priv->queued_activate_item); ++ ++ if (icon_view->priv->queued_activate_item == item) ++ { ++ path = gtk_tree_path_new_from_indices (icon_view->priv->queued_activate_item->index, -1); ++ gtk_icon_view_item_activated (icon_view, path); ++ gtk_tree_path_free (path); ++ } ++ ++ icon_view->priv->queued_activate_item = NULL; ++ } ++ ++ if (icon_view->priv->queued_select_item ++ && mode == HILDON_FREMANTLE ++ && icon_view->priv->hildon_ui_mode == HILDON_UI_MODE_EDIT) ++ { ++ GtkIconViewItem *select_item = icon_view->priv->queued_select_item; ++ ++ free_queued_select_item (icon_view); ++ ++ if (select_item == item) ++ { ++ if (icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) ++ { ++ if (!item->selected) ++ { ++ gtk_icon_view_unselect_all_internal (icon_view); ++ ++ item->selected = TRUE; ++ gtk_icon_view_queue_draw_item (icon_view, item); ++ ++ g_signal_emit (icon_view, ++ icon_view_signals[SELECTION_CHANGED], 0); ++ } ++ } ++ else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE) ++ { ++ item->selected = !item->selected; ++ gtk_icon_view_queue_draw_item (icon_view, item); ++ ++ g_signal_emit (icon_view, ++ icon_view_signals[SELECTION_CHANGED], 0); ++ } ++ ++ icon_view->priv->anchor_item = item; ++ } ++ } ++ + gtk_icon_view_stop_rubberbanding (icon_view); + + remove_scroll_timeout (icon_view); +@@ -2447,6 +2645,9 @@ + gboolean is_in; + gboolean selected; + ++ if (item->is_header) ++ continue; ++ + is_in = gtk_icon_view_item_hit_test (icon_view, item, + x, y, width, height); + +@@ -2687,6 +2888,74 @@ + } + } + ++static inline gboolean ++item_is_header (GtkIconView *icon_view, ++ GtkIconViewItem *item) ++{ ++ gboolean is_header = FALSE; ++ ++ if (icon_view->priv->row_header_func) ++ { ++ GtkTreeIter iter; ++ ++ if (gtk_tree_model_get_flags (icon_view->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST) ++ { ++ GtkTreePath *path; ++ ++ path = gtk_tree_path_new_from_indices (item->index, -1); ++ if (!gtk_tree_model_get_iter (icon_view->priv->model, &iter, path)) ++ return is_header; ++ gtk_tree_path_free (path); ++ } ++ else ++ iter = item->iter; ++ ++ is_header = (* icon_view->priv->row_header_func) (icon_view->priv->model, ++ &iter, ++ NULL, ++ icon_view->priv->row_header_data); ++ } ++ ++ return is_header; ++} ++ ++static gboolean ++search_first_selectable_path (GtkIconView *icon_view, ++ GtkTreePath **path, ++ gboolean search_forward) ++{ ++ int index; ++ GList *list; ++ ++ if (!path || !*path) ++ return FALSE; ++ ++ index = gtk_tree_path_get_indices (*path)[0]; ++ list = g_list_nth (icon_view->priv->items, index); ++ ++ while (list && item_is_header (icon_view, list->data)) ++ { ++ if (search_forward) ++ { ++ index++; ++ list = list->next; ++ } ++ else ++ { ++ index--; ++ list = list->prev; ++ } ++ } ++ ++ if (!list) ++ return FALSE; ++ ++ gtk_tree_path_up (*path); ++ gtk_tree_path_append_index (*path, index); ++ ++ return TRUE; ++} ++ + static GList * + gtk_icon_view_layout_single_row (GtkIconView *icon_view, + GList *first_item, +@@ -2703,6 +2972,7 @@ + gint *max_height; + gint i; + gboolean rtl; ++ GtkIconViewItem *header_item = NULL; + + rtl = gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL; + max_height = g_new0 (gint, icon_view->priv->n_cells); +@@ -2725,6 +2995,17 @@ + GtkIconViewItem *item = items->data; + + gtk_icon_view_calculate_item_size (icon_view, item); ++ ++ if (item->is_header) ++ { ++ header_item = item; ++ item->y = *y + focus_width; ++ ++ /* Include the header item as last item of this row */ ++ items = items->next; ++ break; ++ } ++ + colspan = 1 + (item->width - 1) / (item_width + icon_view->priv->column_spacing); + + item->width = colspan * item_width + (colspan - 1) * icon_view->priv->column_spacing; +@@ -2765,6 +3046,9 @@ + { + GtkIconViewItem *item = items->data; + ++ if (item->is_header) ++ continue; ++ + if (rtl) + { + item->x = *maximum_width - item->width - item->x; +@@ -2778,6 +3062,18 @@ + *y = item->y + item->height + focus_width + icon_view->priv->row_spacing; + } + ++ if (header_item) ++ { ++ /* FIXME: we might want to use allocation->width here instead. */ ++ header_item->x = 0; ++ header_item->width = icon_view->priv->width; ++ ++ header_item->y = *y; ++ header_item->height = HILDON_ROW_HEADER_HEIGHT; ++ ++ *y += HILDON_ROW_HEADER_HEIGHT; ++ } ++ + g_free (max_height); + + return last_item; +@@ -2991,7 +3287,7 @@ + gint spacing; + GList *l; + +- if (item->width != -1 && item->height != -1) ++ if (item->width != -1 && item->height != -1 && !item->is_header) + return; + + if (item->n_cells != icon_view->priv->n_cells) +@@ -3013,6 +3309,11 @@ + + item->width = 0; + item->height = 0; ++ item->is_header = item_is_header (icon_view, item); ++ ++ if (item->is_header) ++ return; ++ + for (l = icon_view->priv->cell_list; l; l = l->next) + { + GtkIconViewCellInfo *info = (GtkIconViewCellInfo *)l->data; +@@ -3153,6 +3454,58 @@ + } + + static void ++gtk_icon_view_paint_row_header (GtkIconView *icon_view, ++ GtkIconViewItem *item, ++ GdkRectangle *area, ++ GdkDrawable *drawable) ++{ ++ gchar *label = NULL; ++ int width, height; ++ gboolean is_header; ++ GtkTreeIter iter; ++ GtkWidget *widget = GTK_WIDGET (icon_view); ++ ++ g_return_if_fail (icon_view->priv->row_header_func != NULL); ++ ++ if (gtk_tree_model_get_flags (icon_view->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST) ++ { ++ GtkTreePath *path; ++ ++ path = gtk_tree_path_new_from_indices (item->index, -1); ++ g_return_if_fail (gtk_tree_model_get_iter (icon_view->priv->model, &iter, path)); ++ gtk_tree_path_free (path); ++ } ++ else ++ iter = item->iter; ++ ++ is_header = (* icon_view->priv->row_header_func) (icon_view->priv->model, ++ &iter, ++ &label, ++ icon_view->priv->row_header_data); ++ ++ g_return_if_fail (is_header == TRUE); ++ g_return_if_fail (icon_view->priv->row_header_layout != NULL); ++ ++ pango_layout_set_text (icon_view->priv->row_header_layout, ++ label, strlen (label)); ++ pango_layout_get_pixel_size (icon_view->priv->row_header_layout, ++ &width, &height); ++ ++ gtk_paint_layout (widget->style, ++ drawable, ++ widget->state, ++ TRUE, ++ area, ++ widget, ++ "iconview-group-header", ++ item->x + (item->width - width) / 2, ++ item->y + item->height - height, ++ icon_view->priv->row_header_layout); ++ ++ g_free (label); ++} ++ ++static void + gtk_icon_view_paint_item (GtkIconView *icon_view, + cairo_t *cr, + GtkIconViewItem *item, +@@ -3169,10 +3522,17 @@ + gint i; + GtkStateType state; + GtkCellRendererState flags; +- ++ gboolean selected = FALSE; ++ + if (icon_view->priv->model == NULL) + return; + ++ if (item->is_header) ++ { ++ gtk_icon_view_paint_row_header (icon_view, item, area, drawable); ++ return; ++ } ++ + gtk_icon_view_set_cell_data (icon_view, item); + + gtk_widget_style_get (GTK_WIDGET (icon_view), +@@ -3181,7 +3541,12 @@ + + padding = focus_width; + +- if (item->selected) ++ if (item->selected ++ || item == icon_view->priv->queued_select_item ++ || item == icon_view->priv->queued_activate_item) ++ selected = TRUE; ++ ++ if (selected) + { + flags = GTK_CELL_RENDERER_SELECTED; + if (gtk_widget_has_focus (GTK_WIDGET (icon_view))) +@@ -3203,7 +3568,7 @@ + item->width, item->height); + #endif + +- if (item->selected) ++ if (selected) + { + gtk_paint_flat_box (GTK_WIDGET (icon_view)->style, + (GdkWindow *) drawable, +@@ -3215,7 +3580,7 @@ + x, y, + item->width, item->height); + } +- ++ + for (l = icon_view->priv->cell_list; l; l = l->next) + { + GtkIconViewCellInfo *info = (GtkIconViewCellInfo *)l->data; +@@ -3264,9 +3629,10 @@ + continue; + + /* If found a editable/activatable cell, draw focus on it. */ +- if (icon_view->priv->cursor_cell < 0 && +- info->cell->mode != GTK_CELL_RENDERER_MODE_INERT) +- icon_view->priv->cursor_cell = i; ++ /* If found a editable/activatable cell, draw focus on it. */ ++ if (icon_view->priv->cursor_cell < 0 && ++ info->cell->mode != GTK_CELL_RENDERER_MODE_INERT) ++ icon_view->priv->cursor_cell = i; + + gtk_icon_view_get_cell_box (icon_view, item, info, &box); + +@@ -3426,11 +3792,24 @@ + AtkObject *obj; + AtkObject *item_obj; + AtkObject *cursor_item_obj; ++ HildonMode mode; + + if (icon_view->priv->cursor_item == item && + (cursor_cell < 0 || cursor_cell == icon_view->priv->cursor_cell)) + return; + ++ /* Cannot set cursor on header */ ++ if (item->is_header) ++ return; ++ ++ gtk_widget_style_get (GTK_WIDGET (icon_view), ++ "hildon-mode", &mode, ++ NULL); ++ ++ /* No cursors in new-style */ ++ if (mode == HILDON_FREMANTLE) ++ return; ++ + obj = gtk_widget_get_accessible (GTK_WIDGET (icon_view)); + if (icon_view->priv->cursor_item != NULL) + { +@@ -3554,6 +3933,9 @@ + if (item->selected) + return; + ++ if (item->is_header) ++ return; ++ + if (icon_view->priv->selection_mode == GTK_SELECTION_NONE) + return; + else if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) +@@ -3578,6 +3960,9 @@ + if (!item->selected) + return; + ++ if (item->is_header) ++ return; ++ + if (icon_view->priv->selection_mode == GTK_SELECTION_NONE || + icon_view->priv->selection_mode == GTK_SELECTION_BROWSE) + return; +@@ -3590,11 +3975,12 @@ + gtk_icon_view_queue_draw_item (icon_view, item); + } + +-static void ++static gint + verify_items (GtkIconView *icon_view) + { + GList *items; + int i = 0; ++ int selected = 0; + + for (items = icon_view->priv->items; items; items = items->next) + { +@@ -3604,8 +3990,13 @@ + g_error ("List item does not match its index: " + "item index %d and list index %d\n", item->index, i); + ++ if (item->selected) ++ selected++; ++ + i++; + } ++ ++ return selected; + } + + static void +@@ -3671,7 +4062,26 @@ + item->index++; + } + +- verify_items (icon_view); ++ if (verify_items (icon_view) < 1) ++ { ++ HildonMode mode; ++ ++ gtk_widget_style_get (GTK_WIDGET (icon_view), ++ "hildon-mode", &mode, ++ NULL); ++ ++ if (mode == HILDON_FREMANTLE ++ && icon_view->priv->hildon_ui_mode == HILDON_UI_MODE_EDIT ++ && icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) ++ { ++ GtkTreePath *tmppath; ++ ++ tmppath = gtk_tree_path_copy (path); ++ search_first_selectable_path (icon_view, &tmppath, TRUE); ++ gtk_icon_view_select_path (icon_view, tmppath); ++ gtk_tree_path_free (tmppath); ++ } ++ } + + gtk_icon_view_queue_layout (icon_view); + } +@@ -3716,8 +4126,28 @@ + + icon_view->priv->items = g_list_delete_link (icon_view->priv->items, list); + +- verify_items (icon_view); +- ++ if (emit && verify_items (icon_view) < 1) ++ { ++ HildonMode mode; ++ ++ gtk_widget_style_get (GTK_WIDGET (icon_view), ++ "hildon-mode", &mode, ++ NULL); ++ ++ if (mode == HILDON_FREMANTLE ++ && icon_view->priv->hildon_ui_mode == HILDON_UI_MODE_EDIT ++ && icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) ++ { ++ GtkTreePath *path; ++ ++ /* The last item was just removed, select the first one */ ++ path = gtk_tree_path_new_first (); ++ search_first_selectable_path (icon_view, &path, TRUE); ++ gtk_icon_view_select_path (icon_view, path); ++ gtk_tree_path_free (path); ++ } ++ } ++ + gtk_icon_view_queue_layout (icon_view); + + if (emit) +@@ -3908,6 +4338,10 @@ + for (items = icon_view->priv->items; items; items = items->next) + { + item = items->data; ++ ++ if (item->is_header) ++ continue; ++ + if (item->row == row && item->col == col) + return item; + } +@@ -4011,6 +4445,9 @@ + { + for (next = item->next; next; next = next->next) + { ++ if (((GtkIconViewItem *)next->data)->is_header) ++ continue; ++ + if (((GtkIconViewItem *)next->data)->col == col) + break; + } +@@ -4026,6 +4463,9 @@ + { + for (next = item->prev; next; next = next->prev) + { ++ if (((GtkIconViewItem *)next->data)->is_header) ++ continue; ++ + if (((GtkIconViewItem *)next->data)->col == col) + break; + } +@@ -4042,6 +4482,28 @@ + return NULL; + } + ++static inline GList * ++find_first_cursor_item (GtkIconView *icon_view, ++ gboolean search_forward) ++{ ++ GList *list; ++ ++ if (search_forward) ++ { ++ list = icon_view->priv->items; ++ while (list && ((GtkIconViewItem *)list->data)->is_header) ++ list = list->next; ++ } ++ else ++ { ++ list = g_list_last (icon_view->priv->items); ++ while (list && ((GtkIconViewItem *)list->data)->is_header) ++ list = list->prev; ++ } ++ ++ return list; ++} ++ + static gboolean + gtk_icon_view_select_all_between (GtkIconView *icon_view, + GtkIconViewItem *anchor, +@@ -4081,7 +4543,7 @@ + if (row1 <= item->row && item->row <= row2 && + col1 <= item->col && item->col <= col2) + { +- if (!item->selected) ++ if (!item->selected && !item->is_header) + { + dirty = TRUE; + item->selected = TRUE; +@@ -4112,11 +4574,16 @@ + if (!icon_view->priv->cursor_item) + { + GList *list; ++ HildonMode mode; + +- if (count > 0) +- list = icon_view->priv->items; +- else +- list = g_list_last (icon_view->priv->items); ++ gtk_widget_style_get (GTK_WIDGET (icon_view), ++ "hildon-mode", &mode, ++ NULL); ++ ++ if (mode == HILDON_FREMANTLE) ++ return; ++ ++ list = find_first_cursor_item (icon_view, count > 0); + + item = list ? list->data : NULL; + cell = -1; +@@ -4190,11 +4657,16 @@ + if (!icon_view->priv->cursor_item) + { + GList *list; ++ HildonMode mode; + +- if (count > 0) +- list = icon_view->priv->items; +- else +- list = g_list_last (icon_view->priv->items); ++ gtk_widget_style_get (GTK_WIDGET (icon_view), ++ "hildon-mode", &mode, ++ NULL); ++ ++ if (mode == HILDON_FREMANTLE) ++ return; ++ ++ list = find_first_cursor_item (icon_view, count > 0); + + item = list ? list->data : NULL; + } +@@ -4250,11 +4722,16 @@ + if (!icon_view->priv->cursor_item) + { + GList *list; ++ HildonMode mode; + +- if (count > 0) +- list = icon_view->priv->items; +- else +- list = g_list_last (icon_view->priv->items); ++ gtk_widget_style_get (GTK_WIDGET (icon_view), ++ "hildon-mode", &mode, ++ NULL); ++ ++ if (mode == HILDON_FREMANTLE) ++ return; ++ ++ list = find_first_cursor_item (icon_view, count > 0); + + item = list ? list->data : NULL; + } +@@ -4321,15 +4798,19 @@ + GtkIconViewItem *item; + GList *list; + gboolean dirty = FALSE; ++ HildonMode mode; + + if (!gtk_widget_has_focus (GTK_WIDGET (icon_view))) + return; + +- if (count < 0) +- list = icon_view->priv->items; +- else +- list = g_list_last (icon_view->priv->items); +- ++ gtk_widget_style_get (GTK_WIDGET (icon_view), ++ "hildon-mode", &mode, ++ NULL); ++ ++ if (mode == HILDON_FREMANTLE) ++ return; ++ ++ list = find_first_cursor_item (icon_view, count < 0); + item = list ? list->data : NULL; + + if (item == icon_view->priv->cursor_item) +@@ -4858,7 +5339,7 @@ + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); + +- item = gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, NULL); ++ item = gtk_icon_view_get_item_at_coords (icon_view, x, y, FALSE, NULL); + + if (!item) + return NULL; +@@ -5278,11 +5759,25 @@ + gtk_icon_view_set_selection_mode (GtkIconView *icon_view, + GtkSelectionMode mode) + { ++ HildonMode hildon_mode; ++ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (mode == icon_view->priv->selection_mode) + return; + ++ gtk_widget_style_get (GTK_WIDGET (icon_view), ++ "hildon-mode", &hildon_mode, ++ NULL); ++ ++ if (hildon_mode == HILDON_FREMANTLE ++ && icon_view->priv->hildon_ui_mode == HILDON_UI_MODE_NORMAL ++ && mode != GTK_SELECTION_NONE) ++ { ++ g_warning ("Cannot change the selection mode to anything other than GTK_SELECTION_NONE in normal-mode.\n"); ++ return; ++ } ++ + if (mode == GTK_SELECTION_NONE || + icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE) + gtk_icon_view_unselect_all (icon_view); +@@ -5897,7 +6392,7 @@ + { + GtkIconViewItem *item = items->data; + +- if (!item->selected) ++ if (!item->selected && !item->is_header) + { + dirty = TRUE; + item->selected = TRUE; +@@ -7648,6 +8143,176 @@ + g_object_notify (G_OBJECT (icon_view), "reorderable"); + } + ++HildonIconViewRowHeaderFunc ++hildon_icon_view_get_row_header_func (GtkIconView *icon_view) ++{ ++ g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); ++ ++ return icon_view->priv->row_header_func; ++} ++ ++static void ++hildon_icon_view_setup_row_header_layout (GtkIconView *icon_view) ++{ ++ GdkColor font_color; ++ GtkStyle *font_style; ++ GtkWidget *widget = GTK_WIDGET (icon_view); ++ ++ font_style = gtk_rc_get_style_by_paths (gtk_settings_get_default (), ++ "EmpSmallSystemFont", ++ NULL, G_TYPE_NONE); ++ if (font_style) ++ { ++ pango_layout_set_font_description (icon_view->priv->row_header_layout, ++ font_style->font_desc); ++ } ++ ++ if (gtk_style_lookup_color (widget->style, "SecondaryTextColor", &font_color)) ++ { ++ PangoAttrList *list; ++ PangoAttribute *attr; ++ ++ list = pango_attr_list_new (); ++ attr = pango_attr_foreground_new (font_color.red, ++ font_color.green, ++ font_color.blue); ++ attr->start_index = 0; ++ attr->end_index = G_MAXINT; ++ pango_attr_list_insert (list, attr); ++ ++ pango_layout_set_attributes (icon_view->priv->row_header_layout, ++ list); ++ ++ pango_attr_list_unref (list); ++ } ++} ++ ++void ++hildon_icon_view_set_row_header_func (GtkIconView *icon_view, ++ HildonIconViewRowHeaderFunc func, ++ gpointer data, ++ GDestroyNotify destroy) ++{ ++ g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); ++ ++ if (icon_view->priv->row_header_destroy) ++ (* icon_view->priv->row_header_destroy) (icon_view->priv->row_header_data); ++ ++ icon_view->priv->row_header_func = func; ++ icon_view->priv->row_header_data = data; ++ icon_view->priv->row_header_destroy = destroy; ++ ++ if (func && !icon_view->priv->row_header_layout) ++ { ++ icon_view->priv->row_header_layout = ++ gtk_widget_create_pango_layout (GTK_WIDGET (icon_view), ""); ++ ++ hildon_icon_view_setup_row_header_layout (icon_view); ++ } ++ else if (!func && icon_view->priv->row_header_layout) ++ { ++ g_object_unref (icon_view->priv->row_header_layout); ++ icon_view->priv->row_header_layout = NULL; ++ } ++ ++ gtk_icon_view_queue_layout (icon_view); ++} ++ ++static void ++free_queued_activate_item (GtkIconView *icon_view) ++{ ++ if (icon_view->priv->queued_activate_item) ++ { ++ gtk_icon_view_queue_draw_item (icon_view, ++ icon_view->priv->queued_activate_item); ++ ++ icon_view->priv->queued_activate_item = NULL; ++ } ++} ++ ++static void ++free_queued_select_item (GtkIconView *icon_view) ++{ ++ if (icon_view->priv->queued_select_item) ++ { ++ gtk_icon_view_queue_draw_item (icon_view, ++ icon_view->priv->queued_select_item); ++ ++ icon_view->priv->queued_select_item = NULL; ++ } ++} ++ ++HildonUIMode ++hildon_icon_view_get_hildon_ui_mode (GtkIconView *icon_view) ++{ ++ g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), 0); ++ ++ return icon_view->priv->hildon_ui_mode; ++} ++ ++void ++hildon_icon_view_set_hildon_ui_mode (GtkIconView *icon_view, ++ HildonUIMode hildon_ui_mode) ++{ ++ HildonMode mode; ++ ++ g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); ++ ++ icon_view->priv->hildon_ui_mode = hildon_ui_mode; ++ ++ gtk_widget_style_get (GTK_WIDGET (icon_view), ++ "hildon-mode", &mode, ++ NULL); ++ ++ if (mode == HILDON_DIABLO) ++ return; ++ ++ if (hildon_ui_mode == HILDON_UI_MODE_NORMAL) ++ { ++ gtk_icon_view_set_selection_mode (icon_view, GTK_SELECTION_NONE); ++ } ++ else if (hildon_ui_mode == HILDON_UI_MODE_EDIT) ++ { ++ int count = 0; ++ GList *list; ++ ++ if (gtk_icon_view_get_selection_mode (icon_view) == GTK_SELECTION_NONE) ++ { ++ gtk_icon_view_set_selection_mode (icon_view, GTK_SELECTION_SINGLE); ++ } ++ ++ if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) ++ { ++ /* Instead of using gtk_icon_view_get_selected_items() we walk ++ * over the list of items ourselves to save allocating/deallocating ++ * all paths. ++ */ ++ for (list = icon_view->priv->items; list; list = list->next) ++ { ++ GtkIconViewItem *item = list->data; ++ ++ if (item->selected) ++ { ++ count++; ++ break; ++ } ++ } ++ ++ if (!count) ++ { ++ GtkTreePath *path; ++ ++ /* Select the first item */ ++ path = gtk_tree_path_new_first (); ++ search_first_selectable_path (icon_view, &path, TRUE); ++ gtk_icon_view_select_path (icon_view, path); ++ gtk_tree_path_free (path); ++ } ++ } ++ } ++ else ++ g_assert_not_reached (); ++} + + /* Accessibility Support */ + +--- a/gtk/gtkiconview.h ++++ b/gtk/gtkiconview.h +@@ -46,6 +46,10 @@ + typedef void (* GtkIconViewForeachFunc) (GtkIconView *icon_view, + GtkTreePath *path, + gpointer data); ++typedef gboolean (* HildonIconViewRowHeaderFunc) (GtkTreeModel *model, ++ GtkTreeIter *iter, ++ gchar **label, ++ gpointer data); + + typedef enum + { +@@ -236,7 +240,13 @@ + gint column); + gint gtk_icon_view_get_tooltip_column (GtkIconView *icon_view); + +- ++HildonIconViewRowHeaderFunc hildon_icon_view_get_row_header_func (GtkIconView *icon_view); ++void hildon_icon_view_set_row_header_func (GtkIconView *icon_view, ++ HildonIconViewRowHeaderFunc func, ++ gpointer data, ++ GDestroyNotify destroy); ++void ++hildon_icon_view_set_hildon_ui_mode (GtkIconView *icon_view, HildonUIMode hildon_ui_mode); + G_END_DECLS + + #endif /* __GTK_ICON_VIEW_H__ */ diff --git a/hildonize-gtk-imcontext.patch b/hildonize-gtk-imcontext.patch new file mode 100644 index 000000000000..11c900fe66d7 --- /dev/null +++ b/hildonize-gtk-imcontext.patch @@ -0,0 +1,486 @@ +--- a/gtk/gtkimcontext.c ++++ b/gtk/gtkimcontext.c +@@ -100,6 +100,17 @@ + * in order for the new input method to become available to GTK+ applications. + */ + ++typedef struct _GtkIMContextPrivate GtkIMContextPrivate; ++ ++#define GTK_IM_CONTEXT_GET_PRIVATE(obj) \ ++ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ ++ GTK_TYPE_IM_CONTEXT, GtkIMContextPrivate)) ++ ++enum { ++ PROP_HILDON_INPUT_MODE = 1, ++ PROP_HILDON_INPUT_DEFAULT ++}; ++ + enum { + PREEDIT_START, + PREEDIT_END, +@@ -107,11 +118,30 @@ + COMMIT, + RETRIEVE_SURROUNDING, + DELETE_SURROUNDING, ++ HAS_SELECTION, ++ CLIPBOARD_OPERATION, + LAST_SIGNAL + }; + ++struct _GtkIMContextPrivate { ++ HildonGtkInputMode mode; ++ HildonGtkInputMode default_mode; ++}; ++ + static guint im_context_signals[LAST_SIGNAL] = { 0 }; + ++static void gtk_im_context_set_property (GObject *object, ++ guint property_id, ++ const GValue *value, ++ GParamSpec *pspec); ++static void gtk_im_context_get_property (GObject *object, ++ guint property_id, ++ GValue *value, ++ GParamSpec *pspec); ++ ++static gboolean gtk_im_context_real_filter_event (GtkIMContext *context, ++ GdkEvent *event); ++ + static void gtk_im_context_real_get_preedit_string (GtkIMContext *context, + gchar **str, + PangoAttrList **attrs, +@@ -188,10 +218,18 @@ + static void + gtk_im_context_class_init (GtkIMContextClass *klass) + { ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ + klass->get_preedit_string = gtk_im_context_real_get_preedit_string; + klass->filter_keypress = gtk_im_context_real_filter_keypress; + klass->get_surrounding = gtk_im_context_real_get_surrounding; + klass->set_surrounding = gtk_im_context_real_set_surrounding; ++ klass->filter_event = gtk_im_context_real_filter_event; ++ ++ gobject_class->set_property = gtk_im_context_set_property; ++ gobject_class->get_property = gtk_im_context_get_property; ++ ++ g_type_class_add_private (klass, sizeof(GtkIMContextPrivate)); + + /** + * GtkIMContext::preedit-start: +@@ -300,6 +338,82 @@ + G_TYPE_BOOLEAN, 2, + G_TYPE_INT, + G_TYPE_INT); ++ /** ++ * GtkIMContext::has-selection: ++ * @context: a #GtkIMContext ++ * ++ * This signal is emitted when input context needs to know if there is ++ * any text selected in the widget. Return TRUE if there is. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ */ ++ im_context_signals[HAS_SELECTION] = ++ g_signal_new ("has_selection", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GtkIMContextClass, has_selection), ++ NULL, NULL, ++ _gtk_marshal_BOOLEAN__VOID, ++ G_TYPE_BOOLEAN, 0); ++ /** ++ * GtkIMContext::clipboard-operation: ++ * @context: a #GtkIMContext ++ * @operation: a #GtkIMContextClipboardOperation ++ * ++ * This signal is emitted when input context wants to copy, cut or paste ++ * text. The widget needs to implement these operations. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ */ ++ im_context_signals[CLIPBOARD_OPERATION] = ++ g_signal_new ("clipboard_operation", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GtkIMContextClass, clipboard_operation), ++ NULL, NULL, ++ _gtk_marshal_VOID__ENUM, ++ G_TYPE_NONE, 1, GTK_TYPE_IM_CONTEXT_CLIPBOARD_OPERATION); ++ ++ /** ++ * GtkIMContext:hildon-input-mode: ++ * ++ * Allowed characters and input mode for this IM context. ++ * See #HildonGtkInputMode. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_HILDON_INPUT_MODE, ++ g_param_spec_flags ("hildon-input-mode", ++ "Hildon input mode", ++ "Allowed characters and input mode", ++ HILDON_TYPE_GTK_INPUT_MODE, ++ HILDON_GTK_INPUT_MODE_FULL | ++ HILDON_GTK_INPUT_MODE_AUTOCAP | ++ HILDON_GTK_INPUT_MODE_DICTIONARY, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GtkIMContext:hildon-input-default: ++ * ++ * Default input mode for this IM context. See #HildonGtkInputMode. ++ * The default setting for this property is %HILDON_GTK_INPUT_MODE_FULL, ++ * which means that the default input mode to be used is up to the ++ * implementation of the IM context. ++ * ++ * Since: maemo 5.0 ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_HILDON_INPUT_DEFAULT, ++ g_param_spec_flags ("hildon-input-default", ++ "Hildon input default", ++ "Default input mode", ++ HILDON_TYPE_GTK_INPUT_MODE, ++ HILDON_GTK_INPUT_MODE_FULL, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + } + + static void +@@ -308,6 +422,50 @@ + } + + static void ++gtk_im_context_set_property (GObject *object, ++ guint property_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ GtkIMContextPrivate *priv = GTK_IM_CONTEXT_GET_PRIVATE (object); ++ ++ switch (property_id) ++ { ++ case PROP_HILDON_INPUT_MODE: ++ priv->mode = g_value_get_flags (value); ++ break; ++ case PROP_HILDON_INPUT_DEFAULT: ++ priv->default_mode = g_value_get_flags (value); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); ++ break; ++ } ++} ++ ++static void ++gtk_im_context_get_property (GObject *object, ++ guint property_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ GtkIMContextPrivate *priv = GTK_IM_CONTEXT_GET_PRIVATE (object); ++ ++ switch (property_id) ++ { ++ case PROP_HILDON_INPUT_MODE: ++ g_value_set_flags (value, priv->mode); ++ break; ++ case PROP_HILDON_INPUT_DEFAULT: ++ g_value_set_flags (value, priv->default_mode); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); ++ break; ++ } ++} ++ ++static void + gtk_im_context_real_get_preedit_string (GtkIMContext *context, + gchar **str, + PangoAttrList **attrs, +@@ -328,6 +486,13 @@ + return FALSE; + } + ++static gboolean ++gtk_im_context_real_filter_event (GtkIMContext *context, ++ GdkEvent *event) ++{ ++ return FALSE; ++} ++ + typedef struct + { + gchar *text; +@@ -710,5 +875,208 @@ + return result; + } + ++/** ++ * hildon_gtk_im_context_filter_event: ++ * @context: a #GtkIMContext ++ * @event: the event ++ * ++ * Allow an input method to internally handle an event. ++ * If this function returns %TRUE, then no further processing ++ * should be done for this event. ++ * ++ * <note><para> ++ * Input methods must be able to accept all types of events (simply ++ * returning %FALSE if the event was not handled), but there is no ++ * obligation for a widget to submit any events to this function. ++ * </para><para> ++ * Widget events that are recommended to be run through this function ++ * are %GDK_BUTTON_PRESS, %GDK_BUTTON_RELEASE, %GDK_2BUTTON_PRESS, ++ * %GDK_3BUTTON_PRESS, %GDK_KEY_PRESS and %GDK_KEY_RELEASE. ++ * </para><para> ++ * Note that if the event passes the filter with the function returning ++ * %FALSE, the widget still needs to process the event itself, this can ++ * include calling gtk_im_context_focus_in(), gtk_im_context_focus_out() ++ * or gtk_im_context_filter_keypress() for focus and keypress events ++ * where applicable. ++ * </para></note> ++ * ++ * Return value: %TRUE if the input method handled the event. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ */ ++gboolean hildon_gtk_im_context_filter_event (GtkIMContext *context, ++ GdkEvent *event) ++{ ++ GtkIMContextClass *klass; ++ ++ g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE); ++ g_return_val_if_fail (event != NULL, FALSE); ++ ++ klass = GTK_IM_CONTEXT_GET_CLASS (context); ++ return klass->filter_event (context, event); ++} ++ ++/** ++ * gtk_im_context_show: ++ * @context: a #GtkIMContext ++ * ++ * Notify the input method that widget thinks the actual ++ * input method show be opened. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ * ++ * Deprecated: Use hildon_gtk_im_context_show() instead. ++ **/ ++void ++gtk_im_context_show (GtkIMContext *context) ++{ ++ hildon_gtk_im_context_show (context); ++} ++ ++/** ++ * gtk_im_context_hide: ++ * @context: a #GtkIMContext ++ * ++ * Notify the input method that widget thinks the actual ++ * input method show be closed. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ * ++ * Deprecated: Use hildon_gtk_im_context_hide() instead. ++ **/ ++void ++gtk_im_context_hide (GtkIMContext *context) ++{ ++ hildon_gtk_im_context_hide (context); ++} ++ ++/** ++ * hildon_gtk_im_context_show: ++ * @context: a #GtkIMContext ++ * ++ * Notify the input method that widget thinks the actual ++ * input method show be opened. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ **/ ++void ++hildon_gtk_im_context_show (GtkIMContext *context) ++{ ++ GtkIMContextClass *klass; ++ ++ g_return_if_fail (GTK_IS_IM_CONTEXT (context)); ++ ++ klass = GTK_IM_CONTEXT_GET_CLASS (context); ++ if (klass->show) ++ klass->show (context); ++} ++ ++/** ++ * hildon_gtk_im_context_hide: ++ * @context: a #GtkIMContext ++ * ++ * Notify the input method that widget thinks the actual ++ * input method show be closed. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ **/ ++void ++hildon_gtk_im_context_hide (GtkIMContext *context) ++{ ++ GtkIMContextClass *klass; ++ ++ g_return_if_fail (GTK_IS_IM_CONTEXT (context)); ++ ++ klass = GTK_IM_CONTEXT_GET_CLASS (context); ++ if (klass->hide) ++ klass->hide (context); ++} ++ ++/** ++ * hildon_gtk_im_context_has_selection: ++ * @context: a #GtkIMContext ++ * ++ * Returns TRUE if the widget attached to this input context has some ++ * text selected in it. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ **/ ++gboolean ++hildon_gtk_im_context_has_selection (GtkIMContext *context) ++{ ++ gboolean result = FALSE; ++ ++ g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), 0); ++ ++ g_signal_emit (context, ++ im_context_signals[HAS_SELECTION], 0, ++ &result); ++ ++ return result; ++} ++ ++/** ++ * hildon_gtk_im_context_copy: ++ * @context: a #GtkIMContext ++ * ++ * Requests from the widget attached to this input context that the ++ * selected text in it is copied to clipboard. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ **/ ++void ++hildon_gtk_im_context_copy (GtkIMContext *context) ++{ ++ g_return_if_fail (GTK_IS_IM_CONTEXT (context)); ++ ++ g_signal_emit (context, im_context_signals[CLIPBOARD_OPERATION], 0, ++ GTK_IM_CONTEXT_CLIPBOARD_OP_COPY); ++} ++ ++/** ++ * hildon_gtk_im_context_cut: ++ * @context: a #GtkIMContext ++ * ++ * Requests from the widget attached to this input context that the ++ * selected text is cut and copied to clipboard. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ **/ ++void ++hildon_gtk_im_context_cut (GtkIMContext *context) ++{ ++ g_return_if_fail (GTK_IS_IM_CONTEXT (context)); ++ ++ g_signal_emit (context, im_context_signals[CLIPBOARD_OPERATION], 0, ++ GTK_IM_CONTEXT_CLIPBOARD_OP_CUT); ++} ++ ++/** ++ * hildon_gtk_im_context_paste: ++ * @context: a #GtkIMContext ++ * ++ * Requests from the widget attached to this input context that the ++ * text in clipboard is pasted to it. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ **/ ++void ++hildon_gtk_im_context_paste (GtkIMContext *context) ++{ ++ g_return_if_fail (GTK_IS_IM_CONTEXT (context)); ++ ++ g_signal_emit (context, im_context_signals[CLIPBOARD_OPERATION], 0, ++ GTK_IM_CONTEXT_CLIPBOARD_OP_PASTE); ++} ++ + #define __GTK_IM_CONTEXT_C__ + #include "gtkaliasdef.c" +--- a/gtk/gtkimcontext.h ++++ b/gtk/gtkimcontext.h +@@ -31,6 +31,13 @@ + + G_BEGIN_DECLS + ++typedef enum ++{ ++ GTK_IM_CONTEXT_CLIPBOARD_OP_COPY, ++ GTK_IM_CONTEXT_CLIPBOARD_OP_CUT, ++ GTK_IM_CONTEXT_CLIPBOARD_OP_PASTE ++} GtkIMContextClipboardOperation; ++ + #define GTK_TYPE_IM_CONTEXT (gtk_im_context_get_type ()) + #define GTK_IM_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_IM_CONTEXT, GtkIMContext)) + #define GTK_IM_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_IM_CONTEXT, GtkIMContextClass)) +@@ -92,11 +99,18 @@ + /*< private >*/ + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); +- void (*_gtk_reserved2) (void); +- void (*_gtk_reserved3) (void); +- void (*_gtk_reserved4) (void); +- void (*_gtk_reserved5) (void); +- void (*_gtk_reserved6) (void); ++ ++ void (*show) (GtkIMContext *context); ++ void (*hide) (GtkIMContext *context); ++ ++ /* Signals again: */ ++ gboolean (*has_selection) (GtkIMContext *context); ++ void (*clipboard_operation) (GtkIMContext *context, ++ GtkIMContextClipboardOperation operation); ++ ++ /* Virtual functions again: */ ++ gboolean (*filter_event) (GtkIMContext *context, ++ GdkEvent *event); + }; + + GType gtk_im_context_get_type (void) G_GNUC_CONST; +@@ -126,6 +140,18 @@ + gboolean gtk_im_context_delete_surrounding (GtkIMContext *context, + gint offset, + gint n_chars); ++gboolean hildon_gtk_im_context_filter_event (GtkIMContext *context, ++ GdkEvent *event); ++ ++void gtk_im_context_show (GtkIMContext *context); ++void gtk_im_context_hide (GtkIMContext *context); ++ ++void hildon_gtk_im_context_show (GtkIMContext *context); ++void hildon_gtk_im_context_hide (GtkIMContext *context); ++gboolean hildon_gtk_im_context_has_selection(GtkIMContext *context); ++void hildon_gtk_im_context_copy (GtkIMContext *context); ++void hildon_gtk_im_context_cut (GtkIMContext *context); ++void hildon_gtk_im_context_paste (GtkIMContext *context); + + G_END_DECLS + diff --git a/hildonize-gtk-menu.patch b/hildonize-gtk-menu.patch new file mode 100644 index 000000000000..d06f52cf6cbb --- /dev/null +++ b/hildonize-gtk-menu.patch @@ -0,0 +1,755 @@ +--- a/gtk/gtkmenu.c ++++ b/gtk/gtkmenu.c +@@ -27,6 +27,8 @@ + #define GTK_MENU_INTERNALS + #include "config.h" + #include <string.h> ++#include <math.h> ++#include <stdlib.h> + #include "gdk/gdkkeysyms.h" + #include "gtkaccellabel.h" + #include "gtkaccelmap.h" +@@ -36,6 +38,7 @@ + #include "gtkmain.h" + #include "gtkmarshalers.h" + #include "gtkmenu.h" ++#include "gtkmenubar.h" + #include "gtktearoffmenuitem.h" + #include "gtkwindow.h" + #include "gtkhbox.h" +@@ -56,6 +59,8 @@ + #define MENU_SCROLL_TIMEOUT1 50 + #define MENU_SCROLL_TIMEOUT2 20 + ++#define SCROLL_DELAY_FACTOR 5 /* Scroll repeat multiplier */ ++ + #define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key" + #define ATTACHED_MENUS "gtk-attached-menus" + +@@ -101,6 +106,14 @@ + guint have_position : 1; + guint ignore_button_release : 1; + guint no_toggle_size : 1; ++ ++ /* For context menu behavior */ ++ gboolean context_menu; ++ int popup_pointer_x; ++ int popup_pointer_y; ++ ++ /* Handling GdkScreen::size-changed */ ++ guint size_changed_id; + }; + + typedef struct +@@ -254,6 +267,11 @@ + + static guint menu_signals[LAST_SIGNAL] = { 0 }; + ++static gint context_menu_counter = 0; ++ ++static void menu_screen_size_changed (GdkScreen *screen, ++ GtkMenu *menu); ++ + static GtkMenuPrivate * + gtk_menu_get_private (GtkMenu *menu) + { +@@ -670,6 +688,42 @@ + GTK_ARROWS_BOTH, + GTK_PARAM_READABLE)); + ++ gtk_widget_class_install_style_property (widget_class, ++ g_param_spec_boolean ("opposite-arrows", ++ P_("Opposite Arrows"), ++ P_("Place the scroll arrows on opposite sides of the menu."), ++ FALSE, ++ GTK_PARAM_READABLE)); ++ /** ++ * GtkMenuItem::arrow-scaling ++ * ++ * Arbitrary constant to scale down the size of the scroll arrow. ++ * ++ * Since: maemo 4.0 ++ * Stability: Unstable ++ */ ++ gtk_widget_class_install_style_property (widget_class, ++ g_param_spec_float ("maemo-arrow-scaling", ++ P_("Arrow Scaling"), ++ P_("Amount of space used up by the scroll arrows, relative to scroll-arrow-vlength"), ++ 0.0, 1.0, 0.7, ++ GTK_PARAM_READABLE)); ++ ++ /** ++ * GtkMenu::maemo-decorated ++ * ++ * Whether the menu window is decorated by the window manager. ++ * ++ * Since: maemo 5.0 ++ * Stability: Unstable ++ */ ++ gtk_widget_class_install_style_property (widget_class, ++ g_param_spec_boolean ("maemo-decorated", ++ P_("Decorated"), ++ P_("Whether the menu window is decorated by the window manager"), ++ TRUE, ++ GTK_PARAM_READABLE)); ++ + gtk_container_class_install_child_property (container_class, + CHILD_PROP_LEFT_ATTACH, + g_param_spec_int ("left-attach", +@@ -713,7 +767,7 @@ + g_param_spec_float ("arrow-scaling", + P_("Arrow Scaling"), + P_("Arbitrary constant to scale down the size of the scroll arrow"), +- 0.0, 1.0, 0.7, ++ 0.0, 1.0, 1.0, + GTK_PARAM_READABLE)); + + binding_set = gtk_binding_set_by_class (class); +@@ -971,6 +1025,28 @@ + case GDK_KEY_RELEASE: + handled = gtk_widget_event (menu, event); + break; ++ case GDK_CLIENT_EVENT: ++ /* Close down the whole hierarchy, but not if we're torn off. Don't call ++ * cancel if the menu isn't visible to avoid extra selection-done ++ * signals. ++ */ ++ if (event->client.message_type == gdk_atom_intern_static_string ("_GTK_DELETE_TEMPORARIES") && ++ _gtk_window_is_on_client_data (GTK_WINDOW (window), (GdkEventClient*)event) == FALSE && ++ window == GTK_MENU (menu)->toplevel && ++ gtk_widget_get_mapped (GTK_MENU (menu)->toplevel)) ++ { ++ gtk_menu_shell_cancel (GTK_MENU_SHELL (menu)); ++ handled = TRUE; ++ } ++ break; ++ case GDK_DELETE: ++ if (window == GTK_MENU (menu)->toplevel && ++ gtk_widget_get_mapped (GTK_MENU (menu)->toplevel)) ++ { ++ gtk_menu_shell_cancel (GTK_MENU_SHELL (menu)); ++ handled = TRUE; ++ } ++ break; + default: + break; + } +@@ -1007,6 +1083,7 @@ + gtk_menu_init (GtkMenu *menu) + { + GtkMenuPrivate *priv = gtk_menu_get_private (menu); ++ gboolean decorated = TRUE; + + menu->parent_menu_item = NULL; + menu->old_active_menu_item = NULL; +@@ -1016,7 +1093,7 @@ + menu->toggle_size = 0; + + menu->toplevel = g_object_connect (g_object_new (GTK_TYPE_WINDOW, +- "type", GTK_WINDOW_POPUP, ++ "type", GTK_WINDOW_TOPLEVEL, + "child", menu, + NULL), + "signal::event", gtk_menu_window_event, menu, +@@ -1026,6 +1103,11 @@ + gtk_window_set_resizable (GTK_WINDOW (menu->toplevel), FALSE); + gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->toplevel), 0); + ++ gtk_widget_style_get (GTK_WIDGET (menu), "maemo-decorated", &decorated, NULL); ++ gtk_window_set_decorated (GTK_WINDOW (menu->toplevel), decorated); ++ gtk_widget_add_events (menu->toplevel, GDK_VISIBILITY_NOTIFY_MASK); ++ gtk_window_set_is_temporary (GTK_WINDOW (menu->toplevel), TRUE); ++ + /* Refloat the menu, so that reference counting for the menu isn't + * affected by it being a child of the toplevel + */ +@@ -1055,6 +1137,14 @@ + priv->upper_arrow_state = GTK_STATE_NORMAL; + priv->lower_arrow_state = GTK_STATE_NORMAL; + ++ priv->context_menu = FALSE; ++ priv->popup_pointer_x = -1; ++ priv->popup_pointer_y = -1; ++ ++ priv->size_changed_id = ++ g_signal_connect (gtk_widget_get_screen (menu->toplevel), "size_changed", ++ G_CALLBACK (menu_screen_size_changed), menu); ++ + priv->have_layout = FALSE; + priv->monitor_num = -1; + } +@@ -1113,10 +1203,24 @@ + priv->title = NULL; + } + ++ if (priv->size_changed_id) ++ { ++ g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (object)), ++ priv->size_changed_id); ++ priv->size_changed_id = 0; ++ } ++ + GTK_OBJECT_CLASS (gtk_menu_parent_class)->destroy (object); + } + + static void ++menu_screen_size_changed (GdkScreen *screen, ++ GtkMenu *menu) ++{ ++ gtk_menu_shell_cancel (GTK_MENU_SHELL (menu)); ++} ++ ++static void + menu_change_screen (GtkMenu *menu, + GdkScreen *new_screen) + { +@@ -1128,6 +1232,13 @@ + return; + } + ++ if (private->size_changed_id) ++ { ++ g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (menu)), ++ private->size_changed_id); ++ private->size_changed_id = 0; ++ } ++ + if (menu->torn_off) + { + gtk_window_set_screen (GTK_WINDOW (menu->tearoff_window), new_screen); +@@ -1136,6 +1247,10 @@ + + gtk_window_set_screen (GTK_WINDOW (menu->toplevel), new_screen); + private->monitor_num = -1; ++ ++ private->size_changed_id = ++ g_signal_connect (new_screen, "size_changed", ++ G_CALLBACK (menu_screen_size_changed), menu); + } + + static void +@@ -1384,6 +1499,54 @@ + return FALSE; + } + ++#define HILDON_MENU_NAME_SHARP "menu_with_corners" ++/* needed to allow different themeing for first level menus */ ++#define HILDON_MENU_NAME_ROUND_FIRST_LEVEL "menu_without_corners_first_level" ++#define HILDON_MENU_NAME_ROUND "menu_without_corners" ++#define HILDON_MENU_NAME_FORCE_SHARP "menu_force_with_corners" ++#define HILDON_MENU_NAME_FORCE_ROUND "menu_force_without_corners" ++ ++/** ++ * Little help function for making some sanity tests on this menu. ++ * Checks that given widget really is a menu and that it has no name ++ * assigned to it yet. ++ * Names used to do hildon theming: ++ * HILDON_MENU_NAME_SHARP for menu with sharp upper corners ++ * HILDON_MENU_NAME_ROUND for menu with round corners ++ */ ++static gboolean ++maemo_menu_check_name (GtkWidget *widget) ++{ ++ gboolean legal_name = FALSE; ++ gchar **tmp = NULL; ++ const gchar *name = NULL; ++ static gchar *menu_names[] = { "GtkMenu", ++ HILDON_MENU_NAME_SHARP, ++ HILDON_MENU_NAME_ROUND, ++ HILDON_MENU_NAME_ROUND_FIRST_LEVEL, ++ NULL }; ++ if (GTK_IS_MENU (widget) && ++ (name = gtk_widget_get_name (widget))) ++ { ++ if (!g_ascii_strcasecmp (name, HILDON_MENU_NAME_FORCE_SHARP) || ++ !g_ascii_strcasecmp (name, HILDON_MENU_NAME_FORCE_ROUND)) ++ { ++ return FALSE; ++ } ++ ++ for (tmp = menu_names; *tmp; tmp++) ++ { ++ if (!g_ascii_strcasecmp (name, *tmp )) ++ { ++ legal_name = TRUE; ++ break; ++ } ++ } ++ } ++ ++ return legal_name; ++} ++ + /** + * gtk_menu_popup: + * @menu: a #GtkMenu. +@@ -1487,6 +1650,16 @@ + { + if (popup_grab_on_window (xgrab_shell->window, activate_time, grab_keyboard)) + GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE; ++ /* Maemo: enable rc-file theming */ ++ if (maemo_menu_check_name (widget)) ++ { ++ if (GTK_IS_MENU_BAR (parent_menu_shell)) ++ gtk_widget_set_name (widget, HILDON_MENU_NAME_SHARP); ++ else if (GTK_IS_MENU (parent_menu_shell)) ++ gtk_widget_set_name( widget, HILDON_MENU_NAME_ROUND); ++ else ++ gtk_widget_set_name (widget, HILDON_MENU_NAME_ROUND_FIRST_LEVEL); ++ } + } + else + { +@@ -1496,6 +1669,10 @@ + transfer_window = menu_grab_transfer_window_get (menu); + if (popup_grab_on_window (transfer_window, activate_time, grab_keyboard)) + GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE; ++ ++ /* Maemo: enable rc-file theming */ ++ if (maemo_menu_check_name (widget)) ++ gtk_widget_set_name (widget, HILDON_MENU_NAME_ROUND_FIRST_LEVEL); + } + + if (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab) +@@ -1585,18 +1762,14 @@ + + gtk_menu_scroll_to (menu, menu->scroll_offset); + +- /* if no item is selected, select the first one */ +- if (!menu_shell->active_menu_item) +- { +- gboolean touchscreen_mode; +- +- g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), +- "gtk-touchscreen-mode", &touchscreen_mode, +- NULL); ++ /* Hildon: save position of the pointer during popup. Not multihead safe. */ ++ priv->context_menu = (context_menu_counter > 0) && !parent_menu_item; + +- if (touchscreen_mode) +- gtk_menu_shell_select_first (menu_shell, TRUE); +- } ++ if (priv->context_menu) ++ gdk_display_get_pointer (gtk_widget_get_display (widget), NULL, ++ &priv->popup_pointer_x, ++ &priv->popup_pointer_y, ++ NULL); + + /* Once everything is set up correctly, map the toplevel window on + the screen. +@@ -1636,6 +1809,7 @@ + menu_shell->ignore_enter = FALSE; + + private->have_position = FALSE; ++ private->context_menu = FALSE; + + gtk_menu_stop_scrolling (menu); + +@@ -2229,12 +2403,17 @@ + { + guint scroll_arrow_height; + GtkArrowPlacement arrow_placement; ++ gboolean opposite_arrows; + + gtk_widget_style_get (GTK_WIDGET (menu), + "scroll-arrow-vlength", &scroll_arrow_height, + "arrow_placement", &arrow_placement, ++ "opposite-arrows", &opposite_arrows, + NULL); + ++ if (opposite_arrows) ++ arrow_placement = GTK_ARROWS_BOTH; ++ + switch (arrow_placement) + { + case GTK_ARROWS_BOTH: +@@ -2710,12 +2889,14 @@ + guint horizontal_padding; + gint scroll_arrow_height; + GtkArrowPlacement arrow_placement; ++ gboolean opposite_arrows; + + gtk_widget_style_get (widget, + "vertical-padding", &vertical_padding, + "horizontal-padding", &horizontal_padding, + "scroll-arrow-vlength", &scroll_arrow_height, + "arrow-placement", &arrow_placement, ++ "opposite-arrows", &opposite_arrows, + NULL); + + border->x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness + horizontal_padding; +@@ -2723,6 +2904,9 @@ + border->width = gdk_window_get_width (widget->window); + border->height = gdk_window_get_height (widget->window); + ++ if (opposite_arrows) ++ arrow_placement = GTK_ARROWS_BOTH; ++ + switch (arrow_placement) + { + case GTK_ARROWS_BOTH: +@@ -2796,6 +2980,9 @@ + gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL); + arrow_size = arrow_scaling * arrow_space; + ++ gtk_widget_style_get (widget, "maemo-arrow-scaling", &arrow_scaling, NULL); ++ arrow_size *= arrow_scaling; ++ + gtk_paint_box (widget->style, + widget->window, + GTK_STATE_NORMAL, +@@ -2900,6 +3087,27 @@ + GTK_WIDGET_CLASS (gtk_menu_parent_class)->show (widget); + } + ++static gint ++distance_traveled (GtkWidget *widget) ++{ ++ GtkMenuPrivate *priv; ++ GdkScreen *screen; ++ GdkDisplay *display; ++ gint x, y, dx, dy; ++ ++ priv = gtk_menu_get_private (GTK_MENU (widget)); ++ ++ screen = gtk_widget_get_screen (widget); ++ display = gdk_screen_get_display (screen); ++ ++ gdk_display_get_pointer (display, NULL, &x, &y, NULL); ++ ++ dx = (priv->popup_pointer_x - x); ++ dy = (priv->popup_pointer_y - y); ++ ++ return abs ((int) sqrt ((double) (dx * dx + dy * dy))); ++} ++ + static gboolean + gtk_menu_button_scroll (GtkMenu *menu, + GdkEventButton *event) +@@ -2983,6 +3191,7 @@ + GdkEventButton *event) + { + GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (widget)); ++ GtkWidget *menu_item; + + if (priv->ignore_button_release) + { +@@ -3001,18 +3210,35 @@ + /* Don't pass down to menu shell if a non-menuitem part of the menu + * was clicked (see comment in button_press()). + */ +- if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) && +- pointer_in_menu_window (widget, event->x_root, event->y_root)) ++ menu_item = gtk_get_event_widget ((GdkEvent*) event); ++ if (!GTK_IS_MENU_ITEM (menu_item) || ++ !GTK_IS_MENU (menu_item->parent)) + { +- /* Ugly: make sure menu_shell->button gets reset to 0 when we +- * bail out early here so it is in a consistent state for the +- * next button_press/button_release in GtkMenuShell. +- * See bug #449371. +- */ +- if (GTK_MENU_SHELL (widget)->active) +- GTK_MENU_SHELL (widget)->button = 0; ++ if (priv->context_menu && ++ (priv->popup_pointer_x >= 0) && ++ (priv->popup_pointer_y >= 0)) ++ { ++ gint threshold; ++ gint distance; + +- return TRUE; ++ g_object_get (gtk_widget_get_settings (widget), ++ "gtk-dnd-drag-threshold", &threshold, ++ NULL); ++ ++ distance = distance_traveled (widget); ++ ++ priv->popup_pointer_x = -1; ++ priv->popup_pointer_y = -1; ++ ++ /* Don't popdown if we traveled less than DND threshold ++ * since popup point, as per the Nokia 770 specs. ++ */ ++ if (distance < threshold) ++ return TRUE; ++ } ++ ++ if (pointer_in_menu_window (widget, event->x_root, event->y_root)) ++ return TRUE; + } + + return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_release_event (widget, event); +@@ -3192,7 +3418,33 @@ + } + } + } +- ++ else if (!can_change_accels) ++ { ++ GtkWidget *toplevel = gtk_menu_get_toplevel (widget); ++ ++ if (toplevel) ++ { ++ GSList *accel_groups; ++ GSList *list; ++ ++ accel_groups = gtk_accel_groups_from_object (G_OBJECT (toplevel)); ++ ++ for (list = accel_groups; list; list = list->next) ++ { ++ GtkAccelGroup *accel_group = list->data; ++ ++ if (gtk_accel_group_query (accel_group, accel_key, accel_mods, ++ NULL)) ++ { ++ gtk_menu_shell_cancel (GTK_MENU_SHELL (widget)); ++ gtk_window_activate_key (GTK_WINDOW (toplevel), event); ++ ++ break; ++ } ++ } ++ } ++ } ++ + return TRUE; + } + +@@ -3271,14 +3523,36 @@ + menu_item = gtk_get_event_widget ((GdkEvent*) event); + if (!GTK_IS_MENU_ITEM (menu_item) || + !GTK_IS_MENU (menu_item->parent)) +- return FALSE; ++ { ++ GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (widget)); ++ ++ if (priv->context_menu) ++ { ++ gint threshold; ++ ++ g_object_get (gtk_widget_get_settings (widget), ++ "gtk-dnd-drag-threshold", &threshold, ++ NULL); ++ ++ /* Context menu mode. If we dragged out of the menu, close ++ * the menu, as by the specs. ++ */ ++ if (!pointer_in_menu_window (widget, event->x_root, event->y_root) && ++ (distance_traveled (widget) >= threshold) && ++ (event->state & GDK_BUTTON1_MASK)) ++ { ++ gtk_menu_shell_deactivate (GTK_MENU_SHELL (widget)); ++ ++ return TRUE; ++ } ++ } ++ ++ return FALSE; ++ } + + menu_shell = GTK_MENU_SHELL (menu_item->parent); + menu = GTK_MENU (menu_shell); + +- if (definitely_within_item (menu_item, event->x, event->y)) +- menu_shell->activate_time = 0; +- + need_enter = (gtk_menu_has_navigation_triangle (menu) || menu_shell->ignore_enter); + + /* Check to see if we are within an active submenu's navigation region +@@ -3346,13 +3620,15 @@ + GtkMenuPrivate *priv = gtk_menu_get_private (menu); + gboolean double_arrows; + GtkArrowPlacement arrow_placement; ++ gboolean opposite_arrows; + + gtk_widget_style_get (GTK_WIDGET (menu), + "double-arrows", &double_arrows, + "arrow-placement", &arrow_placement, ++ "opposite-arrows", &opposite_arrows, + NULL); + +- if (arrow_placement != GTK_ARROWS_BOTH) ++ if (arrow_placement != GTK_ARROWS_BOTH || !opposite_arrows) + return TRUE; + + return double_arrows || (priv->initially_pushed_in && +@@ -3414,17 +3690,101 @@ + gtk_menu_scroll_to (menu, offset); + } + ++#include "gtkseparatormenuitem.h" ++ ++/* Same as _gtk_menu_item_is_selectable() except that this considers ++ * insensitive items valid - otherwise scrolling would skip them over, or stop ++ * scrolling before the begin/end of the menu, both of which would look bad */ ++static gboolean ++_gtk_menu_item_is_scrollable (GtkWidget *menu_item) ++{ ++ if ((!GTK_BIN (menu_item)->child && ++ G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) || ++ GTK_IS_SEPARATOR_MENU_ITEM (menu_item) || ++ !gtk_widget_get_visible (menu_item)) ++ return FALSE; ++ ++ return TRUE; ++} ++ + static void + gtk_menu_do_timeout_scroll (GtkMenu *menu, + gboolean touchscreen_mode) + { + gboolean upper_visible; + gboolean lower_visible; ++ GtkWidget *item = NULL; + + upper_visible = menu->upper_arrow_visible; + lower_visible = menu->lower_arrow_visible; + +- gtk_menu_scroll_by (menu, menu->scroll_step); ++#define VISIBILITY_THRESHOLD 0.70 ++ if (touchscreen_mode) ++ { ++ GdkRectangle visible; ++ GdkRectangle rect; ++ GList *l; ++ ++ /* Scroll by item picking the next item that is not sufficiently ++ * readable. The visibility threshold is to avoid the impression of ++ * skipping an item when there's only one or two pixels obscured. ++ */ ++ ++ visible.x = 0; /* unused */ ++ visible.y = menu->scroll_offset; ++ gdk_drawable_get_size (menu->view_window, &visible.width, &visible.height); ++ ++ if (menu->scroll_step < 0) ++ { ++ /* scrolling up */ ++ for (l = GTK_MENU_SHELL (menu)->children; l != NULL; l = l->next) ++ { ++ GtkWidget *child = l->data; ++ ++ if (!_gtk_menu_item_is_scrollable (child)) ++ continue; ++ ++ /* completely below the top edge, use the previous one */ ++ if (child->allocation.y >= visible.y) ++ break; ++ ++ /* visible/readable enough, use the previous one */ ++ if (gdk_rectangle_intersect (&child->allocation, &visible, &rect) && ++ rect.height / (float)child->allocation.height >= VISIBILITY_THRESHOLD) ++ break; ++ ++ item = child; ++ } ++ } ++ else ++ { ++ /* scrolling down */ ++ for (l = GTK_MENU_SHELL (menu)->children; l != NULL; l = l->next) ++ { ++ GtkWidget *child = l->data; ++ ++ if (!_gtk_menu_item_is_scrollable (child)) ++ continue; ++ ++ /* skip all completely above the bottom edge */ ++ if (child->allocation.y + child->allocation.height < visible.y + visible.height) ++ continue; ++ ++ /* visible/readable enough, try the next one */ ++ if (gdk_rectangle_intersect (&child->allocation, &visible, &rect) && ++ rect.height / (float)child->allocation.height >= VISIBILITY_THRESHOLD) ++ continue; ++ ++ item = child; ++ break; ++ } ++ } ++ } ++ ++ if (item) ++ gtk_menu_scroll_item_visible (GTK_MENU_SHELL (menu), item); ++ else ++ gtk_menu_scroll_by (menu, menu->scroll_step); + + if (touchscreen_mode && + (upper_visible != menu->upper_arrow_visible || +@@ -3470,6 +3830,9 @@ + "gtk-touchscreen-mode", &touchscreen_mode, + NULL); + ++ if (touchscreen_mode) ++ timeout *= SCROLL_DELAY_FACTOR; ++ + gtk_menu_do_timeout_scroll (menu, touchscreen_mode); + + gtk_menu_remove_scroll_timeout (menu); +@@ -3488,7 +3851,7 @@ + gboolean touchscreen_mode; + + g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), +- "gtk-timeout-repeat", &timeout, ++ "gtk-timeout-initial", &timeout, + "gtk-touchscreen-mode", &touchscreen_mode, + NULL); + +@@ -3531,6 +3894,7 @@ + gint win_x, win_y; + gint scroll_arrow_height; + GtkArrowPlacement arrow_placement; ++ gboolean opposite_arrows; + + width = gdk_window_get_width (GTK_WIDGET (menu)->window); + height = gdk_window_get_height (GTK_WIDGET (menu)->window); +@@ -3539,6 +3903,7 @@ + "vertical-padding", &vertical_padding, + "scroll-arrow-vlength", &scroll_arrow_height, + "arrow-placement", &arrow_placement, ++ "opposite-arrows", &opposite_arrows, + NULL); + + border = GTK_CONTAINER (menu)->border_width + +@@ -3546,6 +3911,9 @@ + + gdk_window_get_position (GTK_WIDGET (menu)->window, &win_x, &win_y); + ++ if (opposite_arrows) ++ arrow_placement = GTK_ARROWS_BOTH; ++ + switch (arrow_placement) + { + case GTK_ARROWS_BOTH: +@@ -5368,5 +5736,20 @@ + return !priv->no_toggle_size; + } + ++/* Hildon functions to make context menus behave according to spec */ ++void ++_gtk_menu_push_context_menu_behavior (void) ++{ ++ context_menu_counter++; ++} ++ ++void ++_gtk_menu_pop_context_menu_behavior (void) ++{ ++ g_return_if_fail (context_menu_counter > 0); ++ ++ context_menu_counter--; ++} ++ + #define __GTK_MENU_C__ + #include "gtkaliasdef.c" +--- a/gtk/gtkmenu.h ++++ b/gtk/gtkmenu.h +@@ -213,6 +213,8 @@ + gboolean reserve_toggle_size); + gboolean gtk_menu_get_reserve_toggle_size (GtkMenu *menu); + ++void _gtk_menu_push_context_menu_behavior (void); ++void _gtk_menu_pop_context_menu_behavior (void); + + G_END_DECLS + diff --git a/hildonize-gtk-rbtree.patch b/hildonize-gtk-rbtree.patch new file mode 100644 index 000000000000..d4d5cedabaf8 --- /dev/null +++ b/hildonize-gtk-rbtree.patch @@ -0,0 +1,77 @@ +--- a/gtk/gtkrbtree.c ++++ b/gtk/gtkrbtree.c +@@ -335,6 +335,7 @@ + retval = g_new (GtkRBTree, 1); + retval->parent_tree = NULL; + retval->parent_node = NULL; ++ retval->base_offset = 0; + + retval->nil = g_slice_new (GtkRBNode); + retval->nil->left = NULL; +@@ -962,6 +963,7 @@ + { + GtkRBNode *last; + gint retval; ++ GtkRBTree *origtree = tree; + + g_assert (node); + g_assert (node->left); +@@ -987,7 +989,11 @@ + retval += node->left->offset + GTK_RBNODE_GET_HEIGHT (node); + } + } +- return retval; ++ ++ while (origtree->parent_tree) ++ origtree = origtree->parent_tree; ++ ++ return retval + origtree->base_offset; + } + + gint +@@ -1095,6 +1101,11 @@ + { + g_assert (tree); + ++ /* We (ab)use the fact that "tree" that is passed in is always ++ * the root. ++ */ ++ height -= tree->base_offset; ++ + if ((height < 0) || + (height >= tree->root->offset)) + { +@@ -1411,6 +1422,13 @@ + return depth; + } + ++void ++_gtk_rbtree_set_base_offset (GtkRBTree *tree, ++ short base_offset) ++{ ++ tree->base_offset = base_offset; ++} ++ + static void + _gtk_rbtree_traverse_pre_order (GtkRBTree *tree, + GtkRBNode *node, +--- a/gtk/gtkrbtree.h ++++ b/gtk/gtkrbtree.h +@@ -64,6 +64,7 @@ + GtkRBNode *nil; + GtkRBTree *parent_tree; + GtkRBNode *parent_node; ++ short base_offset; + }; + + struct _GtkRBNode +@@ -170,6 +171,9 @@ + + gint _gtk_rbtree_get_depth (GtkRBTree *tree); + ++void _gtk_rbtree_set_base_offset (GtkRBTree *tree, ++ short base_offset); ++ + /* This func checks the integrity of the tree */ + #ifdef G_ENABLE_DEBUG + void _gtk_rbtree_test (const gchar *where, diff --git a/hildonize-gtk-textview.patch b/hildonize-gtk-textview.patch new file mode 100644 index 000000000000..3a84151073de --- /dev/null +++ b/hildonize-gtk-textview.patch @@ -0,0 +1,811 @@ +--- a/gtk/gtktextview.c ++++ b/gtk/gtktextview.c +@@ -89,6 +89,8 @@ + #define DEBUG_VALIDATION_AND_SCROLLING + #endif + ++#define TEXT_VIEW_MAX_WINDOW_SIZE 16384 ++ + #ifdef DEBUG_VALIDATION_AND_SCROLLING + #define DV(x) (x) + #else +@@ -110,6 +112,8 @@ + guint im_spot_idle; + gchar *im_module; + guint scroll_after_paste : 1; ++ GtkTextBuffer *placeholder_buffer; ++ GtkTextLayout *placeholder_layout; + }; + + +@@ -160,7 +164,10 @@ + PROP_BUFFER, + PROP_OVERWRITE, + PROP_ACCEPTS_TAB, +- PROP_IM_MODULE ++ PROP_IM_MODULE, ++ PROP_HILDON_INPUT_MODE, ++ PROP_HILDON_INPUT_DEFAULT, ++ PROP_HILDON_PLACEHOLDER_TEXT + }; + + static void gtk_text_view_destroy (GtkObject *object); +@@ -324,7 +331,11 @@ + gint offset, + gint n_chars, + GtkTextView *text_view); +- ++static gboolean gtk_text_view_has_selection_handler (GtkIMContext *context, ++ GtkTextView *text_view); ++static void gtk_text_view_clipboard_operation_handler (GtkIMContext *context, ++ GtkIMContextClipboardOperation op, ++ GtkTextView *text_view); + static void gtk_text_view_mark_set_handler (GtkTextBuffer *buffer, + const GtkTextIter *location, + GtkTextMark *mark, +@@ -686,6 +697,62 @@ + NULL, + GTK_PARAM_READWRITE)); + ++ /** ++ * GtkTextView:hildon-input-mode: ++ * ++ * Allowed characters and input mode for the text view. ++ * See #HildonGtkInputMode. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_HILDON_INPUT_MODE, ++ g_param_spec_flags ("hildon-input-mode", ++ P_("Hildon input mode"), ++ P_("Define widget's input mode"), ++ HILDON_TYPE_GTK_INPUT_MODE, ++ HILDON_GTK_INPUT_MODE_FULL | ++ HILDON_GTK_INPUT_MODE_MULTILINE | ++ HILDON_GTK_INPUT_MODE_AUTOCAP | ++ HILDON_GTK_INPUT_MODE_DICTIONARY, ++ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GtkTextView:hildon-input-default: ++ * ++ * Default input mode for this IM context. See #HildonGtkInputMode. ++ * The default setting for this property is %HILDON_GTK_INPUT_MODE_FULL, ++ * which means that the default input mode to be used is up to the ++ * implementation of the IM context. ++ * ++ * Since: maemo 5.0 ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_HILDON_INPUT_DEFAULT, ++ g_param_spec_flags ("hildon-input-default", ++ P_("Hildon input default"), ++ P_("Define widget's default input mode"), ++ HILDON_TYPE_GTK_INPUT_MODE, ++ HILDON_GTK_INPUT_MODE_FULL, ++ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); ++ ++ /** ++ * GtkTextView:hildon-placeholder-text: ++ * ++ * Text to be displayed in the #GtkTextView when it is empty and ++ * unfocused. ++ * ++ * Since: maemo 5 ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_HILDON_PLACEHOLDER_TEXT, ++ g_param_spec_string ("hildon-placeholder-text", ++ P_("Hildon Placeholder text"), ++ P_("Text to be displayed when the text view is empty and unfocused"), ++ "", ++ G_PARAM_READWRITE)); ++ + /* + * Style properties + */ +@@ -695,7 +762,13 @@ + P_("Color with which to draw error-indication underlines"), + GDK_TYPE_COLOR, + GTK_PARAM_READABLE)); +- ++ gtk_widget_class_install_style_property (widget_class, ++ g_param_spec_boolean ("custom-background", ++ P_("Render a custom background"), ++ P_("Provide a hook for theme engines to render a custom background"), ++ FALSE, ++ GTK_PARAM_READABLE)); ++ + /* + * Signals + */ +@@ -1333,6 +1406,10 @@ + G_CALLBACK (gtk_text_view_retrieve_surrounding_handler), text_view); + g_signal_connect (text_view->im_context, "delete-surrounding", + G_CALLBACK (gtk_text_view_delete_surrounding_handler), text_view); ++ g_signal_connect (text_view->im_context, "has_selection", ++ G_CALLBACK (gtk_text_view_has_selection_handler), text_view); ++ g_signal_connect (text_view->im_context, "clipboard_operation", ++ G_CALLBACK (gtk_text_view_clipboard_operation_handler), text_view); + + text_view->cursor_visible = TRUE; + +@@ -1346,6 +1423,9 @@ + + text_view->pending_place_cursor_button = 0; + ++ priv->placeholder_buffer = NULL; ++ priv->placeholder_layout = NULL; ++ + /* We handle all our own redrawing */ + gtk_widget_set_redraw_on_allocate (widget, FALSE); + } +@@ -2929,6 +3009,18 @@ + /* at this point, no "notify::buffer" handler should recreate the buffer. */ + g_assert (text_view->buffer == NULL); + ++ if (priv->placeholder_layout) ++ { ++ g_object_unref (priv->placeholder_layout); ++ priv->placeholder_layout = NULL; ++ } ++ ++ if (priv->placeholder_buffer) ++ { ++ g_object_unref (priv->placeholder_buffer); ++ priv->placeholder_buffer = NULL; ++ } ++ + cancel_pending_scroll (text_view); + + if (text_view->tabs) +@@ -3033,8 +3125,21 @@ + case PROP_IM_MODULE: + g_free (priv->im_module); + priv->im_module = g_value_dup_string (value); ++ + if (GTK_IS_IM_MULTICONTEXT (text_view->im_context)) +- gtk_im_multicontext_set_context_id (GTK_IM_MULTICONTEXT (text_view->im_context), priv->im_module); ++ gtk_im_multicontext_set_context_id (GTK_IM_MULTICONTEXT (text_view->im_context), priv->im_module); ++ break; ++ ++ case PROP_HILDON_INPUT_MODE: ++ hildon_gtk_text_view_set_input_mode (text_view, g_value_get_flags (value)); ++ break; ++ ++ case PROP_HILDON_INPUT_DEFAULT: ++ hildon_gtk_text_view_set_input_default (text_view, g_value_get_flags (value)); ++ break; ++ ++ case PROP_HILDON_PLACEHOLDER_TEXT: ++ hildon_gtk_text_view_set_placeholder_text (text_view, g_value_get_string (value)); + break; + + default: +@@ -3117,6 +3222,18 @@ + g_value_set_string (value, priv->im_module); + break; + ++ case PROP_HILDON_INPUT_MODE: ++ g_value_set_flags (value, hildon_gtk_text_view_get_input_mode (text_view)); ++ break; ++ ++ case PROP_HILDON_INPUT_DEFAULT: ++ g_value_set_flags (value, hildon_gtk_text_view_get_input_default (text_view)); ++ break; ++ ++ case PROP_HILDON_PLACEHOLDER_TEXT: ++ g_value_take_string (value, hildon_gtk_text_view_get_placeholder_text (text_view)); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -3368,13 +3485,34 @@ + widget->allocation.width != allocation->width || + widget->allocation.height != allocation->height; + +- widget->allocation = *allocation; ++ widget->allocation.x = allocation->x; ++ widget->allocation.y = allocation->y; ++ ++ if (allocation->width > TEXT_VIEW_MAX_WINDOW_SIZE) ++ { ++ widget->allocation.width = TEXT_VIEW_MAX_WINDOW_SIZE; ++ g_warning ("gtk_text_view_size_allocate: the text view requires too much width %d, add it directly to a scrollable widget instead of using a viewport", allocation->width); ++ } ++ else ++ { ++ widget->allocation.width = allocation->width; ++ } ++ ++ if (allocation->height > TEXT_VIEW_MAX_WINDOW_SIZE) ++ { ++ widget->allocation.height = TEXT_VIEW_MAX_WINDOW_SIZE; ++ g_warning ("gtk_text_view_size_allocate: the text view requires too much height %d, add it directly to a scrollable widget instead of using a viewport", allocation->height); ++ } ++ else ++ { ++ widget->allocation.height = allocation->height; ++ } + + if (gtk_widget_get_realized (widget)) + { + gdk_window_move_resize (widget->window, +- allocation->x, allocation->y, +- allocation->width, allocation->height); ++ widget->allocation.x, widget->allocation.y, ++ widget->allocation.width, widget->allocation.height); + } + + /* distribute width/height among child windows. Ensure all +@@ -3391,7 +3529,7 @@ + else + focus_edge_width = focus_width; + +- width = allocation->width - focus_edge_width * 2 - GTK_CONTAINER (text_view)->border_width * 2; ++ width = widget->allocation.width - focus_edge_width * 2 - GTK_CONTAINER (text_view)->border_width * 2; + + if (text_view->left_window) + left_rect.width = text_view->left_window->requisition.width; +@@ -3413,7 +3551,7 @@ + bottom_rect.width = text_rect.width; + + +- height = allocation->height - focus_edge_width * 2 - GTK_CONTAINER (text_view)->border_width * 2; ++ height = widget->allocation.height - focus_edge_width * 2 - GTK_CONTAINER (text_view)->border_width * 2; + + if (text_view->top_window) + top_rect.height = text_view->top_window->requisition.height; +@@ -3541,7 +3679,7 @@ + + DV(g_print(">Validating onscreen ("G_STRLOC")\n")); + +- if (SCREEN_HEIGHT (widget) > 0) ++ if (SCREEN_HEIGHT (widget) > 1) + { + GtkTextIter first_para; + +@@ -3969,6 +4107,7 @@ + { + GtkTextView *text_view = GTK_TEXT_VIEW (widget); + PangoContext *ltr_context, *rtl_context; ++ GtkTextViewPrivate *priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view); + + if (gtk_widget_get_realized (widget)) + { +@@ -3992,6 +4131,43 @@ + g_object_unref (ltr_context); + g_object_unref (rtl_context); + } ++ ++ /* FIXME: Of course, when coded properly this and the above would be ++ * factored out in a function and shared. ++ */ ++ if (priv->placeholder_layout && previous_style) ++ { ++ GdkColor font_color; ++ ++ gtk_text_view_set_attributes_from_style (text_view, ++ priv->placeholder_layout->default_style, ++ widget->style); ++ ++ ++ /* Override the color setting */ ++ if (gtk_style_lookup_color (widget->style, "ReversedSecondaryTextColor", ++ &font_color)) ++ { ++ priv->placeholder_layout->default_style->appearance.fg_color = font_color; ++ } ++ ++ ++ ltr_context = gtk_widget_create_pango_context (widget); ++ pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR); ++ rtl_context = gtk_widget_create_pango_context (widget); ++ pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL); ++ ++ gtk_text_layout_set_contexts (priv->placeholder_layout, ++ ltr_context, rtl_context); ++ ++ g_object_unref (ltr_context); ++ g_object_unref (rtl_context); ++ ++ /* The call to gtk_text_layout_set_contexts() invalidates the entire ++ * layout, so re-validate the placeholder layout immediately. ++ */ ++ gtk_text_layout_validate (priv->placeholder_layout, 2000); ++ } + } + + static void +@@ -4331,6 +4507,13 @@ + + gtk_widget_grab_focus (widget); + ++ if (text_view->editable && ++ hildon_gtk_im_context_filter_event (text_view->im_context, (GdkEvent*)event)) ++ { ++ text_view->need_im_reset = TRUE; ++ return TRUE; ++ } ++ + if (event->window != text_view->text_window->bin_window) + { + /* Remove selection if any. */ +@@ -4438,6 +4621,13 @@ + if (event->window != text_view->text_window->bin_window) + return FALSE; + ++ if (text_view->editable && ++ hildon_gtk_im_context_filter_event (text_view->im_context, (GdkEvent*)event)) ++ { ++ text_view->need_im_reset = TRUE; ++ return TRUE; ++ } ++ + if (event->button == 1) + { + if (text_view->drag_start_x >= 0) +@@ -4503,10 +4693,7 @@ + gtk_text_view_check_keymap_direction (text_view); + + if (text_view->editable) +- { +- text_view->need_im_reset = TRUE; + gtk_im_context_focus_in (GTK_TEXT_VIEW (widget)->im_context); +- } + + return FALSE; + } +@@ -4591,6 +4778,8 @@ + GtkTextView *text_view; + GList *child_exposes; + GList *tmp_list; ++ gboolean custom_background = FALSE; ++ GtkTextViewPrivate *priv = GTK_TEXT_VIEW_GET_PRIVATE (widget); + + text_view = GTK_TEXT_VIEW (widget); + +@@ -4617,16 +4806,49 @@ + area->width, area->height); + #endif + ++ gtk_widget_style_get (widget, ++ "custom-background", &custom_background, ++ NULL); ++ ++ if (custom_background) ++ { ++ gtk_paint_flat_box (widget->style, ++ event->window, ++ gtk_widget_get_state (widget), ++ GTK_SHADOW_NONE, ++ area, ++ widget, ++ NULL, ++ - text_view->xoffset, ++ - text_view->yoffset, ++ area->x + area->width + text_view->xoffset, ++ area->y + area->height + text_view->yoffset); ++ } ++ + child_exposes = NULL; +- gtk_text_layout_draw (text_view->layout, +- widget, +- text_view->text_window->bin_window, +- NULL, +- text_view->xoffset, +- text_view->yoffset, +- area->x, area->y, +- area->width, area->height, +- &child_exposes); ++ ++ if (!gtk_widget_has_focus (GTK_WIDGET (text_view)) ++ && priv->placeholder_layout ++ && gtk_text_buffer_get_char_count (get_buffer (text_view)) == 0) ++ gtk_text_layout_draw (priv->placeholder_layout, ++ widget, ++ text_view->text_window->bin_window, ++ NULL, ++ text_view->xoffset, ++ text_view->yoffset, ++ area->x, area->y, ++ area->width, area->height, ++ &child_exposes); ++ else ++ gtk_text_layout_draw (text_view->layout, ++ widget, ++ text_view->text_window->bin_window, ++ NULL, ++ text_view->xoffset, ++ text_view->yoffset, ++ area->x, area->y, ++ area->width, area->height, ++ &child_exposes); + + tmp_list = child_exposes; + while (tmp_list != NULL) +@@ -4696,12 +4918,27 @@ + { + if (gtk_widget_has_focus (widget) && !interior_focus) + { +- gtk_paint_focus (widget->style, widget->window, gtk_widget_get_state (widget), ++ gtk_paint_focus (widget->style, widget->window, ++ gtk_widget_get_state (widget), + NULL, widget, "textview", + 0, 0, + widget->allocation.width, + widget->allocation.height); + } ++ else if (!interior_focus) ++ { ++ HildonMode hildon_mode; ++ ++ gtk_widget_style_get (widget, "hildon-mode", &hildon_mode, NULL); ++ if (hildon_mode == HILDON_FREMANTLE) ++ gtk_paint_shadow (widget->style, widget->window, ++ gtk_widget_get_state (widget), ++ GTK_SHADOW_OUT, ++ NULL, widget, "textview", ++ 0, 0, ++ widget->allocation.width, ++ widget->allocation.height); ++ } + else + { + gdk_window_clear (widget->window); +@@ -5613,8 +5850,6 @@ + GtkTextIter end; + gboolean leave_one = FALSE; + +- gtk_text_view_reset_im_context (text_view); +- + if (type == GTK_DELETE_CHARS) + { + /* Char delete deletes the selection, if one exists */ +@@ -5754,8 +5989,6 @@ + { + GtkTextIter insert; + +- gtk_text_view_reset_im_context (text_view); +- + /* Backspace deletes the selection, if one exists */ + if (gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE, + text_view->editable)) +@@ -6591,11 +6824,7 @@ + { + g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); + +- if (text_view->need_im_reset) +- { +- text_view->need_im_reset = FALSE; +- gtk_im_context_reset (text_view->im_context); +- } ++ gtk_im_context_reset (text_view->im_context); + } + + /** +@@ -7574,6 +7803,39 @@ + return TRUE; + } + ++static gboolean ++gtk_text_view_has_selection_handler (GtkIMContext *context, ++ GtkTextView *text_view) ++{ ++ GtkTextBuffer *buffer; ++ ++ buffer = gtk_text_view_get_buffer (text_view); ++ return gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL); ++} ++ ++static void ++gtk_text_view_clipboard_operation_handler (GtkIMContext *context, ++ GtkIMContextClipboardOperation op, ++ GtkTextView *text_view) ++{ ++ /* Similar to gtk_editable_*_clipboard(), handle these by sending ++ * signals instead of directly calling our internal functions. That ++ * way the application can hook into them if needed. ++ */ ++ switch (op) ++ { ++ case GTK_IM_CONTEXT_CLIPBOARD_OP_COPY: ++ g_signal_emit_by_name (text_view, "copy_clipboard"); ++ break; ++ case GTK_IM_CONTEXT_CLIPBOARD_OP_CUT: ++ g_signal_emit_by_name (text_view, "cut_clipboard"); ++ break; ++ case GTK_IM_CONTEXT_CLIPBOARD_OP_PASTE: ++ g_signal_emit_by_name (text_view, "paste_clipboard"); ++ break; ++ } ++} ++ + static void + gtk_text_view_mark_set_handler (GtkTextBuffer *buffer, + const GtkTextIter *location, +@@ -7595,8 +7857,10 @@ + need_reset = TRUE; + } + ++#if 0 /* FIXME HACK */ + if (need_reset) + gtk_text_view_reset_im_context (text_view); ++#endif + } + + static void +@@ -9319,5 +9583,249 @@ + return gtk_text_layout_move_iter_visually (text_view->layout, iter, count); + } + ++/** ++ * hildon_gtk_text_view_set_input_mode: ++ * @text_view: a #GtkTextView ++ * @mode: a #HildonGtkInputMode ++ * ++ * Sets input mode of the widget. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ */ ++void ++hildon_gtk_text_view_set_input_mode (GtkTextView *text_view, ++ HildonGtkInputMode mode) ++{ ++ g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); ++ ++ if (hildon_gtk_text_view_get_input_mode (text_view) != mode) ++ { ++ g_object_set (G_OBJECT (text_view->im_context), ++ "hildon-input-mode", mode, NULL); ++ g_object_notify (G_OBJECT (text_view), "hildon-input-mode"); ++ } ++} ++ ++/** ++ * hildon_gtk_text_view_get_input_mode: ++ * @text_view: a #GtkTextView ++ * ++ * Gets input mode of the widget. ++ * ++ * Return value: the input mode of the widget. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ */ ++HildonGtkInputMode ++hildon_gtk_text_view_get_input_mode (GtkTextView *text_view) ++{ ++ HildonGtkInputMode mode; ++ ++ g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE); ++ ++ g_object_get (G_OBJECT (text_view->im_context), ++ "hildon-input-mode", &mode, NULL); ++ ++ return mode; ++} ++ ++/** ++ * hildon_gtk_text_view_set_input_default: ++ * @text_view: a #GtkTextView ++ * @mode: a #HildonGtkInputMode ++ * ++ * Sets the default input mode of the widget. ++ * ++ * Since: maemo 5.0 ++ */ ++void ++hildon_gtk_text_view_set_input_default (GtkTextView *text_view, ++ HildonGtkInputMode mode) ++{ ++ g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); ++ ++ if (hildon_gtk_text_view_get_input_default (text_view) != mode) ++ { ++ g_object_set (G_OBJECT (text_view->im_context), ++ "hildon-input-default", mode, NULL); ++ g_object_notify (G_OBJECT (text_view), "hildon-input-default"); ++ } ++} ++ ++/** ++ * hildon_gtk_text_view_get_input_default: ++ * @text_view: a #GtkTextView ++ * ++ * Gets the default input mode of the widget. ++ * ++ * Return value: the default input mode of the widget. ++ * ++ * Since: maemo 5 ++ */ ++HildonGtkInputMode ++hildon_gtk_text_view_get_input_default (GtkTextView *text_view) ++{ ++ HildonGtkInputMode mode; ++ ++ g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE); ++ ++ g_object_get (G_OBJECT (text_view->im_context), ++ "hildon-input-default", &mode, NULL); ++ ++ return mode; ++} ++ ++ ++/* This is more or less a stripped down version of ++ * gtk_text_view_ensure_layout(). ++ */ ++static void ++gtk_text_view_ensure_placeholder_layout (GtkTextView *text_view) ++{ ++ GtkTextViewPrivate *priv; ++ ++ priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view); ++ ++ if (priv->placeholder_layout == NULL) ++ { ++ GdkColor font_color; ++ GtkTextAttributes *style; ++ PangoContext *ltr_context, *rtl_context; ++ GtkWidget *widget = GTK_WIDGET (text_view); ++ ++ priv->placeholder_layout = gtk_text_layout_new (); ++ gtk_text_layout_set_buffer (priv->placeholder_layout, ++ priv->placeholder_buffer); ++ ++ gtk_text_layout_set_cursor_visible (priv->placeholder_layout, ++ FALSE); ++ ++ ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view)); ++ pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR); ++ rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view)); ++ pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL); ++ ++ gtk_text_layout_set_contexts (priv->placeholder_layout, ++ ltr_context, rtl_context); ++ ++ g_object_unref (ltr_context); ++ g_object_unref (rtl_context); ++ ++ ++ style = gtk_text_attributes_new (); ++ ++ gtk_widget_ensure_style (widget); ++ gtk_text_view_set_attributes_from_style (text_view, ++ style, widget->style); ++ ++ /* Override the color setting */ ++ if (gtk_style_lookup_color (widget->style, "ReversedSecondaryTextColor", ++ &font_color)) ++ { ++ style->appearance.fg_color = font_color; ++ } ++ ++ style->pixels_above_lines = text_view->pixels_above_lines; ++ style->pixels_below_lines = text_view->pixels_below_lines; ++ style->pixels_inside_wrap = text_view->pixels_inside_wrap; ++ style->left_margin = text_view->left_margin; ++ style->right_margin = text_view->right_margin; ++ style->indent = text_view->indent; ++ style->tabs = text_view->tabs ? pango_tab_array_copy (text_view->tabs) : NULL; ++ ++ style->wrap_mode = text_view->wrap_mode; ++ style->justification = text_view->justify; ++ style->direction = gtk_widget_get_direction (GTK_WIDGET (text_view)); ++ ++ gtk_text_layout_set_default_style (priv->placeholder_layout, style); ++ ++ gtk_text_attributes_unref (style); ++ } ++ ++ /* Now make sure the layout is validated. Since we expect the ++ * placeholder to only be a single line, this should be quick. ++ */ ++ gtk_text_layout_validate (priv->placeholder_layout, 2000); ++} ++ ++/** ++ * hildon_gtk_text_view_set_placeholder_text: ++ * @text_view: a #GtkTextView. ++ * @placeholder_text: a string to be displayed when @text_view is empty ++ * and unfocused or %NULL to remove current placeholder text. ++ * ++ * Sets a text string to be displayed when @entry is empty and unfocused. ++ * This can be provided to give a visual hint of the expected contents ++ * of the #GtkEntry. ++ * ++ * Since: maemo 5. ++ */ ++void ++hildon_gtk_text_view_set_placeholder_text (GtkTextView *text_view, ++ const gchar *placeholder_text) ++{ ++ GtkTextViewPrivate *priv; ++ ++ g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); ++ ++ priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view); ++ ++ if (!priv->placeholder_buffer) ++ priv->placeholder_buffer = gtk_text_buffer_new (NULL); ++ ++ if (placeholder_text) ++ { ++ gtk_text_buffer_set_text (priv->placeholder_buffer, placeholder_text, -1); ++ gtk_text_view_ensure_placeholder_layout (text_view); ++ } ++ else ++ { ++ g_object_unref (priv->placeholder_layout); ++ priv->placeholder_layout = NULL; ++ ++ g_object_unref (priv->placeholder_buffer); ++ priv->placeholder_buffer = NULL; ++ } ++ ++ if (gtk_text_buffer_get_char_count (get_buffer (text_view)) == 0 ++ && !gtk_widget_has_focus (GTK_WIDGET (text_view))) ++ gtk_widget_queue_draw (GTK_WIDGET (text_view)); ++ ++ g_object_notify (G_OBJECT (text_view), "hildon-placeholder-text"); ++} ++ ++/** ++ * hildon_gtk_text_view_get_placeholder_text: ++ * @text_view: a #GtkTextView ++ * ++ * Gets the text to be displayed if @text_view is empty and unfocused. ++ * The returned string must be freed using g_free(). ++ * ++ * Returns: an allocated string or %NULL if no placeholder text is set. ++ * ++ * Since: maemo 5. ++ */ ++gchar * ++hildon_gtk_text_view_get_placeholder_text (GtkTextView *text_view) ++{ ++ GtkTextViewPrivate *priv; ++ GtkTextIter start, end; ++ gchar *text; ++ ++ g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL); ++ ++ priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view); ++ ++ gtk_text_buffer_get_start_iter (priv->placeholder_buffer, &start); ++ gtk_text_buffer_get_end_iter (priv->placeholder_buffer, &end); ++ ++ text = gtk_text_buffer_get_text (priv->placeholder_buffer, ++ &start, &end, FALSE); ++ ++ return text; ++} ++ + #define __GTK_TEXT_VIEW_C__ + #include "gtkaliasdef.c" +--- a/gtk/gtktextview.h ++++ b/gtk/gtktextview.h +@@ -371,6 +371,18 @@ + /* note that the return value of this changes with the theme */ + GtkTextAttributes* gtk_text_view_get_default_attributes (GtkTextView *text_view); + ++void hildon_gtk_text_view_set_input_mode (GtkTextView *text_view, ++ HildonGtkInputMode mode); ++HildonGtkInputMode hildon_gtk_text_view_get_input_mode (GtkTextView *text_view); ++ ++void hildon_gtk_text_view_set_input_default (GtkTextView *text_view, ++ HildonGtkInputMode mode); ++HildonGtkInputMode hildon_gtk_text_view_get_input_default (GtkTextView *text_view); ++ ++void hildon_gtk_text_view_set_placeholder_text (GtkTextView *text_view, ++ const gchar *text); ++gchar *hildon_gtk_text_view_get_placeholder_text (GtkTextView *text_view); ++ + G_END_DECLS + + #endif /* __GTK_TEXT_VIEW_H__ */ diff --git a/hildonize-gtk-widget.patch b/hildonize-gtk-widget.patch new file mode 100644 index 000000000000..42ea933bcb5a --- /dev/null +++ b/hildonize-gtk-widget.patch @@ -0,0 +1,932 @@ +--- a/gtk/gtkwidget.c ++++ b/gtk/gtkwidget.c +@@ -54,6 +54,12 @@ + #include "gtkbuildable.h" + #include "gtkbuilderprivate.h" + #include "gtkalias.h" ++#include <x11/gdkx.h> ++#include <stdlib.h> ++#include "gtkmenu.h" ++#include "gtkmenuitem.h" ++#include "gtkicontheme.h" ++#include "gtkdnd.h" + + /** + * SECTION:gtkwidget +@@ -127,6 +133,8 @@ + #define WIDGET_CLASS(w) GTK_WIDGET_GET_CLASS (w) + #define INIT_PATH_SIZE (512) + ++#define TAP_AND_HOLD_TIMER_COUNTER 6 ++#define TAP_AND_HOLD_TIMER_INTERVAL 100 + + enum { + SHOW, +@@ -197,6 +205,10 @@ + KEYNAV_FAILED, + DRAG_FAILED, + DAMAGE_EVENT, ++ INSENSITIVE_PRESS, ++ TAP_AND_HOLD, ++ TAP_AND_HOLD_SETUP, ++ TAP_AND_HOLD_QUERY, + LAST_SIGNAL + }; + +@@ -224,7 +236,8 @@ + PROP_TOOLTIP_MARKUP, + PROP_TOOLTIP_TEXT, + PROP_WINDOW, +- PROP_DOUBLE_BUFFERED ++ PROP_DOUBLE_BUFFERED, ++ PROP_TAP_AND_HOLD + }; + + typedef struct _GtkStateData GtkStateData; +@@ -352,6 +365,46 @@ + static void gtk_widget_get_draw_rectangle (GtkWidget *widget, + GdkRectangle *rect); + ++typedef struct ++{ ++ GtkWidget *menu; ++ guint timer_id; ++ ++ GtkMenuPositionFunc func; ++ gint x, y; ++ gint timer_counter; ++ gint signals_connected : 1; ++ guint interval; ++ GdkWindow *tah_on_window; ++ ++#ifdef TAP_AND_HOLD_ANIMATION ++ GdkPixbufAnimation *anim; ++ GdkPixbufAnimationIter *iter; ++#endif ++} TahData; ++ ++/* --- Tap And Hold --- */ ++static gboolean gtk_widget_tap_and_hold_timeout (GtkWidget *widget); ++static gboolean gtk_widget_tap_and_hold_button_press (GtkWidget *widget, ++ GdkEvent *event, ++ TahData *td); ++static gboolean gtk_widget_tap_and_hold_event_stop (GtkWidget *widget, ++ gpointer unused, ++ TahData *td); ++static void gtk_widget_real_tap_and_hold_setup (GtkWidget *widget, ++ GtkWidget *menu, ++ GtkCallback func, ++ GtkWidgetTapAndHoldFlags flags ); ++static void gtk_widget_real_tap_and_hold (GtkWidget *widget); ++static gboolean gtk_widget_tap_and_hold_query (GtkWidget *widget, ++ GdkEvent *event); ++static gboolean gtk_widget_real_tap_and_hold_query (GtkWidget *widget, ++ GdkEvent *event); ++ ++static gboolean gtk_widget_tap_and_hold_query_accumulator (GSignalInvocationHint *ihint, ++ GValue *return_accu, ++ const GValue *handler_return, ++ gpointer dummy); + + /* --- variables --- */ + static gpointer gtk_widget_parent_class = NULL; +@@ -439,6 +492,23 @@ + { + GTK_WIDGET_GET_CLASS (object)->dispatch_child_properties_changed (GTK_WIDGET (object), n_pspecs, pspecs); + } ++static void ++maemo_widget_constructed (GObject *object) ++{ ++ static GQuark quark_maemo_widget_customizer = 0; ++ ++ if (!quark_maemo_widget_customizer) ++ { ++ quark_maemo_widget_customizer = ++ g_quark_from_static_string ("maemo_widget_customizer"); ++ } ++ ++ void (*hook) (GtkWidget*) = g_type_get_qdata (G_OBJECT_TYPE (object), ++ quark_maemo_widget_customizer); ++ ++ if (hook) ++ hook (GTK_WIDGET (object)); ++} + + static void + gtk_widget_class_init (GtkWidgetClass *klass) +@@ -475,6 +545,7 @@ + cpn_context.dispatcher = child_property_notify_dispatcher; + _gtk_widget_child_property_notify_context = &cpn_context; + ++ gobject_class->constructed = maemo_widget_constructed; + gobject_class->dispose = gtk_widget_dispose; + gobject_class->finalize = gtk_widget_finalize; + gobject_class->set_property = gtk_widget_set_property; +@@ -681,7 +752,6 @@ + P_("Whether gtk_widget_show_all() should not affect this widget"), + FALSE, + GTK_PARAM_READWRITE)); +- + /** + * GtkWidget:has-tooltip: + * +@@ -778,6 +848,28 @@ + GTK_PARAM_READWRITE)); + + /** ++ * GtkWidget:tap-and-hold-state: ++ * ++ * Sets the state (#GtkStateType) to be used to the tap and hold ++ * functionality. The default is GTK_STATE_NORMAL. ++ * ++ * Deprecated: Functionality for setting and getting this propery is not ++ * implemented. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_TAP_AND_HOLD, ++ g_param_spec_int ("tap-and-hold-state", ++ P_("Tap and hold State type"), ++ P_("Sets the state to be used to the tap and hold functionality. The default is GTK_STATE_NORMAL"), ++ 0, ++ 4, /*4 == Last state in GTK+-2.0*/ ++ GTK_STATE_NORMAL, ++ G_PARAM_READWRITE)); ++ ++ /** + * GtkWidget::show: + * @widget: the object which received the signal. + */ +@@ -2399,6 +2491,96 @@ + _gtk_marshal_BOOLEAN__UINT, + G_TYPE_BOOLEAN, 1, G_TYPE_UINT); + ++ /** ++ * GtkWidget::insensitive-press: ++ * @widget: the object which received the signal ++ * ++ * If a widget is insensitive and it receives click event, ++ * the signal is emited. Signal is made to clarify situations where ++ * a widget is not easily noticable as an insensitive widget. ++ * ++ * Deprecated: Use hildon_helper_set_insensitive_message() instead. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ */ ++ widget_signals[INSENSITIVE_PRESS] = ++ g_signal_new ("insensitive_press", ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_FIRST, ++ 0, ++ NULL, NULL, ++ _gtk_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ /** ++ * GtkWidget::tap-and-hold: ++ * @widget: the object which received the signal ++ * ++ * The signal is emited when tap and hold activity occurs. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ */ ++ widget_signals[TAP_AND_HOLD] = ++ g_signal_new_class_handler ("tap_and_hold", ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ G_CALLBACK (gtk_widget_real_tap_and_hold), ++ NULL, NULL, ++ _gtk_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ /** ++ * GtkWidget::tap-and-hold-setup: ++ * @widget: the object which received the signal ++ * @menu: the menu to be opened. ++ * @func: the menu position function ++ * @flags: deprecated ++ * ++ * Enables the tap and hold functionality to the @widget. ++ * Usually a @menu is used at tap and hold signal, ++ * but this is optional. Setup can be run and some other functionality ++ * may be connected to it as well. Usually this signal is not used, ++ * instead the virtual function is over written. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ */ ++ widget_signals[TAP_AND_HOLD_SETUP] = ++ g_signal_new_class_handler ("tap_and_hold_setup", ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ G_CALLBACK (gtk_widget_real_tap_and_hold_setup), ++ NULL, NULL, ++ /*FIXME -- OBJECT_POINTER_FLAGS*/ ++ _gtk_marshal_VOID__OBJECT_UINT_FLAGS, ++ G_TYPE_NONE, 3, ++ G_TYPE_OBJECT, ++ G_TYPE_POINTER, ++ G_TYPE_UINT); ++ ++ /** ++ * GtkWidget::tap-and-hold-query: ++ * @widget: the object which received the signal ++ * @returns: %FALSE if tap and hold is allowed to be started ++ * ++ * Signal is used in a situation where tap and hold is not allowed to be ++ * started in some mysterious reason. A good mysterious reason could be, ++ * a widget which area is big and only part of it is allowed to start ++ * tap and hold. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ */ ++ widget_signals[TAP_AND_HOLD_QUERY] = ++ g_signal_new_class_handler ("tap_and_hold_query", ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ G_CALLBACK (gtk_widget_real_tap_and_hold_query), ++ gtk_widget_tap_and_hold_query_accumulator, NULL, ++ _gtk_marshal_BOOLEAN__BOXED, ++ G_TYPE_BOOLEAN, 1, ++ GDK_TYPE_EVENT); ++ + binding_set = gtk_binding_set_by_class (klass); + gtk_binding_entry_add_signal (binding_set, GDK_F10, GDK_SHIFT_MASK, + "popup-menu", 0); +@@ -2466,7 +2648,19 @@ + P_("Aspect ratio with which to draw insertion cursor"), + 0.0, 1.0, 0.04, + GTK_PARAM_READABLE)); +- ++ gtk_widget_class_install_style_property (klass, ++ g_param_spec_boolean ("maemo-position-theming", ++ P_("Maemo position theming"), ++ P_("Theming hint to allow rounded corner effects on border children"), ++ FALSE, ++ GTK_PARAM_READABLE)); ++ gtk_widget_class_install_style_property (klass, ++ g_param_spec_enum ("hildon-mode", ++ P_("Hildon Mode"), ++ P_("The mode according to which widgets should behave"), ++ HILDON_TYPE_MODE, ++ HILDON_DIABLO, ++GTK_PARAM_READABLE)); + /** + * GtkWidget:draw-border: + * +@@ -2749,6 +2943,8 @@ + case PROP_DOUBLE_BUFFERED: + gtk_widget_set_double_buffered (widget, g_value_get_boolean (value)); + break; ++ case PROP_TAP_AND_HOLD: ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -2858,6 +3054,8 @@ + case PROP_DOUBLE_BUFFERED: + g_value_set_boolean (value, gtk_widget_get_double_buffered (widget)); + break; ++ case PROP_TAP_AND_HOLD: ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -4149,6 +4347,9 @@ + gtk_widget_invalidate_widget_windows (widget->parent, invalidate); + gdk_region_destroy (invalidate); + } ++ ++ if (gtk_widget_is_toplevel (widget)) ++ _gtk_container_post_size_allocate (GTK_CONTAINER (widget)); + } + + /** +@@ -11449,5 +11650,586 @@ + return res; + } + ++void gtk_widget_set_hildon_focus_handling( GtkWidget *widget, gboolean hildon_like ) ++{ ++} ++ ++gboolean gtk_widget_get_hildon_focus_handling( GtkWidget *widget ) ++{ ++ return FALSE; ++} ++ ++ ++/* -- Tap and hold implementation -- */ ++ ++static TahData* ++gtk_widget_peek_tah_data (GtkWidget *widget) ++{ ++ TahData *td = g_object_get_data (G_OBJECT (widget), "MaemoGtkWidget-tap-and-hold"); ++ return td; ++} ++ ++static void ++tap_and_hold_stop_animation (TahData *td) ++{ ++#ifdef TAP_AND_HOLD_ANIMATION ++ if (td->tah_on_window) ++ gdk_window_set_cursor (td->tah_on_window, NULL); ++ td->tah_on_window = NULL; ++ ++ if (td->anim) ++ g_object_unref (td->anim); ++ td->anim = NULL; ++ ++ if (td->iter) ++ g_object_unref (td->iter); ++ td->iter = NULL; ++#endif ++} ++ ++static void ++tap_and_hold_free_data (gpointer data) ++{ ++ TahData *td = data; ++ if (td) ++ { ++ if (td->timer_id) ++ g_source_remove (td->timer_id); ++ td->timer_id = 0; ++ ++ if (GTK_IS_MENU (td->menu)) ++ g_object_unref (td->menu); ++ td->menu = NULL; ++ ++ tap_and_hold_stop_animation (td); ++ ++ g_free (td); ++ } ++} ++ ++static void ++gtk_widget_set_tah_data (GtkWidget *widget, TahData *td) ++{ ++ g_object_set_data_full (G_OBJECT (widget), "MaemoGtkWidget-tap-and-hold", ++ td, tap_and_hold_free_data); ++} ++ ++static TahData* ++gtk_widget_get_tah_data (GtkWidget *widget) ++{ ++ TahData *td = gtk_widget_peek_tah_data (widget); ++ if (!td) ++ { ++ td = g_new0 (TahData, 1); ++ td->interval = TAP_AND_HOLD_TIMER_INTERVAL; ++ gtk_widget_set_tah_data (widget, td); ++ } ++ return td; ++} ++ ++static void ++tap_and_hold_remove_timer (GtkWidget *widget) ++{ ++ TahData *td = gtk_widget_peek_tah_data (widget); ++ if (td) ++ { ++ if (td->timer_id) ++ { ++ g_source_remove (td->timer_id); ++ td->timer_id = 0; ++ } ++ ++ td->x = td->y = td->timer_counter = 0; ++ tap_and_hold_stop_animation (td); ++ } ++} ++ ++#ifdef TAP_AND_HOLD_ANIMATION ++static GdkPixbufAnimation * ++tap_and_hold_load_animation_for_screen (GdkScreen *screen) ++{ ++ GtkIconTheme *theme; ++ GtkIconInfo *info; ++ const char *filename = NULL; ++ GdkPixbufAnimation *anim = NULL; ++ GError *error = NULL; ++ ++ theme = gtk_icon_theme_get_for_screen (screen); ++ ++ info = gtk_icon_theme_lookup_icon (theme, "qgn_indi_tap_hold_a", ++ GTK_ICON_SIZE_BUTTON, ++ GTK_ICON_LOOKUP_NO_SVG); ++ if (info) ++ filename = gtk_icon_info_get_filename (info); ++ if (!info || !filename) ++ { ++ g_warning ("Unable to find tap and hold icon filename"); ++ goto out; ++ } ++ ++ anim = gdk_pixbuf_animation_new_from_file (filename, &error); ++ if (!anim) ++ { ++ g_warning ("Unable to load tap and hold animation: %s", error->message); ++ goto out; ++ } ++ ++out: ++ if (info) ++ gtk_icon_info_free (info); ++ if (error) ++ g_error_free (error); ++ ++ return anim; ++} ++#endif ++ ++static void ++tap_and_hold_init_animation (TahData *td) ++{ ++#ifdef TAP_AND_HOLD_ANIMATION ++ if (!td->anim) ++ td->anim = tap_and_hold_load_animation_for_screen (gdk_drawable_get_screen (td->tah_on_window)); ++ ++ if (td->anim) ++ { ++ if (td->iter) ++ g_object_unref (td->iter); ++ td->iter = gdk_pixbuf_animation_get_iter (td->anim, NULL); ++ ++ td->interval = gdk_pixbuf_animation_iter_get_delay_time (td->iter); ++ } ++#endif ++} ++ ++static gboolean ++tap_and_hold_animation_timeout (GtkWidget *widget) ++{ ++#ifdef TAP_AND_HOLD_ANIMATION ++ TahData *td = gtk_widget_peek_tah_data (widget); ++ ++ if (!td || !GDK_IS_WINDOW (td->tah_on_window)) ++ { ++ tap_and_hold_remove_timer (widget); ++ return FALSE; ++ } ++ ++ if (td->anim) ++ { ++ guint new_interval = 0; ++ GTimeVal time; ++ GdkScreen *screen; ++ GdkPixbuf *pic; ++ GdkCursor *cursor; ++ const gchar *x_hot, *y_hot; ++ gint x, y; ++ ++ g_get_current_time (&time); ++ screen = gdk_screen_get_default (); ++ pic = gdk_pixbuf_animation_iter_get_pixbuf (td->iter); ++ ++ pic = gdk_pixbuf_copy (pic); ++ ++ if (!GDK_IS_PIXBUF (pic)) ++ return TRUE; ++ ++ x_hot = gdk_pixbuf_get_option (pic, "x_hot"); ++ y_hot = gdk_pixbuf_get_option (pic, "y_hot"); ++ x = (x_hot) ? atoi(x_hot) : gdk_pixbuf_get_width(pic) / 2; ++ y = (y_hot) ? atoi(y_hot) : gdk_pixbuf_get_height(pic) / 2; ++ ++ cursor = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), pic, ++ x, y); ++ g_object_unref (pic); ++ ++ if (!cursor) ++ return TRUE; ++ ++ gdk_window_set_cursor (td->tah_on_window, cursor); ++ gdk_cursor_unref (cursor); ++ ++ gdk_pixbuf_animation_iter_advance (td->iter, &time); ++ ++ new_interval = gdk_pixbuf_animation_iter_get_delay_time (td->iter); ++ ++ if (new_interval != td->interval && td->timer_counter) ++ { ++ td->interval = new_interval; ++ td->timer_id = g_timeout_add (td->interval, ++ (GSourceFunc)gtk_widget_tap_and_hold_timeout, widget); ++ return FALSE; ++ } ++ } ++#endif ++ return TRUE; ++} ++ ++/** ++ * gtk_widget_tap_and_hold_menu_position_top: ++ * @menu: a #GtkMenu ++ * @x: x cordinate to be returned ++ * @y: y cordinate to be returned ++ * @push_in: If going off screen, push it pack on the screen ++ * @widget: a #GtkWidget ++ * ++ * Pre-made menu positioning function. ++ * It positiones the @menu over the @widget. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ **/ ++void ++gtk_widget_tap_and_hold_menu_position_top (GtkWidget *menu, ++ gint *x, ++ gint *y, ++ gboolean *push_in, ++ GtkWidget *widget) ++{ ++ /* ++ * This function positiones the menu above widgets. ++ * This is a modified version of the position function ++ * gtk_combo_box_position_over. ++ */ ++ GtkWidget *topw; ++ GtkRequisition requisition; ++ gint screen_width = 0; ++ gint menu_xpos = 0; ++ gint menu_ypos = 0; ++ gint w_xpos = 0, w_ypos = 0; ++ gtk_widget_size_request (menu, &requisition); ++ ++ topw = gtk_widget_get_toplevel (widget); ++ gdk_window_get_origin (topw->window, &w_xpos, &w_ypos); ++ ++ menu_xpos += widget->allocation.x + w_xpos; ++ menu_ypos += widget->allocation.y + w_ypos - requisition.height; ++ ++ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ++ menu_xpos = menu_xpos + widget->allocation.width - requisition.width; ++ ++ screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget)); ++ ++ if (menu_xpos < w_xpos) ++ menu_xpos = w_xpos; ++ else if ((menu_xpos + requisition.width) > screen_width) ++ menu_xpos -= ((menu_xpos + requisition.width) - screen_width); ++ if (menu_ypos < w_ypos) ++ menu_ypos = w_ypos; ++ ++ *x = menu_xpos; ++ *y = menu_ypos; ++ *push_in = TRUE; ++} ++ ++/** ++ * gtk_widget_tap_and_hold_setup: ++ * @widget : a #GtkWidget ++ * @menu : a #GtkMenu or %NULL ++ * @func : a #GtkMenuPositionFunc or %NULL ++ * @flags : a #GtkWidgetTapAndHoldFlags ++ * ++ * Setups the tap and hold functionality to the @widget. ++ * The @menu is shown when the functionality is activated. ++ * If the @menu is wanted to be positioned in a different way than the ++ * gtk+ default, the menuposition @func can be passed as a third parameter. ++ * Fourth parameter, @flags is deprecated and has no effect. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ */ ++void ++gtk_widget_tap_and_hold_setup (GtkWidget *widget, ++ GtkWidget *menu, ++ GtkCallback func, ++ GtkWidgetTapAndHoldFlags flags) ++{ ++ g_return_if_fail (GTK_IS_WIDGET (widget)); ++ g_return_if_fail (menu == NULL || GTK_IS_MENU (menu)); ++ ++ g_signal_emit (widget, widget_signals[TAP_AND_HOLD_SETUP], 0, menu, func, ++ flags); ++} ++ ++static void ++gtk_widget_real_tap_and_hold_setup (GtkWidget *widget, ++ GtkWidget *menu, ++ GtkCallback func, ++ GtkWidgetTapAndHoldFlags flags) ++{ ++ TahData *td; ++ ++ g_return_if_fail (GTK_IS_WIDGET (widget)); ++ g_return_if_fail (menu == NULL || GTK_IS_MENU (menu)); ++ ++ td = gtk_widget_get_tah_data (widget); ++ if (td->signals_connected) ++ return; ++ ++ if (menu != NULL) ++ { ++ g_object_ref_sink (menu); ++ ++ if (gtk_menu_get_attach_widget (GTK_MENU (menu)) == NULL) ++ gtk_menu_attach_to_widget (GTK_MENU (menu), widget, NULL); ++ } ++ ++ td->menu = menu; ++ td->func = (GtkMenuPositionFunc)func; ++ td->signals_connected = TRUE; ++ td->timer_counter = 0; ++ ++ g_signal_connect (widget, "button-press-event", ++ G_CALLBACK (gtk_widget_tap_and_hold_button_press), td); ++ g_signal_connect (widget, "button-release-event", ++ G_CALLBACK (gtk_widget_tap_and_hold_event_stop), td); ++ g_signal_connect (widget, "leave-notify-event", ++ G_CALLBACK (gtk_widget_tap_and_hold_event_stop), td); ++ g_signal_connect (widget, "drag-begin", ++ G_CALLBACK (gtk_widget_tap_and_hold_event_stop), td); ++} ++ ++static void ++gtk_widget_real_tap_and_hold (GtkWidget *widget) ++{ ++ TahData *td = gtk_widget_peek_tah_data (widget); ++ if (td && GTK_IS_MENU (td->menu)) ++ gtk_menu_popup (GTK_MENU (td->menu), NULL, NULL, ++ (GtkMenuPositionFunc)td->func, ++ widget, 1, gdk_x11_get_server_time (widget->window)); ++} ++ ++static gboolean ++gtk_widget_tap_and_hold_timeout (GtkWidget *widget) ++{ ++ TahData *td = gtk_widget_peek_tah_data (widget); ++ gboolean result; ++ gint x = 0, y = 0; ++ ++ GDK_THREADS_ENTER (); ++ ++ if (!td || !GDK_IS_WINDOW (td->tah_on_window)) ++ { ++ tap_and_hold_remove_timer (widget); ++ ++ GDK_THREADS_LEAVE (); ++ return FALSE; ++ } ++ ++ /* A small timeout before starting the tap and hold */ ++ if (td->timer_counter == TAP_AND_HOLD_TIMER_COUNTER) ++ { ++ td->timer_counter--; ++ ++ GDK_THREADS_LEAVE (); ++ return TRUE; ++ } ++ ++ result = tap_and_hold_animation_timeout (widget); ++ ++ if (td->timer_counter) ++ td->timer_counter--; ++ else ++ td->timer_id = 0; ++ ++ gdk_display_get_pointer (gdk_drawable_get_display (td->tah_on_window), ++ NULL, &x, &y, NULL); ++ ++ /* Did we dragged too far from the start point */ ++ if (gtk_drag_check_threshold (widget, td->x, td->y, x, y)) ++ { ++ tap_and_hold_remove_timer (widget); ++ ++ GDK_THREADS_LEAVE (); ++ return FALSE; ++ } ++ ++ /* Was that the last cycle -> tah starts */ ++ if (!td->timer_id) ++ { ++ tap_and_hold_remove_timer (widget); ++ ++ _gtk_menu_push_context_menu_behavior (); ++ ++ g_signal_emit (widget, widget_signals[TAP_AND_HOLD], 0); ++ ++ _gtk_menu_pop_context_menu_behavior (); ++ ++ GDK_THREADS_LEAVE (); ++ return FALSE; ++ } ++ ++ GDK_THREADS_LEAVE (); ++ return result; ++} ++ ++static gboolean ++gtk_widget_tap_and_hold_query_accumulator (GSignalInvocationHint *ihint, ++ GValue *return_accu, ++ const GValue *handler_return, ++ gpointer dummy) ++{ ++ gboolean tap_and_hold_not_allowed; ++ ++ /* The semantics of the tap-and-hold-query return value differs from ++ * the normal event signal handlers. ++ */ ++ ++ tap_and_hold_not_allowed = g_value_get_boolean (handler_return); ++ g_value_set_boolean (return_accu, tap_and_hold_not_allowed); ++ ++ /* Now that a single handler has run, we stop emission. */ ++ return FALSE; ++} ++ ++static gboolean ++gtk_widget_real_tap_and_hold_query (GtkWidget *widget, ++ GdkEvent *event) ++{ ++ return FALSE; ++} ++ ++static gboolean ++gtk_widget_tap_and_hold_query (GtkWidget *widget, ++ GdkEvent *event) ++{ ++ gboolean return_value = FALSE; ++ ++ g_signal_emit (G_OBJECT (widget), widget_signals[TAP_AND_HOLD_QUERY], ++ 0, event, &return_value); ++ ++ return return_value; ++} ++ ++static gboolean ++gtk_widget_tap_and_hold_button_press (GtkWidget *widget, ++ GdkEvent *event, ++ TahData *td) ++{ ++ if (event->button.type == GDK_2BUTTON_PRESS) ++ return FALSE; ++ ++ if (!gtk_widget_tap_and_hold_query (widget, event) && !td->timer_id) ++ { ++ GdkWindow *root_window; ++ gdk_display_get_pointer (gtk_widget_get_display (widget), ++ NULL, &td->x, &td->y, NULL); ++ ++ td->timer_counter = TAP_AND_HOLD_TIMER_COUNTER; ++ /* We set the cursor in the root window for the TAH animation to be ++ visible in every possible case, like windows completely covering ++ some widget to filter events. ++ */ ++ root_window = gtk_widget_get_root_window (widget); ++ if (root_window == NULL) ++ { ++ /* We are getting events from a widget that's not in a ++ hierarchy, it might happen (like putting a dummy widget ++ as user_data in a GdkWindow). Try really hard to get ++ the root window ++ */ ++ root_window = gdk_screen_get_root_window (gdk_screen_get_default ()); ++ } ++ td->tah_on_window = root_window; ++ tap_and_hold_init_animation (td); ++ td->timer_id = g_timeout_add (td->interval, ++ (GSourceFunc) ++ gtk_widget_tap_and_hold_timeout, widget); ++ } ++ return FALSE; ++} ++ ++static gboolean ++gtk_widget_tap_and_hold_event_stop (GtkWidget *widget, ++ gpointer unused, ++ TahData *td) ++{ ++ if (td->timer_id) ++ tap_and_hold_remove_timer (widget); ++ ++ return FALSE; ++} ++ ++ ++/** ++ * gtk_widget_insensitive_press: ++ * @widget: a #GtkWidget ++ * ++ * Emits the "insensitive-press" signal. ++ * ++ * Deprecated: Use hildon_helper_set_insensitive_message() instead. ++ * ++ * Since: maemo 1.0 ++ * Stability: Unstable ++ */ ++void ++gtk_widget_insensitive_press ( GtkWidget *widget ) ++{ ++ g_return_if_fail (GTK_IS_WIDGET (widget)); ++ g_signal_emit(widget, widget_signals[INSENSITIVE_PRESS], 0); ++} ++ ++#define HILDON_HEIGHT_FINGER 70 ++ ++#define HILDON_HEIGHT_THUMB 105 ++ ++#define HILDON_WIDTH_FULLSCREEN (gdk_screen_get_width (gdk_screen_get_default ())) ++ ++#define HILDON_WIDTH_HALFSCREEN (HILDON_WIDTH_FULLSCREEN / 2) ++ ++/** ++ * hildon_gtk_widget_set_theme_size: ++ * @widget: A #GtkWidget ++ * @size: #HildonSizeType flags indicating the size of the widget ++ * ++ * This function sets the requested size of a widget using one of the ++ * predefined sizes. ++ * ++ * It also changes the widget name (see gtk_widget_set_name()) so it ++ * can be themed accordingly. ++ * ++ * Since: maemo 2.0 ++ * Stability: Unstable ++ **/ ++void ++hildon_gtk_widget_set_theme_size (GtkWidget *widget, ++ HildonSizeType size) ++{ ++ gint width = -1; ++ gint height = -1; ++ gchar *widget_name = NULL; ++ ++ g_return_if_fail (GTK_IS_WIDGET (widget)); ++ ++ /* Requested height */ ++ if (size & HILDON_SIZE_FINGER_HEIGHT) ++ { ++ height = HILDON_HEIGHT_FINGER; ++ widget_name = "-finger"; ++ } ++ else if (size & HILDON_SIZE_THUMB_HEIGHT) ++ { ++ height = HILDON_HEIGHT_THUMB; ++ widget_name = "-thumb"; ++ } ++ ++ if (widget_name) ++ widget_name = g_strconcat (g_type_name (G_OBJECT_TYPE (widget)), ++ widget_name, NULL); ++ ++ /* Requested width */ ++ if (size & HILDON_SIZE_HALFSCREEN_WIDTH) ++ width = HILDON_WIDTH_HALFSCREEN; ++ else if (size & HILDON_SIZE_FULLSCREEN_WIDTH) ++ width = HILDON_WIDTH_FULLSCREEN; ++ ++ gtk_widget_set_size_request (widget, width, height); ++ ++ if (widget_name) ++ { ++ gtk_widget_set_name (widget, widget_name); ++ g_free (widget_name); ++ } ++} ++ + #define __GTK_WIDGET_C__ + #include "gtkaliasdef.c" +--- a/gtk/gtkwidget.h ++++ b/gtk/gtkwidget.h +@@ -130,6 +130,14 @@ + GTK_WIDGET_HELP_WHATS_THIS + } GtkWidgetHelpType; + ++ typedef enum ++ { ++ GTK_TAP_AND_HOLD_NONE = 0, ++ GTK_TAP_AND_HOLD_PASS_PRESS = 1 << 0, ++ GTK_TAP_AND_HOLD_NO_SIGNALS = 1 << 1, ++ GTK_TAP_AND_HOLD_NO_INTERNALS = 1 << 2 ++ } GtkWidgetTapAndHoldFlags; ++ + /* Macro for casting a pointer to a GtkWidget or GtkWidgetClass pointer. + * Macros for testing whether `widget' or `klass' are of type GTK_TYPE_WIDGET. + */ +@@ -1345,6 +1353,20 @@ + void _gtk_widget_buildable_finish_accelerator (GtkWidget *widget, + GtkWidget *toplevel, + gpointer user_data); ++void gtk_widget_tap_and_hold_menu_position_top (GtkWidget *menu, ++ gint *x, ++ gint *y, ++ gboolean *push_in, ++ GtkWidget *widget); ++void gtk_widget_tap_and_hold_setup (GtkWidget *widget, ++ GtkWidget *menu, ++ GtkCallback func, ++ GtkWidgetTapAndHoldFlags flags); ++ ++void gtk_widget_insensitive_press ( GtkWidget *widget ); ++ ++void hildon_gtk_widget_set_theme_size (GtkWidget *widget, ++ HildonSizeType size); + + G_END_DECLS + diff --git a/hildonize-gtk-window.patch b/hildonize-gtk-window.patch new file mode 100644 index 000000000000..ccf00f227d10 --- /dev/null +++ b/hildonize-gtk-window.patch @@ -0,0 +1,278 @@ +--- a/gtk/gtkwindow.c ++++ b/gtk/gtkwindow.c +@@ -103,6 +103,7 @@ + PROP_STARTUP_ID, + + PROP_MNEMONICS_VISIBLE, ++ PROP_TEMPORARY, + + LAST_ARG + }; +@@ -190,6 +191,7 @@ + + guint mnemonics_visible : 1; + guint mnemonics_visible_set : 1; ++ guint is_temporary : 1; + + GdkWindowTypeHint type_hint; + +@@ -805,6 +807,26 @@ + 1.0, + GTK_PARAM_READWRITE)); + ++ /** ++ * GtkWindow:temporary: ++ * ++ * Whether the window is "temporary" (completion popups, menus, etc) and should be ++ * automatically closed when it receives the _GTK_DELETE_TEMPORARIES ClientMessage. ++ * If set to TRUE GTK will send a delete-event to the window whenever it receives ++ * a _GTK_DELETE_TEMPORARIES message, so this needs to be properly handled by the ++ * widget. ++ * ++ * Since: maemo 4.0 ++ * Stability: Unstable ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_DELETABLE, ++ g_param_spec_boolean ("temporary", ++ P_("Temporary"), ++ P_("Whether the window should be closed when it receives the _GTK_DELETE_TEMPORARIES ClientMessage"), ++ FALSE, ++ GTK_PARAM_READWRITE)); ++ + window_signals[SET_FOCUS] = + g_signal_new (I_("set-focus"), + G_TYPE_FROM_CLASS (gobject_class), +@@ -1084,6 +1106,9 @@ + case PROP_MNEMONICS_VISIBLE: + gtk_window_set_mnemonics_visible (window, g_value_get_boolean (value)); + break; ++ case PROP_TEMPORARY: ++ gtk_window_set_is_temporary (window, g_value_get_boolean (value)); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -1202,6 +1227,8 @@ + case PROP_MNEMONICS_VISIBLE: + g_value_set_boolean (value, priv->mnemonics_visible); + break; ++ case PROP_TEMPORARY: ++ g_value_set_boolean (value, gtk_window_get_is_temporary (window)); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -4699,6 +4726,9 @@ + else + gdk_window_withdraw (widget->window); + ++ /* Keep it in sync with configure_request_count. */ ++ gdk_window_reset_toplevel_updates_libgtk_only (widget->window); ++ + window->configure_request_count = 0; + window->configure_notify_received = FALSE; + +@@ -5347,6 +5377,7 @@ + + static GdkAtom atom_rcfiles = GDK_NONE; + static GdkAtom atom_iconthemes = GDK_NONE; ++static GdkAtom atom_temporaries = GDK_NONE; + + static void + send_client_message_to_embedded_windows (GtkWidget *widget, +@@ -5376,6 +5407,73 @@ + } + } + ++/** ++ * gtk_window_set_is_temporary: ++ * @window: a #GtkWindow ++ * @setting: %TRUE if the window should be closed when it receives the _GTK_DELETE_TEMPORARIES ClientMessage ++ * ++ * Since: maemo 4.0 ++ * Stability: Unstable ++ */ ++void ++gtk_window_set_is_temporary (GtkWindow *window, ++ gboolean setting) ++{ ++ GtkWindowPrivate *priv; ++ ++ g_return_if_fail (GTK_IS_WINDOW (window)); ++ ++ priv = GTK_WINDOW_GET_PRIVATE (window); ++ ++ if (priv->is_temporary != setting) ++ { ++ priv->is_temporary = setting; ++ ++ g_object_notify (G_OBJECT (window), "temporary"); ++ } ++} ++ ++/** ++ * gtk_window_get_is_temporary: ++ * @window: a #GtkWindow ++ * ++ * Return value: %TRUE if the window is marked as temporary. ++ * ++ * Since: maemo 4.0 ++ * Stability: Unstable ++ */ ++gboolean ++gtk_window_get_is_temporary (GtkWindow *window) ++{ ++ GtkWindowPrivate *priv; ++ ++ g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); ++ ++ priv = GTK_WINDOW_GET_PRIVATE (window); ++ return priv->is_temporary; ++} ++ ++static void ++delete_if_temporary (GtkWidget *widget, GdkEventClient *client) ++{ ++ GtkWindowPrivate *priv = GTK_WINDOW_GET_PRIVATE (widget); ++ ++ if (priv->is_temporary && ++ _gtk_window_is_on_client_data (GTK_WINDOW (widget), client) == FALSE) ++ { ++ /* synthesize delete-event to close the window */ ++ GdkEvent *event; ++ ++ event = gdk_event_new (GDK_DELETE); ++ ++ event->any.window = g_object_ref (widget->window); ++ event->any.send_event = TRUE; ++ ++ gtk_main_do_event (event); ++ gdk_event_free (event); ++ } ++} ++ + static gint + gtk_window_client_event (GtkWidget *widget, + GdkEventClient *event) +@@ -5384,18 +5482,29 @@ + { + atom_rcfiles = gdk_atom_intern_static_string ("_GTK_READ_RCFILES"); + atom_iconthemes = gdk_atom_intern_static_string ("_GTK_LOAD_ICONTHEMES"); ++ atom_temporaries = gdk_atom_intern_static_string ("_GTK_DELETE_TEMPORARIES"); + } + + if (event->message_type == atom_rcfiles) + { +- send_client_message_to_embedded_windows (widget, atom_rcfiles); +- gtk_rc_reparse_all_for_settings (gtk_widget_get_settings (widget), FALSE); ++ /* The theme may have been changed and a resource file may happen to have ++ the exact same modification time, so we pass TRUE for force_reload. ++ See NB#151715 for a discussion. */ ++ gtk_rc_reparse_all_for_settings (gtk_widget_get_settings (widget), TRUE); + } + + if (event->message_type == atom_iconthemes) + { + send_client_message_to_embedded_windows (widget, atom_iconthemes); +- _gtk_icon_theme_check_reload (gtk_widget_get_display (widget)); ++ _gtk_icon_theme_check_reload (gtk_widget_get_display (widget)); ++ } ++ ++ if (event->message_type == atom_temporaries) ++ { ++ send_client_message_to_embedded_windows (widget, atom_temporaries); ++ if (gtk_widget_get_mapped (widget) ++ && gtk_window_get_is_temporary (GTK_WINDOW (widget))) ++ delete_if_temporary (widget, event); + } + + return FALSE; +@@ -8612,5 +8721,72 @@ + + #endif + ++GtkWidget *gtk_window_get_prev_focus_widget( GtkWindow *window ) ++{ ++ return NULL; ++ ++} ++ ++void gtk_window_set_prev_focus_widget( GtkWindow *window, GtkWidget *widget ) ++{ ++} ++ ++#if defined(GDK_WINDOWING_X11) ++gboolean ++_gtk_window_is_on_client_data (GtkWindow *window, GdkEventClient *event) ++{ ++ XID xid = GDK_WINDOW_XID (GTK_WIDGET (window)->window); ++ return memcmp (&xid, (XID*)(&event->data.b[12]), sizeof(XID)) == 0; ++} ++ ++/** ++ * gtk_window_close_other_temporaries: ++ * ++ * Sends a _GTK_DELETE_TEMPORARIES ClientEvent to all other toplevel windows ++ * ++ * Since: maemo 4.0 ++ * Stability: Unstable ++ */ ++void ++gtk_window_close_other_temporaries (GtkWindow *window) ++{ ++ GList *toplevels; ++ GdkEventClient client; ++ XID xid = GDK_WINDOW_XID (GTK_WIDGET (window)->window); ++ ++ memset(&client, 0, sizeof(client)); ++ client.message_type = gdk_atom_intern ("_GTK_DELETE_TEMPORARIES", FALSE); ++ client.data_format = 8; ++ memcpy (((XID*)(&client.data.b[12])),&xid, sizeof(XID)); ++ gdk_event_send_clientmessage_toall ((GdkEvent*)&client); ++ ++ /* The client messages are sent out of process and won't be ++ * delivered before this function returns. If the caller is ++ * a modal dialog and thus grabs, the delete events for this ++ * process could get ignored. ++ */ ++ toplevels = gtk_window_list_toplevels (); ++ g_list_foreach (toplevels, (GFunc)g_object_ref, NULL); ++ ++ while (toplevels) ++ { ++ GtkWindow *toplevel = toplevels->data; ++ toplevels = g_list_delete_link (toplevels, toplevels); ++ ++ /* We check for MAPPED here instead of comparing to the ++ * window argument, because there can be unmapped toplevels ++ * that are != window. ++ */ ++ if (gtk_widget_get_mapped (GTK_WIDGET (toplevel)) ++ && gtk_window_get_is_temporary (toplevel)) ++ delete_if_temporary (GTK_WIDGET (toplevel), &client); ++ ++ g_object_unref (toplevel); ++ } ++ ++ g_list_free (toplevels); ++} ++#endif /* GDK_WINDOWING_X11 */ ++ + #define __GTK_WINDOW_C__ + #include "gtkaliasdef.c" +--- a/gtk/gtkwindow.h ++++ b/gtk/gtkwindow.h +@@ -279,6 +279,13 @@ + gboolean setting); + gboolean gtk_window_get_deletable (GtkWindow *window); + ++void gtk_window_set_is_temporary (GtkWindow *window, ++ gboolean setting); ++gboolean gtk_window_get_is_temporary (GtkWindow *window); ++gboolean _gtk_window_is_on_client_data (GtkWindow *window, ++ GdkEventClient *event); ++void gtk_window_close_other_temporaries (GtkWindow *window); ++ + void gtk_window_set_icon_list (GtkWindow *window, + GList *list); + GList* gtk_window_get_icon_list (GtkWindow *window); diff --git a/xid-collision-debug.patch b/xid-collision-debug.patch new file mode 100644 index 000000000000..187ce879589d --- /dev/null +++ b/xid-collision-debug.patch @@ -0,0 +1,20 @@ + gdk/x11/gdkxid.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git c/gdk/x11/gdkxid.c i/gdk/x11/gdkxid.c +index 1005f9e40c..71578f8fcf 100644 +--- c/gdk/x11/gdkxid.c ++++ i/gdk/x11/gdkxid.c +@@ -56,10 +56,10 @@ _gdk_xid_table_insert (GdkDisplay *display, + if (!display_x11->xid_ht) + display_x11->xid_ht = g_hash_table_new ((GHashFunc) gdk_xid_hash, + (GEqualFunc) gdk_xid_equal); +- ++/* + if (g_hash_table_lookup (display_x11->xid_ht, xid)) + g_warning ("XID collision, trouble ahead"); +- ++*/ + g_hash_table_insert (display_x11->xid_ht, xid, data); + } + |