2011-09-16 Jerome Lambourg * modules/engines/ms-windows/xp_theme_defs.h: Complete definitions of CP_* enum. * modules/engines/ms-windows/xp_theme.h: Add XP_THEME_ELEMENT_COMBOBUTTON_XP and XP_THEME_ELEMENT_COMBOBOX elements to allow better theming of ComboBoxes * modules/engines/ms-windows/xp_theme.c: (xp_theme_map_gtk_state): Make the combobox react to gtk states. Change the mapping of COMBOBUTTON to CP_DROPDOWNBUTTONRIGHT when available (e.g. Version > XP) * modules/engines/ms-windows/msw_theme.c: Refine the display of Combobox: Add theming definitions for ComboBox with entries, minor fix when retrieving system metrics for combobox frames. (get_parent_combo_box): New method to retrieve the enclosing combo box. (is_combo_box_child): Simplify the implementation by using get_parent_combo_box. (draw_box): Improve the way comboboxes are drawn. (draw_shadow): Improve the way comboboxes are drawn. For K726-005, K510-021 and K915-011 2011-09-16 Jerome Lambourg * modules/engines/ms-windows/xp_theme.[ch]: Add the new element XP_THEME_ELEMENT_TAB_ITEM_BOTH_EDGE that allows drawing a notebook tab that have both edges in line with the notebook body. * modules/engines/ms-windows/msw_style.c: (get_notebook_tab_position): New helper function telling if the tab being drawn is the first one, the last one, both, or none. (draw_themed_tab_button): Simplify (and fix) the way tabs are drawn, in particular when the tab is not on top of the container. (draw_box_gap): Fix the drawing of the gap for notebooks. Submitted at https://bugzilla.gnome.org/show_bug.cgi?id=650300 2011-07-26 Nicolas Setton * gtk/gtkimcontextsimple.c (check_win32_special_cases): Fix handling of special cases for dead acute and dead diaeresis under Win32. For these characters followed by a space, emit an apostrophe and double quotes respectively. Submitted at https://bugzilla.gnome.org/show_bug.cgi?id=655357 2011-07-22 Arnaud Charlet * gdk/win32/gdkdisplay-win32.c (inner_clipboard_window_procedure): Fix build with --disable-debug 2011-05-25 Nicolas Setton * gtkimcontextsimple.c (gtk_im_context_simple_filter_keypress): Do not discard key events with state CTRL + ALT, as some keyboards do use this key combination for legitimate character input. * gdkevents-win32.c(build_key_event_state): Detect CTRL + ALT as an equivalent group to ALT_GR. For K517-001. 2011-04-27 Nicolas Setton * gdk/win32/gdkevents-win32.c (gdk_event_translate): Refine previous patch (for K307-005), propagating button press/releases to grabbed window only for events received outside of Gtk+ windows. This prevents an offset problem when dragging scroll bar sliders. For K422-021. This chunk should be considered temporary and not to be submitted for the time being. 2011-04-10 Arnaud Charlet * modules/engines/ms-windows/msw_style.c (setup_system_styles): Fix typo. Submitted at https://bugzilla.gnome.org/show_bug.cgi?id=647460 (draw_tab_button): Always use custom code to draw tab. Submitted at https://bugzilla.gnome.org/show_bug.cgi?id=647461 2011-04-06 Nicolas Setton * gdk/win32/gdkevents-win32.c (doesnt_want_button_release, doesnt_want_button_press): reintroduce subprograms. (gdk_event_translate): Before generating GDK_BUTTON_PRESS and GDK_BUTTON_RELEASE, call propagate to switch window to the window, if any, which is currently grabbing the events. Fixes K307-005. Submitted at https://bugzilla.gnome.org/show_bug.cgi?id=646930 2010-10-21 Nicolas Setton * gdk/win32/gdkevents-win32.c (generate_button_event): Remove wrong call to _gdk_event_button_generate. Fixes JA21-010. 2010-02-01 Arnaud Charlet * gdk/win32/gdkdrawable-win32.c (draw_segments): Ignore LineTo errors. 2009-09-29 Nicolas Setton * gdk/win32/gdkevents-win32.c: Protect against infinite loop in the events processing queue, in cases where the translation of events cause the same events to be regenerated. This protects against two cases of loops that have been observed under Citrix. (gdk_event_translate): Protect against reentry on move or size events that are being re-posted by Gtk+, which happens when a Citrix window receives move events but has never been mapped to a Gtk+ window. (_gdk_event_queue): After having processed all the messages, reset the reentry flags. 2009-07-20 Arnaud Charlet * configure: Work around ^M handling on cygwin with igncr 2006-06-05 Arnaud Charlet * gdk/win32/gdkgc-win32.c (_gdk_win32_gdkregion_to_hrgn): Disable harmless warning which is occurring often with Gtk+ >= 2.8.18 2002-11-25 Arnaud Charlet * gdk/win32/gdkevents-win32.c (handle_wm_paint): Force an update when paint message is received, otherwise windows in background are not immediately refreshed when moving a modal window on top of them. * gdk/Makefile.in: Remove gdk-win32res.o dependency, since we do not want to use the default Gtk+ icon. --- gdk/win32/gdkevents-win32.c.old 2009-12-22 12:37:10.468370000 +0100 +++ gdk/win32/gdkevents-win32.c 2010-01-06 17:04:42.923000000 +0100 @@ -1692,6 +1692,12 @@ handle_wm_paint (MSG *msg, _gdk_window_invalidate_for_expose (window, update_region); gdk_region_destroy (update_region); + /* Force a process_updates to refresh visible windows + * when receiving a paint message. */ + + if (!GDK_WINDOW_DESTROYED (window) && gdk_window_is_visible (window)) + gdk_window_process_updates (window, FALSE); + DeleteObject (hrgn); } --- gdk/Makefile.in.orig 2010-09-23 16:27:22.000000000 +0200 +++ gdk/Makefile.in 2010-10-08 11:06:41.724000000 +0200 @@ -664,8 +664,8 @@ libgdk_quartz_2_0_la_LIBADD = quartz/lib libgdk_quartz_2_0_la_LDFLAGS = $(LDADD) libgdk_win32_2_0_la_SOURCES = $(common_sources) gdkkeynames.c libgdk_win32_2_0_la_LIBADD = win32/libgdk-win32.la $(GDK_DEP_LIBS) -libgdk_win32_2_0_la_DEPENDENCIES = win32/libgdk-win32.la win32/rc/gdk-win32-res.o gdk.def -libgdk_win32_2_0_la_LDFLAGS = -Wl,win32/rc/gdk-win32-res.o -export-symbols $(srcdir)/gdk.def $(LDADD) +libgdk_win32_2_0_la_DEPENDENCIES = win32/libgdk-win32.la gdk.def +libgdk_win32_2_0_la_LDFLAGS = -export-symbols $(srcdir)/gdk.def $(LDADD) @HAVE_INTROSPECTION_TRUE@introspection_files = $(filter-out \ @HAVE_INTROSPECTION_TRUE@ gdkkeysyms-compat.h, \ @HAVE_INTROSPECTION_TRUE@ $(gdk_public_h_sources)) \ --- gdk/win32/gdkgc-win32.c.orig +++ gdk/win32/gdkgc-win32.c 2006-06-05 14:56:59.515625000 +0200 @@ -1160,8 +1160,7 @@ _gdk_win32_gdkregion_to_hrgn (GdkRegion if (rect->bottom > rgndata->rdh.rcBound.bottom) rgndata->rdh.rcBound.bottom = rect->bottom; } - if ((hrgn = ExtCreateRegion (NULL, nbytes, rgndata)) == NULL) - WIN32_API_FAILED ("ExtCreateRegion"); + hrgn = ExtCreateRegion (NULL, nbytes, rgndata); g_free (rgndata); --- configure.old 2009-02-23 15:54:03.650263500 +0100 +++ configure 2009-02-23 15:54:54.711263500 +0100 @@ -36969,6 +36971,7 @@ if test "$ac_cs_awk_cr" = "a${ac_cr}b"; else ac_cs_awk_cr=$ac_cr fi +ac_cs_awk_cr=$ac_cr echo 'BEGIN {' >"$tmp/subs1.awk" && _ACEOF *** gdk/win32/gdkevents-win32.c.before_patch 2009-09-22 20:06:09.000000000 +0200 --- gdk/win32/gdkevents-win32.c 2009-09-24 16:25:23.000000000 +0200 *************** static UINT client_message; *** 133,138 **** --- 133,144 ---- static UINT got_gdk_events_message; static HWND modal_win32_dialog = NULL; + /* The following variables are used to protect against re-entry in the + _gdk_event_queue main loop, which can happen under Citrix. */ + static gint activate_events = 0; + static gint move_or_size_events = 0; + static gint reentry_threshold = 3; + #if 0 static HKL latin_locale = NULL; #endif *************** gdk_event_translate (MSG *msg, *** 2324,2330 **** */ GDK_NOTE (EVENTS, g_print (" (posted)")); ! PostMessageW (msg->hwnd, msg->message, msg->wParam, msg->lParam); } else if (msg->message == WM_CREATE) { --- 2330,2340 ---- */ GDK_NOTE (EVENTS, g_print (" (posted)")); ! /* Do not post this message if we have posted it too many times ! within the same event loop. */ ! move_or_size_events += 1; ! if (move_or_size_events < reentry_threshold) ! PostMessageW (msg->hwnd, msg->message, msg->wParam, msg->lParam); } else if (msg->message == WM_CREATE) { *************** gdk_event_translate (MSG *msg, *** 3695,3701 **** if (is_modally_blocked (window) && LOWORD (msg->wParam) == WA_ACTIVE) { GdkWindow *modal_current = _gdk_modal_current (); ! SetActiveWindow (GDK_WINDOW_HWND (modal_current)); *ret_valp = 0; return_val = TRUE; break; --- 3705,3717 ---- if (is_modally_blocked (window) && LOWORD (msg->wParam) == WA_ACTIVE) { GdkWindow *modal_current = _gdk_modal_current (); ! ! /* Do not change the active window if we have done this too many ! times within the same event loop. */ ! activate_events += 1; ! if (activate_events < reentry_threshold) ! SetActiveWindow (GDK_WINDOW_HWND (modal_current)); ! *ret_valp = 0; return_val = TRUE; break; *************** _gdk_events_queue (GdkDisplay *display) *** 3775,3780 **** --- 3791,3801 ---- TranslateMessage (&msg); DispatchMessageW (&msg); } + + /* we are leaving the events queue processing: reset flags that protect + against re-entry */ + activate_events = 0; + move_or_size_events = 0; } static gboolean --- gdk/win32/gdkdrawable-win32.c.old 2010-01-31 18:48:07.679800000 +0100 +++ gdk/win32/gdkdrawable-win32.c 2010-01-31 18:48:32.429800000 +0100 @@ -1246,7 +1246,7 @@ draw_segments (GdkGCWin32 *gcwin32, GDK_NOTE (DRAW, g_print (" +%d+%d..+%d+%d", x1, y1, x2, y2)); GDI_CALL (MoveToEx, (hdc, x1, y1, NULL)) && - GDI_CALL (LineTo, (hdc, x2, y2)); + LineTo (hdc, x2, y2); } GDK_NOTE (DRAW, g_print ("\n")); --- gdk/win32/gdkevents-win32.c.old 2011-04-05 09:23:55.913000000 +0200 +++ gdk/win32/gdkevents-win32.c 2011-04-05 17:07:19.272000000 +0200 @@ -1727,8 +1734,10 @@ generate_button_event (GdkEventType type append_event (event); +#if 0 if (type == GDK_BUTTON_PRESS) _gdk_event_button_generate (_gdk_display, event); +#endif } static void --- gdk/win32/gdkevents-win32.c.original 2011-04-06 09:26:44.000000000 -0700 +++ gdk/win32/gdkevents-win32.c 2011-04-06 09:29:45.000000000 -0700 @@ -1459,6 +1459,20 @@ doesnt_want_char (gint mask, return !(mask & (GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK)); } +static gboolean +doesnt_want_button_release (gint mask, + MSG *msg) +{ + return !(mask & GDK_BUTTON_RELEASE_MASK); +} + +static gboolean +doesnt_want_button_press (gint mask, + MSG *msg) +{ + return !(mask & GDK_BUTTON_PRESS_MASK); +} + static void handle_configure_event (MSG *msg, GdkWindow *window) @@ -2284,8 +2298,17 @@ gdk_event_translate (MSG *msg, break; /* TODO_CSW? Emulate X11's automatic active grab */ - generate_button_event (GDK_BUTTON_PRESS, button, - window, msg); + + grab = _gdk_display_get_last_pointer_grab (_gdk_display); + + if (grab != NULL && + !propagate (&window, msg, + grab->window, grab->owner_events, grab->event_mask, + doesnt_want_button_press, TRUE)) + ; + else if (!GDK_WINDOW_DESTROYED (window)) + generate_button_event (GDK_BUTTON_PRESS, button, + window, msg); return_val = TRUE; break; @@ -2323,8 +2346,15 @@ gdk_event_translate (MSG *msg, } #endif - generate_button_event (GDK_BUTTON_RELEASE, button, - window, msg); + grab = _gdk_display_get_last_pointer_grab (_gdk_display); + + if (grab != NULL && + !propagate (&window, msg, + grab->window, grab->owner_events, grab->event_mask, + doesnt_want_button_release, TRUE)) + ; + else if (!GDK_WINDOW_DESTROYED (window)) + generate_button_event (GDK_BUTTON_RELEASE, button, window, msg); return_val = TRUE; break; --- modules/engines/ms-windows/msw_style.c.old +++ modules/engines/ms-windows/msw_style.c @@ -849,7 +849,7 @@ setup_system_styles (GtkStyle *style) sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_GRAYTEXT, &style->fg[GTK_STATE_INSENSITIVE]); sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNTEXT, - &style->bg[GTK_STATE_ACTIVE]); + &style->fg[GTK_STATE_ACTIVE]); sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOWTEXT, &style->fg[GTK_STATE_PRELIGHT]); @@ -2590,7 +2590,9 @@ draw_tab_button (GtkStyle *style, const gchar *detail, gint x, gint y, gint width, gint height, gint gap_side) { +#if 0 if (gap_side == GTK_POS_TOP || gap_side == GTK_POS_BOTTOM) +#endif { /* experimental tab-drawing code from mozilla */ RECT rect; --- gdk/win32/gdkevents-win32.c.intermediate 2011-04-26 16:44:14.000000000 +0200 +++ gdk/win32/gdkevents-win32.c 2011-04-27 13:54:12.000000000 +0200 @@ -2301,7 +2301,8 @@ gdk_event_translate (MSG *msg, grab = _gdk_display_get_last_pointer_grab (_gdk_display); - if (grab != NULL && + if (window == _gdk_root && + grab != NULL && !propagate (&window, msg, grab->window, grab->owner_events, grab->event_mask, doesnt_want_button_press, TRUE)) @@ -2348,7 +2349,8 @@ gdk_event_translate (MSG *msg, grab = _gdk_display_get_last_pointer_grab (_gdk_display); - if (grab != NULL && + if (window == _gdk_root && + grab != NULL && !propagate (&window, msg, grab->window, grab->owner_events, grab->event_mask, doesnt_want_button_release, TRUE)) --- gdk/win32/gdkevents-win32.c.orig 2011-05-25 12:25:29.000000000 +0200 +++ gdk/win32/gdkevents-win32.c 2011-05-25 12:26:52.000000000 +0200 @@ -679,6 +679,9 @@ build_key_event_state (GdkEvent *event, event->key.state |= GDK_CONTROL_MASK; if (key_state[VK_MENU] & 0x80) event->key.state |= GDK_MOD1_MASK; + + if ((key_state[VK_CONTROL] & 0x80) && (key_state[VK_MENU] & 0x80)) + event->key.group = 1; } } --- gdk/win32/gdkdisplay-win32.c.old 2011-07-22 12:06:42.618000000 +0200 +++ gdk/win32/gdkdisplay-win32.c 2011-07-22 12:07:29.311000000 +0200 @@ -386,7 +386,7 @@ inner_clipboard_window_procedure (HWND if (_gdk_debug_flags & GDK_DEBUG_DND) { while ((nFormat = EnumClipboardFormats (nFormat)) != 0) - g_print ("%s ", _gdk_win32_cf_to_string (nFormat)); + GDK_NOTE (DND, g_print ("%s ", _gdk_win32_cf_to_string (nFormat))); } GDK_NOTE (DND, g_print (" \n")); --- gtk/gtkimcontextsimple.c.original 2011-07-26 15:59:06.000000000 +0200 +++ gtk/gtkimcontextsimple.c 2011-07-26 16:00:44.000000000 +0200 @@ -302,9 +302,9 @@ check_win32_special_cases (GtkIMContextS switch (context_simple->compose_buffer[0]) { case GDK_dead_acute: - value = 0x00B4; break; + value = 0x0027; break; case GDK_dead_diaeresis: - value = 0x00A8; break; + value = 0x0022; break; } if (value > 0) { diff --git modules/engines/ms-windows/msw_style.c modules/engines/ms-windows/msw_style.c index 38bc1e6..b060de2 100644 --- modules/engines/ms-windows/msw_style.c +++ modules/engines/ms-windows/msw_style.c @@ -41,7 +41,7 @@ #include #include -#include "gtk/gtk.h" +#include "gdk/gdk.h" #include "gtk/gtk.h" #ifdef BUILDING_STANDALONE @@ -2347,6 +2347,68 @@ DrawTab (HDC hdc, const RECT R, gint32 aPosition, gboolean aSelected, DrawEdge (hdc, &shadeRect, EDGE_RAISED, BF_SOFT | shadeFlag); } +static void +get_notebook_tab_position (GtkNotebook *notebook, gboolean *start, gboolean *end) +{ + /* default value */ + *start = TRUE; + *end = FALSE; + + gboolean found_tabs = FALSE; + gint i, n_pages; + + n_pages = gtk_notebook_get_n_pages (notebook); + for (i = 0; i < n_pages; i++) { + GtkWidget *tab_child; + GtkWidget *tab_label; + gboolean expand; + GtkPackType pack_type; + + tab_child = gtk_notebook_get_nth_page (notebook, i); + + /* Skip invisible tabs */ + tab_label = gtk_notebook_get_tab_label (notebook, tab_child); + if (!tab_label || !GTK_WIDGET_VISIBLE (tab_label)) + continue; + /* This is the same what the notebook does internally. */ + if (tab_label && !gtk_widget_get_child_visible (tab_label)) { + /* One child is hidden because scroll arrows are present. + * So both corners are rounded. */ + *start = FALSE; + *end = FALSE; + return; + } + + gtk_notebook_query_tab_label_packing + (notebook, tab_child, + &expand, + NULL, /* don't need fill */ + &pack_type); + + if (!found_tabs) { + // First tab found + found_tabs = TRUE; + if (gtk_notebook_get_current_page (notebook) == i) { + *start = TRUE; + } else { + *start = FALSE; + } + } + + if (gtk_notebook_get_current_page (notebook) == i) { + if (expand) { + *end = TRUE; + } else { + *end = FALSE; + return; + } + } else { + *end = FALSE; + return; + } + } +} + static gboolean draw_themed_tab_button (GtkStyle *style, GdkWindow *window, @@ -2356,221 +2418,126 @@ draw_themed_tab_button (GtkStyle *style, gint width, gint height, gint gap_side) { GdkPixmap *pixmap = NULL; - gint border_width = - gtk_container_get_border_width (GTK_CONTAINER (notebook)); - GtkWidget *widget = GTK_WIDGET (notebook); GdkRectangle draw_rect, clip_rect; - GdkPixbufRotation rotation = GDK_PIXBUF_ROTATE_NONE; cairo_t *cr; - - if (gap_side == GTK_POS_TOP) - { - int widget_right; - - if (state_type == GTK_STATE_NORMAL) - { - draw_rect.x = x; - draw_rect.y = y; - draw_rect.width = width + 2; - draw_rect.height = height; - - clip_rect = draw_rect; - clip_rect.height--; - } - else - { - draw_rect.x = x + 2; - draw_rect.y = y; - draw_rect.width = width - 2; - draw_rect.height = height - 2; - clip_rect = draw_rect; - } - - /* If we are currently drawing the right-most tab, and if that tab is the selected tab... */ - widget_right = widget->allocation.x + widget->allocation.width - border_width - 2; - - if (draw_rect.x + draw_rect.width >= widget_right) - { - draw_rect.width = clip_rect.width = widget_right - draw_rect.x; - } - } - if (gap_side == GTK_POS_BOTTOM) - { - int widget_right; - - if (state_type == GTK_STATE_NORMAL) - { - draw_rect.x = x; - draw_rect.y = y; - draw_rect.width = width + 2; - draw_rect.height = height; - - clip_rect = draw_rect; - } - else - { - draw_rect.x = x + 2; - draw_rect.y = y + 2; - draw_rect.width = width - 2; - draw_rect.height = height - 2; - clip_rect = draw_rect; - } - - /* If we are currently drawing the right-most tab, and if that tab is the selected tab... */ - widget_right = widget->allocation.x + widget->allocation.width - border_width - 2; - - if (draw_rect.x + draw_rect.width >= widget_right) - { - draw_rect.width = clip_rect.width = widget_right - draw_rect.x; - } - - rotation = GDK_PIXBUF_ROTATE_UPSIDEDOWN; + gboolean start, stop; + XpThemeElement element; + GdkPixbuf *pixbuf; + GdkPixbuf *rotated; + + get_notebook_tab_position (notebook, &start, &stop); + if (start && stop) { + // Both edges of the notebook are covered by the item + element = XP_THEME_ELEMENT_TAB_ITEM_BOTH_EDGE; + } else if (start) { + // The start edge is covered by the item + element = XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE; + } else if (stop) { + // the stop edge is reached by the item + element = XP_THEME_ELEMENT_TAB_ITEM_RIGHT_EDGE; + } else { + // no edge should be aligned with the tab + element = XP_THEME_ELEMENT_TAB_ITEM; + } + + if (state_type == GTK_STATE_ACTIVE) { + switch (gap_side) { + case GTK_POS_TOP: + y -= 1; + break; + case GTK_POS_BOTTOM: + y += 1; + break; + case GTK_POS_LEFT: + x -= 1; + break; + case GTK_POS_RIGHT: + x += 1; + break; } - else if (gap_side == GTK_POS_LEFT) - { - int widget_bottom; + } - if (state_type == GTK_STATE_NORMAL) - { - draw_rect.x = x; - draw_rect.y = y; - draw_rect.width = width; - draw_rect.height = height + 2; + draw_rect.x = x; + draw_rect.y = y; + draw_rect.width = width; + draw_rect.height = height; + clip_rect = draw_rect; - clip_rect = draw_rect; - clip_rect.width--; - } - else - { - draw_rect.x = x; - draw_rect.y = y + 2; - draw_rect.width = width - 2; - draw_rect.height = height - 2; - clip_rect = draw_rect; - } - - /* If we are currently drawing the bottom-most tab, and if that tab is the selected tab... */ - widget_bottom = widget->allocation.x + widget->allocation.height - border_width - 2; - - if (draw_rect.y + draw_rect.height >= widget_bottom) - { - draw_rect.height = clip_rect.height = widget_bottom - draw_rect.y; - } - - rotation = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE; - } - else if (gap_side == GTK_POS_RIGHT) + /* Take care of obvious case where the cliping is an empty region */ + if (clip_rect.width <= 0 || clip_rect.height <= 0) + return TRUE; + + /* Simple case: tabs on top are just drawn as is */ + if (gap_side == GTK_POS_TOP) { - int widget_bottom; - - if (state_type == GTK_STATE_NORMAL) - { - draw_rect.x = x + 1; - draw_rect.y = y; - draw_rect.width = width; - draw_rect.height = height + 2; - - clip_rect = draw_rect; - clip_rect.width--; - } - else - { - draw_rect.x = x + 2; - draw_rect.y = y + 2; - draw_rect.width = width - 2; - draw_rect.height = height - 2; - clip_rect = draw_rect; - } - - /* If we are currently drawing the bottom-most tab, and if that tab is the selected tab... */ - widget_bottom = widget->allocation.x + widget->allocation.height - border_width - 2; - - if (draw_rect.y + draw_rect.height >= widget_bottom) - { - draw_rect.height = clip_rect.height = widget_bottom - draw_rect.y; - } - - rotation = GDK_PIXBUF_ROTATE_CLOCKWISE; + return xp_theme_draw + (window, element, style, + draw_rect.x, draw_rect.y, + draw_rect.width, draw_rect.height, + state_type, &clip_rect); } - if (gap_side == GTK_POS_TOP) + /* For other cases, we need to print the tab on a pixmap, and then rotate + it according to the gap side */ + if (gap_side == GTK_POS_LEFT || gap_side == GTK_POS_RIGHT) { - if (!xp_theme_draw (window, XP_THEME_ELEMENT_TAB_ITEM, style, - draw_rect.x, draw_rect.y, - draw_rect.width, draw_rect.height, - state_type, &clip_rect)) + /* pixmap will have width/height inverted as we'll rotate +- PI / 2 */ + pixmap = gdk_pixmap_new (window, clip_rect.height, clip_rect.width, -1); + + if (!xp_theme_draw (pixmap, element, style, + draw_rect.y - clip_rect.y, draw_rect.x - clip_rect.x, + draw_rect.height, draw_rect.width, state_type, 0)) { + g_object_unref (pixmap); return FALSE; } + + pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, NULL, 0, 0, 0, 0, + clip_rect.height, clip_rect.width); + g_object_unref (pixmap); } else { - GdkPixbuf *pixbuf; - GdkPixbuf *rotated; + pixmap = gdk_pixmap_new (window, clip_rect.width, clip_rect.height, -1); - if (gap_side == GTK_POS_LEFT || gap_side == GTK_POS_RIGHT) + if (!xp_theme_draw (pixmap, element, style, + draw_rect.x - clip_rect.x, draw_rect.y - clip_rect.y, + draw_rect.width, draw_rect.height, state_type, 0)) { - pixmap = gdk_pixmap_new (window, clip_rect.height, clip_rect.width, -1); - - if (!xp_theme_draw (pixmap, XP_THEME_ELEMENT_TAB_ITEM, style, - draw_rect.y - clip_rect.y, draw_rect.x - clip_rect.x, - draw_rect.height, draw_rect.width, state_type, 0)) - { - g_object_unref (pixmap); - return FALSE; - } - - pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, NULL, 0, 0, 0, 0, - clip_rect.height, clip_rect.width); - g_object_unref (pixmap); - } - else - { - pixmap = gdk_pixmap_new (window, clip_rect.width, clip_rect.height, -1); - - if (!xp_theme_draw (pixmap, XP_THEME_ELEMENT_TAB_ITEM, style, - draw_rect.x - clip_rect.x, draw_rect.y - clip_rect.y, - draw_rect.width, draw_rect.height, state_type, 0)) - { - g_object_unref (pixmap); - return FALSE; - } - - pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, NULL, 0, 0, 0, 0, - clip_rect.width, clip_rect.height); g_object_unref (pixmap); + return FALSE; } - rotated = gdk_pixbuf_rotate_simple (pixbuf, rotation); - g_object_unref (pixbuf); - pixbuf = rotated; + pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, NULL, 0, 0, 0, 0, + clip_rect.width, clip_rect.height); + g_object_unref (pixmap); + } - // XXX - This is really hacky and evil. When we're drawing the left-most tab - // while it is active on a bottom-oriented notebook, there is one white - // pixel at the top. There may be a better solution than this if someone - // has time to discover it. - if (gap_side == GTK_POS_BOTTOM && state_type == GTK_STATE_NORMAL - && x == widget->allocation.x) - { - int rowstride = gdk_pixbuf_get_rowstride (pixbuf); - int n_channels = gdk_pixbuf_get_n_channels (pixbuf); - int psub = 0; + if (pixbuf == NULL) return FALSE; - guchar *pixels = gdk_pixbuf_get_pixels (pixbuf); - guchar *p = pixels + rowstride; + /* At this point, we have a pixbuf with the tab printed on it. Let's rotate + it and then print it on screen */ - for (psub = 0; psub < n_channels; psub++) - { - pixels[psub] = p[psub]; - } - } + /* The following transformations are performed: + BOTTOM: just flip horizontally + LEFT: flip horizontaly, then rotate clockwise + RIGHT: rotate clockwise */ + if (gap_side == GTK_POS_BOTTOM || gap_side == GTK_POS_LEFT) { + rotated = gdk_pixbuf_flip (pixbuf, FALSE); + g_object_unref (pixbuf); + pixbuf = rotated; + } + if (gap_side == GTK_POS_LEFT || gap_side == GTK_POS_RIGHT) { + rotated = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE); + g_object_unref (pixbuf); + pixbuf = rotated; + } - cr = gdk_cairo_create (window); - gdk_cairo_set_source_pixbuf (cr, pixbuf, clip_rect.x, clip_rect.y); - cairo_paint (cr); - cairo_destroy (cr); - g_object_unref (pixbuf); - } + cr = gdk_cairo_create (window); + gdk_cairo_set_source_pixbuf (cr, pixbuf, clip_rect.x, clip_rect.y); + cairo_paint (cr); + cairo_destroy (cr); + g_object_unref (pixbuf); return TRUE; } @@ -2676,36 +2643,22 @@ draw_box_gap (GtkStyle *style, GdkWindow *window, GtkStateType state_type, { GtkNotebook *notebook = GTK_NOTEBOOK (widget); int side = gtk_notebook_get_tab_pos (notebook); - int x2 = x, y2 = y, w2 = width, h2 = height; - - if (side == GTK_POS_TOP) - { - x2 = x; - y2 = y - gtk_notebook_get_tab_vborder (notebook); - w2 = width; - h2 = height + gtk_notebook_get_tab_vborder (notebook) * 2; - } - else if (side == GTK_POS_BOTTOM) - { - x2 = x; - y2 = y; - w2 = width; - h2 = height + gtk_notebook_get_tab_vborder (notebook) * 2; - } - else if (side == GTK_POS_LEFT) - { - x2 = x - gtk_notebook_get_tab_hborder (notebook); - y2 = y; - w2 = width + gtk_notebook_get_tab_hborder (notebook); - h2 = height; - } - else if (side == GTK_POS_RIGHT) - { - x2 = x; - y2 = y; - w2 = width + gtk_notebook_get_tab_hborder (notebook) * 2; - h2 = height; - } + int x2 = x, y2 = y; + int w2 = width + style->xthickness, h2 = height + style->ythickness; + + switch (side) { + case GTK_POS_TOP: + y2 -= 1; + break; + case GTK_POS_BOTTOM: + break; + case GTK_POS_LEFT: + x2 -= 1; + break; + case GTK_POS_RIGHT: + w2 += 1; + break; + } if (xp_theme_draw (window, XP_THEME_ELEMENT_TAB_PANE, style, x2, y2, w2, h2, state_type, area)) diff --git modules/engines/ms-windows/xp_theme.c modules/engines/ms-windows/xp_theme.c index bdd4240..48fb594 100644 --- modules/engines/ms-windows/xp_theme.c +++ modules/engines/ms-windows/xp_theme.c @@ -121,6 +121,7 @@ static const short element_part_map[XP_THEME_ELEMENT__SIZEOF] = { TABP_TABITEM, TABP_TABITEMLEFTEDGE, TABP_TABITEMRIGHTEDGE, + TABP_TABITEMBOTHEDGE, TABP_PANE, SBP_THUMBBTNHORZ, SBP_THUMBBTNVERT, @@ -408,6 +409,7 @@ xp_theme_get_handle_by_element (XpThemeElement element) case XP_THEME_ELEMENT_TAB_ITEM: case XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE: case XP_THEME_ELEMENT_TAB_ITEM_RIGHT_EDGE: + case XP_THEME_ELEMENT_TAB_ITEM_BOTH_EDGE: case XP_THEME_ELEMENT_TAB_PANE: klazz = XP_THEME_CLASS_TAB; break; @@ -536,6 +538,7 @@ xp_theme_map_gtk_state (XpThemeElement element, GtkStateType state) case XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE: case XP_THEME_ELEMENT_TAB_ITEM_RIGHT_EDGE: + case XP_THEME_ELEMENT_TAB_ITEM_BOTH_EDGE: case XP_THEME_ELEMENT_TAB_ITEM: switch (state) { diff --git modules/engines/ms-windows/xp_theme.h modules/engines/ms-windows/xp_theme.h index dfacb43..33a56b3 100644 --- modules/engines/ms-windows/xp_theme.h +++ modules/engines/ms-windows/xp_theme.h @@ -59,6 +59,7 @@ typedef enum XP_THEME_ELEMENT_TAB_ITEM, XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE, XP_THEME_ELEMENT_TAB_ITEM_RIGHT_EDGE, + XP_THEME_ELEMENT_TAB_ITEM_BOTH_EDGE, XP_THEME_ELEMENT_TAB_PANE, XP_THEME_ELEMENT_SCROLLBAR_H, XP_THEME_ELEMENT_SCROLLBAR_V, diff --git modules/engines/ms-windows/xp_theme.c modules/engines/ms-windows/xp_theme.c index 48fb594..ede814d 100644 --- modules/engines/ms-windows/xp_theme.c +++ modules/engines/ms-windows/xp_theme.c @@ -117,6 +117,8 @@ static const short element_part_map[XP_THEME_ELEMENT__SIZEOF] = { BP_PUSHBUTTON, HP_HEADERITEM, CP_DROPDOWNBUTTON, + CP_DROPDOWNBUTTONRIGHT, + CP_READONLY, TABP_BODY, TABP_TABITEM, TABP_TABITEMLEFTEDGE, @@ -402,6 +404,8 @@ xp_theme_get_handle_by_element (XpThemeElement element) break; case XP_THEME_ELEMENT_COMBOBUTTON: + case XP_THEME_ELEMENT_COMBOBUTTON_XP: + case XP_THEME_ELEMENT_COMBOBOX: klazz = XP_THEME_CLASS_COMBOBOX; break; @@ -532,6 +536,29 @@ xp_theme_map_gtk_state (XpThemeElement element, GtkStateType state) } break; + case XP_THEME_ELEMENT_COMBOBUTTON: + case XP_THEME_ELEMENT_COMBOBUTTON_XP: + case XP_THEME_ELEMENT_COMBOBOX: + switch (state) + { + case GTK_STATE_SELECTED: + case GTK_STATE_ACTIVE: + ret = TS_PRESSED; + break; + + case GTK_STATE_PRELIGHT: + ret = TS_HOT; + break; + + case GTK_STATE_INSENSITIVE: + ret = TS_DISABLED; + break; + + default: + ret = TS_NORMAL; + } + break; + case XP_THEME_ELEMENT_TAB_PANE: ret = 1; break; diff --git modules/engines/ms-windows/xp_theme.h modules/engines/ms-windows/xp_theme.h index 33a56b3..621239c 100644 --- modules/engines/ms-windows/xp_theme.h +++ modules/engines/ms-windows/xp_theme.h @@ -54,7 +54,9 @@ typedef enum XP_THEME_ELEMENT_INCONSISTENT_CHECKBOX, XP_THEME_ELEMENT_BUTTON, XP_THEME_ELEMENT_LIST_HEADER, + XP_THEME_ELEMENT_COMBOBUTTON_XP, XP_THEME_ELEMENT_COMBOBUTTON, + XP_THEME_ELEMENT_COMBOBOX, XP_THEME_ELEMENT_BODY, XP_THEME_ELEMENT_TAB_ITEM, XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE, diff --git modules/engines/ms-windows/xp_theme_defs.h modules/engines/ms-windows/xp_theme_defs.h index aaae6d9..e933689 100644 --- modules/engines/ms-windows/xp_theme_defs.h +++ modules/engines/ms-windows/xp_theme_defs.h @@ -89,7 +89,14 @@ enum { }; enum { - CP_DROPDOWNBUTTON = 1 + CP_DROPDOWNBUTTON = 1, + CP_BACKGROUND = 2, + CP_TRANSPARENTBACKGROUND = 3, + CP_BORDER = 4, + CP_READONLY = 5, + CP_DROPDOWNBUTTONRIGHT = 6, + CP_DROPDOWNBUTTONLEFT = 7, + CP_CUEBANNER = 8 }; enum { diff --git modules/engines/ms-windows/msw_style.c modules/engines/ms-windows/msw_style.c index b060de2..1f245c2 100644 --- modules/engines/ms-windows/msw_style.c +++ modules/engines/ms-windows/msw_style.c @@ -752,6 +752,18 @@ setup_msw_rc_style (void) gtk_rc_parse_string (buf); g_snprintf (buf, sizeof (buf), + "style \"msw-combobox-entry\" = \"msw-default\"\n" + "{\n" + "xthickness = 1\n" + "ythickness = 1\n" + "GtkWidget::focus-padding = 0\n" + "GtkWidget::focus-line-width = 0\n" + "GtkEntry::inner-border = { 4, 0, 0, 0 }\n" + "}\n" + "widget_class \"*ComboBox*GtkEntry*\" style \"msw-combobox-entry\"\n"); + gtk_rc_parse_string (buf); + + g_snprintf (buf, sizeof (buf), "style \"msw-combobox\" = \"msw-default\"\n" "{\n" "GtkComboBox::shadow-type = in\n" @@ -759,8 +771,8 @@ setup_msw_rc_style (void) "ythickness = %d\n" "}\n" "class \"GtkComboBox\" style \"msw-combobox\"\n", - xp_theme_is_active()? 1 : GetSystemMetrics (SM_CXEDGE), - xp_theme_is_active()? 1 : GetSystemMetrics (SM_CYEDGE)); + !xp_theme_is_active()? 1 : GetSystemMetrics (SM_CXEDGE), + !xp_theme_is_active()? 1 : GetSystemMetrics (SM_CYEDGE)); gtk_rc_parse_string (buf); /* size of tree view header */ @@ -919,21 +931,27 @@ map_gtk_progress_bar_to_xp (GtkProgressBar *progress_bar, gboolean trough) return ret; } -static gboolean -is_combo_box_child (GtkWidget *w) +static GtkComboBox* +get_parent_combo_box (GtkWidget *w) { GtkWidget *tmp; if (w == NULL) - return FALSE; + return NULL; for (tmp = w->parent; tmp; tmp = tmp->parent) { if (GTK_IS_COMBO_BOX (tmp)) - return TRUE; + return GTK_COMBO_BOX (tmp); } - return FALSE; + return NULL; +} + +static gboolean +is_combo_box_child (GtkWidget *w) +{ + return get_parent_combo_box (w) != NULL; } /* This function is not needed anymore */ @@ -1807,32 +1825,41 @@ draw_box (GtkStyle *style, { if (is_combo_box_child (widget) && detail && !strcmp (detail, "button")) { - RECT rect; - XpDCInfo dc_info; - DWORD border; - HDC dc; - int cx; - - border = (GTK_TOGGLE_BUTTON (widget)->active ? DFCS_PUSHED | DFCS_FLAT : 0); - - dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect); - DrawFrameControl (dc, &rect, DFC_SCROLL, DFCS_SCROLLDOWN | border); - release_window_dc (&dc_info); - - if (xp_theme_is_active () - && xp_theme_draw (window, XP_THEME_ELEMENT_COMBOBUTTON, style, x, y, - width, height, state_type, area)) - { - cx = GetSystemMetrics(SM_CXVSCROLL); - x += width - cx; - width = cx; + GtkComboBox *combo = get_parent_combo_box (widget); + gboolean old_style = get_windows_version () <= WINXP_VERSION; + GtkWidget *parent = gtk_widget_get_parent (widget); + XpThemeElement element; + gboolean appears_as_list; + gtk_widget_style_get + (GTK_WIDGET (combo), + "appears-as-list", &appears_as_list, + NULL); + + if (gtk_combo_box_get_wrap_width (combo) || !appears_as_list) + { + /* The button covers the whole area in this case: let's keep it on + the right */ + int cx = GetSystemMetrics(SM_CXVSCROLL); + x += width - cx; + width = cx; + } - dc = get_window_dc (style, window, state_type, &dc_info, x, y, width - cx, height, &rect); - FillRect (dc, &rect, GetSysColorBrush (COLOR_WINDOW)); - release_window_dc (&dc_info); - return; - } + /* We need to draw the button just above the parent frame. We thus + need to increase the size of the box according to the frame's + thickness */ + y -= gtk_widget_get_style (parent)->ythickness; + width += gtk_widget_get_style (parent)->xthickness; + height += 2 * gtk_widget_get_style (parent)->ythickness; + + /* This draws the button on the right of a combobox. On XP and earlier, + the underlying combobutton style is not available, so we fallback to + a compatible element */ + element = old_style ? XP_THEME_ELEMENT_COMBOBUTTON_XP : XP_THEME_ELEMENT_COMBOBUTTON; + if (xp_theme_draw (window, element, style, x, y, width, height, state_type, area)) + { + return; + } } if (detail && @@ -2765,21 +2792,31 @@ draw_shadow (GtkStyle *style, gboolean is_handlebox; gboolean is_toolbar; - if (detail && !strcmp (detail, "frame")) + if (is_combo_box_child (widget) || (detail && !strcmp (detail, "combobox"))) + { + /* entries inside a combobox do not need frames */ + if (detail && !strcmp (detail, "entry")) return; + + gboolean old_style = get_windows_version () <= WINXP_VERSION; + XpThemeElement element; + + /* This is the background element of a combobox */ + /* Combo box theme elements are not available in XP and older: in this + case we use a simple button element */ + + element = old_style ? XP_THEME_ELEMENT_BUTTON : XP_THEME_ELEMENT_COMBOBOX; + if (xp_theme_draw (window, element, style, x, y, width, height, state_type, area)) + return; + } + else if (detail && !strcmp (detail, "frame")) { HDC dc; RECT rect; XpDCInfo dc_info; - - dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect); - if (is_combo_box_child (widget)) - { - FillRect (dc, &rect, GetSysColorBrush (COLOR_WINDOW)); - } - else if (is_popup_window_child (widget)) + if (is_popup_window_child (widget)) { FrameRect (dc, &rect, GetSysColorBrush (COLOR_WINDOWFRAME)); } @@ -2816,7 +2853,8 @@ draw_shadow (GtkStyle *style, return; } - if (detail && (!strcmp (detail, "entry") || !strcmp (detail, "combobox"))) + + if (detail && !strcmp (detail, "entry")) { if (shadow_type != GTK_SHADOW_IN) return;