diff --git a/configure.ac b/configure.ac index 29297423f..f4aa7b84c 100644 --- a/configure.ac +++ b/configure.ac @@ -399,6 +399,11 @@ PKG_CHECK_MODULES(BASE_DEPENDENCIES, gdk-pixbuf-2.0 >= gdk_pixbuf_required_version dnl fribidi >= fribidi_required_version]) +PKG_CHECK_EXISTS([atk >= 2.12], + [AC_DEFINE([HAVE_ATK_VALUE_2_12], [1], + [Define if ATK provides the 2.12 AtkValue interface])]) + + ## In addition to checking that cairo is present, we also need to ## check that the correct cairo backend is there. E.g. if the GDK ## target is win32 we need the cairo-win32 backend and so on. @@ -469,7 +474,7 @@ if test "x$enable_rebuilds" = "xyes" && \ fi AC_SUBST(REBUILD) -AC_CHECK_FUNCS(lstat mkstemp flockfile getc_unlocked) +AC_CHECK_FUNCS(lstat mkstemp flockfile getc_unlocked _getc_nolock) AC_CHECK_FUNCS(localtime_r) # _NL_TIME_FIRST_WEEKDAY is an enum and not a define @@ -1392,14 +1397,39 @@ CFLAGS="$CFLAGS $PANGO_CFLAGS" if $PKG_CONFIG --uninstalled $PANGO_PACKAGES; then : else - gtk_save_LIBS="$LIBS" + gtk_save_LIBS="$LIBS" LIBS="$PANGO_LIBS $LIBS" + AC_LINK_IFELSE( [AC_LANG_CALL([], [pango_context_new])], [:], - [AC_MSG_ERROR([ + [gtk_pango_rpath_link= + + if test "x$os_linux" = xyes; then + for gtk_pango_flag in $LDFLAGS $PANGO_LIBS; do + case "$gtk_pango_flag" in + -L/*) + gtk_pango_rpath_link="$gtk_pango_rpath_link -Wl,-rpath-link,${gtk_pango_flag#-L}" + ;; + esac + done + fi + + if test -n "$gtk_pango_rpath_link"; then + LDFLAGS="$LDFLAGS $gtk_pango_rpath_link" + + AC_LINK_IFELSE( + [AC_LANG_CALL([], [pango_context_new])], + [:], + [gtk_pango_rpath_link=]) + fi + + if test -z "$gtk_pango_rpath_link"; then + AC_MSG_ERROR([ *** Can't link to Pango. Pango is required to build -*** GTK+. For more information see http://www.pango.org])]) +*** GTK+. For more information see http://www.pango.org]) + fi]) + LIBS="$gtk_save_LIBS" fi @@ -1533,6 +1563,11 @@ LIBS="$old_LIBS" # Printing system checks ################################################################ +AC_ARG_ENABLE(cups-secret-service, + [AS_HELP_STRING([--disable-cups-secret-service] + [disable Secret Service support in the cups print backend])],, + [enable_cups_secret_service=yes]) + AC_ARG_ENABLE(cups, [AS_HELP_STRING([--disable-cups] [disable cups print backend])],, @@ -1579,6 +1614,17 @@ else AM_CONDITIONAL(HAVE_CUPS, true) + if test "x$enable_cups_secret_service" != "xno"; then + PKG_CHECK_EXISTS([libsecret-1], [], + [AC_MSG_ERROR([ +*** libsecret-1 is required when CUPS Secret Service support is enabled. +*** Install the libsecret development files or configure +*** with --disable-cups-secret-service. +])]) + AC_DEFINE([HAVE_CUPS_SECRET_SERVICE], [1], + [Define to 1 if CUPS Secret Service support is enabled]) + fi + gtk_save_cflags="$CFLAGS" CFLAGS="$CUPS_CFLAGS" AC_COMPILE_IFELSE( @@ -1594,6 +1640,7 @@ else gtk_save_libs="$LIBS" LIBS="$CUPS_LIBS" AC_CHECK_FUNCS(httpGetAuthString) + AC_CHECK_FUNCS(httpConnect2 httpReconnect2) LIBS="$gtk_save_libs" fi fi diff --git a/docs/reference/gdk/Makefile.am b/docs/reference/gdk/Makefile.am index c0facc151..43a67508c 100644 --- a/docs/reference/gdk/Makefile.am +++ b/docs/reference/gdk/Makefile.am @@ -37,7 +37,7 @@ IGNORE_HFILES= \ # Extra files to add when scanning (relative to $srcdir) EXTRA_HFILES= \ - ../../../gdk/x11/gdkx.h + $(top_srcdir)/gdk/x11/gdkx.h # CFLAGS and LDFLAGS for compiling scan program. Only needed # if $(DOC_MODULE).types is non-empty. diff --git a/docs/reference/gdk/gdk2-sections.txt b/docs/reference/gdk/gdk2-sections.txt index c3d6040b7..4987c31ee 100644 --- a/docs/reference/gdk/gdk2-sections.txt +++ b/docs/reference/gdk/gdk2-sections.txt @@ -335,6 +335,10 @@ gdk_draw_indexed_image gdk_draw_gray_image gdk_draw_rgb_32_image gdk_draw_rgb_32_image_dithalign +gdk_draw_rgb_48_image +gdk_draw_rgb_48_image_dithalign +gdk_draw_rgbx_64_image +gdk_draw_rgbx_64_image_dithalign GdkRgbDither diff --git a/gdk/Makefile.am b/gdk/Makefile.am index 1b186afa1..2b68f98a6 100644 --- a/gdk/Makefile.am +++ b/gdk/Makefile.am @@ -145,6 +145,7 @@ gdk_c_sources = \ gdkrectangle.c \ gdkregion-generic.c \ gdkrgb.c \ + gdkrgb16.c \ gdkscreen.c \ gdkselection.c \ gdkvisual.c \ @@ -213,6 +214,11 @@ Gdk_2_0_gir_CFLAGS = \ -I$(top_builddir) \ -I$(top_builddir)/gdk \ $(INCLUDES) + +if OS_WIN32 +Gdk_2_0_gir_CFLAGS += -I$(srcdir)/win32 -I$(srcdir) +endif + INTROSPECTION_GIRS += Gdk-2.0.gir if USE_X11 @@ -268,6 +274,69 @@ introspection_files += $(filter-out x11/gdkx.h, $(x11_introspection_files)) endif # USE_X11 +if OS_WIN32 +win32_introspection_files = \ + win32/gdkapplaunchcontext-win32.c \ + win32/gdkcolor-win32.c \ + win32/gdkcursor-win32.c \ + win32/gdkdisplay-win32.c \ + win32/gdkdnd-win32.c \ + win32/gdkdrawable-win32.c \ + win32/gdkdrawable-win32.h \ + win32/gdkevents-win32.c \ + win32/gdkfont-win32.c \ + win32/gdkgc-win32.c \ + win32/gdkgeometry-win32.c \ + win32/gdkglobals-win32.c \ + win32/gdkimage-win32.c \ + win32/gdkim-win32.c \ + win32/gdkinput.c \ + win32/gdkinput-win32.c \ + win32/gdkinput-win32.h \ + win32/gdkkeys-win32.c \ + win32/gdkmain-win32.c \ + win32/gdkpixmap-win32.c \ + win32/gdkpixmap-win32.h \ + win32/gdkprivate-win32.h \ + win32/gdkproperty-win32.c \ + win32/gdkscreen-win32.c \ + win32/gdkselection-win32.c \ + win32/gdkspawn-win32.c \ + win32/gdktestutils-win32.c \ + win32/gdkvisual-win32.c \ + win32/gdkwin32.h \ + win32/gdkwin32id.c \ + win32/gdkwin32keys.h \ + win32/gdkwindow-win32.c \ + win32/gdkwindow-win32.h + +GdkWin32-2.0.gir: $(gdktargetlib) Gdk-2.0.gir Makefile +GdkWin32_2_0_gir_SCANNERFLAGS = --identifier-prefix=Gdk --symbol-prefix=gdk --include-uninstalled=Gdk-2.0.gir +GdkWin32_2_0_gir_INCLUDES = Gio-2.0 GdkPixbuf-2.0 Pango-1.0 +GdkWin32_2_0_gir_LIBS = $(gdktargetlib) +GdkWin32_2_0_gir_FILES = $(filter-out \ + win32/gdkdrawable-win32.h \ + win32/gdkinput-win32.h \ + win32/gdkpixmap-win32.h \ + win32/gdkprivate-win32.h \ + win32/gdkwindow-win32.h, \ + $(win32_introspection_files)) +GdkWin32_2_0_gir_EXPORT_PACKAGES = gdk-win32-2.0 +GdkWin32_2_0_gir_CFLAGS = \ + -DGDK_COMPILATION \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + -I$(top_builddir)/gdk \ + -I$(srcdir)/win32 \ + -I$(srcdir) \ + $(INCLUDES) \ + -L$(top_builddir)/gdk +INTROSPECTION_GIRS += GdkWin32-2.0.gir + +introspection_files += $(filter-out win32/gdkwin32.h, $(win32_introspection_files)) + +endif # OS_WIN32 + girdir = $(datadir)/gir-1.0 gir_DATA = $(INTROSPECTION_GIRS) diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols index ee1bc0c98..b48eac70b 100644 --- a/gdk/gdk.symbols +++ b/gdk/gdk.symbols @@ -1118,6 +1118,15 @@ gdk_draw_rgb_image_dithalign #endif #endif +#if IN_HEADER(__GDK_RGB_H__) +#if IN_FILE(__GDK_RGB16_C__) +gdk_draw_rgb_48_image +gdk_draw_rgb_48_image_dithalign +gdk_draw_rgbx_64_image +gdk_draw_rgbx_64_image_dithalign +#endif +#endif + #if IN_HEADER(__GDK_SCREEN_H__) #if IN_FILE(__GDK_SCREEN_C__) gdk_screen_get_type G_GNUC_CONST diff --git a/gdk/gdkrgb.c b/gdk/gdkrgb.c index b0ff66833..e4f63ddaf 100644 --- a/gdk/gdkrgb.c +++ b/gdk/gdkrgb.c @@ -1464,19 +1464,33 @@ gdk_rgb_convert_gray8_gray (GdkRgbInfo *image_info, GdkImage *image, } } -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -#define HAIRY_CONVERT_565 + +#if G_BYTE_ORDER == G_BIG_ENDIAN +#if defined(__powerpc__) || defined(__powerpc64__) || \ + defined(__ppc__) || defined(__ppc64__) || \ + defined(__PPC__) || defined(__PPC64__) || \ + defined(__POWERPC__) || defined(_ARCH_PPC) +#define GDK_RGB_FAST_BE565_RGB +#define GDK_RGB_FAST_BE565_GRAY +#define GDK_RGB_FAST_BE565_DITHER +#elif defined(__s390x__) +#define GDK_RGB_FAST_BE565_RGB +#elif defined(__aarch64__) +#define GDK_RGB_FAST_BE565_RGB +#define GDK_RGB_FAST_BE565_GRAY +#elif defined(__arm__) && defined(__ARM_ARCH) && \ + __ARM_ARCH >= 5 +#define GDK_RGB_FAST_BE565_RGB +#define GDK_RGB_FAST_BE565_GRAY +#elif defined(__m68k__) +#define GDK_RGB_FAST_BE565_GRAY +#endif #endif -#ifdef HAIRY_CONVERT_565 /* Render a 24-bit RGB image in buf into the GdkImage, without dithering. - This assumes native byte ordering - what should really be done is to - check whether the image byte_order is consistent with the _ENDIAN - config flag, and if not, use a different function. - - This one is even faster than the one below - its inner loop loads 3 - words (i.e. 4 24-bit pixels), does a lot of shifting and masking, - then writes 2 words. */ + The four-pixel inner loop uses compile-time-specialized native word + layouts for little- and big-endian systems. */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN || defined(GDK_RGB_FAST_BE565_RGB) static void gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, gint x0, gint y0, gint width, gint height, @@ -1520,6 +1534,7 @@ gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, r1b0g0r0 = ((guint32 *)bp2)[0]; g2r2b1g1 = ((guint32 *)bp2)[1]; b3g3r3b2 = ((guint32 *)bp2)[2]; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN ((guint32 *)obptr)[0] = ((r1b0g0r0 & 0xf8) << 8) | ((r1b0g0r0 & 0xfc00) >> 5) | @@ -1534,6 +1549,22 @@ gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, ((b3g3r3b2 & 0xf800) << 16) | ((b3g3r3b2 & 0xfc0000) << 3) | ((b3g3r3b2 & 0xf8000000) >> 11); +#else + ((guint32 *)obptr)[0] = + (r1b0g0r0 & 0xf8000000) | + ((r1b0g0r0 & 0x00fc0000) << 3) | + ((r1b0g0r0 & 0x0000f800) << 5) | + ((r1b0g0r0 & 0x000000f8) << 8) | + ((g2r2b1g1 & 0xfc000000) >> 21) | + ((g2r2b1g1 & 0x00f80000) >> 19); + ((guint32 *)obptr)[1] = + ((g2r2b1g1 & 0x0000f800) << 16) | + ((g2r2b1g1 & 0x000000fc) << 19) | + ((b3g3r3b2 & 0xf8000000) >> 11) | + ((b3g3r3b2 & 0x00f80000) >> 8) | + ((b3g3r3b2 & 0x0000fc00) >> 5) | + ((b3g3r3b2 & 0x000000f8) >> 3); +#endif bp2 += 12; obptr += 8; } @@ -1553,27 +1584,6 @@ gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, } } #else -/* Render a 24-bit RGB image in buf into the GdkImage, without dithering. - This assumes native byte ordering - what should really be done is to - check whether the image byte_order is consistent with the _ENDIAN - config flag, and if not, use a different function. - - This routine is faster than the one included with Gtk 1.0 for a number - of reasons: - - 1. Shifting instead of lookup tables (less memory traffic). - - 2. Much less register pressure, especially because shifts are - in the code. - - 3. A memcpy is avoided (i.e. the transfer function). - - 4. On big-endian architectures, byte swapping is avoided. - - That said, it wouldn't be hard to make it even faster - just make an - inner loop that reads 3 words (i.e. 4 24-bit pixels), does a lot of - shifting and masking, then writes 2 words. -*/ static void gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, gint x0, gint y0, gint width, gint height, @@ -1607,7 +1617,7 @@ gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, } #endif -#ifdef HAIRY_CONVERT_565 +#if G_BYTE_ORDER == G_LITTLE_ENDIAN || defined(GDK_RGB_FAST_BE565_GRAY) static void gdk_rgb_convert_565_gray (GdkRgbInfo *image_info, GdkImage *image, gint x0, gint y0, gint width, gint height, @@ -1645,6 +1655,7 @@ gdk_rgb_convert_565_gray (GdkRgbInfo *image_info, GdkImage *image, guint32 g3g2g1g0; g3g2g1g0 = ((guint32 *)bp2)[0]; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN ((guint32 *)obptr)[0] = ((g3g2g1g0 & 0xf8) << 8) | ((g3g2g1g0 & 0xfc) << 3) | @@ -1659,6 +1670,22 @@ gdk_rgb_convert_565_gray (GdkRgbInfo *image_info, GdkImage *image, (g3g2g1g0 & 0xf8000000) | ((g3g2g1g0 & 0xfc000000) >> 5) | ((g3g2g1g0 & 0xf8000000) >> 11); +#else + ((guint32 *)obptr)[0] = + (g3g2g1g0 & 0xf8000000) | + ((g3g2g1g0 & 0xfc000000) >> 5) | + ((g3g2g1g0 & 0xf8000000) >> 11) | + ((g3g2g1g0 & 0x00f80000) >> 8) | + ((g3g2g1g0 & 0x00fc0000) >> 13) | + ((g3g2g1g0 & 0x00f80000) >> 19); + ((guint32 *)obptr)[1] = + ((g3g2g1g0 & 0x0000f800) << 16) | + ((g3g2g1g0 & 0x0000fc00) << 11) | + ((g3g2g1g0 & 0x0000f800) << 5) | + ((g3g2g1g0 & 0x000000f8) << 8) | + ((g3g2g1g0 & 0x000000fc) << 3) | + ((g3g2g1g0 & 0x000000f8) >> 3); +#endif bp2 += 4; obptr += 8; } @@ -1745,7 +1772,7 @@ gdk_rgb_convert_565_br (GdkRgbInfo *image_info, GdkImage *image, /* Thanks to Ray Lehtiniemi for a patch that resulted in a ~25% speedup in this mode. */ -#ifdef HAIRY_CONVERT_565 +#if G_BYTE_ORDER == G_LITTLE_ENDIAN || defined(GDK_RGB_FAST_BE565_DITHER) static void gdk_rgb_convert_565_d (GdkRgbInfo *image_info, GdkImage *image, gint x0, gint y0, gint width, gint height, @@ -1800,6 +1827,7 @@ gdk_rgb_convert_565_d (GdkRgbInfo *image_info, GdkImage *image, r1b0g0r0 = ((guint32 *)bp2)[0]; g2r2b1g1 = ((guint32 *)bp2)[1]; b3g3r3b2 = ((guint32 *)bp2)[2]; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN rgb02 = ((r1b0g0r0 & 0xff) << 20) + ((r1b0g0r0 & 0xff00) << 2) + @@ -1846,6 +1874,54 @@ gdk_rgb_convert_565_d (GdkRgbInfo *image_info, GdkImage *image, ((rgb13 & 0x0f800000) << 4) | ((rgb13 & 0x0003f000) << 9) | ((rgb13 & 0x000000f8) << 13); +#else + rgb02 = + ((r1b0g0r0 & 0xff000000) >> 4) + + ((r1b0g0r0 & 0x00ff0000) >> 6) + + ((r1b0g0r0 & 0x0000ff00) >> 8) + + dmp[x & (DM_WIDTH - 1)]; + rgb02 += 0x10040100 + - ((rgb02 & 0x1e0001e0) >> 5) + - ((rgb02 & 0x00070000) >> 6); + rgb13 = + ((r1b0g0r0 & 0x000000ff) << 20) + + ((g2r2b1g1 & 0xff000000) >> 14) + + ((g2r2b1g1 & 0x00ff0000) >> 16) + + dmp[(x + 1) & (DM_WIDTH - 1)]; + rgb13 += 0x10040100 + - ((rgb13 & 0x1e0001e0) >> 5) + - ((rgb13 & 0x00070000) >> 6); + ((guint32 *)obptr)[0] = + ((rgb02 & 0x0f800000) << 4) | + ((rgb02 & 0x0003f000) << 9) | + ((rgb02 & 0x000000f8) << 13) | + ((rgb13 & 0x0f800000) >> 12) | + ((rgb13 & 0x0003f000) >> 7) | + ((rgb13 & 0x000000f8) >> 3); + rgb02 = + ((g2r2b1g1 & 0x0000ff00) << 12) + + ((g2r2b1g1 & 0x000000ff) << 10) + + ((b3g3r3b2 & 0xff000000) >> 24) + + dmp[(x + 2) & (DM_WIDTH - 1)]; + rgb02 += 0x10040100 + - ((rgb02 & 0x1e0001e0) >> 5) + - ((rgb02 & 0x00070000) >> 6); + rgb13 = + ((b3g3r3b2 & 0x00ff0000) << 4) + + ((b3g3r3b2 & 0x0000ff00) << 2) + + (b3g3r3b2 & 0x000000ff) + + dmp[(x + 3) & (DM_WIDTH - 1)]; + rgb13 += 0x10040100 + - ((rgb13 & 0x1e0001e0) >> 5) + - ((rgb13 & 0x00070000) >> 6); + ((guint32 *)obptr)[1] = + ((rgb02 & 0x0f800000) << 4) | + ((rgb02 & 0x0003f000) << 9) | + ((rgb02 & 0x000000f8) << 13) | + ((rgb13 & 0x0f800000) >> 12) | + ((rgb13 & 0x0003f000) >> 7) | + ((rgb13 & 0x000000f8) >> 3); +#endif bp2 += 12; obptr += 8; } @@ -1916,6 +1992,10 @@ gdk_rgb_convert_565_d (GdkRgbInfo *image_info, GdkImage *image, } #endif +#undef GDK_RGB_FAST_BE565_RGB +#undef GDK_RGB_FAST_BE565_GRAY +#undef GDK_RGB_FAST_BE565_DITHER + static void gdk_rgb_convert_555 (GdkRgbInfo *image_info, GdkImage *image, gint x0, gint y0, gint width, gint height, @@ -2242,6 +2322,68 @@ gdk_rgb_convert_8880_br (GdkRgbInfo *image_info, GdkImage *image, } } +#define DEFINE_GDK_RGB_CONVERT_101010(name, high, low, to_order) \ +static void \ +name (GdkRgbInfo *image_info, GdkImage *image, \ + gint x0, gint y0, gint width, gint height, \ + const guchar *buf, int rowstride, \ + gint x_align, gint y_align, GdkRgbCmap *cmap) \ +{ \ + int y, w; \ + guchar *obuf, *p; \ + gint bpl; \ + const guchar *bptr, *bp2; \ + guint32 r, g, b; \ + guint32 pixel; \ + guint32 alpha_mask; \ + \ + alpha_mask = image_info->visual->depth == 32 ? 0xc0000000U : 0; \ + \ + bptr = buf; \ + bpl = image->bpl; \ + obuf = ((guchar *) image->mem) + y0 * bpl + x0 * 4; \ + \ + for (y = 0; y < height; y++) \ + { \ + bp2 = bptr; \ + p = obuf; \ + w = width; \ + \ + while (w--) \ + { \ + r = *bp2++; \ + g = *bp2++; \ + b = *bp2++; \ + \ + r = (r << 2) | (r >> 6); \ + g = (g << 2) | (g >> 6); \ + b = (b << 2) | (b >> 6); \ + \ + pixel = alpha_mask | \ + (high << 20) | \ + (g << 10) | \ + low; \ + \ + *((guint32 *) p) = to_order (pixel); \ + p += 4; \ + } \ + \ + bptr += rowstride; \ + obuf += bpl; \ + } \ +} + +DEFINE_GDK_RGB_CONVERT_101010 (gdk_rgb_convert_101010_r_low_lsb, + b, r, GUINT32_TO_LE) +DEFINE_GDK_RGB_CONVERT_101010 (gdk_rgb_convert_101010_b_low_lsb, + r, b, GUINT32_TO_LE) +DEFINE_GDK_RGB_CONVERT_101010 (gdk_rgb_convert_101010_r_low_msb, + b, r, GUINT32_TO_BE) +DEFINE_GDK_RGB_CONVERT_101010 (gdk_rgb_convert_101010_b_low_msb, + r, b, GUINT32_TO_BE) + +#undef DEFINE_GDK_RGB_CONVERT_101010 + /* Generic truecolor/directcolor conversion function. Slow, but these are oddball modes. */ static void @@ -2555,7 +2697,7 @@ gdk_rgb_convert_4_pack (GdkRgbInfo *image_info, GdkImage *image, r = *bp2++; g = *bp2++; b = *bp2++; - dith = (dmp[(x_align + x + 1) & (DM_WIDTH - 1)] << 2) | 3; + dith = (dmp[(x_align + x) & (DM_WIDTH - 1)] << 2) | 3; ix = (((r + dith) & 0x100) >> 2) | (((g + 258 - dith) & 0x100) >> 5) | (((b + dith) & 0x100) >> 8); @@ -2564,7 +2706,7 @@ gdk_rgb_convert_4_pack (GdkRgbInfo *image_info, GdkImage *image, obptr++; x++; } - while (x < width) + while (x + 1 < width) { r = *bp2++; g = *bp2++; @@ -2575,14 +2717,11 @@ gdk_rgb_convert_4_pack (GdkRgbInfo *image_info, GdkImage *image, (((b + dith) & 0x100) >> 8); pix0 = (colorcube_d[ix]); x++; - if (x == width) - pix1 = (*obptr & 0x0F); - else { r = *bp2++; g = *bp2++; b = *bp2++; - dith = (dmp[(x_align + x + 1) & (DM_WIDTH - 1)] << 2) | 3; + dith = (dmp[(x_align + x) & (DM_WIDTH - 1)] << 2) | 3; ix = (((r + dith) & 0x100) >> 2) | (((g + 258 - dith) & 0x100) >> 5) | (((b + dith) & 0x100) >> 8); @@ -2591,6 +2730,18 @@ gdk_rgb_convert_4_pack (GdkRgbInfo *image_info, GdkImage *image, } *obptr++ = (pix0 << 4) | pix1; } + if (x < width) + { + r = *bp2++; + g = *bp2++; + b = *bp2++; + dith = (dmp[(x_align + x) & (DM_WIDTH - 1)] << 2) | 3; + ix = (((r + dith) & 0x100) >> 2) | + (((g + 258 - dith) & 0x100) >> 5) | + (((b + dith) & 0x100) >> 8); + pix0 = (colorcube_d[ix]); + *obptr = (pix0 << 4) | (*obptr & 0x0F); + } bptr += rowstride; obuf += bpl; } @@ -3195,6 +3346,34 @@ gdk_rgb_select_conv (GdkRgbInfo *image_info) #endif } #endif + else if (bpp == 32 && (depth == 30 || depth == 32) && + vtype == GDK_VISUAL_TRUE_COLOR && + byte_order == GDK_LSB_FIRST && + red_mask == 0x000003ff && + green_mask == 0x000ffc00 && + blue_mask == 0x3ff00000) + conv = gdk_rgb_convert_101010_r_low_lsb; + else if (bpp == 32 && (depth == 30 || depth == 32) && + vtype == GDK_VISUAL_TRUE_COLOR && + byte_order == GDK_LSB_FIRST && + red_mask == 0x3ff00000 && + green_mask == 0x000ffc00 && + blue_mask == 0x000003ff) + conv = gdk_rgb_convert_101010_b_low_lsb; + else if (bpp == 32 && (depth == 30 || depth == 32) && + vtype == GDK_VISUAL_TRUE_COLOR && + byte_order == GDK_MSB_FIRST && + red_mask == 0x000003ff && + green_mask == 0x000ffc00 && + blue_mask == 0x3ff00000) + conv = gdk_rgb_convert_101010_r_low_msb; + else if (bpp == 32 && (depth == 30 || depth == 32) && + vtype == GDK_VISUAL_TRUE_COLOR && + byte_order == GDK_MSB_FIRST && + red_mask == 0x3ff00000 && + green_mask == 0x000ffc00 && + blue_mask == 0x000003ff) + conv = gdk_rgb_convert_101010_b_low_msb; else if (vtype == GDK_VISUAL_TRUE_COLOR && byte_order == GDK_LSB_FIRST) { conv = gdk_rgb_convert_truecolor_lsb; @@ -3261,7 +3440,7 @@ gdk_rgb_select_conv (GdkRgbInfo *image_info) conv = gdk_rgb_convert_gray4_pack; conv_d = gdk_rgb_convert_gray4_d_pack; } - else if (bpp == 4 && depth == 4 && + else if (bpp == 4 && depth <= 4 && depth >= 2 && vtype == GDK_VISUAL_STATIC_COLOR) conv = gdk_rgb_convert_4_pack; diff --git a/gdk/gdkrgb.h b/gdk/gdkrgb.h index dd71d403f..44c576fb5 100644 --- a/gdk/gdkrgb.h +++ b/gdk/gdkrgb.h @@ -147,6 +147,48 @@ gboolean gdk_rgb_colormap_ditherable (GdkColormap *cmap); #endif #endif /* GDK_DISABLE_DEPRECATED */ +void gdk_draw_rgb_48_image (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgb_buf, + gint rowstride); +void gdk_draw_rgb_48_image_dithalign (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgb_buf, + gint rowstride, + gint xdith, + gint ydith); +void gdk_draw_rgbx_64_image (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgbx_buf, + gint rowstride); +void gdk_draw_rgbx_64_image_dithalign + (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgbx_buf, + gint rowstride, + gint xdith, + gint ydith); + G_END_DECLS diff --git a/gdk/gdkrgb16.c b/gdk/gdkrgb16.c new file mode 100644 index 000000000..2564ef0d8 --- /dev/null +++ b/gdk/gdkrgb16.c @@ -0,0 +1,742 @@ +/* GTK - The GIMP Toolkit + * High-precision GdkRGB image conversion + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + */ + +#include "config.h" + +#include + +#include "gdkinternals.h" +#include "gdkrgb.h" +#include "gdkscreen.h" +#include "gdkalias.h" + +typedef void (*GdkRgb16ConvFunc) (GdkImage *image, + gint x0, + gint y0, + gint width, + gint height, + const guint16 *buf, + gint rowstride); + +#define GDK_RGB16_STORE_RGB(p, r, g, b, a) G_STMT_START { \ + (p)[0] = (r) >> 8; \ + (p)[1] = (g) >> 8; \ + (p)[2] = (b) >> 8; \ + (p) += 3; \ +} G_STMT_END + +#define GDK_RGB16_STORE_BGR(p, r, g, b, a) G_STMT_START { \ + (p)[0] = (b) >> 8; \ + (p)[1] = (g) >> 8; \ + (p)[2] = (r) >> 8; \ + (p) += 3; \ +} G_STMT_END + +#define GDK_RGB16_STORE_BGRA(p, r, g, b, a) G_STMT_START { \ + (p)[0] = (b) >> 8; \ + (p)[1] = (g) >> 8; \ + (p)[2] = (r) >> 8; \ + (p)[3] = 0xff; \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_ARGB(p, r, g, b, a) G_STMT_START { \ + (p)[0] = 0xff; \ + (p)[1] = (r) >> 8; \ + (p)[2] = (g) >> 8; \ + (p)[3] = (b) >> 8; \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_RGB0_NATIVE(p, r, g, b, a) G_STMT_START { \ + guint32 pixel__ = (((b) >> 8) << 16) | \ + (((g) >> 8) << 8) | \ + ((r) >> 8); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_565(p, r, g, b, a) G_STMT_START { \ + guint16 pixel__ = ((r) & 0xf800) | \ + (((g) & 0xfc00) >> 5) | \ + ((b) >> 11); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 2; \ +} G_STMT_END + +#define GDK_RGB16_STORE_565_BR(p, r, g, b, a) G_STMT_START { \ + guint16 pixel__ = ((r) & 0xf800) | \ + (((g) & 0xfc00) >> 5) | \ + ((b) >> 11); \ + pixel__ = (pixel__ >> 8) | (pixel__ << 8); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 2; \ +} G_STMT_END + +#define GDK_RGB16_STORE_555(p, r, g, b, a) G_STMT_START { \ + guint16 pixel__ = (((r) & 0xf800) >> 1) | \ + (((g) & 0xf800) >> 6) | \ + ((b) >> 11); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 2; \ +} G_STMT_END + +#define GDK_RGB16_STORE_555_BR(p, r, g, b, a) G_STMT_START { \ + guint16 pixel__ = (((r) & 0xf800) >> 1) | \ + (((g) & 0xf800) >> 6) | \ + ((b) >> 11); \ + pixel__ = (pixel__ >> 8) | (pixel__ << 8); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 2; \ +} G_STMT_END + +#define GDK_RGB16_STORE_101010_R_LOW_LE(p, r, g, b, a) \ +G_STMT_START { \ + guint32 pixel__ = (a) | \ + (((b) >> 6) << 20) | \ + (((g) >> 6) << 10) | \ + ((r) >> 6); \ + pixel__ = GUINT32_TO_LE (pixel__); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_101010_B_LOW_LE(p, r, g, b, a) \ +G_STMT_START { \ + guint32 pixel__ = (a) | \ + (((r) >> 6) << 20) | \ + (((g) >> 6) << 10) | \ + ((b) >> 6); \ + pixel__ = GUINT32_TO_LE (pixel__); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_101010_R_LOW_BE(p, r, g, b, a) \ +G_STMT_START { \ + guint32 pixel__ = (a) | \ + (((b) >> 6) << 20) | \ + (((g) >> 6) << 10) | \ + ((r) >> 6); \ + pixel__ = GUINT32_TO_BE (pixel__); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_101010_B_LOW_BE(p, r, g, b, a) \ +G_STMT_START { \ + guint32 pixel__ = (a) | \ + (((r) >> 6) << 20) | \ + (((g) >> 6) << 10) | \ + ((b) >> 6); \ + pixel__ = GUINT32_TO_BE (pixel__); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 4; \ +} G_STMT_END + +#define DEFINE_GDK_RGB16_CONVERTER(name, words, pixel_bytes, store) \ +static void \ +name (GdkImage *image, \ + gint x0, \ + gint y0, \ + gint width, \ + gint height, \ + const guint16 *buf, \ + gint rowstride) \ +{ \ + const guchar *row; \ + const guint16 *src; \ + guchar *dst_row; \ + guchar *dst; \ + guint32 r, g, b; \ + guint32 alpha_mask; \ + gint x, y; \ + \ + row = (const guchar *) buf; \ + dst_row = ((guchar *) image->mem) + \ + y0 * image->bpl + x0 * (pixel_bytes); \ + alpha_mask = image->depth == 32 ? 0xc0000000U : 0; \ + (void) alpha_mask; \ + \ + for (y = 0; y < height; y++) \ + { \ + src = (const guint16 *) row; \ + dst = dst_row; \ + \ + for (x = 0; x < width; x++) \ + { \ + r = src[0]; \ + g = src[1]; \ + b = src[2]; \ + src += (words); \ + store (dst, r, g, b, alpha_mask); \ + } \ + \ + row += rowstride; \ + dst_row += image->bpl; \ + } \ +} + +#define DEFINE_GDK_RGB16_PAIR(suffix, pixel_bytes, store) \ + DEFINE_GDK_RGB16_CONVERTER (gdk_rgb48_convert_ ## suffix, \ + 3, pixel_bytes, store) \ + DEFINE_GDK_RGB16_CONVERTER (gdk_rgbx64_convert_ ## suffix, \ + 4, pixel_bytes, store) + +DEFINE_GDK_RGB16_PAIR (rgb, 3, GDK_RGB16_STORE_RGB) +DEFINE_GDK_RGB16_PAIR (bgr, 3, GDK_RGB16_STORE_BGR) +DEFINE_GDK_RGB16_PAIR (bgra, 4, GDK_RGB16_STORE_BGRA) +DEFINE_GDK_RGB16_PAIR (argb, 4, GDK_RGB16_STORE_ARGB) +DEFINE_GDK_RGB16_PAIR (rgb0_native, 4, GDK_RGB16_STORE_RGB0_NATIVE) +DEFINE_GDK_RGB16_PAIR (565, 2, GDK_RGB16_STORE_565) +DEFINE_GDK_RGB16_PAIR (565_br, 2, GDK_RGB16_STORE_565_BR) +DEFINE_GDK_RGB16_PAIR (555, 2, GDK_RGB16_STORE_555) +DEFINE_GDK_RGB16_PAIR (555_br, 2, GDK_RGB16_STORE_555_BR) +DEFINE_GDK_RGB16_PAIR (101010_r_low_le, 4, + GDK_RGB16_STORE_101010_R_LOW_LE) +DEFINE_GDK_RGB16_PAIR (101010_b_low_le, 4, + GDK_RGB16_STORE_101010_B_LOW_LE) +DEFINE_GDK_RGB16_PAIR (101010_r_low_be, 4, + GDK_RGB16_STORE_101010_R_LOW_BE) +DEFINE_GDK_RGB16_PAIR (101010_b_low_be, 4, + GDK_RGB16_STORE_101010_B_LOW_BE) + +#undef DEFINE_GDK_RGB16_PAIR +#undef DEFINE_GDK_RGB16_CONVERTER + +#undef GDK_RGB16_STORE_RGB +#undef GDK_RGB16_STORE_BGR +#undef GDK_RGB16_STORE_BGRA +#undef GDK_RGB16_STORE_ARGB +#undef GDK_RGB16_STORE_RGB0_NATIVE +#undef GDK_RGB16_STORE_565 +#undef GDK_RGB16_STORE_565_BR +#undef GDK_RGB16_STORE_555 +#undef GDK_RGB16_STORE_555_BR +#undef GDK_RGB16_STORE_101010_R_LOW_LE +#undef GDK_RGB16_STORE_101010_B_LOW_LE +#undef GDK_RGB16_STORE_101010_R_LOW_BE +#undef GDK_RGB16_STORE_101010_B_LOW_BE + +static GdkRgb16ConvFunc +gdk_rgb16_select_converter (GdkDrawable *drawable, + gboolean is_rgbx, + GdkRgbDither dith) +{ + GdkVisual *visual; + GdkByteOrder byte_order; + GdkScreen *screen; + guint32 red_mask, green_mask, blue_mask; + gint depth; + gint bits_per_pixel; + gboolean byte_reversed; + gboolean mask_rgb; + gboolean mask_bgr; + +#define SELECT_PAIR(suffix) \ + return is_rgbx ? gdk_rgbx64_convert_ ## suffix \ + : gdk_rgb48_convert_ ## suffix + + /* + * Preserve the existing GdkRGB dither path for maximum dithering + * and for unknown future values. + */ + if (dith != GDK_RGB_DITHER_NONE && + dith != GDK_RGB_DITHER_NORMAL) + return NULL; + + visual = gdk_drawable_get_visual (drawable); + if (visual == NULL) + return NULL; + + if (gdk_visual_get_visual_type (visual) != GDK_VISUAL_TRUE_COLOR) + return NULL; + + screen = gdk_visual_get_screen (visual); + depth = gdk_visual_get_depth (visual); + byte_order = gdk_visual_get_byte_order (visual); + + gdk_visual_get_red_pixel_details (visual, &red_mask, NULL, NULL); + gdk_visual_get_green_pixel_details (visual, &green_mask, NULL, NULL); + gdk_visual_get_blue_pixel_details (visual, &blue_mask, NULL, NULL); + + bits_per_pixel = _gdk_windowing_get_bits_for_depth + (gdk_screen_get_display (screen), depth); + +#if G_BYTE_ORDER == G_BIG_ENDIAN + byte_reversed = byte_order == GDK_LSB_FIRST; +#else + byte_reversed = byte_order == GDK_MSB_FIRST; +#endif + + mask_rgb = red_mask == 0xff0000 && + green_mask == 0x00ff00 && + blue_mask == 0x0000ff; + + mask_bgr = red_mask == 0x0000ff && + green_mask == 0x00ff00 && + blue_mask == 0xff0000; + + if (bits_per_pixel == 16 && + depth == 16 && + red_mask == 0xf800 && + green_mask == 0x07e0 && + blue_mask == 0x001f) + { + if (byte_reversed) + SELECT_PAIR (565_br); + + SELECT_PAIR (565); + } + + if (bits_per_pixel == 16 && + depth == 15 && + red_mask == 0x7c00 && + green_mask == 0x03e0 && + blue_mask == 0x001f) + { + if (byte_reversed) + SELECT_PAIR (555_br); + + SELECT_PAIR (555); + } + + if (bits_per_pixel == 24 && + depth == 24 && + ((mask_rgb && byte_order == GDK_LSB_FIRST) || + (mask_bgr && byte_order == GDK_MSB_FIRST))) + SELECT_PAIR (bgr); + + if (bits_per_pixel == 24 && + depth == 24 && + ((mask_rgb && byte_order == GDK_MSB_FIRST) || + (mask_bgr && byte_order == GDK_LSB_FIRST))) + SELECT_PAIR (rgb); + + if (bits_per_pixel == 32 && + (depth == 24 || depth == 32) && + mask_rgb && + byte_order == GDK_MSB_FIRST) + SELECT_PAIR (argb); + + if (bits_per_pixel == 32 && + (depth == 24 || depth == 32) && + mask_rgb && + byte_order == GDK_LSB_FIRST) + SELECT_PAIR (bgra); + +#if G_BYTE_ORDER == G_BIG_ENDIAN + if (bits_per_pixel == 32 && + depth == 24 && + mask_bgr && + byte_order == GDK_MSB_FIRST) + SELECT_PAIR (rgb0_native); +#else + if (bits_per_pixel == 32 && + depth == 24 && + mask_bgr && + byte_order == GDK_LSB_FIRST) + SELECT_PAIR (rgb0_native); +#endif + + if (bits_per_pixel == 32 && + (depth == 30 || depth == 32) && + byte_order == GDK_LSB_FIRST && + red_mask == 0x000003ff && + green_mask == 0x000ffc00 && + blue_mask == 0x3ff00000) + SELECT_PAIR (101010_r_low_le); + + if (bits_per_pixel == 32 && + (depth == 30 || depth == 32) && + byte_order == GDK_LSB_FIRST && + red_mask == 0x3ff00000 && + green_mask == 0x000ffc00 && + blue_mask == 0x000003ff) + SELECT_PAIR (101010_b_low_le); + + if (bits_per_pixel == 32 && + (depth == 30 || depth == 32) && + byte_order == GDK_MSB_FIRST && + red_mask == 0x000003ff && + green_mask == 0x000ffc00 && + blue_mask == 0x3ff00000) + SELECT_PAIR (101010_r_low_be); + + if (bits_per_pixel == 32 && + (depth == 30 || depth == 32) && + byte_order == GDK_MSB_FIRST && + red_mask == 0x3ff00000 && + green_mask == 0x000ffc00 && + blue_mask == 0x000003ff) + SELECT_PAIR (101010_b_low_be); + +#undef SELECT_PAIR + + return NULL; +} + +static void +gdk_rgb16_draw_direct (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + const guint16 *buf, + gint pixel_words, + gint rowstride, + GdkRgb16ConvFunc converter) +{ + GdkScreen *screen; + GdkImage *image; + const guint16 *tile; + gint depth; + gint x0, y0; + gint xs0, ys0; + gint width1, height1; + + screen = gdk_drawable_get_screen (drawable); + depth = gdk_drawable_get_depth (drawable); + + for (y0 = 0; y0 < height; y0 += GDK_SCRATCH_IMAGE_HEIGHT) + { + height1 = MIN (height - y0, GDK_SCRATCH_IMAGE_HEIGHT); + + for (x0 = 0; x0 < width; x0 += GDK_SCRATCH_IMAGE_WIDTH) + { + width1 = MIN (width - x0, GDK_SCRATCH_IMAGE_WIDTH); + + tile = (const guint16 *) + ((const guchar *) buf + + y0 * rowstride + + x0 * pixel_words * sizeof (guint16)); + + image = _gdk_image_get_scratch (screen, + width1, + height1, + depth, + &xs0, + &ys0); + + converter (image, + xs0, + ys0, + width1, + height1, + tile, + rowstride); + + gdk_draw_image (drawable, + gc, + image, + xs0, + ys0, + x + x0, + y + y0, + width1, + height1); + } + } +} + +static void +gdk_rgb16_draw_fallback (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *buf, + gint pixel_words, + gint rowstride, + gint xdith, + gint ydith) +{ + const guchar *input_row; + const guint16 *input; + guchar *stage; + guchar *output; + gint stage_rowstride; + gint x0, y0; + gint x1, y1; + gint width1, height1; + + stage = g_malloc (MIN (width, GDK_SCRATCH_IMAGE_WIDTH) * + MIN (height, GDK_SCRATCH_IMAGE_HEIGHT) * 3); + + for (y0 = 0; y0 < height; y0 += GDK_SCRATCH_IMAGE_HEIGHT) + { + height1 = MIN (height - y0, GDK_SCRATCH_IMAGE_HEIGHT); + + for (x0 = 0; x0 < width; x0 += GDK_SCRATCH_IMAGE_WIDTH) + { + width1 = MIN (width - x0, GDK_SCRATCH_IMAGE_WIDTH); + stage_rowstride = width1 * 3; + + input_row = (const guchar *) buf + + y0 * rowstride + + x0 * pixel_words * sizeof (guint16); + output = stage; + + for (y1 = 0; y1 < height1; y1++) + { + input = (const guint16 *) input_row; + + for (x1 = 0; x1 < width1; x1++) + { + *output++ = input[0] >> 8; + *output++ = input[1] >> 8; + *output++ = input[2] >> 8; + input += pixel_words; + } + + input_row += rowstride; + } + + gdk_draw_rgb_image_dithalign (drawable, + gc, + x + x0, + y + y0, + width1, + height1, + dith, + stage, + stage_rowstride, + xdith, + ydith); + } + } + + g_free (stage); +} + +static void +gdk_draw_rgb16_image (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *buf, + gint rowstride, + gint xdith, + gint ydith, + gboolean is_rgbx) +{ + GdkRgb16ConvFunc converter; + gint pixel_words; + + g_return_if_fail (GDK_IS_DRAWABLE (drawable)); + g_return_if_fail (GDK_IS_GC (gc)); + g_return_if_fail (buf != NULL); + g_return_if_fail ((((guintptr) buf) & 1) == 0); + g_return_if_fail ((rowstride & 1) == 0); + + if (width <= 0 || height <= 0) + return; + + pixel_words = is_rgbx ? 4 : 3; + converter = gdk_rgb16_select_converter (drawable, is_rgbx, dith); + + if (converter != NULL) + gdk_rgb16_draw_direct (drawable, + gc, + x, + y, + width, + height, + buf, + pixel_words, + rowstride, + converter); + else + gdk_rgb16_draw_fallback (drawable, + gc, + x, + y, + width, + height, + dith, + buf, + pixel_words, + rowstride, + xdith, + ydith); +} + +/** + * gdk_draw_rgb_48_image: + * @drawable: a #GdkDrawable + * @gc: a #GdkGC + * @x: destination X coordinate + * @y: destination Y coordinate + * @width: image width + * @height: image height + * @dith: dithering mode + * @rgb_buf: host-endian RGB data with three 16-bit channels per pixel + * @rowstride: number of bytes between consecutive rows + * + * Draws 48-bit RGB image data. + **/ +void +gdk_draw_rgb_48_image (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgb_buf, + gint rowstride) +{ + gdk_draw_rgb16_image (drawable, + gc, + x, + y, + width, + height, + dith, + rgb_buf, + rowstride, + 0, + 0, + FALSE); +} + +/** + * gdk_draw_rgb_48_image_dithalign: + * @drawable: a #GdkDrawable + * @gc: a #GdkGC + * @x: destination X coordinate + * @y: destination Y coordinate + * @width: image width + * @height: image height + * @dith: dithering mode + * @rgb_buf: host-endian RGB data with three 16-bit channels per pixel + * @rowstride: number of bytes between consecutive rows + * @xdith: X dither offset + * @ydith: Y dither offset + * + * Draws 48-bit RGB image data with explicit dither alignment. + **/ +void +gdk_draw_rgb_48_image_dithalign (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgb_buf, + gint rowstride, + gint xdith, + gint ydith) +{ + gdk_draw_rgb16_image (drawable, + gc, + x, + y, + width, + height, + dith, + rgb_buf, + rowstride, + xdith, + ydith, + FALSE); +} + +/** + * gdk_draw_rgbx_64_image: + * @drawable: a #GdkDrawable + * @gc: a #GdkGC + * @x: destination X coordinate + * @y: destination Y coordinate + * @width: image width + * @height: image height + * @dith: dithering mode + * @rgbx_buf: host-endian RGBX data with four 16-bit channels per pixel + * @rowstride: number of bytes between consecutive rows + * + * Draws 64-bit RGBX image data. The X channel is ignored. + **/ +void +gdk_draw_rgbx_64_image (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgbx_buf, + gint rowstride) +{ + gdk_draw_rgb16_image (drawable, + gc, + x, + y, + width, + height, + dith, + rgbx_buf, + rowstride, + 0, + 0, + TRUE); +} + +/** + * gdk_draw_rgbx_64_image_dithalign: + * @drawable: a #GdkDrawable + * @gc: a #GdkGC + * @x: destination X coordinate + * @y: destination Y coordinate + * @width: image width + * @height: image height + * @dith: dithering mode + * @rgbx_buf: host-endian RGBX data with four 16-bit channels per pixel + * @rowstride: number of bytes between consecutive rows + * @xdith: X dither offset + * @ydith: Y dither offset + * + * Draws 64-bit RGBX image data with explicit dither alignment. + * The X channel is ignored. + **/ +void +gdk_draw_rgbx_64_image_dithalign (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgbx_buf, + gint rowstride, + gint xdith, + gint ydith) +{ + gdk_draw_rgb16_image (drawable, + gc, + x, + y, + width, + height, + dith, + rgbx_buf, + rowstride, + xdith, + ydith, + TRUE); +} + +#define __GDK_RGB16_C__ +#include "gdkaliasdef.c" diff --git a/gdk/makefile.msc b/gdk/makefile.msc index e9f816931..6175c3f21 100644 --- a/gdk/makefile.msc +++ b/gdk/makefile.msc @@ -92,6 +92,7 @@ gdk_OBJECTS = \ gdkrectangle.obj \ gdkregion-generic.obj \ gdkrgb.obj \ + gdkrgb16.obj \ gdkscreen.obj \ gdkselection.obj \ gdkvisual.obj \ diff --git a/gdk/makegdkalias.pl b/gdk/makegdkalias.pl index 97a6e6007..27646cc0a 100755 --- a/gdk/makegdkalias.pl +++ b/gdk/makegdkalias.pl @@ -14,7 +14,7 @@ print < -#ifdef G_HAVE_GNUC_VISIBILITY +#if defined(G_HAVE_GNUC_VISIBILITY) && !defined(__APPLE__) EOF diff --git a/gdk/quartz/GdkQuartzView.c b/gdk/quartz/GdkQuartzView.c index 368e040ec..bad737da2 100644 --- a/gdk/quartz/GdkQuartzView.c +++ b/gdk/quartz/GdkQuartzView.c @@ -599,6 +599,25 @@ gdk_screen_get_rgba_colormap (_gdk_screen); } +-(void)viewWillDraw +{ + /* macOS 11 (Big Sur) and newer may use a higher precision backing + * format for layer contents. For GTK2's 8-bit drawing this can add + * avoidable conversion overhead. Use a dynamic Objective-C call here + * instead of a hard QuartzCore/CALayer dependency, so old SDKs and old + * macOS targets keep building and keep their old behaviour. + */ + if (gdk_quartz_osx_version () >= GDK_OSX_BIG_SUR) + { + id layer = [self layer]; + + if (layer && [layer respondsToSelector: @selector(setContentsFormat:)]) + [layer setValue: @"RGBA8Uint" forKey: @"contentsFormat"]; + } + + [super viewWillDraw]; +} + -(void)drawRect: (NSRect)rect { GdkRectangle gdk_rect; diff --git a/gdk/quartz/gdkdrawable-quartz.c b/gdk/quartz/gdkdrawable-quartz.c index 11dd12737..eb520f08d 100644 --- a/gdk/quartz/gdkdrawable-quartz.c +++ b/gdk/quartz/gdkdrawable-quartz.c @@ -893,7 +893,8 @@ gdk_quartz_drawable_release_context (GdkDrawable *drawable, if (window_impl->in_paint_rect_count == 0) { _gdk_quartz_drawable_flush (drawable); - [window_impl->view unlockFocus]; + if (gdk_quartz_osx_version () < GDK_OSX_MOJAVE) + [window_impl->view unlockFocus]; } } else if (GDK_IS_PIXMAP_IMPL_QUARTZ (drawable)) diff --git a/gdk/quartz/gdkglobals-quartz.c b/gdk/quartz/gdkglobals-quartz.c index f013f2830..4e596eef5 100644 --- a/gdk/quartz/gdkglobals-quartz.c +++ b/gdk/quartz/gdkglobals-quartz.c @@ -34,9 +34,20 @@ gdk_quartz_osx_version (void) if (minor == GDK_OSX_UNSUPPORTED) { - OSErr err = Gestalt (gestaltSystemVersionMinor, (SInt32*)&minor); + gint32 major = GDK_OSX_UNSUPPORTED; + OSErr err = Gestalt (gestaltSystemVersionMajor, (SInt32*)&major); g_return_val_if_fail (err == noErr, GDK_OSX_UNSUPPORTED); + + err = Gestalt (gestaltSystemVersionMinor, (SInt32*)&minor); + g_return_val_if_fail (err == noErr, GDK_OSX_UNSUPPORTED); + + /* GTK2 encodes macOS 10.x releases by their minor version. + * Map macOS 11+ into the same sequence: + * 11 -> 16, 12 -> 17, 13 -> 18, ... + */ + if (major > 10) + minor = major + 5; } if (minor < GDK_OSX_MIN) diff --git a/gdk/quartz/gdkquartz.h b/gdk/quartz/gdkquartz.h index 8206db019..42aac50b9 100644 --- a/gdk/quartz/gdkquartz.h +++ b/gdk/quartz/gdkquartz.h @@ -54,7 +54,13 @@ typedef enum GDK_OSX_SIERRA = 12, GDK_OSX_HIGH_SIERRA = 13, GDK_OSX_MOJAVE = 14, - GDK_OSX_CURRENT = 14, + GDK_OSX_CATALINA = 15, + GDK_OSX_BIG_SUR = 16, + GDK_OSX_MONTEREY = 17, + GDK_OSX_VENTURA = 18, + GDK_OSX_SONOMA = 19, + GDK_OSX_SEQUOIA = 20, + GDK_OSX_CURRENT = 20, GDK_OSX_NEW = 99 } GdkOSXVersion; diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c index 6c7a2facb..38ada80b2 100644 --- a/gdk/quartz/gdkwindow-quartz.c +++ b/gdk/quartz/gdkwindow-quartz.c @@ -104,7 +104,8 @@ gdk_window_impl_quartz_get_context (GdkDrawable *drawable, * and for widgets that send fake expose events like the arrow * buttons in spinbuttons or the position marker in rulers. */ - if (window_impl->in_paint_rect_count == 0) + if (window_impl->in_paint_rect_count == 0 && + gdk_quartz_osx_version () < GDK_OSX_MOJAVE) { if (![window_impl->view lockFocusIfCanDraw]) return NULL; @@ -468,7 +469,8 @@ _gdk_windowing_before_process_all_updates (void) { in_process_all_updates = TRUE; - NSDisableScreenUpdates (); + if (gdk_quartz_osx_version () < GDK_OSX_MOJAVE) + NSDisableScreenUpdates (); } void @@ -489,8 +491,9 @@ _gdk_windowing_after_process_all_updates (void) /* 10.14 needs to be told that the view needs to be redrawn, see * https://gitlab.gnome.org/GNOME/gtk/issues/1479 */ - if (gdk_quartz_osx_version() >= GDK_OSX_MOJAVE) - [[nswindow contentView] setNeedsDisplay:YES]; + if (gdk_quartz_osx_version () >= GDK_OSX_MOJAVE && + gdk_quartz_osx_version () < GDK_OSX_BIG_SUR) + [[nswindow contentView] setNeedsDisplay:YES]; [nswindow enableFlushWindow]; [nswindow flushWindow]; [nswindow release]; @@ -502,7 +505,8 @@ _gdk_windowing_after_process_all_updates (void) in_process_all_updates = FALSE; - NSEnableScreenUpdates (); + if (gdk_quartz_osx_version () < GDK_OSX_MOJAVE) + NSEnableScreenUpdates (); } static void diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c index d8ab30347..9a34083c4 100644 --- a/gdk/win32/gdkkeys-win32.c +++ b/gdk/win32/gdkkeys-win32.c @@ -692,6 +692,7 @@ update_keymap (GdkKeymap *gdk_keymap) if (hkls_len != keymap->layout_handles->len) keymap->keysym_tab = g_renew (guint, keymap->keysym_tab, keysym_tab_size); + memset (key_state, 0, sizeof(key_state)); memset (keymap->keysym_tab, 0, keysym_tab_size); g_array_set_size (keymap->layout_handles, hkls_len); g_array_set_size (keymap->options, hkls_len); diff --git a/gdk/win32/gdkwindow-win32.c b/gdk/win32/gdkwindow-win32.c index b7ea0404a..1a21a1b67 100644 --- a/gdk/win32/gdkwindow-win32.c +++ b/gdk/win32/gdkwindow-win32.c @@ -2767,6 +2767,10 @@ _gdk_windowing_window_at_pointer (GdkDisplay *display, * WindowFromPoint() can find our windows, we follow similar logic * here, and ignore invisible and disabled windows. */ + UINT cwp_flags = CWP_SKIPDISABLED | + CWP_SKIPINVISIBLE | + CWP_SKIPTRANSPARENT; + hwnd = GetDesktopWindow (); do { window = gdk_win32_handle_table_lookup ((GdkNativeWindow) hwnd); @@ -2777,8 +2781,7 @@ _gdk_windowing_window_at_pointer (GdkDisplay *display, break; screen_to_client (hwnd, screen_pt, &client_pt); - hwndc = ChildWindowFromPointEx (hwnd, client_pt, CWP_SKIPDISABLED | - CWP_SKIPINVISIBLE); + hwndc = ChildWindowFromPointEx (hwnd, client_pt, cwp_flags); /* Verify that we're really inside the client area of the window */ if (hwndc != hwnd) @@ -2789,6 +2792,8 @@ _gdk_windowing_window_at_pointer (GdkDisplay *display, hwndc = hwnd; } + /* Only ignore top-level transparent windows */ + cwp_flags &= ~CWP_SKIPTRANSPARENT; } while (hwndc != hwnd && (hwnd = hwndc, 1)); } diff --git a/gdk/x11/gdkscreen-x11.h b/gdk/x11/gdkscreen-x11.h index 7df8e0e7f..35d5d6c13 100644 --- a/gdk/x11/gdkscreen-x11.h +++ b/gdk/x11/gdkscreen-x11.h @@ -73,7 +73,7 @@ struct _GdkScreenX11 GdkVisualPrivate *system_visual; GdkVisualPrivate **visuals; gint nvisuals; - gint available_depths[7]; + gint available_depths[8]; gint navailable_depths; GdkVisualType available_types[6]; gint navailable_types; diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 8f5e496a3..31cc16730 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -2,6 +2,9 @@ include $(top_srcdir)/Makefile.decl -include $(INTROSPECTION_MAKEFILE) INTROSPECTION_GIRS = +if OS_WIN32 +INTROSPECTION_SCANNER_ENV = PATH="$(builddir):$(builddir)/.libs:$${PATH}" +endif INTROSPECTION_SCANNER_ARGS = \ --warn-all \ --add-include-path=../gdk @@ -1139,7 +1142,7 @@ $(GTK_UPDATE_ICON_CACHE_MANIFEST): echo '' ) >$@ $(GTK_UPDATE_ICON_CACHE_RC): - (echo -e '#include \nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST '$(GTK_UPDATE_ICON_CACHE_MANIFEST)) >$@ + (echo -e '#include \nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "'$(GTK_UPDATE_ICON_CACHE_MANIFEST)'"') >$@ $(GTK_UPDATE_ICON_CACHE_MANIFEST_OBJECT): $(GTK_UPDATE_ICON_CACHE_RC) $(GTK_UPDATE_ICON_CACHE_MANIFEST) $(WINDRES) --input $< --output $@ --output-format=coff diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c index 2e453db04..e6f6be63f 100644 --- a/gtk/gtkcalendar.c +++ b/gtk/gtkcalendar.c @@ -688,8 +688,7 @@ gtk_calendar_init (GtkCalendar *calendar) #else static const char *month_format = NULL; char buffer[255]; - GDateTime *tmp_dt; - gchar *tmp_str; + time_t tmp_time; #endif GtkCalendarPrivate *priv; gchar *year_before; @@ -710,15 +709,9 @@ gtk_calendar_init (GtkCalendar *calendar) for (i=0; i<7; i++) { #ifndef G_OS_WIN32 - tmp_dt = g_date_time_new_utc (2001, 1, i + 7, 12, 0, 0.0); - if (tmp_dt) - { - tmp_str = g_date_time_format (tmp_dt, "%a"); - default_abbreviated_dayname[i] = tmp_str ? tmp_str : g_strdup (""); - g_date_time_unref (tmp_dt); - } - else - default_abbreviated_dayname[i] = g_strdup (""); + tmp_time = (i + 3) * 86400; + strftime (buffer, sizeof (buffer), "%a", gmtime (&tmp_time)); + default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL); #else if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7, wbuffer, G_N_ELEMENTS (wbuffer))) @@ -732,30 +725,24 @@ gtk_calendar_init (GtkCalendar *calendar) for (i=0; i<12; i++) { #ifndef G_OS_WIN32 - tmp_dt = g_date_time_new_utc (2001, i + 1, 1, 12, 0, 0.0); + tmp_time = i * 2764800; if (G_UNLIKELY (month_format == NULL)) { buffer[0] = '\0'; month_format = "%OB"; - tmp_str = tmp_dt ? g_date_time_format (tmp_dt, month_format) : NULL; - if (tmp_str) - { - g_strlcpy (buffer, tmp_str, sizeof (buffer)); - g_free (tmp_str); - } + strftime (buffer, sizeof (buffer), month_format, gmtime (&tmp_time)); + /* "%OB" is not supported in Linux with glibc < 2.27 */ if (!strcmp (buffer, "%OB") || !strcmp (buffer, "OB") || !strcmp (buffer, "")) - month_format = "%B"; - } - - if (tmp_dt) - { - tmp_str = g_date_time_format (tmp_dt, month_format); - default_monthname[i] = tmp_str ? tmp_str : g_strdup (""); - g_date_time_unref (tmp_dt); + { + month_format = "%B"; + strftime (buffer, sizeof (buffer), month_format, gmtime (&tmp_time)); + } } else - default_monthname[i] = g_strdup (""); + strftime (buffer, sizeof (buffer), month_format, gmtime (&tmp_time)); + + default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL); #else if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i, wbuffer, G_N_ELEMENTS (wbuffer))) diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c index 2f7007fd2..e4c98c8c2 100644 --- a/gtk/gtkcontainer.c +++ b/gtk/gtkcontainer.c @@ -40,6 +40,9 @@ #include "gtktoolbar.h" #include #include +#ifdef GDK_WINDOWING_QUARTZ +#include "gdk/quartz/gdkquartz.h" +#endif #include "gtkalias.h" @@ -1357,7 +1360,16 @@ gtk_container_idle_sizer (gpointer data) gtk_container_check_resize (GTK_CONTAINER (widget)); } +#ifdef GDK_WINDOWING_QUARTZ + /* macOS 11 and newer can trigger expensive full-window redraws here. + * Keep the traditional update processing path for older Quartz targets, + * but let the normal GDK update idle handle updates on Big Sur and newer. + */ + if (gdk_quartz_osx_version () < GDK_OSX_BIG_SUR) + gdk_window_process_all_updates (); +#else gdk_window_process_all_updates (); +#endif return FALSE; } diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c index 2ce2d6c8f..9f9aac514 100644 --- a/gtk/gtkfilechooserdefault.c +++ b/gtk/gtkfilechooserdefault.c @@ -140,14 +140,12 @@ _gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, c #endif - typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass; #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass)) #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT)) #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass)) -#define MAX_LOADING_TIME 500 #define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder") @@ -406,11 +404,11 @@ static void remove_bookmark_button_clicked_cb (GtkButton *button, static void update_cell_renderer_attributes (GtkFileChooserDefault *impl); -static void load_remove_timer (GtkFileChooserDefault *impl, LoadState new_load_state); static void browse_files_center_selected_row (GtkFileChooserDefault *impl); static void view_mode_set (GtkFileChooserDefault *impl, ViewMode view_mode); -static void view_mode_combo_box_changed_cb (GtkComboBox *combo, +static void icon_view_scale_create (GtkFileChooserDefault *impl); +static void view_mode_toggle_button_toggled_cb (GtkToggleButton *toggle, GtkFileChooserDefault *impl); static void icon_view_scale_value_changed_cb (GtkRange *range, @@ -917,9 +915,6 @@ gtk_file_chooser_default_finalize (GObject *object) search_clear_model (impl, FALSE); recent_clear_model (impl, FALSE); - /* stopping the load above should have cleared this */ - g_assert (impl->load_timeout_id == 0); - g_free (impl->preview_display_name); g_free (impl->edited_new_text); @@ -3937,6 +3932,7 @@ popup_menu_detach_cb (GtkWidget *attach_widget, impl->browse_files_popup_menu = NULL; impl->browse_files_popup_menu_add_shortcut_item = NULL; + impl->browse_files_popup_menu_copy_location_item = NULL; impl->browse_files_popup_menu_hidden_files_item = NULL; } @@ -3948,6 +3944,63 @@ add_to_shortcuts_cb (GtkMenuItem *item, bookmarks_add_selected_folder (impl); } +static void +copy_location_foreach_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GString *locations = data; + GFile *file; + gchar *location; + + gtk_tree_model_get (model, iter, + MODEL_COL_FILE, &file, + -1); + + if (file == NULL) + return; + + location = g_file_get_path (file); + + if (location == NULL) + location = g_file_get_uri (file); + + if (location != NULL) + { + if (locations->len != 0) + g_string_append_c (locations, '\n'); + + g_string_append (locations, location); + g_free (location); + } + + g_object_unref (file); +} + +static void +copy_location_cb (GtkMenuItem *item, + GtkFileChooserDefault *impl) +{ + GString *locations; + + locations = g_string_new (NULL); + + current_selection_selected_foreach (impl, + copy_location_foreach_cb, + locations); + + if (locations->len != 0) + gtk_clipboard_set_text ( + gtk_widget_get_clipboard (impl->browse_files_current_view, + GDK_SELECTION_CLIPBOARD), + locations->str, + -1); + + g_string_free (locations, TRUE); +} + + /* Callback used when the "Show Hidden Files" menu item is toggled */ static void show_hidden_toggled_cb (GtkCheckMenuItem *item, @@ -4297,6 +4350,16 @@ file_list_build_popup_menu (GtkFileChooserDefault *impl) gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item); + item = gtk_image_menu_item_new_with_mnemonic (_("_Copy Location")); + impl->browse_files_popup_menu_copy_location_item = item; + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), + gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, + GTK_ICON_SIZE_MENU)); + g_signal_connect (item, "activate", + G_CALLBACK (copy_location_cb), impl); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item); + item = gtk_separator_menu_item_new (); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item); @@ -4390,6 +4453,9 @@ file_list_update_popup_menu (GtkFileChooserDefault *impl) * bookmarks_check_add_sensitivity() */ + gtk_widget_set_sensitive (impl->browse_files_popup_menu_copy_location_item, + current_selection_count_selected_rows (impl) > 0); + /* 'Show Hidden Files' */ g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item, G_CALLBACK (show_hidden_toggled_cb), impl); @@ -4522,10 +4588,11 @@ list_popup_menu_cb (GtkWidget *widget, */ static gboolean list_button_press_event_cb (GtkWidget *widget, - GdkEventButton *event, - GtkFileChooserDefault *impl) + GdkEventButton *event, + GtkFileChooserDefault *impl) { static gboolean in_press = FALSE; + GtkTreePath *path = NULL; if (in_press) return FALSE; @@ -4533,9 +4600,56 @@ list_button_press_event_cb (GtkWidget *widget, if (!_gtk_button_event_triggers_context_menu (event)) return FALSE; - in_press = TRUE; - gtk_widget_event (widget, (GdkEvent *) event); - in_press = FALSE; + if (widget == impl->browse_files_tree_view) + { + GtkTreeView *tree_view; + GtkTreeSelection *selection; + + tree_view = GTK_TREE_VIEW (widget); + + if (gtk_tree_view_get_path_at_pos (tree_view, + (gint) event->x, + (gint) event->y, + &path, NULL, NULL, NULL)) + { + selection = gtk_tree_view_get_selection (tree_view); + + if (!gtk_tree_selection_path_is_selected (selection, path)) + { + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_path (selection, path); + } + } + } + else + { + GtkIconView *icon_view; + + icon_view = GTK_ICON_VIEW (widget); + path = gtk_icon_view_get_path_at_pos (icon_view, + (gint) event->x, + (gint) event->y); + + if (path != NULL) + { + if (!gtk_icon_view_path_is_selected (icon_view, path)) + { + gtk_icon_view_unselect_all (icon_view); + gtk_icon_view_select_path (icon_view, path); + } + + gtk_icon_view_set_cursor (icon_view, path, NULL, FALSE); + } + } + + if (path != NULL) + gtk_tree_path_free (path); + else + { + in_press = TRUE; + gtk_widget_event (widget, (GdkEvent *) event); + in_press = FALSE; + } file_list_popup_menu (impl, event); return TRUE; @@ -5208,12 +5322,22 @@ view_mode_set (GtkFileChooserDefault *impl, ViewMode view_mode) return; impl->view_mode = view_mode; - gtk_combo_box_set_active (GTK_COMBO_BOX (impl->view_mode_combo_box), - view_mode); + gtk_toggle_button_set_active ( + GTK_TOGGLE_BUTTON (impl->view_mode_toggle_button), + view_mode == VIEW_MODE_ICON); /* Creating the target view */ if (view_mode == VIEW_MODE_ICON) { + if (!impl->icon_view_scale_hbox) + { + icon_view_scale_create (impl); + gtk_box_pack_end ( + GTK_BOX (impl->browse_path_bar_hbox), + impl->icon_view_scale_hbox, + FALSE, FALSE, 0); + } + create_browse_files_icon_view (impl); impl->browse_files_current_view = impl->browse_files_icon_view; old_view = impl->browse_files_tree_view; @@ -5246,7 +5370,8 @@ view_mode_set (GtkFileChooserDefault *impl, ViewMode view_mode) impl->browse_files_icon_view = NULL; impl->list_icon_renderer = NULL; impl->list_name_renderer = NULL; - gtk_widget_hide (impl->icon_view_scale_hbox); + if (impl->icon_view_scale_hbox) + gtk_widget_hide (impl->icon_view_scale_hbox); } else g_assert_not_reached (); @@ -5264,12 +5389,14 @@ view_mode_set (GtkFileChooserDefault *impl, ViewMode view_mode) browse_files_center_selected_row (impl); } -/* Callback used when view mode combo box active item is changed */ +/* Callback used when the view mode toggle button is toggled. */ static void -view_mode_combo_box_changed_cb (GtkComboBox *combo, +view_mode_toggle_button_toggled_cb (GtkToggleButton *toggle, GtkFileChooserDefault *impl) { - ViewMode target = gtk_combo_box_get_active (combo); + ViewMode target = gtk_toggle_button_get_active (toggle) + ? VIEW_MODE_ICON + : VIEW_MODE_LIST; view_mode_set (impl, target); } @@ -5698,20 +5825,15 @@ location_button_toggled_cb (GtkToggleButton *toggle, location_mode_set (impl, new_mode, FALSE); } -/* Creates a combo box with two items: List View and Icon View. */ +/* Creates the view mode toggle. */ static void -view_mode_combo_box_create (GtkFileChooserDefault *impl) +view_mode_toggle_button_create (GtkFileChooserDefault *impl) { - impl->view_mode_combo_box = gtk_combo_box_text_new (); - gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(impl->view_mode_combo_box), - _("List View")); /* VIEW_MODE_LIST */ - gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(impl->view_mode_combo_box), - _("Icon View")); /* VIEW_MODE_ICON */ - gtk_combo_box_set_active (GTK_COMBO_BOX(impl->view_mode_combo_box), - VIEW_MODE_LIST); + impl->view_mode_toggle_button = + gtk_toggle_button_new_with_label (_("Icon View")); - g_signal_connect (impl->view_mode_combo_box, "changed", - G_CALLBACK (view_mode_combo_box_changed_cb), impl); + g_signal_connect (impl->view_mode_toggle_button, "toggled", + G_CALLBACK (view_mode_toggle_button_toggled_cb), impl); } /* Creates a hscale for the icon view. */ @@ -5719,46 +5841,48 @@ static void icon_view_scale_create (GtkFileChooserDefault *impl) { GtkObject *adj; + GtkWidget *zoom_icon; GtkWidget *image; impl->icon_view_scale_hbox = gtk_hbox_new (FALSE, 12); - impl->icon_view_scale_zoom_out_icon = gtk_event_box_new (); - gtk_event_box_set_visible_window (GTK_EVENT_BOX (impl->icon_view_scale_zoom_out_icon), FALSE); + zoom_icon = gtk_event_box_new (); + gtk_event_box_set_visible_window (GTK_EVENT_BOX (zoom_icon), FALSE); image = gtk_image_new_from_stock (GTK_STOCK_ZOOM_OUT, GTK_ICON_SIZE_BUTTON); - gtk_container_add (GTK_CONTAINER (impl->icon_view_scale_zoom_out_icon), image); + gtk_container_add (GTK_CONTAINER (zoom_icon), image); gtk_widget_show (image); - g_object_set_data (G_OBJECT (impl->icon_view_scale_zoom_out_icon), + g_object_set_data (G_OBJECT (zoom_icon), "gtk-file-chooser-zoom-direction", GINT_TO_POINTER (-1)); - gtk_widget_set_tooltip_text (impl->icon_view_scale_zoom_out_icon, _("Zoom out")); - g_signal_connect (impl->icon_view_scale_zoom_out_icon, "button-press-event", + gtk_widget_set_tooltip_text (zoom_icon, _("Zoom out")); + g_signal_connect (zoom_icon, "button-press-event", G_CALLBACK (icon_view_zoom_button_press_cb), impl); - gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->icon_view_scale_zoom_out_icon); - gtk_box_pack_start (GTK_BOX (impl->icon_view_scale_hbox), impl->icon_view_scale_zoom_out_icon, FALSE, FALSE, 0); - gtk_widget_show (impl->icon_view_scale_zoom_out_icon); + gtk_size_group_add_widget (impl->browse_path_bar_size_group, zoom_icon); + gtk_box_pack_start (GTK_BOX (impl->icon_view_scale_hbox), zoom_icon, FALSE, FALSE, 0); + gtk_widget_show (zoom_icon); - adj = gtk_adjustment_new (32, 32, 256, 16, 16, 0); + adj = gtk_adjustment_new (impl->icon_view_icon_size, + 32, 256, 16, 16, 0); impl->icon_view_scale = gtk_hscale_new (GTK_ADJUSTMENT (adj)); gtk_scale_set_draw_value (GTK_SCALE (impl->icon_view_scale), FALSE); gtk_widget_set_size_request (impl->icon_view_scale, 100, -1); gtk_box_pack_start (GTK_BOX (impl->icon_view_scale_hbox), impl->icon_view_scale, FALSE, FALSE, 0); gtk_widget_show (impl->icon_view_scale); - impl->icon_view_scale_zoom_in_icon = gtk_event_box_new (); - gtk_event_box_set_visible_window (GTK_EVENT_BOX (impl->icon_view_scale_zoom_in_icon), FALSE); + zoom_icon = gtk_event_box_new (); + gtk_event_box_set_visible_window (GTK_EVENT_BOX (zoom_icon), FALSE); image = gtk_image_new_from_stock (GTK_STOCK_ZOOM_IN, GTK_ICON_SIZE_BUTTON); - gtk_container_add (GTK_CONTAINER (impl->icon_view_scale_zoom_in_icon), image); + gtk_container_add (GTK_CONTAINER (zoom_icon), image); gtk_widget_show (image); - g_object_set_data (G_OBJECT (impl->icon_view_scale_zoom_in_icon), + g_object_set_data (G_OBJECT (zoom_icon), "gtk-file-chooser-zoom-direction", GINT_TO_POINTER (1)); - gtk_widget_set_tooltip_text (impl->icon_view_scale_zoom_in_icon, _("Zoom in")); - g_signal_connect (impl->icon_view_scale_zoom_in_icon, "button-press-event", + gtk_widget_set_tooltip_text (zoom_icon, _("Zoom in")); + g_signal_connect (zoom_icon, "button-press-event", G_CALLBACK (icon_view_zoom_button_press_cb), impl); - gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->icon_view_scale_zoom_in_icon); - gtk_box_pack_start (GTK_BOX (impl->icon_view_scale_hbox), impl->icon_view_scale_zoom_in_icon, FALSE, FALSE, 0); - gtk_widget_show (impl->icon_view_scale_zoom_in_icon); + gtk_size_group_add_widget (impl->browse_path_bar_size_group, zoom_icon); + gtk_box_pack_start (GTK_BOX (impl->icon_view_scale_hbox), zoom_icon, FALSE, FALSE, 0); + gtk_widget_show (zoom_icon); g_signal_connect (impl->icon_view_scale, "value-changed", G_CALLBACK (icon_view_scale_value_changed_cb), impl); @@ -5883,9 +6007,9 @@ path_bar_widgets_create (GtkFileChooserDefault *impl) impl->browse_path_bar_size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL); gtk_size_group_set_ignore_hidden (impl->browse_path_bar_size_group, FALSE); - /* View mode combo box */ - view_mode_combo_box_create (impl); - gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->view_mode_combo_box, FALSE, FALSE, 0); + /* View mode selector */ + view_mode_toggle_button_create (impl); + gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->view_mode_toggle_button, FALSE, FALSE, 0); /* Location button */ location_button_create (impl); @@ -5908,10 +6032,6 @@ path_bar_widgets_create (GtkFileChooserDefault *impl) /* Widgets for special modes (recently-used in Open mode, Search mode) */ special_mode_widgets_create (impl); - /* Icon view scale */ - icon_view_scale_create (impl); - gtk_box_pack_end (GTK_BOX (impl->browse_path_bar_hbox), impl->icon_view_scale_hbox, FALSE, FALSE, 0); - /* Create Folder */ impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder")); g_signal_connect (impl->browse_new_folder_button, "clicked", @@ -6477,9 +6597,9 @@ update_appearance (GtkFileChooserDefault *impl) if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) - gtk_widget_show (impl->view_mode_combo_box); + gtk_widget_show (impl->view_mode_toggle_button); else - gtk_widget_hide (impl->view_mode_combo_box); + gtk_widget_hide (impl->view_mode_toggle_button); if (impl->location_entry) _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action); @@ -7043,13 +7163,16 @@ settings_load (GtkFileChooserDefault *impl) location_mode_set (impl, location_mode, TRUE); - g_signal_handlers_block_by_func (impl->icon_view_scale, - G_CALLBACK (icon_view_scale_value_changed_cb), - impl); - gtk_range_set_value (GTK_RANGE (impl->icon_view_scale), icon_view_scale); - g_signal_handlers_unblock_by_func (impl->icon_view_scale, - G_CALLBACK (icon_view_scale_value_changed_cb), - impl); + if (impl->icon_view_scale) + { + g_signal_handlers_block_by_func (impl->icon_view_scale, + G_CALLBACK (icon_view_scale_value_changed_cb), + impl); + gtk_range_set_value (GTK_RANGE (impl->icon_view_scale), icon_view_scale); + g_signal_handlers_unblock_by_func (impl->icon_view_scale, + G_CALLBACK (icon_view_scale_value_changed_cb), + impl); + } view_mode_set (impl, view_mode); @@ -7352,61 +7475,8 @@ load_set_model (GtkFileChooserDefault *impl) profile_end ("end", NULL); } -/* Timeout callback used when the loading timer expires */ -static gboolean -load_timeout_cb (gpointer data) -{ - GtkFileChooserDefault *impl; - profile_start ("start", NULL); - impl = GTK_FILE_CHOOSER_DEFAULT (data); - g_assert (impl->load_state == LOAD_PRELOAD); - g_assert (impl->load_timeout_id != 0); - g_assert (impl->browse_files_model != NULL); - - impl->load_timeout_id = 0; - impl->load_state = LOAD_LOADING; - - load_set_model (impl); - - profile_end ("end", NULL); - - return FALSE; -} - -/* Sets up a new load timer for the model and switches to the LOAD_PRELOAD state */ -static void -load_setup_timer (GtkFileChooserDefault *impl) -{ - g_assert (impl->load_timeout_id == 0); - g_assert (impl->load_state != LOAD_PRELOAD); - - impl->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl); - impl->load_state = LOAD_PRELOAD; -} - -/* Removes the load timeout; changes the impl->load_state to the specified value. */ -static void -load_remove_timer (GtkFileChooserDefault *impl, LoadState new_load_state) -{ - if (impl->load_timeout_id != 0) - { - g_assert (impl->load_state == LOAD_PRELOAD); - - g_source_remove (impl->load_timeout_id); - impl->load_timeout_id = 0; - } - else - g_assert (impl->load_state == LOAD_EMPTY || - impl->load_state == LOAD_LOADING || - impl->load_state == LOAD_FINISHED); - - g_assert (new_load_state == LOAD_EMPTY || - new_load_state == LOAD_LOADING || - new_load_state == LOAD_FINISHED); - impl->load_state = new_load_state; -} /* Selects the first row in the file list */ static void @@ -7625,15 +7695,8 @@ browse_files_model_finished_loading_cb (GtkFileSystemModel *model, if (error) show_error_on_reading_current_folder (impl, error); - if (impl->load_state == LOAD_PRELOAD) - { - load_remove_timer (impl, LOAD_FINISHED); - load_set_model (impl); - } - else if (impl->load_state == LOAD_LOADING) - { - /* Nothing */ - } + if (impl->load_state == LOAD_LOADING) + load_set_model (impl); else { /* We can't g_assert_not_reached(), as something other than us may have @@ -7643,8 +7706,6 @@ browse_files_model_finished_loading_cb (GtkFileSystemModel *model, return; } - g_assert (impl->load_timeout_id == 0); - impl->load_state = LOAD_FINISHED; pending_select_files_process (impl); @@ -7660,7 +7721,7 @@ static void stop_loading_and_clear_list_model (GtkFileChooserDefault *impl, gboolean remove_from_treeview) { - load_remove_timer (impl, LOAD_EMPTY); + impl->load_state = LOAD_EMPTY; if (impl->browse_files_model) { @@ -8087,12 +8148,15 @@ set_list_model (GtkFileChooserDefault *impl, gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_SIZE, size_sort_func, impl, NULL); gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_MTIME, mtime_sort_func, impl, NULL); gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), NULL, NULL, NULL); - set_sort_column (impl); + gtk_tree_sortable_set_sort_column_id + (GTK_TREE_SORTABLE (impl->browse_files_model), + impl->sort_column, + impl->sort_order); impl->list_sort_ascending = TRUE; g_signal_connect (impl->browse_files_model, "sort-column-changed", G_CALLBACK (list_sort_column_changed_cb), impl); - load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */ + impl->load_state = LOAD_LOADING; g_signal_connect (impl->browse_files_model, "finished-loading", G_CALLBACK (browse_files_model_finished_loading_cb), impl); diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h index 193dea2e3..40466c68a 100644 --- a/gtk/gtkfilechooserprivate.h +++ b/gtk/gtkfilechooserprivate.h @@ -122,8 +122,7 @@ struct _GtkFileChooserWidgetPrivate typedef enum { LOAD_EMPTY, /* There is no model */ - LOAD_PRELOAD, /* Model is loading and a timer is running; model isn't inserted into the tree yet */ - LOAD_LOADING, /* Timeout expired, model is inserted into the tree, but not fully loaded yet */ + LOAD_LOADING, /* Model is loading; inserted into the view when loading finishes */ LOAD_FINISHED /* Model is fully loaded and inserted into the tree */ } LoadState; @@ -182,6 +181,7 @@ struct _GtkFileChooserDefault GtkWidget *browse_files_icon_view; GtkWidget *browse_files_popup_menu; GtkWidget *browse_files_popup_menu_add_shortcut_item; + GtkWidget *browse_files_popup_menu_copy_location_item; GtkWidget *browse_files_popup_menu_hidden_files_item; GtkWidget *browse_files_popup_menu_size_column_item; GtkWidget *browse_files_popup_menu_sort_by_name_item; @@ -227,11 +227,9 @@ struct _GtkFileChooserDefault GtkWidget *extra_align; GtkWidget *extra_widget; - GtkWidget *view_mode_combo_box; + GtkWidget *view_mode_toggle_button; GtkWidget *icon_view_scale_hbox; GtkWidget *icon_view_scale; - GtkWidget *icon_view_scale_zoom_in_icon; - GtkWidget *icon_view_scale_zoom_out_icon; guint icon_view_scale_timeout_id; guint icon_view_scale_visible_timeout_id; guint icon_view_scale_after_layout_id; @@ -264,7 +262,6 @@ struct _GtkFileChooserDefault LoadState load_state; ReloadState reload_state; - guint load_timeout_id; OperationMode operation_mode; diff --git a/gtk/gtkfilesystemmodel.c b/gtk/gtkfilesystemmodel.c index 52fd17a08..6adc1c37a 100644 --- a/gtk/gtkfilesystemmodel.c +++ b/gtk/gtkfilesystemmodel.c @@ -36,89 +36,17 @@ /*** Structure: how GtkFileSystemModel works * - * This is a custom GtkTreeModel used to hold a collection of files for GtkFileChooser. There are two use cases: + * File nodes have stable IDs in fixed-size slot storage. model->order holds + * logical sort order and model->visible_order is the GtkTreeModel projection. + * file_lookup permanently maps GFile to stable node ID. * - * 1. The model populates itself from a folder, using the GIO file enumerator API. This happens if you use - * _gtk_file_system_model_new_for_directory(). This is the normal usage for showing the contents of a folder. + * Cached column values live in sparse 64-ID pages allocated per used column, + * so unused columns have no per-node GValue storage. * - * 2. The caller populates the model by hand, with files not necessarily in the same folder. This happens - * if you use _gtk_file_system_model_new() and then _gtk_file_system_model_add_and_query_file(). This is - * the special kind of usage for "search" and "recent-files", where the file chooser gives the model the - * files to be displayed. - * - * Internal data structure - * ----------------------- - * - * Each file is kept in a FileModelNode structure. Each FileModelNode holds a GFile* and other data. All the - * node structures have the same size, determined at runtime, depending on the number of columns that were passed - * to _gtk_file_system_model_new() or _gtk_file_system_model_new_for_directory() (that is, the size of a node is - * not sizeof (FileModelNode), but rather model->node_size). The last field in the FileModelNode structure, - * node->values[], is an array of GValue, used to hold the data for those columns. - * - * The model stores an array of FileModelNode structures in model->files. This is a GArray where each element is - * model->node_size bytes in size (the model computes that node size when initializing itself). There are - * convenience macros, get_node() and node_index(), to access that array based on an array index or a pointer to - * a node inside the array. - * - * The model accesses files through two of its fields: - * - * model->files - GArray of FileModelNode structures. - * - * model->file_lookup - hash table that maps a GFile* to an index inside the model->files array. - * - * The model->file_lookup hash table is populated lazily. It is both accessed and populated with the - * node_get_for_file() function. The invariant is that the files in model->files[n] for n < g_hash_table_size - * (model->file_lookup) are already added to the hash table. The hash table will get cleared when we re-sort the - * files, as the array will be in a different order and the indexes need to be rebuilt. - * - * Each FileModelNode has a node->visible field, which indicates whether the node is visible in the GtkTreeView. - * A node may be invisible if, for example, it corresponds to a hidden file and the file chooser is not showing - * hidden files. Also, a file filter may be explicitly set onto the model, for example, to only show files that - * match "*.jpg". In this case, node->filtered_out says whether the node failed the filter. The ultimate - * decision on whether a node is visible or not in the treeview is distilled into the node->visible field. - * The reason for having a separate node->filtered_out field is so that the file chooser can query whether - * a (filtered-out) folder should be made sensitive in the GUI. - * - * Visible rows vs. possibly-invisible nodes - * ----------------------------------------- - * - * Since not all nodes in the model->files array may be visible, we need a way to map visible row indexes from - * the treeview to array indexes in our array of files. And thus we introduce a bit of terminology: - * - * index - An index in the model->files array. All variables/fields that represent indexes are either called - * "index" or "i_*", or simply "i" for things like loop counters. - * - * row - An index in the GtkTreeView, i.e. the index of a row within the outward-facing API of the - * GtkFileSystemModel. However, note that our rows are 1-based, not 0-based, for the reason explained in the - * following paragraph. Variables/fields that represent visible rows are called "row", or "r_*", or simply - * "r". - * - * Each FileModelNode has a node->row field which is the number of visible rows in the treeview, *before and - * including* that node. This means that node->row is 1-based, instead of 0-based --- this makes some code - * simpler, believe it or not :) This also means that when the calling GtkTreeView gives us a GtkTreePath, we - * turn the 0-based treepath into a 1-based row for our purposes. If a node is not visible, it will have the - * same row number as its closest preceding visible node. - * - * We try to compute the node->row fields lazily. A node is said to be "valid" if its node->row is accurate. - * For this, the model keeps a model->n_nodes_valid field which is the count of valid nodes starting from the - * beginning of the model->files array. When a node changes its information, or when a node gets deleted, that - * node and the following ones get invalidated by simply setting model->n_nodes_valid to the array index of the - * node. If the model happens to need a node's row number and that node is in the model->files array after - * model->n_nodes_valid, then the nodes get re-validated up to the sought node. See node_validate_rows() for - * this logic. - * - * You never access a node->row directly. Instead, call node_get_tree_row(). That function will validate the nodes - * up to the sought one if the node is not valid yet, and it will return a proper 0-based row. - * - * Sorting - * ------- - * - * The model implements the GtkTreeSortable interface. To avoid re-sorting - * every time a node gets added (which would lead to O(n^2) performance during - * the initial population of the model), the model can freeze itself (with - * freeze_updates()) during the intial population process. When the model is - * frozen, sorting will not happen. The model will sort itself when the freeze - * count goes back to zero, via corresponding calls to thaw_updates(). + * Directory enumeration uses one bounded GIO batch as its pending queue. + * Integration is cooperatively drained in short main-loop slices; the next + * GIO batch is requested only after the current one has been consumed. + * Editing has its own hold state and no longer reuses directory batching. */ /*** DEFINES ***/ @@ -130,69 +58,107 @@ /* random number that everyone else seems to use, too */ #define FILES_PER_QUERY 100 +#define MODEL_DRAIN_SLICE_US 2000 +#define MODEL_DRAIN_CLOCK_GRANULARITY 4 +#define MODEL_FINALIZE_CLOCK_GRANULARITY 8 + +enum +{ + MODEL_FINALIZE_NONE, + MODEL_FINALIZE_MERGE, + MODEL_FINALIZE_VISIBLE, + MODEL_FINALIZE_REPLAY +}; + +#define VALUE_PAGE_SHIFT 6 +#define VALUE_PAGE_SIZE (1u << VALUE_PAGE_SHIFT) +#define VALUE_PAGE_MASK (VALUE_PAGE_SIZE - 1u) typedef struct _FileModelNode FileModelNode; typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass; struct _FileModelNode { - GFile * file; /* file represented by this node or NULL for editable */ - GFileInfo * info; /* info for this file or NULL if unknown */ + GFile * file; + GFileInfo * info; - guint row; /* if valid (see model->n_valid_indexes), visible nodes before and including - * this one - see the "Structure" comment above. - */ + guint order_pos; + guint visible_pos; + guint generation; - guint visible :1; /* if the file is currently visible */ - guint filtered_out :1;/* if the file is currently filtered out (i.e. it didn't pass the filters) */ - guint frozen_add :1; /* true if the model was frozen and the entry has not been added yet */ - - GValue values[1]; /* actually n_columns values */ + guint visible :1; + guint filtered_out :1; + guint held_add :1; }; +typedef struct +{ + GValue values[VALUE_PAGE_SIZE]; +} ValuePage; + +typedef struct +{ + GFile *file; + GFileMonitorEvent type; +} PendingMonitorChange; + +typedef GWeakRef PendingMonitorQuery; + struct _GtkFileSystemModel { GObject parent_instance; + GFile * dir; + char * attributes; + GFileMonitor * dir_monitor; + GFileEnumerator * dir_enumerator; + GCancellable * cancellable; + GList * pending_files; + guint drain_source; - GFile * dir; /* directory that's displayed */ - guint dir_thaw_source;/* GSource id for unfreezing the model */ - char * attributes; /* attributes the file info must contain, or NULL for all attributes */ - GFileMonitor * dir_monitor; /* directory that is monitored, or NULL if monitoring was not supported */ + GArray * sort_scratch; + guint sort_width; + guint sort_left; + guint sort_i; + guint sort_j; + guint sort_k; + guint sort_mid; + guint sort_right; + guint finalize_phase; + GError * load_error; - GCancellable * cancellable; /* cancellable in use for all operations - cancelled on dispose */ - GArray * files; /* array of FileModelNode containing all our files */ - gsize node_size; /* Size of a FileModelNode structure once its ->values field has n_columns */ - guint n_nodes_valid; /* count of valid nodes (i.e. those whose node->row is accurate) */ - GHashTable * file_lookup; /* mapping of GFile => array index in model->files - * This hash table doesn't always have the same number of entries as the files array; - * it can get cleared completely when we resort. - * The hash table gets re-populated in node_get_for_file() if this mismatch is - * detected. - */ + GQueue * pending_monitor_changes; + GHashTable * pending_monitor_queries; - guint n_columns; /* number of columns */ - GType * column_types; /* types of each column */ - GtkFileSystemModelGetValue get_func; /* function to call to fill in values in columns */ - gpointer get_data; /* data to pass to get_func */ + GArray * files; + GArray * order; + GArray * visible_order; + guint free_id_head; + GHashTable * file_lookup; - GtkFileFilter * filter; /* filter to use for deciding which nodes are visible */ + guint n_columns; + GType * column_types; + GPtrArray ** value_pages; + GtkFileSystemModelGetValue get_func; + gpointer get_data; - int sort_column_id; /* current sorting column */ - GtkSortType sort_order; /* current sorting order */ - GList * sort_list; /* list of sorting functions */ - GtkTreeIterCompareFunc default_sort_func; /* default sort function */ - gpointer default_sort_data; /* data to pass to default sort func */ - GDestroyNotify default_sort_destroy; /* function to call to destroy default_sort_data */ + GtkFileFilter * filter; - guint frozen; /* number of times we're frozen */ + int sort_column_id; + GtkSortType sort_order; + GList * sort_list; + GtkTreeIterCompareFunc default_sort_func; + gpointer default_sort_data; + GDestroyNotify default_sort_destroy; - gboolean filter_on_thaw :1;/* set when filtering needs to happen upon thawing */ - gboolean sort_on_thaw :1;/* set when sorting needs to happen upon thawing */ + guint editing_hold :1; + guint refilter_pending :1; + guint resort_pending :1; + guint directory_loading :1; - guint show_hidden :1; /* whether to show hidden files */ - guint show_folders :1;/* whether to show folders */ - guint show_files :1; /* whether to show files */ - guint filter_folders :1;/* whether filter applies to folders */ + guint show_hidden :1; + guint show_folders :1; + guint show_files :1; + guint filter_folders :1; }; #define GTK_FILE_SYSTEM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass)) @@ -208,8 +174,17 @@ struct _GtkFileSystemModelClass void (*finished_loading) (GtkFileSystemModel *model, GError *error); }; -static void freeze_updates (GtkFileSystemModel *model); -static void thaw_updates (GtkFileSystemModel *model); +static void gtk_file_system_model_got_files (GObject *object, + GAsyncResult *res, + gpointer data); +static gboolean gtk_file_system_model_drain (gpointer data); +static void gtk_file_system_model_schedule_work (GtkFileSystemModel *model); +static gboolean gtk_file_system_model_finalize_load (gpointer data); +static void gtk_file_system_model_begin_finalize_load (GtkFileSystemModel *model, + GError *error); +static void gtk_file_system_model_apply_monitor_change (GtkFileSystemModel *model, + GFile *file, + GFileMonitorEvent type); static guint node_get_for_file (GtkFileSystemModel *model, GFile *file); @@ -221,89 +196,118 @@ static void remove_file (GtkFileSystemModel *model, GFile *file); /* iter setup: - * @user_data: the model - * @user_data2: GUINT_TO_POINTER of array index of current entry - * - * All other fields are unused. Note that the array index does not corrspond - * 1:1 with the path index as entries might not be visible. + * @user_data: the model + * @user_data2: stable node ID + * @user_data3: node generation */ -#define ITER_INDEX(iter) GPOINTER_TO_UINT((iter)->user_data2) -#define ITER_IS_VALID(model, iter) ((model) == (iter)->user_data) -#define ITER_INIT_FROM_INDEX(model, _iter, _index) G_STMT_START {\ - g_assert (_index < (model)->files->len); \ +#define ITER_INDEX(iter) GPOINTER_TO_UINT ((iter)->user_data2) + +#define get_node(_model, _index) \ + (&g_array_index ((_model)->files, FileModelNode, (_index))) + +static gboolean +iter_is_valid (GtkFileSystemModel *model, + GtkTreeIter *iter) +{ + guint id; + FileModelNode *node; + + if (iter == NULL || iter->user_data != model) + return FALSE; + + id = ITER_INDEX (iter); + if (id >= model->files->len) + return FALSE; + + node = get_node (model, id); + return (id == 0 || node->file != NULL) && + node->generation == GPOINTER_TO_UINT (iter->user_data3); +} + +#define ITER_IS_VALID(model, iter) iter_is_valid ((model), (iter)) + +#define ITER_INIT_FROM_INDEX(model, _iter, _index) G_STMT_START { \ + FileModelNode *_node; \ + g_assert ((_index) < (model)->files->len); \ + _node = get_node ((model), (_index)); \ + g_assert ((_index) == 0 || _node->file != NULL); \ (_iter)->user_data = (model); \ (_iter)->user_data2 = GUINT_TO_POINTER (_index); \ -}G_STMT_END + (_iter)->user_data3 = GUINT_TO_POINTER (_node->generation); \ +} G_STMT_END -/*** FileModelNode ***/ - -/* Get a FileModelNode structure given an index in the model->files array of nodes */ -#define get_node(_model, _index) ((FileModelNode *) ((_model)->files->data + (_index) * (_model)->node_size)) - -/* Get an index within the model->files array of nodes, given a FileModelNode* */ -#define node_index(_model, _node) (((gchar *) (_node) - (_model)->files->data) / (_model)->node_size) - -/* @up_to_index: smallest model->files array index that will be valid after this call - * @up_to_row: smallest node->row that will be valid after this call - * - * If you want to validate up to an index or up to a row, specify the index or - * the row you want and specify G_MAXUINT for the other argument. Pass - * G_MAXUINT for both arguments for "validate everything". - */ static void -node_validate_rows (GtkFileSystemModel *model, guint up_to_index, guint up_to_row) +order_update_positions_from (GtkFileSystemModel *model, + guint first) { - guint i, row; + guint i; - if (model->files->len == 0) - return; - - up_to_index = MIN (up_to_index, model->files->len - 1); - - i = model->n_nodes_valid; - if (i != 0) - row = get_node (model, i - 1)->row; - else - row = 0; - - while (i <= up_to_index && row <= up_to_row) + for (i = first; i < model->order->len; i++) { - FileModelNode *node = get_node (model, i); - if (node->visible) - row++; - node->row = row; - i++; + guint id = g_array_index (model->order, guint, i); + get_node (model, id)->order_pos = i; + } +} + +static void +visible_update_positions_from (GtkFileSystemModel *model, + guint first) +{ + guint i; + + for (i = first; i < model->visible_order->len; i++) + { + guint id = g_array_index (model->visible_order, guint, i); + get_node (model, id)->visible_pos = i; } - model->n_nodes_valid = i; } static guint -node_get_tree_row (GtkFileSystemModel *model, guint index) +visible_find_order_pos (GtkFileSystemModel *model, + guint order_pos, + gboolean after) { - if (model->n_nodes_valid <= index) - node_validate_rows (model, index, G_MAXUINT); + guint lo = get_node (model, 0)->visible ? 1 : 0; + guint hi = model->visible_order->len; - return get_node (model, index)->row - 1; -} + while (lo < hi) + { + guint mid = lo + (hi - lo) / 2; + guint id = g_array_index (model->visible_order, guint, mid); + guint pos = get_node (model, id)->order_pos; -static void -node_invalidate_index (GtkFileSystemModel *model, guint id) -{ - model->n_nodes_valid = MIN (model->n_nodes_valid, id); + if (pos < order_pos || (after && pos == order_pos)) + lo = mid + 1; + else + hi = mid; + } + + return lo; } static GtkTreePath * -tree_path_new_from_node (GtkFileSystemModel *model, guint id) +tree_path_new_from_node (GtkFileSystemModel *model, + guint id) { - guint r = node_get_tree_row (model, id); + FileModelNode *node = get_node (model, id); + guint row = G_MAXUINT; - g_assert (r < model->files->len); + if (node->visible) + row = node->visible_pos; + else if (id != 0) + { + guint pos = visible_find_order_pos (model, node->order_pos, TRUE); + if (pos > 0) + row = pos - 1; + } - return gtk_tree_path_new_from_indices (r, -1); + g_assert (row != G_MAXUINT); + return gtk_tree_path_new_from_indices (row, -1); } static void -emit_row_inserted_for_node (GtkFileSystemModel *model, guint id) +emit_row_inserted_for_node (GtkFileSystemModel *model, + guint id) { GtkTreePath *path; GtkTreeIter iter; @@ -315,7 +319,8 @@ emit_row_inserted_for_node (GtkFileSystemModel *model, guint id) } static void -emit_row_changed_for_node (GtkFileSystemModel *model, guint id) +emit_row_changed_for_node (GtkFileSystemModel *model, + guint id) { GtkTreePath *path; GtkTreeIter iter; @@ -327,7 +332,8 @@ emit_row_changed_for_node (GtkFileSystemModel *model, guint id) } static void -emit_row_deleted_for_row (GtkFileSystemModel *model, guint row) +emit_row_deleted_for_row (GtkFileSystemModel *model, + guint row) { GtkTreePath *path; @@ -337,11 +343,167 @@ emit_row_deleted_for_row (GtkFileSystemModel *model, guint row) } static void -node_set_visible_and_filtered_out (GtkFileSystemModel *model, guint id, gboolean visible, gboolean filtered_out) +visible_remove_node (GtkFileSystemModel *model, + guint id) { FileModelNode *node = get_node (model, id); + guint pos; - /* Filteredness */ + g_assert (node->visible); + pos = node->visible_pos; + + g_array_remove_index (model->visible_order, pos); + node->visible = FALSE; + visible_update_positions_from (model, pos); +} + +static void +value_page_free (gpointer data) +{ + ValuePage *page = data; + guint slot; + + if (page) + for (slot = 0; slot < VALUE_PAGE_SIZE; slot++) + if (G_VALUE_TYPE (&page->values[slot]) != G_TYPE_INVALID) + g_value_unset (&page->values[slot]); + + g_free (page); +} + +static void +file_model_node_clear (gpointer data) +{ + FileModelNode *node = data; + + g_clear_object (&node->file); + g_clear_object (&node->info); +} + +static GValue * +value_cache_slot (GtkFileSystemModel *model, + guint id, + guint column, + gboolean create) +{ + GPtrArray *pages; + ValuePage *page; + guint page_index; + + g_assert (column < model->n_columns); + + pages = model->value_pages[column]; + if (pages == NULL) + { + if (!create) + return NULL; + + pages = g_ptr_array_new_with_free_func (value_page_free); + model->value_pages[column] = pages; + } + + page_index = id >> VALUE_PAGE_SHIFT; + if (page_index >= pages->len) + { + if (!create) + return NULL; + + g_ptr_array_set_size (pages, page_index + 1); + } + + page = pages->pdata[page_index]; + if (page == NULL && create) + { + page = g_new0 (ValuePage, 1); + pages->pdata[page_index] = page; + } + + if (page == NULL) + return NULL; + + return &page->values[id & VALUE_PAGE_MASK]; +} + +static gboolean +value_cache_clear_slot (GtkFileSystemModel *model, + guint id, + guint column) +{ + GValue *value = value_cache_slot (model, id, column, FALSE); + + if (value == NULL || G_VALUE_TYPE (value) == G_TYPE_INVALID) + return FALSE; + + g_value_unset (value); + return TRUE; +} + +static void +value_cache_clear_node (GtkFileSystemModel *model, + guint id) +{ + guint column; + + for (column = 0; column < model->n_columns; column++) + value_cache_clear_slot (model, id, column); +} + + + +static guint +node_allocate_slot (GtkFileSystemModel *model) +{ + FileModelNode *node; + guint id; + + if (model->free_id_head != 0) + { + id = model->free_id_head; + node = get_node (model, id); + model->free_id_head = node->order_pos; + } + else + { + id = model->files->len; + g_array_set_size (model->files, id + 1); + node = get_node (model, id); + node->generation = 1; + } + + return id; +} + +static void +node_retire_slot (GtkFileSystemModel *model, + guint id) +{ + FileModelNode *node; + guint generation; + + g_assert (id != 0); + + node = get_node (model, id); + file_model_node_clear (node); + value_cache_clear_node (model, id); + + generation = node->generation + 1; + if (generation == 0) + generation = 1; + + memset (node, 0, sizeof (*node)); + node->generation = generation; + node->order_pos = model->free_id_head; + + model->free_id_head = id; +} + +static void +node_set_visible_and_filtered_out (GtkFileSystemModel *model, + guint id, + gboolean visible, + gboolean filtered_out) +{ + FileModelNode *node = get_node (model, id); if (node->filtered_out != filtered_out) { @@ -350,27 +512,25 @@ node_set_visible_and_filtered_out (GtkFileSystemModel *model, guint id, gboolean emit_row_changed_for_node (model, id); } - /* Visibility */ - - if (node->visible == visible || - node->frozen_add) + if (node->visible == visible || node->held_add) return; if (visible) { + guint pos; + + g_assert (!node->visible); + pos = id == 0 ? 0 : + visible_find_order_pos (model, node->order_pos, FALSE); + g_array_insert_val (model->visible_order, pos, id); node->visible = TRUE; - node_invalidate_index (model, id); + visible_update_positions_from (model, pos); emit_row_inserted_for_node (model, id); } else { - guint row; - - row = node_get_tree_row (model, id); - g_assert (row < model->files->len); - - node->visible = FALSE; - node_invalidate_index (model, id); + guint row = node->visible_pos; + visible_remove_node (model, id); emit_row_deleted_for_row (model, row); } } @@ -524,73 +684,29 @@ gtk_file_system_model_get_column_type (GtkTreeModel *tree_model, return model->column_types[i]; } -static int -compare_indices (gconstpointer key, gconstpointer _node) -{ - const FileModelNode *node = _node; - - return GPOINTER_TO_UINT (key) - node->row; -} - static gboolean gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n) + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) { GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); - char *node; guint id; - guint row_to_find; g_return_val_if_fail (n >= 0, FALSE); - if (parent != NULL) + if (parent != NULL || (guint) n >= model->visible_order->len) return FALSE; - row_to_find = n + 1; /* plus one as our node->row numbers are 1-based; see the "Structure" comment at the beginning */ - - if (model->n_nodes_valid > 0 && - get_node (model, model->n_nodes_valid - 1)->row >= row_to_find) - { - /* Fast path - the nodes are valid up to the sought one. - * - * First, find a node with the sought row number...*/ - - node = bsearch (GUINT_TO_POINTER (row_to_find), - model->files->data, - model->n_nodes_valid, - model->node_size, - compare_indices); - if (node == NULL) - return FALSE; - - /* ... Second, back up until we find the first visible node with that row number */ - - id = node_index (model, node); - while (!get_node (model, id)->visible) - id--; - - g_assert (get_node (model, id)->row == row_to_find); - } - else - { - /* Slow path - the nodes need to be validated up to the sought one */ - - node_validate_rows (model, G_MAXUINT, n); /* note that this is really "n", not row_to_find - see node_validate_rows() */ - id = model->n_nodes_valid - 1; - if (model->n_nodes_valid == 0 || get_node (model, id)->row != row_to_find) - return FALSE; - } - + id = g_array_index (model->visible_order, guint, n); ITER_INIT_FROM_INDEX (model, iter, id); return TRUE; } static gboolean gtk_file_system_model_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) + GtkTreeIter *iter, + GtkTreePath *path) { g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); @@ -605,12 +721,11 @@ gtk_file_system_model_get_iter (GtkTreeModel *tree_model, static GtkTreePath * gtk_file_system_model_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter) + GtkTreeIter *iter) { GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); g_return_val_if_fail (ITER_IS_VALID (model, iter), NULL); - return tree_path_new_from_node (model, ITER_INDEX (iter)); } @@ -638,25 +753,29 @@ gtk_file_system_model_get_value (GtkTreeModel *tree_model, static gboolean gtk_file_system_model_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter) + GtkTreeIter *iter) { GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); - guint i; + FileModelNode *node; + guint pos; g_return_val_if_fail (ITER_IS_VALID (model, iter), FALSE); - for (i = ITER_INDEX (iter) + 1; i < model->files->len; i++) - { - FileModelNode *node = get_node (model, i); + node = get_node (model, ITER_INDEX (iter)); - if (node->visible) - { - ITER_INIT_FROM_INDEX (model, iter, i); - return TRUE; - } - } + if (node->visible) + pos = node->visible_pos + 1; + else if (ITER_INDEX (iter) != 0) + pos = visible_find_order_pos (model, node->order_pos, TRUE); + else + return FALSE; - return FALSE; + if (pos >= model->visible_order->len) + return FALSE; + + ITER_INIT_FROM_INDEX (model, iter, + g_array_index (model->visible_order, guint, pos)); + return TRUE; } static gboolean @@ -676,14 +795,14 @@ gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model, static gint gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter) + GtkTreeIter *iter) { GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); if (iter) return 0; - return node_get_tree_row (model, model->files->len - 1) + 1; + return model->visible_order->len; } static gboolean @@ -743,7 +862,7 @@ sort_data_init (SortData *data, GtkFileSystemModel *model) { GtkTreeDataSortHeader *header; - if (model->files->len <= 2) + if (model->order->len <= 1) return FALSE; switch (model->sort_column_id) @@ -757,7 +876,8 @@ sort_data_init (SortData *data, GtkFileSystemModel *model) case GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID: return FALSE; default: - header = _gtk_tree_data_list_get_header (model->sort_list, model->sort_column_id); + header = _gtk_tree_data_list_get_header (model->sort_list, + model->sort_column_id); if (header == NULL) return FALSE; data->func = header->func; @@ -771,14 +891,67 @@ sort_data_init (SortData *data, GtkFileSystemModel *model) } static int -compare_array_element (gconstpointer a, gconstpointer b, gpointer user_data) +compare_order_element (gconstpointer a, gconstpointer b, gpointer user_data) { SortData *data = user_data; GtkTreeIter itera, iterb; + guint ida = *(const guint *) a; + guint idb = *(const guint *) b; - ITER_INIT_FROM_INDEX (data->model, &itera, node_index (data->model, a)); - ITER_INIT_FROM_INDEX (data->model, &iterb, node_index (data->model, b)); - return data->func (GTK_TREE_MODEL (data->model), &itera, &iterb, data->data) * data->order; + ITER_INIT_FROM_INDEX (data->model, &itera, ida); + ITER_INIT_FROM_INDEX (data->model, &iterb, idb); + + return data->func (GTK_TREE_MODEL (data->model), + &itera, &iterb, data->data) * data->order; +} + +static void +visible_order_rebuild_after_sort (GtkFileSystemModel *model) +{ + guint old_len = model->visible_order->len; + int *new_order; + guint row = 0; + guint i; + FileModelNode *editable; + + if (old_len == 0) + return; + + new_order = g_new (int, old_len); + g_array_set_size (model->visible_order, 0); + + editable = get_node (model, 0); + if (editable->visible) + { + guint id = 0; + new_order[row] = editable->visible_pos; + g_array_append_val (model->visible_order, id); + editable->visible_pos = row++; + } + + for (i = 0; i < model->order->len; i++) + { + guint id = g_array_index (model->order, guint, i); + FileModelNode *node = get_node (model, id); + + if (!node->visible) + continue; + + new_order[row] = node->visible_pos; + g_array_append_val (model->visible_order, id); + node->visible_pos = row++; + } + + g_assert (row == old_len); + + { + GtkTreePath *path = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model), + path, NULL, new_order); + gtk_tree_path_free (path); + } + + g_free (new_order); } static void @@ -786,67 +959,26 @@ gtk_file_system_model_sort (GtkFileSystemModel *model) { SortData data; - if (model->frozen) + if (model->directory_loading || model->editing_hold) { - model->sort_on_thaw = TRUE; + model->resort_pending = TRUE; return; } if (sort_data_init (&data, model)) { - GtkTreePath *path; - guint i; - guint r, n_visible_rows; - - node_validate_rows (model, G_MAXUINT, G_MAXUINT); - n_visible_rows = node_get_tree_row (model, model->files->len - 1) + 1; - model->n_nodes_valid = 0; - g_hash_table_remove_all (model->file_lookup); - _gtk_sort_array (get_node (model, 1), /* start at index 1; don't sort the editable row */ - model->files->len - 1, - model->node_size, - compare_array_element, + _gtk_sort_array (model->order->data, + model->order->len, + sizeof (guint), + compare_order_element, &data); - g_assert (model->n_nodes_valid == 0); - g_assert (g_hash_table_size (model->file_lookup) == 0); - if (n_visible_rows) - { - int *new_order = g_new (int, n_visible_rows); - - r = 0; - for (i = 0; i < model->files->len; i++) - { - FileModelNode *node = get_node (model, i); - if (!node->visible) - { - node->row = r; - continue; - } - - new_order[r] = node->row - 1; - r++; - node->row = r; - } - g_assert (r == n_visible_rows); - path = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model), - path, - NULL, - new_order); - gtk_tree_path_free (path); - g_free (new_order); - } + order_update_positions_from (model, 0); + visible_order_rebuild_after_sort (model); } - model->sort_on_thaw = FALSE; + model->resort_pending = FALSE; } -static void -gtk_file_system_model_sort_node (GtkFileSystemModel *model, guint node) -{ - /* FIXME: improve */ - gtk_file_system_model_sort (model); -} static gboolean gtk_file_system_model_get_sort_column_id (GtkTreeSortable *sortable, @@ -1023,6 +1155,280 @@ enum { static guint file_system_model_signals[LAST_SIGNAL] = { 0 }; +static void +pending_monitor_change_free (gpointer data) +{ + PendingMonitorChange *change = data; + + g_object_unref (change->file); + g_free (change); +} + +static void +gtk_file_system_model_prepare_merge (GtkFileSystemModel *model) +{ + guint n = model->order->len; + + model->sort_mid = MIN (model->sort_left + model->sort_width, n); + model->sort_right = MIN (model->sort_left + 2 * model->sort_width, n); + model->sort_i = model->sort_left; + model->sort_j = model->sort_mid; + model->sort_k = model->sort_left; +} + +static void +gtk_file_system_model_prepare_visible (GtkFileSystemModel *model) +{ + FileModelNode *editable = get_node (model, 0); + + g_array_set_size (model->visible_order, 0); + if (editable->visible) + { + guint id = 0; + + editable->visible_pos = 0; + g_array_append_val (model->visible_order, id); + } + + model->sort_left = 0; + model->finalize_phase = MODEL_FINALIZE_VISIBLE; + model->refilter_pending = FALSE; +} + +static void +gtk_file_system_model_restart_initial_sort (GtkFileSystemModel *model) +{ + SortData data; + guint n = model->order->len; + + model->resort_pending = FALSE; + + if (sort_data_init (&data, model)) + { + if (model->sort_scratch == NULL) + model->sort_scratch = + g_array_sized_new (FALSE, FALSE, sizeof (guint), n); + g_array_set_size (model->sort_scratch, n); + + model->sort_width = 1; + model->sort_left = 0; + gtk_file_system_model_prepare_merge (model); + model->finalize_phase = MODEL_FINALIZE_MERGE; + } + else + gtk_file_system_model_prepare_visible (model); +} + +static void +gtk_file_system_model_schedule_work (GtkFileSystemModel *model) +{ + GSourceFunc func; + + if (model->drain_source != 0 || model->editing_hold) + return; + + if (model->pending_files != NULL) + func = gtk_file_system_model_drain; + else if (model->finalize_phase != MODEL_FINALIZE_NONE) + func = gtk_file_system_model_finalize_load; + else + return; + + model->drain_source = + gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE, + func, + g_object_ref (model), + g_object_unref); +} + +static void +gtk_file_system_model_begin_finalize_load (GtkFileSystemModel *model, + GError *error) +{ + g_assert (model->directory_loading); + g_assert (model->finalize_phase == MODEL_FINALIZE_NONE); + g_assert (model->pending_files == NULL); + + g_clear_error (&model->load_error); + model->load_error = error ? g_error_copy (error) : NULL; + + gtk_file_system_model_restart_initial_sort (model); + gtk_file_system_model_schedule_work (model); +} + +static gboolean +gtk_file_system_model_finalize_load (gpointer data) +{ + GtkFileSystemModel *model = data; + gint64 deadline = g_get_monotonic_time () + MODEL_DRAIN_SLICE_US; + guint processed = 0; + + if (model->editing_hold && model->directory_loading) + { + model->drain_source = 0; + return FALSE; + } + + if (model->resort_pending) + gtk_file_system_model_restart_initial_sort (model); + + while (TRUE) + { + guint n = model->order->len; + + if (model->finalize_phase == MODEL_FINALIZE_MERGE) + { + SortData data; + + if (!sort_data_init (&data, model)) + { + gtk_file_system_model_prepare_visible (model); + continue; + } + + while (model->sort_k < model->sort_right) + { + guint id; + + if (model->sort_i < model->sort_mid && + (model->sort_j >= model->sort_right || + compare_order_element + (&g_array_index (model->order, guint, model->sort_i), + &g_array_index (model->order, guint, model->sort_j), + &data) <= 0)) + id = g_array_index (model->order, guint, model->sort_i++); + else + id = g_array_index (model->order, guint, model->sort_j++); + + g_array_index (model->sort_scratch, guint, model->sort_k++) = id; + + if ((++processed % MODEL_FINALIZE_CLOCK_GRANULARITY) == 0 && + g_get_monotonic_time () >= deadline) + return TRUE; + } + + model->sort_left = model->sort_right; + if (model->sort_left < n) + { + gtk_file_system_model_prepare_merge (model); + continue; + } + + /* Make the completed pass canonical and reuse the old buffer. */ + { + GArray *tmp = model->order; + + model->order = model->sort_scratch; + model->sort_scratch = tmp; + } + + if (model->resort_pending) + { + gtk_file_system_model_restart_initial_sort (model); + continue; + } + + if (model->sort_width >= n / 2 + n % 2) + { + gtk_file_system_model_prepare_visible (model); + continue; + } + + model->sort_width *= 2; + model->sort_left = 0; + gtk_file_system_model_prepare_merge (model); + continue; + } + + if (model->finalize_phase == MODEL_FINALIZE_VISIBLE) + { + if (model->refilter_pending) + gtk_file_system_model_prepare_visible (model); + + while (model->sort_left < n) + { + guint pos = model->sort_left++; + guint id = g_array_index (model->order, guint, pos); + FileModelNode *node = get_node (model, id); + gboolean filtered_out; + gboolean visible; + + node->order_pos = pos; + filtered_out = node_should_be_filtered_out (model, id); + visible = node_should_be_visible (model, id, filtered_out); + + node->filtered_out = filtered_out; + node->visible = visible; + + if (visible) + { + node->visible_pos = model->visible_order->len; + g_array_append_val (model->visible_order, id); + } + + if ((++processed % MODEL_FINALIZE_CLOCK_GRANULARITY) == 0 && + g_get_monotonic_time () >= deadline) + return TRUE; + } + + if (model->resort_pending) + { + gtk_file_system_model_restart_initial_sort (model); + continue; + } + + if (model->refilter_pending) + { + gtk_file_system_model_prepare_visible (model); + continue; + } + + model->directory_loading = FALSE; + model->refilter_pending = FALSE; + model->resort_pending = FALSE; + g_clear_pointer (&model->sort_scratch, g_array_unref); + + model->finalize_phase = MODEL_FINALIZE_REPLAY; + if (g_get_monotonic_time () >= deadline) + return TRUE; + continue; + } + + if (model->finalize_phase == MODEL_FINALIZE_REPLAY) + { + while (model->pending_monitor_changes != NULL && + !g_queue_is_empty (model->pending_monitor_changes)) + { + PendingMonitorChange *change; + + change = g_queue_pop_head (model->pending_monitor_changes); + + gtk_file_system_model_apply_monitor_change (model, + change->file, + change->type); + pending_monitor_change_free (change); + + if ((++processed % MODEL_DRAIN_CLOCK_GRANULARITY) == 0 && + g_get_monotonic_time () >= deadline) + return TRUE; + } + + model->finalize_phase = MODEL_FINALIZE_NONE; + model->drain_source = 0; + + g_signal_emit (model, + file_system_model_signals[FINISHED_LOADING], + 0, model->load_error); + g_clear_error (&model->load_error); + + return FALSE; + } + + g_assert_not_reached (); + } +} + + G_DEFINE_TYPE_WITH_CODE (GtkFileSystemModel, _gtk_file_system_model, G_TYPE_OBJECT, @@ -1038,16 +1444,23 @@ gtk_file_system_model_dispose (GObject *object) { GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object); - if (model->dir_thaw_source) + if (model->drain_source != 0) { - g_source_remove (model->dir_thaw_source); - model->dir_thaw_source = 0; + g_source_remove (model->drain_source); + model->drain_source = 0; } g_cancellable_cancel (model->cancellable); + g_clear_pointer (&model->pending_monitor_queries, g_hash_table_destroy); + if (model->dir_monitor) g_file_monitor_cancel (model->dir_monitor); + g_list_free_full (model->pending_files, g_object_unref); + model->pending_files = NULL; + + g_clear_object (&model->dir_enumerator); + G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->dispose (object); } @@ -1058,35 +1471,41 @@ gtk_file_system_model_finalize (GObject *object) GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object); guint i; - for (i = 0; i < model->files->len; i++) + if (model->value_pages) { - int v; - - FileModelNode *node = get_node (model, i); - if (node->file) - g_object_unref (node->file); - if (node->info) - g_object_unref (node->info); - - for (v = 0; v < model->n_columns; v++) - if (G_VALUE_TYPE (&node->values[v]) != G_TYPE_INVALID) - g_value_unset (&node->values[v]); + for (i = 0; i < model->n_columns; i++) + if (model->value_pages[i]) + g_ptr_array_free (model->value_pages[i], TRUE); + g_free (model->value_pages); } - g_array_free (model->files, TRUE); + + g_clear_pointer (&model->files, g_array_unref); + + g_clear_pointer (&model->sort_scratch, g_array_unref); + g_clear_error (&model->load_error); + if (model->pending_monitor_changes) + g_queue_free_full (model->pending_monitor_changes, + pending_monitor_change_free); + + g_clear_pointer (&model->order, g_array_unref); + g_clear_pointer (&model->visible_order, g_array_unref); g_object_unref (model->cancellable); g_free (model->attributes); + if (model->dir) g_object_unref (model->dir); if (model->dir_monitor) g_object_unref (model->dir_monitor); + g_hash_table_destroy (model->file_lookup); + if (model->filter) g_object_unref (model->filter); g_slice_free1 (sizeof (GType) * model->n_columns, model->column_types); - _gtk_tree_data_list_header_free (model->sort_list); + if (model->default_sort_destroy) model->default_sort_destroy (model->default_sort_data); @@ -1133,95 +1552,113 @@ gtk_file_system_model_closed_enumerator (GObject *object, GAsyncResult *res, gpo g_file_enumerator_close_finish (G_FILE_ENUMERATOR (object), res, NULL); } + +static void +gtk_file_system_model_request_next_files (GtkFileSystemModel *model) +{ + if (model->dir_enumerator == NULL || + g_cancellable_is_cancelled (model->cancellable)) + return; + + g_file_enumerator_next_files_async (model->dir_enumerator, + FILES_PER_QUERY, + IO_PRIORITY, + model->cancellable, + gtk_file_system_model_got_files, + model); +} + static gboolean -thaw_func (gpointer data) +gtk_file_system_model_drain (gpointer data) { GtkFileSystemModel *model = data; + gint64 deadline; + guint processed = 0; - thaw_updates (model); - model->dir_thaw_source = 0; + if (model->editing_hold) + { + model->drain_source = 0; + return FALSE; + } + deadline = g_get_monotonic_time () + MODEL_DRAIN_SLICE_US; + + while (model->pending_files != NULL) + { + GList *link = model->pending_files; + GFileInfo *info = link->data; + const char *name; + + model->pending_files = + g_list_delete_link (model->pending_files, link); + + name = g_file_info_get_name (info); + if (name != NULL) + { + GFile *file = g_file_get_child (model->dir, name); + add_file (model, file, info); + g_object_unref (file); + } + + g_object_unref (info); + processed++; + + if ((processed % MODEL_DRAIN_CLOCK_GRANULARITY) == 0 && + g_get_monotonic_time () >= deadline) + break; + } + + if (model->pending_files != NULL) + return TRUE; + + model->drain_source = 0; + gtk_file_system_model_request_next_files (model); return FALSE; } static void -gtk_file_system_model_got_files (GObject *object, GAsyncResult *res, gpointer data) +gtk_file_system_model_got_files (GObject *object, + GAsyncResult *res, + gpointer data) { GFileEnumerator *enumerator = G_FILE_ENUMERATOR (object); - GtkFileSystemModel *model = data; - GList *walk, *files; + GtkFileSystemModel *model = data; /* only a valid pointer if not cancelled */ + GList *files; GError *error = NULL; - gdk_threads_enter (); - files = g_file_enumerator_next_files_finish (enumerator, res, &error); - if (files) + if (files != NULL) { - if (model->dir_thaw_source == 0) - { - freeze_updates (model); - model->dir_thaw_source = gdk_threads_add_timeout_full (IO_PRIORITY + 1, - 50, - thaw_func, - model, - NULL); - } - - for (walk = files; walk; walk = walk->next) - { - const char *name; - GFileInfo *info; - GFile *file; - - info = walk->data; - name = g_file_info_get_name (info); - if (name == NULL) - { - /* Shouldn't happen, but the APIs allow it */ - g_object_unref (info); - continue; - } - file = g_file_get_child (model->dir, name); - add_file (model, file, info); - g_object_unref (file); - g_object_unref (info); - } - g_list_free (files); - - g_file_enumerator_next_files_async (enumerator, - g_file_is_native (model->dir) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY, - IO_PRIORITY, - model->cancellable, - gtk_file_system_model_got_files, - model); - } - else - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - { - g_file_enumerator_close_async (enumerator, - IO_PRIORITY, - model->cancellable, - gtk_file_system_model_closed_enumerator, - NULL); - if (model->dir_thaw_source != 0) - { - g_source_remove (model->dir_thaw_source); - model->dir_thaw_source = 0; - thaw_updates (model); - } - - g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error); - } - - if (error) - g_error_free (error); + gdk_threads_enter (); + g_assert (model->pending_files == NULL); + model->pending_files = files; + gtk_file_system_model_schedule_work (model); + gdk_threads_leave (); + return; } - gdk_threads_leave (); + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + gdk_threads_enter (); + + g_file_enumerator_close_async (enumerator, + IO_PRIORITY, + model->cancellable, + gtk_file_system_model_closed_enumerator, + NULL); + + g_clear_object (&model->dir_enumerator); + + gtk_file_system_model_begin_finalize_load (model, error); + gdk_threads_leave (); + } + + if (error) + g_error_free (error); } + static void gtk_file_system_model_query_done (GObject * object, GAsyncResult *res, @@ -1230,7 +1667,7 @@ gtk_file_system_model_query_done (GObject * object, GtkFileSystemModel *model = data; /* only a valid pointer if not cancelled */ GFile *file = G_FILE (object); GFileInfo *info; - guint id; + guint n_files; info = g_file_query_info_finish (file, res, NULL); if (info == NULL) @@ -1238,22 +1675,57 @@ gtk_file_system_model_query_done (GObject * object, gdk_threads_enter (); + n_files = model->files->len; + _gtk_file_system_model_update_file (model, file, info); - id = node_get_for_file (model, file); - gtk_file_system_model_sort_node (model, id); + if (model->files->len == n_files) + gtk_file_system_model_sort (model); g_object_unref (info); gdk_threads_leave (); } + static void -gtk_file_system_model_monitor_change (GFileMonitor * monitor, - GFile * file, - GFile * other_file, - GFileMonitorEvent type, - GtkFileSystemModel *model) +gtk_file_system_model_monitor_query_done (GObject *object, + GAsyncResult *res, + gpointer data) +{ + PendingMonitorQuery *query = data; + GtkFileSystemModel *model = g_weak_ref_get (query); + GFileInfo *info = g_file_query_info_finish (G_FILE (object), res, NULL); + + if (model) + { + gdk_threads_enter (); + + if (model->pending_monitor_queries && + g_hash_table_lookup (model->pending_monitor_queries, + G_FILE (object)) == query) + { + g_hash_table_remove (model->pending_monitor_queries, + G_FILE (object)); + if (info) + _gtk_file_system_model_update_file (model, G_FILE (object), info); + } + + gdk_threads_leave (); + g_object_unref (model); + } + + g_clear_object (&info); + + g_weak_ref_clear (query); + g_free (query); +} + + +static void +gtk_file_system_model_apply_monitor_change (GtkFileSystemModel *model, + GFile *file, + GFileMonitorEvent type) { switch (type) { @@ -1261,21 +1733,43 @@ gtk_file_system_model_monitor_change (GFileMonitor * monitor, case G_FILE_MONITOR_EVENT_CHANGED: case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: /* We can treat all of these the same way */ - g_file_query_info_async (file, - model->attributes, - G_FILE_QUERY_INFO_NONE, - IO_PRIORITY, - model->cancellable, - gtk_file_system_model_query_done, - model); + { + PendingMonitorQuery *query; + + query = g_new (PendingMonitorQuery, 1); + g_weak_ref_init (query, G_OBJECT (model)); + + gdk_threads_enter (); + + if (model->pending_monitor_queries == NULL) + model->pending_monitor_queries = + g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, + (GDestroyNotify) g_object_unref, NULL); + + g_hash_table_replace (model->pending_monitor_queries, + g_object_ref (file), query); + + gdk_threads_leave (); + + g_file_query_info_async (file, + model->attributes, + G_FILE_QUERY_INFO_NONE, + IO_PRIORITY, + model->cancellable, + gtk_file_system_model_monitor_query_done, + query); + } break; + case G_FILE_MONITOR_EVENT_DELETED: - gdk_threads_enter (); + gdk_threads_enter (); + if (model->pending_monitor_queries) + g_hash_table_remove (model->pending_monitor_queries, file); remove_file (model, file); - gdk_threads_leave (); + gdk_threads_leave (); break; + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - /* FIXME: use freeze/thaw with this somehow? */ case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: case G_FILE_MONITOR_EVENT_UNMOUNTED: default: @@ -1285,46 +1779,73 @@ gtk_file_system_model_monitor_change (GFileMonitor * monitor, } static void -gtk_file_system_model_got_enumerator (GObject *dir, GAsyncResult *res, gpointer data) +gtk_file_system_model_monitor_change (GFileMonitor * monitor, + GFile * file, + GFile * other_file, + GFileMonitorEvent type, + GtkFileSystemModel *model) { - GtkFileSystemModel *model = data; + if (model->directory_loading || + model->drain_source != 0) + { + PendingMonitorChange *change; + + change = g_new (PendingMonitorChange, 1); + change->file = g_object_ref (file); + change->type = type; + + if (model->pending_monitor_changes == NULL) + model->pending_monitor_changes = g_queue_new (); + + g_queue_push_tail (model->pending_monitor_changes, change); + return; + } + + gtk_file_system_model_apply_monitor_change (model, file, type); +} + +static void +gtk_file_system_model_got_enumerator (GObject *object, + GAsyncResult *res, + gpointer data) +{ + GtkFileSystemModel *model = data; /* only a valid pointer if not cancelled */ GFileEnumerator *enumerator; GError *error = NULL; - gdk_threads_enter (); - - enumerator = g_file_enumerate_children_finish (G_FILE (dir), res, &error); + enumerator = g_file_enumerate_children_finish (G_FILE (object), res, &error); if (enumerator == NULL) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - { - g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error); + { + gdk_threads_enter (); + gtk_file_system_model_begin_finalize_load (model, error); + gdk_threads_leave (); + } + + if (error) g_error_free (error); - } - } - else - { - g_file_enumerator_next_files_async (enumerator, - g_file_is_native (model->dir) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY, - IO_PRIORITY, - model->cancellable, - gtk_file_system_model_got_files, - model); - g_object_unref (enumerator); - model->dir_monitor = g_file_monitor_directory (model->dir, - G_FILE_MONITOR_NONE, - model->cancellable, - NULL); /* we don't mind if directory monitoring isn't supported, so the GError is NULL here */ - if (model->dir_monitor) - g_signal_connect (model->dir_monitor, - "changed", - G_CALLBACK (gtk_file_system_model_monitor_change), - model); + return; } + gdk_threads_enter (); + + g_assert (model->dir_enumerator == NULL); + model->dir_enumerator = enumerator; + + model->dir_monitor = g_file_monitor_directory (model->dir, + G_FILE_MONITOR_NONE, + model->cancellable, + NULL); + if (model->dir_monitor) + g_signal_connect (model->dir_monitor, "changed", + G_CALLBACK (gtk_file_system_model_monitor_change), model); + + gtk_file_system_model_request_next_files (model); gdk_threads_leave (); } + static void gtk_file_system_model_set_n_columns (GtkFileSystemModel *model, gint n_columns, @@ -1337,27 +1858,34 @@ gtk_file_system_model_set_n_columns (GtkFileSystemModel *model, model->n_columns = n_columns; model->column_types = g_slice_alloc (sizeof (GType) * n_columns); - - model->node_size = sizeof (FileModelNode) + sizeof (GValue) * (n_columns - 1); /* minus 1 because FileModelNode.values[] has a default size of 1 */ + model->value_pages = g_new0 (GPtrArray *, n_columns); for (i = 0; i < (guint) n_columns; i++) { GType type = va_arg (args, GType); - if (! _gtk_tree_data_list_check_type (type)) - { - g_error ("%s: type %s cannot be a column type for GtkFileSystemModel\n", G_STRLOC, g_type_name (type)); - return; /* not reached */ - } + if (!_gtk_tree_data_list_check_type (type)) + { + g_error ("%s: type %s cannot be a column type for GtkFileSystemModel\n", + G_STRLOC, g_type_name (type)); + return; + } model->column_types[i] = type; } - model->sort_list = _gtk_tree_data_list_header_new (n_columns, model->column_types); + model->sort_list = + _gtk_tree_data_list_header_new (n_columns, model->column_types); - model->files = g_array_sized_new (FALSE, FALSE, model->node_size, FILES_PER_QUERY); - /* add editable node at start */ + model->files = + g_array_sized_new (FALSE, TRUE, sizeof (FileModelNode), FILES_PER_QUERY); + g_array_set_clear_func (model->files, file_model_node_clear); + model->order = + g_array_sized_new (FALSE, FALSE, sizeof (guint), FILES_PER_QUERY); + model->visible_order = + g_array_sized_new (FALSE, FALSE, sizeof (guint), FILES_PER_QUERY); g_array_set_size (model->files, 1); - memset (get_node (model, 0), 0, model->node_size); + + get_node (model, 0)->generation = 1; } static void @@ -1369,6 +1897,7 @@ gtk_file_system_model_set_directory (GtkFileSystemModel *model, model->dir = g_object_ref (dir); model->attributes = g_strdup (attributes); + model->directory_loading = TRUE; g_file_enumerate_children_async (model->dir, attributes, @@ -1476,22 +2005,22 @@ gtk_file_system_model_refilter_all (GtkFileSystemModel *model) { guint i; - if (model->frozen) + if (model->directory_loading || model->editing_hold) { - model->filter_on_thaw = TRUE; + model->refilter_pending = TRUE; return; } - freeze_updates (model); + for (i = 0; i < model->order->len; i++) + { + guint id = g_array_index (model->order, guint, i); + node_compute_visibility_and_filters (model, id); + } - /* start at index 1, don't change the editable */ - for (i = 1; i < model->files->len; i++) - node_compute_visibility_and_filters (model, i); - - model->filter_on_thaw = FALSE; - thaw_updates (model); + model->refilter_pending = FALSE; } + /** * _gtk_file_system_model_set_show_hidden: * @model: a #GtkFileSystemModel @@ -1620,12 +2149,13 @@ _gtk_file_system_model_get_cancellable (GtkFileSystemModel *model) **/ gboolean _gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model, - GtkTreeIter *iter) + GtkTreeIter *iter) { FileModelNode *node; g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE); g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (ITER_IS_VALID (model, iter), FALSE); node = get_node (model, ITER_INDEX (iter)); return node->visible; @@ -1646,12 +2176,13 @@ _gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model, **/ gboolean _gtk_file_system_model_iter_is_filtered_out (GtkFileSystemModel *model, - GtkTreeIter *iter) + GtkTreeIter *iter) { FileModelNode *node; g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE); g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (ITER_IS_VALID (model, iter), FALSE); node = get_node (model, ITER_INDEX (iter)); return node->filtered_out; @@ -1676,12 +2207,13 @@ _gtk_file_system_model_iter_is_filtered_out (GtkFileSystemModel *model, **/ GFileInfo * _gtk_file_system_model_get_info (GtkFileSystemModel *model, - GtkTreeIter *iter) + GtkTreeIter *iter) { FileModelNode *node; g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL); g_return_val_if_fail (iter != NULL, NULL); + g_return_val_if_fail (ITER_IS_VALID (model, iter), NULL); node = get_node (model, ITER_INDEX (iter)); g_assert (node->info == NULL || G_IS_FILE_INFO (node->info)); @@ -1702,11 +2234,13 @@ _gtk_file_system_model_get_info (GtkFileSystemModel *model, **/ GFile * _gtk_file_system_model_get_file (GtkFileSystemModel *model, - GtkTreeIter *iter) + GtkTreeIter *iter) { FileModelNode *node; g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL); + g_return_val_if_fail (iter != NULL, NULL); + g_return_val_if_fail (ITER_IS_VALID (model, iter), NULL); node = get_node (model, ITER_INDEX (iter)); return node->file; @@ -1731,63 +2265,43 @@ _gtk_file_system_model_get_file (GtkFileSystemModel *model, **/ const GValue * _gtk_file_system_model_get_value (GtkFileSystemModel *model, - GtkTreeIter * iter, + GtkTreeIter *iter, int column) { FileModelNode *node; + GValue *value; + guint id; g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL); - g_return_val_if_fail (column >= 0 && (guint) column < model->n_columns, NULL); + g_return_val_if_fail (iter != NULL, NULL); + g_return_val_if_fail (ITER_IS_VALID (model, iter), NULL); + g_return_val_if_fail (column >= 0 && + (guint) column < model->n_columns, NULL); - node = get_node (model, ITER_INDEX (iter)); + id = ITER_INDEX (iter); + node = get_node (model, id); + value = value_cache_slot (model, id, column, TRUE); - if (!G_VALUE_TYPE (&node->values[column])) + if (G_VALUE_TYPE (value) == G_TYPE_INVALID) { - g_value_init (&node->values[column], model->column_types[column]); - if (!model->get_func (model, - node->file, - node->info, - column, - &node->values[column], - model->get_data)) + g_value_init (value, model->column_types[column]); + + if (!model->get_func (model, node->file, node->info, column, + value, model->get_data)) { - g_value_unset (&node->values[column]); + g_value_unset (value); return NULL; } } - return &node->values[column]; + return value; } static guint node_get_for_file (GtkFileSystemModel *model, - GFile * file) + GFile *file) { - guint i; - - i = GPOINTER_TO_UINT (g_hash_table_lookup (model->file_lookup, file)); - if (i != 0) - return i; - - /* Node 0 is the editable row and has no associated file or entry in the table, so we start counting from 1. - * - * The invariant here is that the files in model->files[n] for n < g_hash_table_size (model->file_lookup) - * are already added to the hash table. The table can get cleared when we re-sort; this loop merely rebuilds - * our (file -> index) mapping on demand. - * - * If we exit the loop, the next pending batch of mappings will be resolved when this function gets called again - * with another file that is not yet in the mapping. - */ - for (i = g_hash_table_size (model->file_lookup) + 1; i < model->files->len; i++) - { - FileModelNode *node = get_node (model, i); - - g_hash_table_insert (model->file_lookup, node->file, GUINT_TO_POINTER (i)); - if (g_file_equal (node->file, file)) - return i; - } - - return 0; + return GPOINTER_TO_UINT (g_hash_table_lookup (model->file_lookup, file)); } /** @@ -1830,26 +2344,6 @@ _gtk_file_system_model_get_iter_for_file (GtkFileSystemModel *model, * if the index is equal or after the specified id. We use this to slide the * mappings up or down when a node is added or removed, respectively. */ -static void -adjust_file_lookup (GtkFileSystemModel *model, guint id, int increment) -{ - GHashTableIter iter; - gpointer key; - gpointer value; - - g_hash_table_iter_init (&iter, model->file_lookup); - - while (g_hash_table_iter_next (&iter, &key, &value)) - { - guint index = GPOINTER_TO_UINT (value); - - if (index >= id) - { - index += increment; - g_hash_table_iter_replace (&iter, GUINT_TO_POINTER (index)); - } - } -} /** * add_file: @@ -1862,30 +2356,71 @@ adjust_file_lookup (GtkFileSystemModel *model, guint id, int increment) **/ static void add_file (GtkFileSystemModel *model, - GFile *file, - GFileInfo *info) + GFile *file, + GFileInfo *info) { FileModelNode *node; + guint id; g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); g_return_if_fail (G_IS_FILE (file)); g_return_if_fail (G_IS_FILE_INFO (info)); - node = g_slice_alloc0 (model->node_size); + if (node_get_for_file (model, file) != 0) + return; + + id = node_allocate_slot (model); + node = get_node (model, id); node->file = g_object_ref (file); - if (info) - node->info = g_object_ref (info); - node->frozen_add = model->frozen ? TRUE : FALSE; + node->info = g_object_ref (info); - g_array_append_vals (model->files, node, 1); - g_slice_free1 (model->node_size, node); + g_hash_table_insert (model->file_lookup, + node->file, GUINT_TO_POINTER (id)); - if (!model->frozen) - node_compute_visibility_and_filters (model, model->files->len -1); + g_array_append_val (model->order, id); + node->order_pos = model->order->len - 1; - gtk_file_system_model_sort_node (model, model->files->len -1); + if (model->directory_loading) + return; + + if (model->editing_hold) + { + node->held_add = TRUE; + model->resort_pending = TRUE; + return; + } + + { + SortData data; + guint lo = 0; + guint hi = node->order_pos; + + if (sort_data_init (&data, model)) + { + while (lo < hi) + { + guint mid = lo + (hi - lo) / 2; + guint mid_id = g_array_index (model->order, guint, mid); + int cmp = compare_order_element (&id, &mid_id, &data); + + if (cmp < 0) + hi = mid; + else + lo = mid + 1; + } + + if (lo != node->order_pos) + { + g_array_remove_index (model->order, node->order_pos); + g_array_insert_val (model->order, lo, id); + order_update_positions_from (model, lo); + } + } + } + node_compute_visibility_and_filters (model, id); } + /** * remove_file: * @model: the model @@ -1897,12 +2432,12 @@ add_file (GtkFileSystemModel *model, **/ static void remove_file (GtkFileSystemModel *model, - GFile *file) + GFile *file) { FileModelNode *node; gboolean was_visible; + guint row = G_MAXUINT; guint id; - guint row; g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); g_return_if_fail (G_IS_FILE (file)); @@ -1913,20 +2448,22 @@ remove_file (GtkFileSystemModel *model, node = get_node (model, id); was_visible = node->visible; - row = node_get_tree_row (model, id); - node_invalidate_index (model, id); + if (was_visible) + { + row = node->visible_pos; + visible_remove_node (model, id); + } + g_assert (id != 0); + { + guint pos = node->order_pos; + + g_array_remove_index (model->order, pos); + order_update_positions_from (model, pos); + } g_hash_table_remove (model->file_lookup, file); - g_object_unref (node->file); - adjust_file_lookup (model, id, -1); - - if (node->info) - g_object_unref (node->info); - - g_array_remove_index (model->files, id); - - /* We don't need to resort, as removing a row doesn't change the sorting order of the other rows */ + node_retire_slot (model, id); if (was_visible) emit_row_deleted_for_row (model, row); @@ -1948,7 +2485,7 @@ _gtk_file_system_model_update_file (GtkFileSystemModel *model, GFileInfo *info) { FileModelNode *node; - guint i, id; + guint id; GFileInfo *old_info; g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); @@ -1959,7 +2496,7 @@ _gtk_file_system_model_update_file (GtkFileSystemModel *model, if (id == 0) { add_file (model, file, info); - id = node_get_for_file (model, file); + return; } node = get_node (model, id); @@ -1970,11 +2507,7 @@ _gtk_file_system_model_update_file (GtkFileSystemModel *model, if (old_info) g_object_unref (old_info); - for (i = 0; i < model->n_columns; i++) - { - if (G_VALUE_TYPE (&node->values[i])) - g_value_unset (&node->values[i]); - } + value_cache_clear_node (model, id); if (node->visible) emit_row_changed_for_node (model, id); @@ -2010,62 +2543,6 @@ _gtk_file_system_model_set_filter (GtkFileSystemModel *model, gtk_file_system_model_refilter_all (model); } -/** - * freeze_updates: - * @model: a #GtkFileSystemModel - * - * Freezes most updates on the model, so that performing multiple operations on - * the files in the model do not cause any events. Use thaw_updates() to resume - * proper operations. It is fine to call this function multiple times as long as - * freeze and thaw calls are balanced. - **/ -static void -freeze_updates (GtkFileSystemModel *model) -{ - g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); - - model->frozen++; -} - -/** - * thaw_updates: - * @model: a #GtkFileSystemModel - * - * Undoes the effect of a previous call to freeze_updates() - **/ -static void -thaw_updates (GtkFileSystemModel *model) -{ - gboolean stuff_added; - - g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); - g_return_if_fail (model->frozen > 0); - - model->frozen--; - if (model->frozen > 0) - return; - - stuff_added = get_node (model, model->files->len - 1)->frozen_add; - - if (model->filter_on_thaw) - gtk_file_system_model_refilter_all (model); - if (model->sort_on_thaw) - gtk_file_system_model_sort (model); - if (stuff_added) - { - guint i; - - for (i = 0; i < model->files->len; i++) - { - FileModelNode *node = get_node (model, i); - - if (!node->frozen_add) - continue; - node->frozen_add = FALSE; - node_compute_visibility_and_filters (model, i); - } - } -} /** * _gtk_file_system_model_clear_cache: @@ -2080,16 +2557,17 @@ thaw_updates (GtkFileSystemModel *model) **/ void _gtk_file_system_model_clear_cache (GtkFileSystemModel *model, - int column) + int column) { - guint i; + guint8 *changed; int start, end; - gboolean changed; + int c; + guint i; g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); g_return_if_fail (column >= -1 && (guint) column < model->n_columns); - if (column > -1) + if (column >= 0) { start = column; end = column + 1; @@ -2100,42 +2578,75 @@ _gtk_file_system_model_clear_cache (GtkFileSystemModel *model, end = model->n_columns; } - for (i = 0; i < model->files->len; i++) - { - FileModelNode *node = get_node (model, i); + changed = g_new0 (guint8, model->files->len); - changed = FALSE; - for (column = start; column < end; column++) + for (c = start; c < end; c++) + { + GPtrArray *pages = model->value_pages[c]; + guint page_index; + + if (pages == NULL) + continue; + + for (page_index = 0; page_index < pages->len; page_index++) { - if (!G_VALUE_TYPE (&node->values[column])) + ValuePage *page = pages->pdata[page_index]; + guint slot; + guint base; + + if (page == NULL) continue; - g_value_unset (&node->values[column]); - changed = TRUE; - } + base = page_index << VALUE_PAGE_SHIFT; + for (slot = 0; slot < VALUE_PAGE_SIZE; slot++) + { + guint id = base + slot; + GValue *value; - if (changed && node->visible) - emit_row_changed_for_node (model, i); + if (id >= model->files->len) + break; + + value = &page->values[slot]; + if (G_VALUE_TYPE (value) == G_TYPE_INVALID) + continue; + + g_value_unset (value); + changed[id] = TRUE; + } + } } + /* Emit at most one change signal for each visible row, in row order. */ + for (i = 0; i < model->visible_order->len; i++) + { + guint id = g_array_index (model->visible_order, guint, i); + if (changed[id]) + emit_row_changed_for_node (model, id); + } + + g_free (changed); + /* FIXME: resort? */ } + void _gtk_file_system_model_clear_cache_for_iter (GtkFileSystemModel *model, - GtkTreeIter *iter, - int column) + GtkTreeIter *iter, + int column) { FileModelNode *node; - int start, end; gboolean changed = FALSE; + guint id; + int start, end; + int c; g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); g_return_if_fail (iter != NULL); g_return_if_fail (ITER_IS_VALID (model, iter)); g_return_if_fail (column >= -1 && (guint) column < model->n_columns); - if (column > -1) + if (column >= 0) { start = column; end = column + 1; @@ -2146,19 +2657,14 @@ _gtk_file_system_model_clear_cache_for_iter (GtkFileSystemModel *model, end = model->n_columns; } - node = get_node (model, ITER_INDEX (iter)); + id = ITER_INDEX (iter); + node = get_node (model, id); - for (column = start; column < end; column++) - { - if (!G_VALUE_TYPE (&node->values[column])) - continue; - - g_value_unset (&node->values[column]); - changed = TRUE; - } + for (c = start; c < end; c++) + changed |= value_cache_clear_slot (model, id, c); if (changed && node->visible) - emit_row_changed_for_node (model, ITER_INDEX (iter)); + emit_row_changed_for_node (model, id); } /** @@ -2200,20 +2706,26 @@ _gtk_file_system_model_add_and_query_file (GtkFileSystemModel *model, * using this temporary row, call _gtk_file_system_model_remove_editable(). **/ void -_gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter) +_gtk_file_system_model_add_editable (GtkFileSystemModel *model, + GtkTreeIter *iter) { g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + g_return_if_fail (iter != NULL); g_return_if_fail (!get_node (model, 0)->visible); node_set_visible_and_filtered_out (model, 0, TRUE, FALSE); ITER_INIT_FROM_INDEX (model, iter, 0); - /* we don't want file system changes to affect the model while - * editing is in place - */ - freeze_updates (model); + model->editing_hold = TRUE; + + if (model->directory_loading && model->drain_source != 0) + { + g_source_remove (model->drain_source); + model->drain_source = 0; + } } + /** * _gtk_file_system_model_remove_editable: * @model: a #GtkFileSystemModel @@ -2225,10 +2737,32 @@ _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *ite void _gtk_file_system_model_remove_editable (GtkFileSystemModel *model) { + guint i; + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); - g_return_if_fail (get_node (model, 0)->visible); + g_return_if_fail (model->editing_hold); - thaw_updates (model); + model->editing_hold = FALSE; - node_set_visible_and_filtered_out (model, 0, FALSE, FALSE); + if (model->resort_pending) + gtk_file_system_model_sort (model); + if (model->refilter_pending) + gtk_file_system_model_refilter_all (model); + + for (i = 0; i < model->order->len; i++) + { + guint id = g_array_index (model->order, guint, i); + FileModelNode *node = get_node (model, id); + + if (!node->held_add) + continue; + + node->held_add = FALSE; + node_compute_visibility_and_filters (model, id); + } + + if (get_node (model, 0)->visible) + node_set_visible_and_filtered_out (model, 0, FALSE, FALSE); + + gtk_file_system_model_schedule_work (model); } diff --git a/gtk/gtkfontbutton.c b/gtk/gtkfontbutton.c index 08aea103e..68df02b8d 100644 --- a/gtk/gtkfontbutton.c +++ b/gtk/gtkfontbutton.c @@ -889,7 +889,8 @@ gtk_font_button_update_font_info (GtkFontButton *font_button) if (style == NULL || !g_ascii_strcasecmp (style, "Regular")) family_style = g_strdup (family); else - family_style = g_strdup_printf ("%s %s", family, style); + family_style = g_strdup_printf ("%s %s", family, + _gtk_font_style_name_translate (style)); gtk_label_set_text (GTK_LABEL (font_button->priv->font_label), family_style); diff --git a/gtk/gtkfontsel.c b/gtk/gtkfontsel.c index 701d22841..6bc2ce7ae 100644 --- a/gtk/gtkfontsel.c +++ b/gtk/gtkfontsel.c @@ -76,6 +76,91 @@ #define DEFAULT_FONT_NAME "Sans 10" +#ifdef ENABLE_NLS +/* Pango uses these standard English names for common font faces. + * Keep the msgids explicit for gettext, but only translate exact matches: + * font-specific face names must remain untouched. + */ +#define FONT_STYLE_ULTRA_LIGHT N_("Ultra-Light") +#define FONT_STYLE_LIGHT N_("Light") +#define FONT_STYLE_MEDIUM N_("Medium") +#define FONT_STYLE_NORMAL N_("Normal") +#define FONT_STYLE_REGULAR N_("Regular") +#define FONT_STYLE_ITALIC N_("Italic") +#define FONT_STYLE_OBLIQUE N_("Oblique") +#define FONT_STYLE_SEMI_BOLD N_("Semi-Bold") +#define FONT_STYLE_BOLD N_("Bold") +#define FONT_STYLE_ULTRA_BOLD N_("Ultra-Bold") +#define FONT_STYLE_HEAVY N_("Heavy") +#define FONT_STYLE_BOLD_ITALIC N_("Bold Italic") + +const gchar * +_gtk_font_style_name_translate (const gchar *name) +{ + switch (name[0]) + { + case 'B': + if (strcmp (name, FONT_STYLE_BOLD) == 0) + return _(FONT_STYLE_BOLD); + if (strcmp (name, FONT_STYLE_BOLD_ITALIC) == 0) + return _(FONT_STYLE_BOLD_ITALIC); + break; + + case 'H': + if (strcmp (name, FONT_STYLE_HEAVY) == 0) + return _(FONT_STYLE_HEAVY); + break; + + case 'I': + if (strcmp (name, FONT_STYLE_ITALIC) == 0) + return _(FONT_STYLE_ITALIC); + break; + + case 'L': + if (strcmp (name, FONT_STYLE_LIGHT) == 0) + return _(FONT_STYLE_LIGHT); + break; + + case 'M': + if (strcmp (name, FONT_STYLE_MEDIUM) == 0) + return _(FONT_STYLE_MEDIUM); + break; + + case 'N': + if (strcmp (name, FONT_STYLE_NORMAL) == 0) + return _(FONT_STYLE_NORMAL); + break; + + case 'O': + if (strcmp (name, FONT_STYLE_OBLIQUE) == 0) + return _(FONT_STYLE_OBLIQUE); + break; + + case 'R': + if (strcmp (name, FONT_STYLE_REGULAR) == 0) + return _(FONT_STYLE_REGULAR); + break; + + case 'S': + if (strcmp (name, FONT_STYLE_SEMI_BOLD) == 0) + return _(FONT_STYLE_SEMI_BOLD); + break; + + case 'U': + if (strcmp (name, FONT_STYLE_ULTRA_LIGHT) == 0) + return _(FONT_STYLE_ULTRA_LIGHT); + if (strcmp (name, FONT_STYLE_ULTRA_BOLD) == 0) + return _(FONT_STYLE_ULTRA_BOLD); + break; + + default: + break; + } + + return name; +} +#endif /* ENABLE_NLS */ + /* This is the initial and maximum height of the preview entry (it expands when large font sizes are selected). Initial height is also the minimum. */ #define INITIAL_PREVIEW_HEIGHT 44 @@ -734,13 +819,22 @@ gtk_font_selection_select_font (GtkTreeSelection *selection, } } -static int -cmp_families (const void *a, const void *b) +typedef struct { - const char *a_name = pango_font_family_get_name (*(PangoFontFamily **)a); - const char *b_name = pango_font_family_get_name (*(PangoFontFamily **)b); + PangoFontFamily *family; + const gchar *name; + gchar *collate_key; +} FontFamilySortInfo; - return g_utf8_collate (a_name, b_name); +static int +cmp_families (const void *a, + const void *b) +{ + const FontFamilySortInfo *family_a = a; + const FontFamilySortInfo *family_b = b; + + return strcmp (family_a->collate_key, + family_b->collate_key); } static void @@ -748,6 +842,7 @@ gtk_font_selection_show_available_fonts (GtkFontSelection *fontsel) { GtkListStore *model; PangoFontFamily **families; + FontFamilySortInfo *sorted_families; PangoFontFamily *match_family = NULL; gint n_families, i; GtkTreeIter match_row; @@ -755,27 +850,37 @@ gtk_font_selection_show_available_fonts (GtkFontSelection *fontsel) model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fontsel->family_list))); pango_context_list_families (gtk_widget_get_pango_context (GTK_WIDGET (fontsel)), - &families, &n_families); - qsort (families, n_families, sizeof (PangoFontFamily *), cmp_families); + &families, &n_families); + + sorted_families = g_new (FontFamilySortInfo, n_families); + for (i = 0; i < n_families; i++) + { + const gchar *name = pango_font_family_get_name (families[i]); + + sorted_families[i].family = families[i]; + sorted_families[i].name = name; + sorted_families[i].collate_key = g_utf8_collate_key (name ? name : "", -1); + } + + qsort (sorted_families, n_families, sizeof (FontFamilySortInfo), cmp_families); gtk_list_store_clear (model); - for (i=0; ifamily_list), &match_row); #ifdef INCLUDE_FONT_ENTRIES gtk_entry_set_text (GTK_ENTRY (fontsel->font_entry), - pango_font_family_get_name (match_family)); + pango_font_family_get_name (match_family)); #endif /* INCLUDE_FONT_ENTRIES */ } + for (i = 0; i < n_families; i++) + g_free (sorted_families[i].collate_key); + + g_free (sorted_families); g_free (families); } @@ -813,18 +922,22 @@ compare_font_descriptions (const PangoFontDescription *a, const PangoFontDescrip return 0; } -static int -faces_sort_func (const void *a, const void *b) +typedef struct { - PangoFontDescription *desc_a = pango_font_face_describe (*(PangoFontFace **)a); - PangoFontDescription *desc_b = pango_font_face_describe (*(PangoFontFace **)b); + PangoFontFace *face; + PangoFontDescription *desc; + const gchar *name; +} FontFaceSortInfo; - int ord = compare_font_descriptions (desc_a, desc_b); +static int +faces_sort_func (const void *a, + const void *b) +{ + const FontFaceSortInfo *face_a = a; + const FontFaceSortInfo *face_b = b; - pango_font_description_free (desc_a); - pango_font_description_free (desc_b); - - return ord; + return compare_font_descriptions (face_a->desc, + face_b->desc); } static gboolean @@ -844,6 +957,7 @@ gtk_font_selection_show_available_styles (GtkFontSelection *fontsel) { gint n_faces, i; PangoFontFace **faces; + FontFaceSortInfo *sorted_faces; PangoFontDescription *old_desc; GtkListStore *model; GtkTreeIter match_row; @@ -857,38 +971,40 @@ gtk_font_selection_show_available_styles (GtkFontSelection *fontsel) old_desc= NULL; pango_font_family_list_faces (fontsel->family, &faces, &n_faces); - qsort (faces, n_faces, sizeof (PangoFontFace *), faces_sort_func); + + sorted_faces = g_new (FontFaceSortInfo, n_faces); + for (i = 0; i < n_faces; i++) + { + sorted_faces[i].face = faces[i]; + sorted_faces[i].desc = pango_font_face_describe (faces[i]); + sorted_faces[i].name = pango_font_face_get_face_name (faces[i]); + } + + qsort (sorted_faces, n_faces, sizeof (FontFaceSortInfo), faces_sort_func); gtk_list_store_clear (model); - for (i=0; i < n_faces; i++) + for (i = 0; i < n_faces; i++) { GtkTreeIter iter; - const gchar *str = pango_font_face_get_face_name (faces[i]); + const gchar *str = sorted_faces[i].name; - gtk_list_store_append (model, &iter); - gtk_list_store_set (model, &iter, - FACE_COLUMN, faces[i], - FACE_NAME_COLUMN, str, - -1); + gtk_list_store_insert_with_values (model, &iter, -1, + FACE_COLUMN, sorted_faces[i].face, + FACE_NAME_COLUMN, _gtk_font_style_name_translate (str), + -1); if (i == 0) - { - match_row = iter; - match_face = faces[i]; - } - else if (old_desc) - { - PangoFontDescription *tmp_desc = pango_font_face_describe (faces[i]); - - if (font_description_style_equal (tmp_desc, old_desc)) - { - match_row = iter; - match_face = faces[i]; - } - - pango_font_description_free (tmp_desc); - } + { + match_row = iter; + match_face = sorted_faces[i].face; + } + else if (old_desc && + font_description_style_equal (sorted_faces[i].desc, old_desc)) + { + match_row = iter; + match_face = sorted_faces[i].face; + } } if (old_desc) @@ -905,6 +1021,10 @@ gtk_font_selection_show_available_styles (GtkFontSelection *fontsel) set_cursor_to_iter (GTK_TREE_VIEW (fontsel->face_list), &match_row); } + for (i = 0; i < n_faces; i++) + pango_font_description_free (sorted_faces[i].desc); + + g_free (sorted_faces); g_free (faces); } @@ -975,8 +1095,9 @@ gtk_font_selection_show_available_sizes (GtkFontSelection *fontsel, { GtkTreeIter iter; - gtk_list_store_append (model, &iter); - gtk_list_store_set (model, &iter, SIZE_COLUMN, font_sizes[i], -1); + gtk_list_store_insert_with_values (model, &iter, -1, + SIZE_COLUMN, font_sizes[i], + -1); if (font_sizes[i] * PANGO_SCALE == fontsel->size) set_cursor_to_iter (GTK_TREE_VIEW (fontsel->size_list), &iter); diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c index 0d4435c72..f8aaf9a3b 100644 --- a/gtk/gtkicontheme.c +++ b/gtk/gtkicontheme.c @@ -2945,6 +2945,10 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, if (mime_type && strcmp (mime_type, "image/svg+xml") == 0) is_svg = TRUE; +#ifdef G_OS_WIN32 + else if (content_type && strcmp (content_type, ".svg") == 0) + is_svg = TRUE; +#endif } g_object_unref (file_info); diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c index 618f7132d..acd2e6f49 100644 --- a/gtk/gtkiconview.c +++ b/gtk/gtkiconview.c @@ -3450,7 +3450,9 @@ gtk_icon_view_set_cursor_item (GtkIconView *icon_view, if (item_obj != NULL) { + G_GNUC_BEGIN_IGNORE_DEPRECATIONS atk_focus_tracker_notify (item_obj); + G_GNUC_END_IGNORE_DEPRECATIONS atk_object_notify_state_change (item_obj, ATK_STATE_FOCUSED, TRUE); g_object_unref (item_obj); } @@ -7917,7 +7919,7 @@ gtk_icon_view_item_accessible_image_get_image_position (AtkImage *image, if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) return; - atk_component_get_position (ATK_COMPONENT (image), x, y, coord_type); + atk_component_get_extents (ATK_COMPONENT (image), x, y, NULL, NULL, coord_type); if (get_pixbuf_box (GTK_ICON_VIEW (item->widget), item->item, &box)) { @@ -8601,7 +8603,7 @@ gtk_icon_view_item_accessible_get_extents (AtkComponent *component, if (gtk_icon_view_item_accessible_is_showing (item)) { parent_obj = gtk_widget_get_accessible (item->widget); - atk_component_get_position (ATK_COMPONENT (parent_obj), &l_x, &l_y, coord_type); + atk_component_get_extents (ATK_COMPONENT (parent_obj), &l_x, &l_y, NULL, NULL, coord_type); *x = l_x + item->item->x; *y = l_y + item->item->y; } diff --git a/gtk/gtkimmodule.c b/gtk/gtkimmodule.c index a3a77c474..07c38f0b4 100644 --- a/gtk/gtkimmodule.c +++ b/gtk/gtkimmodule.c @@ -309,11 +309,186 @@ add_builtin_module (const gchar *module_name, return module; } +/* + * Keep IM module parsing independent of Pango's deprecated helpers. + */ + +/* + * Use unlocked input for the privately owned module-cache stream. + */ +#if defined(HAVE_GETC_UNLOCKED) +#define GTK_IM_MODULE_GETC(stream) getc_unlocked (stream) +#elif defined(HAVE__GETC_NOLOCK) +#define GTK_IM_MODULE_GETC(stream) _getc_nolock (stream) +#else +#define GTK_IM_MODULE_GETC(stream) getc (stream) +#endif + +static gchar * +gtk_im_module_scan_string (const gchar **pos) +{ + const gchar *p; + const gchar *q; + gchar *result; + gchar *out; + gsize len; + + p = *pos; + + while (g_ascii_isspace (*p)) + p++; + + if (*p != '"') + return NULL; + + p++; + q = p; + len = 0; + + while (*q && *q != '"') + { + if (*q == '\\') + { + q++; + + if (!*q) + return NULL; + } + + q++; + len++; + } + + if (*q != '"') + return NULL; + + result = g_malloc (len + 1); + out = result; + + while (p < q) + { + gchar c; + + c = *p++; + + if (c == '\\') + { + c = *p++; + + switch (c) + { + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + default: break; + } + } + + *out++ = c; + } + + *out = '\0'; + *pos = q + 1; + + return result; +} + +static gboolean +gtk_im_module_skip_space (const gchar **pos) +{ + while (g_ascii_isspace (**pos)) + (*pos)++; + + return **pos != '\0'; +} + +static gint +gtk_im_module_read_line (FILE *stream, + GString *str) +{ + gboolean escaped; + gboolean comment; + gint n_read; + gint lines; + gint c; + gint next_c; + + escaped = FALSE; + comment = FALSE; + n_read = 0; + lines = 1; + + g_string_truncate (str, 0); + + while (TRUE) + { + c = GTK_IM_MODULE_GETC (stream); + + if (c == EOF) + { + if (escaped) + g_string_append_c (str, '\\'); + + break; + } + + n_read++; + + if (c == '\r' || c == '\n') + { + next_c = GTK_IM_MODULE_GETC (stream); + + if (next_c != EOF && + !((c == '\r' && next_c == '\n') || + (c == '\n' && next_c == '\r'))) + ungetc (next_c, stream); + + if (escaped) + { + escaped = FALSE; + lines++; + continue; + } + + break; + } + + if (escaped) + { + escaped = FALSE; + + if (c == '#') + g_string_append_c (str, '#'); + else + { + g_string_append_c (str, '\\'); + g_string_append_c (str, c); + } + } + else if (!comment) + { + if (c == '#') + comment = TRUE; + else if (c == '\\') + escaped = TRUE; + else + g_string_append_c (str, c); + } + } + + + return n_read > 0 ? lines : 0; +} + +#undef GTK_IM_MODULE_GETC + static void gtk_im_module_initialize (void) { GString *line_buf = g_string_new (NULL); - GString *tmp_buf = g_string_new (NULL); gchar *filename = gtk_rc_get_im_module_file(); FILE *file; gboolean have_error = FALSE; @@ -387,18 +562,17 @@ gtk_im_module_initialize (void) * we allow no file at all. */ g_string_free (line_buf, TRUE); - g_string_free (tmp_buf, TRUE); g_free (filename); return; } - while (!have_error && pango_read_line (file, line_buf)) + while (!have_error && gtk_im_module_read_line (file, line_buf)) { const char *p; p = line_buf->str; - if (!pango_skip_space (&p)) + if (!gtk_im_module_skip_space (&p)) { /* Blank line marking the end of a module */ @@ -413,61 +587,62 @@ gtk_im_module_initialize (void) } if (!module) - { - /* Read a module location - */ - module = g_object_new (GTK_TYPE_IM_MODULE, NULL); + { + /* Read a module location + */ + module = g_object_new (GTK_TYPE_IM_MODULE, NULL); + module->path = gtk_im_module_scan_string (&p); - if (!pango_scan_string (&p, tmp_buf) || - pango_skip_space (&p)) - { - g_warning ("Error parsing context info in '%s'\n %s", - filename, line_buf->str); - have_error = TRUE; - } + if (!module->path || gtk_im_module_skip_space (&p)) + { + g_warning ("Error parsing context info in '%s'\n %s", + filename, line_buf->str); + have_error = TRUE; + continue; + } - module->path = g_strdup (tmp_buf->str); #ifdef G_OS_WIN32 - correct_libdir_prefix (&module->path); + correct_libdir_prefix (&module->path); #endif - g_type_module_set_name (G_TYPE_MODULE (module), module->path); - } + g_type_module_set_name (G_TYPE_MODULE (module), module->path); + } else { GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1); /* Read information about a context type */ - if (!pango_scan_string (&p, tmp_buf)) - goto context_error; - info->context_id = g_strdup (tmp_buf->str); + info->context_id = gtk_im_module_scan_string (&p); + if (!info->context_id) + goto context_error; - if (!pango_scan_string (&p, tmp_buf)) - goto context_error; - info->context_name = g_strdup (tmp_buf->str); + info->context_name = gtk_im_module_scan_string (&p); + if (!info->context_name) + goto context_error; - if (!pango_scan_string (&p, tmp_buf)) - goto context_error; - info->domain = g_strdup (tmp_buf->str); + info->domain = gtk_im_module_scan_string (&p); + if (!info->domain) + goto context_error; - if (!pango_scan_string (&p, tmp_buf)) - goto context_error; - info->domain_dirname = g_strdup (tmp_buf->str); + info->domain_dirname = gtk_im_module_scan_string (&p); + if (!info->domain_dirname) + goto context_error; #ifdef G_OS_WIN32 correct_localedir_prefix ((char **) &info->domain_dirname); #endif - if (!pango_scan_string (&p, tmp_buf)) - goto context_error; - info->default_locales = g_strdup (tmp_buf->str); + info->default_locales = gtk_im_module_scan_string (&p); + if (!info->default_locales) + goto context_error; - if (pango_skip_space (&p)) + if (gtk_im_module_skip_space (&p)) goto context_error; infos = g_slist_prepend (infos, info); continue; context_error: + free_info (info); g_warning ("Error parsing context info in '%s'\n %s", filename, line_buf->str); have_error = TRUE; @@ -491,7 +666,6 @@ gtk_im_module_initialize (void) fclose (file); g_string_free (line_buf, TRUE); - g_string_free (tmp_buf, TRUE); g_free (filename); } diff --git a/gtk/gtkmodules.c b/gtk/gtkmodules.c index da5568ffa..efde1eeb4 100644 --- a/gtk/gtkmodules.c +++ b/gtk/gtkmodules.c @@ -123,49 +123,44 @@ split_file_list (const gchar *str) } static gchar ** -get_module_path (void) +get_module_path (gint *env_dirs) { const gchar *module_path_env; const gchar *exe_prefix; const gchar *home_dir; - gchar *home_gtk_dir = NULL; - gchar *module_path; - gchar *default_dir; + gchar **path; + gint count; static gchar **result = NULL; + static gint n_env_dirs = 0; if (result) - return result; - - home_dir = g_get_home_dir(); - if (home_dir) - home_gtk_dir = g_build_filename (home_dir, ".gtk-2.0", NULL); + { + *env_dirs = n_env_dirs; + return result; + } + home_dir = g_get_home_dir (); module_path_env = g_getenv ("GTK_PATH"); exe_prefix = g_getenv ("GTK_EXE_PREFIX"); + result = split_file_list (module_path_env); + + for (path = result; *path; path++) + n_env_dirs++; + + count = n_env_dirs; + result = g_renew (gchar *, result, count + (home_dir ? 3 : 2)); + + if (home_dir) + result[count++] = g_build_filename (home_dir, ".gtk-2.0", NULL); + if (exe_prefix) - default_dir = g_build_filename (exe_prefix, "lib", "gtk-2.0", NULL); + result[count++] = g_build_filename (exe_prefix, "lib", "gtk-2.0", NULL); else - default_dir = g_build_filename (GTK_LIBDIR, "gtk-2.0", NULL); + result[count++] = g_build_filename (GTK_LIBDIR, "gtk-2.0", NULL); - if (module_path_env && home_gtk_dir) - module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S, - module_path_env, home_gtk_dir, default_dir, NULL); - else if (module_path_env) - module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S, - module_path_env, default_dir, NULL); - else if (home_gtk_dir) - module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S, - home_gtk_dir, default_dir, NULL); - else - module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S, - default_dir, NULL); - - g_free (home_gtk_dir); - g_free (default_dir); - - result = split_file_list (module_path); - g_free (module_path); + result[count] = NULL; + *env_dirs = n_env_dirs; return result; } @@ -181,18 +176,21 @@ get_module_path (void) gchar ** _gtk_get_module_path (const gchar *type) { - gchar **paths = get_module_path(); gchar **path; + gchar **paths; gchar **result; gint count = 0; + gint env_dirs; + + paths = get_module_path (&env_dirs); for (path = paths; *path; path++) count++; - result = g_new (gchar *, count * 5 + 1); + result = g_new (gchar *, count * 4 + env_dirs + 1); count = 0; - for (path = get_module_path (); *path; path++) + for (path = paths; *path; path++) { gint use_version, use_host; @@ -219,10 +217,11 @@ _gtk_get_module_path (const gchar *type) * live directly in .../.libs directories, for example print backends, * theme engines or input modules. * - * Keep this limited to absolute paths so the removed fallback that could - * load modules from the current working directory is not reintroduced. + * Keep this limited to absolute GTK_PATH entries so the removed fallback + * that could load modules from the current working directory is not + * reintroduced for built-in default paths. */ - if (g_path_is_absolute (*path)) + if (path < paths + env_dirs && g_path_is_absolute (*path)) result[count++] = g_strdup (*path); } diff --git a/gtk/gtkprintbackend.c b/gtk/gtkprintbackend.c index 66f5b41a9..5efd124e5 100644 --- a/gtk/gtkprintbackend.c +++ b/gtk/gtkprintbackend.c @@ -48,8 +48,22 @@ struct _GtkPrintBackendPrivate GtkPrintBackendStatus status; char **auth_info_required; char **auth_info; +#ifdef HAVE_CUPS_SECRET_SERVICE + guint store_auth_info : 1; +#endif }; +#ifdef HAVE_CUPS_SECRET_SERVICE +typedef gboolean (*GtkPrintBackendCanStoreAuthInfoFunc) + (GtkPrintBackend *backend); + +typedef void (*GtkPrintBackendSetPasswordFullFunc) + (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info, + gboolean store_auth_info); +#endif + enum { PRINTER_LIST_CHANGED, PRINTER_LIST_DONE, @@ -459,6 +473,9 @@ gtk_print_backend_init (GtkPrintBackend *backend) (GDestroyNotify) g_object_unref); priv->auth_info_required = NULL; priv->auth_info = NULL; +#ifdef HAVE_CUPS_SECRET_SERVICE + priv->store_auth_info = FALSE; +#endif } static void @@ -675,6 +692,41 @@ gtk_print_backend_set_password (GtkPrintBackend *backend, GTK_PRINT_BACKEND_GET_CLASS (backend)->set_password (backend, auth_info_required, auth_info); } + +#ifdef HAVE_CUPS_SECRET_SERVICE +static void +gtk_print_backend_set_password_full (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info, + gboolean store_auth_info) +{ + GtkPrintBackendClass *class; + + class = GTK_PRINT_BACKEND_GET_CLASS (backend); + + if (class->_gtk_reserved2 != NULL) + ((GtkPrintBackendSetPasswordFullFunc) class->_gtk_reserved2) + (backend, + auth_info_required, + auth_info, + store_auth_info); + else + gtk_print_backend_set_password (backend, + auth_info_required, + auth_info); +} + + +static void +store_auth_info_toggled (GtkToggleButton *button, + GtkPrintBackend *backend) +{ + backend->priv->store_auth_info = + gtk_toggle_button_get_active (button); +} +#endif + + static void store_entry (GtkEntry *entry, gpointer user_data) @@ -698,10 +750,23 @@ password_dialog_response (GtkWidget *dialog, GtkPrintBackendPrivate *priv = backend->priv; gint i; +#ifdef HAVE_CUPS_SECRET_SERVICE + if (response_id == GTK_RESPONSE_OK) + gtk_print_backend_set_password_full (backend, + priv->auth_info_required, + priv->auth_info, + priv->store_auth_info); + else + gtk_print_backend_set_password_full (backend, + priv->auth_info_required, + NULL, + FALSE); +#else if (response_id == GTK_RESPONSE_OK) gtk_print_backend_set_password (backend, priv->auth_info_required, priv->auth_info); else gtk_print_backend_set_password (backend, priv->auth_info_required, NULL); +#endif for (i = 0; i < g_strv_length (priv->auth_info_required); i++) if (priv->auth_info[i] != NULL) @@ -738,10 +803,23 @@ request_password (GtkPrintBackend *backend, gchar **ai_default = (gchar **) auth_info_default; gchar **ai_display = (gchar **) auth_info_display; gboolean *ai_visible = (gboolean *) auth_info_visible; +#ifdef HAVE_CUPS_SECRET_SERVICE + GtkPrintBackendClass *class; + gboolean can_store_auth_info = FALSE; +#endif priv->auth_info_required = g_strdupv (ai_required); length = g_strv_length (ai_required); priv->auth_info = g_new0 (gchar *, length + 1); +#ifdef HAVE_CUPS_SECRET_SERVICE + priv->store_auth_info = FALSE; + + class = GTK_PRINT_BACKEND_GET_CLASS (backend); + if (class->_gtk_reserved1 != NULL) + can_store_auth_info = + ((GtkPrintBackendCanStoreAuthInfoFunc) class->_gtk_reserved1) + (backend); +#endif dialog = gtk_dialog_new_with_buttons ( _("Authentication"), NULL, GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, @@ -810,6 +888,18 @@ request_password (GtkPrintBackend *backend, } } +#ifdef HAVE_CUPS_SECRET_SERVICE + if (can_store_auth_info) + { + GtkWidget *remember; + + remember = gtk_check_button_new_with_mnemonic (_("_Remember password")); + gtk_box_pack_start (GTK_BOX (vbox), remember, FALSE, FALSE, 6); + g_signal_connect (remember, "toggled", + G_CALLBACK (store_auth_info_toggled), backend); + } +#endif + if (focus != NULL) { gtk_widget_grab_focus (focus); diff --git a/gtk/gtkprintunixdialog.c b/gtk/gtkprintunixdialog.c index da9010b96..af64a5a37 100644 --- a/gtk/gtkprintunixdialog.c +++ b/gtk/gtkprintunixdialog.c @@ -796,7 +796,9 @@ printer_status_cb (GtkPrintBackend *backend, /* When the pause state change then we need to update sensitive property * of GTK_RESPONSE_OK button inside of selected_printer_changed function. */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->printer_treeview)); + priv->internal_printer_change = TRUE; selected_printer_changed (selection, dialog); + priv->internal_printer_change = FALSE; if (gtk_print_backend_printer_list_is_done (backend) && gtk_printer_is_default (printer) && diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h index b2c2df999..cc1b50dfa 100644 --- a/gtk/gtkprivate.h +++ b/gtk/gtkprivate.h @@ -134,6 +134,13 @@ const gchar *_gtk_get_data_prefix (); #endif /* G_OS_WIN32 */ +#ifdef ENABLE_NLS +G_GNUC_INTERNAL +const gchar *_gtk_font_style_name_translate (const gchar *name); +#else +#define _gtk_font_style_name_translate(name) (name) +#endif + gboolean _gtk_fnmatch (const char *pattern, const char *string, gboolean no_leading_period); diff --git a/gtk/gtkquartz.c b/gtk/gtkquartz.c index 87ba87745..caf8404f8 100644 --- a/gtk/gtkquartz.c +++ b/gtk/gtkquartz.c @@ -67,7 +67,10 @@ _gtk_quartz_create_image_from_pixbuf (GdkPixbuf *pixbuf) } [nsimage lockFocus]; - context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + if (gdk_quartz_osx_version () < GDK_OSX_YOSEMITE) + context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + else + context = [[NSGraphicsContext currentContext] CGContext]; CGContextDrawImage (context, CGRectMake (0, 0, pixbuf_width, pixbuf_height), image); [nsimage unlockFocus]; diff --git a/gtk/gtkrc.c b/gtk/gtkrc.c index 7bfbea85c..d316a0d90 100644 --- a/gtk/gtkrc.c +++ b/gtk/gtkrc.c @@ -808,6 +808,8 @@ gtk_rc_parse_named (GtkRcContext *context, gchar *path = NULL; const gchar *home_dir; gchar *subpath; + const gchar * const *xdg_data_dirs; + gint i; if (type) subpath = g_strconcat ("gtk-2.0-", type, @@ -829,6 +831,22 @@ gtk_rc_parse_named (GtkRcContext *context, } } + if (!path) + { + xdg_data_dirs = g_get_system_data_dirs (); + for (i = 0; xdg_data_dirs[i]; i++) + { + path = g_build_filename (xdg_data_dirs[i], "themes", name, subpath, NULL); + if (g_file_test (path, G_FILE_TEST_EXISTS)) + break; + else + { + g_free (path); + path = NULL; + } + } + } + if (!path) { gchar *theme_dir = gtk_rc_get_theme_dir (); diff --git a/gtk/gtktextbtree.c b/gtk/gtktextbtree.c index 2cd097800..ce2bd1973 100644 --- a/gtk/gtktextbtree.c +++ b/gtk/gtktextbtree.c @@ -2407,7 +2407,6 @@ _gtk_text_btree_get_text (const GtkTextIter *start_orig, GtkTextLineSegment *seg; GtkTextLineSegment *end_seg; GString *retval; - gchar *str; GtkTextIter iter; GtkTextIter start; GtkTextIter end; @@ -2439,9 +2438,7 @@ _gtk_text_btree_get_text (const GtkTextIter *start_orig, copy_segment (retval, include_hidden, include_nonchars, &iter, &end); - str = retval->str; - g_string_free (retval, FALSE); - return str; + return g_string_free (retval, FALSE); } gint diff --git a/gtk/gtktooltips.h b/gtk/gtktooltips.h index eb1e0a0be..fbafd9939 100644 --- a/gtk/gtktooltips.h +++ b/gtk/gtktooltips.h @@ -74,7 +74,11 @@ struct _GtkTooltips guint have_grab : 1; guint use_sticky_delay : 1; gint timer_tag; - GTimeVal last_popdown; + struct + { + glong tv_sec; + glong tv_usec; + } last_popdown; }; struct _GtkTooltipsClass diff --git a/gtk/gtktrayicon.h b/gtk/gtktrayicon.h index 9ad1c54f2..ab4eda2f5 100644 --- a/gtk/gtktrayicon.h +++ b/gtk/gtktrayicon.h @@ -46,12 +46,12 @@ struct _GtkTrayIconClass { GtkPlugClass parent_class; - void (*__gtk_reserved1); - void (*__gtk_reserved2); - void (*__gtk_reserved3); - void (*__gtk_reserved4); - void (*__gtk_reserved5); - void (*__gtk_reserved6); + void (*__gtk_reserved1) (void); + void (*__gtk_reserved2) (void); + void (*__gtk_reserved3) (void); + void (*__gtk_reserved4) (void); + void (*__gtk_reserved5) (void); + void (*__gtk_reserved6) (void); }; GType gtk_tray_icon_get_type (void) G_GNUC_CONST; diff --git a/gtk/makegtkalias.pl b/gtk/makegtkalias.pl index 3f956b05e..67ec7c973 100755 --- a/gtk/makegtkalias.pl +++ b/gtk/makegtkalias.pl @@ -14,7 +14,7 @@ print < -#ifdef G_HAVE_GNUC_VISIBILITY +#if defined(G_HAVE_GNUC_VISIBILITY) && !defined(__APPLE__) EOF diff --git a/gtk/queryimmodules.c b/gtk/queryimmodules.c index d20637e10..e118b9c19 100644 --- a/gtk/queryimmodules.c +++ b/gtk/queryimmodules.c @@ -159,9 +159,6 @@ query_module (const char *dir, const char *name, GString *contents) { void (*list) (const GtkIMContextInfo ***contexts, guint *n_contexts); - G_GNUC_UNUSED void (*init) (GTypeModule *type_module); - G_GNUC_UNUSED void (*exit) (void); - G_GNUC_UNUSED GtkIMContext *(*create) (const gchar *context_id); gpointer list_ptr; gpointer init_ptr; @@ -196,9 +193,6 @@ query_module (const char *dir, const char *name, GString *contents) int i; list = list_ptr; - init = init_ptr; - exit = exit_ptr; - create = create_ptr; print_escaped (contents, path); g_string_append_c (contents, '\n'); diff --git a/gtk/tests/builder.c b/gtk/tests/builder.c index ebba220e9..121012704 100644 --- a/gtk/tests/builder.c +++ b/gtk/tests/builder.c @@ -132,7 +132,7 @@ static int object = 0; static int object_after = 0; void /* exported for GtkBuilder */ -signal_normal (GtkWindow *window, GParamSpec spec) +signal_normal (GtkWindow *window, GParamSpec *spec) { g_assert (GTK_IS_WINDOW (window)); g_assert (normal == 0); @@ -142,7 +142,7 @@ signal_normal (GtkWindow *window, GParamSpec spec) } void /* exported for GtkBuilder */ -signal_after (GtkWindow *window, GParamSpec spec) +signal_after (GtkWindow *window, GParamSpec *spec) { g_assert (GTK_IS_WINDOW (window)); g_assert (normal == 1); @@ -152,7 +152,7 @@ signal_after (GtkWindow *window, GParamSpec spec) } void /* exported for GtkBuilder */ -signal_object (GtkButton *button, GParamSpec spec) +signal_object (GtkButton *button, GParamSpec *spec) { g_assert (GTK_IS_BUTTON (button)); g_assert (object == 0); @@ -162,7 +162,7 @@ signal_object (GtkButton *button, GParamSpec spec) } void /* exported for GtkBuilder */ -signal_object_after (GtkButton *button, GParamSpec spec) +signal_object_after (GtkButton *button, GParamSpec *spec) { g_assert (GTK_IS_BUTTON (button)); g_assert (object == 1); @@ -172,28 +172,28 @@ signal_object_after (GtkButton *button, GParamSpec spec) } void /* exported for GtkBuilder */ -signal_first (GtkButton *button, GParamSpec spec) +signal_first (GtkButton *button, GParamSpec *spec) { g_assert (normal == 0); normal = 10; } void /* exported for GtkBuilder */ -signal_second (GtkButton *button, GParamSpec spec) +signal_second (GtkButton *button, GParamSpec *spec) { g_assert (normal == 10); normal = 20; } void /* exported for GtkBuilder */ -signal_extra (GtkButton *button, GParamSpec spec) +signal_extra (GtkButton *button, GParamSpec *spec) { g_assert (normal == 20); normal = 30; } void /* exported for GtkBuilder */ -signal_extra2 (GtkButton *button, GParamSpec spec) +signal_extra2 (GtkButton *button, GParamSpec *spec) { g_assert (normal == 30); normal = 40; diff --git a/gtk/updateiconcache.c b/gtk/updateiconcache.c index 4a3158e12..e167d2f8f 100644 --- a/gtk/updateiconcache.c +++ b/gtk/updateiconcache.c @@ -25,6 +25,15 @@ #include #include #include + +#ifdef _MSC_VER +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & _S_IFMT) == _S_IFREG) +#endif +#endif #include #ifdef HAVE_UNISTD_H #include @@ -608,6 +617,16 @@ replace_backslashes_with_slashes (gchar *path) path[i] = '/'; } +static gint +compare_string_pointers (gconstpointer a, + gconstpointer b) +{ + const gchar * const *str_a = a; + const gchar * const *str_b = b; + + return strcmp (*str_a, *str_b); +} + static GList * scan_directory (const gchar *base_path, const gchar *subdir, @@ -617,10 +636,13 @@ scan_directory (const gchar *base_path, { GHashTable *dir_hash; GDir *dir; - const gchar *name; + GPtrArray *names; + const gchar *entry_name; + gchar *name; gchar *dir_path; gboolean dir_added = FALSE; guint dir_index = 0xffff; + guint i; dir_path = g_build_path ("/", base_path, subdir, NULL); @@ -628,91 +650,103 @@ scan_directory (const gchar *base_path, dir = g_dir_open (dir_path, 0, NULL); if (!dir) - return directories; + { + g_free (dir_path); + return directories; + } + + names = g_ptr_array_new_with_free_func (g_free); + + while ((entry_name = g_dir_read_name (dir))) + g_ptr_array_add (names, g_strdup (entry_name)); + + g_dir_close (dir); + + g_ptr_array_sort (names, compare_string_pointers); dir_hash = g_hash_table_new (g_str_hash, g_str_equal); - while ((name = g_dir_read_name (dir))) + for (i = 0; i < names->len; i++) { + GStatBuf stat_buf; gchar *path; - gboolean retval; - int flags = 0; - Image *image; - gchar *basename, *dot; + name = g_ptr_array_index (names, i); path = g_build_filename (dir_path, name, NULL); - retval = g_file_test (path, G_FILE_TEST_IS_DIR); - if (retval) - { - gchar *subsubdir; + if (g_stat (path, &stat_buf) == 0) + { + if (S_ISDIR (stat_buf.st_mode)) + { + gchar *subsubdir; - if (subdir) - subsubdir = g_build_path ("/", subdir, name, NULL); - else - subsubdir = g_strdup (name); - directories = scan_directory (base_path, subsubdir, files, - directories, depth + 1); - g_free (subsubdir); + if (subdir) + subsubdir = g_build_path ("/", subdir, name, NULL); + else + subsubdir = g_strdup (name); - continue; - } + directories = scan_directory (base_path, subsubdir, files, + directories, depth + 1); + g_free (subsubdir); + } + else if (subdir != NULL && S_ISREG (stat_buf.st_mode)) + { + gint flags = 0; + Image *image; + gchar *dot; - /* ignore images in the toplevel directory */ - if (subdir == NULL) - continue; + dot = strrchr (name, '.'); - retval = g_file_test (path, G_FILE_TEST_IS_REGULAR); - if (retval) - { - if (g_str_has_suffix (name, ".png")) - flags |= HAS_SUFFIX_PNG; - else if (g_str_has_suffix (name, ".svg")) - flags |= HAS_SUFFIX_SVG; - else if (g_str_has_suffix (name, ".xpm")) - flags |= HAS_SUFFIX_XPM; - else if (g_str_has_suffix (name, ".icon")) - flags |= HAS_ICON_FILE; + if (dot != NULL) + { + if (strcmp (dot, ".png") == 0) + flags = HAS_SUFFIX_PNG; + else if (strcmp (dot, ".svg") == 0) + flags = HAS_SUFFIX_SVG; + else if (strcmp (dot, ".xpm") == 0) + flags = HAS_SUFFIX_XPM; + else if (strcmp (dot, ".icon") == 0) + flags = HAS_ICON_FILE; + } - if (flags == 0) - continue; + if (flags != 0) + { + *dot = '\0'; - basename = g_strdup (name); - dot = strrchr (basename, '.'); - *dot = '\0'; + image = g_hash_table_lookup (dir_hash, name); + if (!image) + { + if (!dir_added) + { + dir_added = TRUE; + dir_index = g_list_length (directories); + directories = + g_list_append (directories, + g_strdup (subdir)); + } - image = g_hash_table_lookup (dir_hash, basename); - if (!image) - { - if (!dir_added) - { - dir_added = TRUE; - if (subdir) - { - dir_index = g_list_length (directories); - directories = g_list_append (directories, g_strdup (subdir)); - } - else - continue; - } + image = g_new0 (Image, 1); + image->dir_index = dir_index; + g_hash_table_insert (dir_hash, + g_strdup (name), + image); + } - image = g_new0 (Image, 1); - image->dir_index = dir_index; - g_hash_table_insert (dir_hash, g_strdup (basename), image); - } + image->flags |= flags; - image->flags |= flags; + maybe_cache_image_data (image, path); + maybe_cache_icon_data (image, path); - maybe_cache_image_data (image, path); - maybe_cache_icon_data (image, path); - - g_free (basename); - } + *dot = '.'; + } + } + } g_free (path); } - g_dir_close (dir); + g_ptr_array_free (names, TRUE); + g_free (dir_path); /* Move dir into the big file hash */ g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files); @@ -1375,6 +1409,34 @@ write_dir_index (FILE *cache, int offset, GList *directories) return TRUE; } +static void +free_hash_context (HashContext *context) +{ + gint i; + + for (i = 0; i < context->size; i++) + { + HashNode *node = context->nodes[i]; + + while (node) + { + HashNode *next = node->next; + GList *l; + + for (l = node->image_list; l; l = l->next) + g_free (l->data); + + g_list_free (node->image_list); + g_free (node->name); + g_free (node); + + node = next; + } + } + + g_free (context->nodes); +} + static gboolean write_file (FILE *cache, GHashTable *files, GList *directories) { @@ -1418,6 +1480,11 @@ write_file (FILE *cache, GHashTable *files, GList *directories) return FALSE; } + g_hash_table_destroy (string_pool); + string_pool = NULL; + + free_hash_context (&context); + return TRUE; } @@ -1544,6 +1611,8 @@ opentmp: exit (1); } + g_hash_table_destroy (files); + if (!safe_fclose (cache)) { g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno)); @@ -1623,6 +1692,9 @@ opentmp: if (!quiet) g_printerr (_("Cache file created successfully.\n")); + + g_free (tmp_cache_path); + g_free (cache_path); } static void @@ -1658,6 +1730,9 @@ write_csource (const gchar *path) } g_printf ("0x%02x\n};\n", (guint8)data[i]); + + g_free (data); + g_free (cache_path); } static GOptionEntry args[] = { @@ -1721,6 +1796,7 @@ main (int argc, char **argv) g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE); g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); path = argv[1]; #ifdef G_OS_WIN32 diff --git a/modules/other/gail/Makefile.am b/modules/other/gail/Makefile.am index 991512dea..1b9a4c6bd 100644 --- a/modules/other/gail/Makefile.am +++ b/modules/other/gail/Makefile.am @@ -72,6 +72,7 @@ libgailincludedir=$(includedir)/gail-1.0/gail gail_private_h_sources = \ gail.h \ + gailvalue.h \ gailadjustment.h \ gailarrow.h \ gailbooleancell.h \ diff --git a/modules/other/gail/gail.c b/modules/other/gail/gail.c index 3669a99ce..619fbf12f 100644 --- a/modules/other/gail/gail.c +++ b/modules/other/gail/gail.c @@ -587,9 +587,11 @@ gail_focus_notify (GtkWidget *widget) /* * Do not report focus on redundant object */ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS if (atk_obj && - (atk_object_get_role(atk_obj) != ATK_ROLE_REDUNDANT_OBJECT)) - atk_focus_tracker_notify (atk_obj); + (atk_object_get_role(atk_obj) != ATK_ROLE_REDUNDANT_OBJECT)) + atk_focus_tracker_notify (atk_obj); + G_GNUC_END_IGNORE_DEPRECATIONS if (atk_obj && transient) g_object_unref (atk_obj); if (subsequent_focus_widget) @@ -938,8 +940,10 @@ gail_accessibility_module_init (void) /* LIBGNOMECANVAS SUPPORT */ GAIL_WIDGET_SET_FACTORY (GTK_TYPE_OBJECT, gail_object); + G_GNUC_BEGIN_IGNORE_DEPRECATIONS atk_focus_tracker_init (gail_focus_tracker_init); focus_tracker_id = atk_add_focus_tracker (gail_focus_tracker); + G_GNUC_END_IGNORE_DEPRECATIONS /* Initialize the GailUtility class */ g_type_class_unref (g_type_class_ref (GAIL_TYPE_UTIL)); @@ -974,7 +978,9 @@ gnome_accessibility_module_shutdown (void) return; } gail_initialized = FALSE; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS atk_remove_focus_tracker (focus_tracker_id); + G_GNUC_END_IGNORE_DEPRECATIONS fprintf (stderr, "GTK Accessibility Module shutdown\n"); diff --git a/modules/other/gail/gailadjustment.c b/modules/other/gail/gailadjustment.c index f1bb53f2b..021b79357 100644 --- a/modules/other/gail/gailadjustment.c +++ b/modules/other/gail/gailadjustment.c @@ -22,6 +22,7 @@ #include #include #include "gailadjustment.h" +#include "gailvalue.h" static void gail_adjustment_class_init (GailAdjustmentClass *klass); @@ -32,16 +33,25 @@ static void gail_adjustment_real_initialize (AtkObject *obj, static void atk_value_interface_init (AtkValueIface *iface); -static void gail_adjustment_get_current_value (AtkValue *obj, - GValue *value); -static void gail_adjustment_get_maximum_value (AtkValue *obj, - GValue *value); -static void gail_adjustment_get_minimum_value (AtkValue *obj, - GValue *value); -static void gail_adjustment_get_minimum_increment (AtkValue *obj, - GValue *value); -static gboolean gail_adjustment_set_current_value (AtkValue *obj, - const GValue *value); + +#ifdef HAVE_ATK_VALUE_2_12 +GAIL_DEFINE_ADJUSTMENT_VALUE_READERS ( + gail_adjustment, GAIL_ADJUSTMENT (obj)->adjustment, + adjustment->value, adjustment->upper) +GAIL_DEFINE_ADJUSTMENT_VALUE_INCREMENT ( + gail_adjustment, GAIL_ADJUSTMENT (obj)->adjustment, + GAIL_VALUE_MINIMUM_INCREMENT (adjustment)) + +static void +gail_adjustment_set_value (AtkValue *obj, gdouble value) +{ + GtkAdjustment *adjustment; + + adjustment = GAIL_ADJUSTMENT (obj)->adjustment; + if (adjustment != NULL) + gtk_adjustment_set_value (adjustment, value); +} +#endif static void gail_adjustment_destroyed (GtkAdjustment *adjustment, GailAdjustment *gail_adjustment); @@ -105,9 +115,15 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_minimum_value = gail_adjustment_get_minimum_value; iface->get_minimum_increment = gail_adjustment_get_minimum_increment; iface->set_current_value = gail_adjustment_set_current_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_adjustment_get_value_and_text; + iface->get_range = gail_adjustment_get_range; + iface->get_increment = gail_adjustment_get_increment; + iface->set_value = gail_adjustment_set_value; +#endif } -static void +void gail_adjustment_get_current_value (AtkValue *obj, GValue *value) { @@ -127,7 +143,7 @@ gail_adjustment_get_current_value (AtkValue *obj, g_value_set_double (value,current_value); } -static void +void gail_adjustment_get_maximum_value (AtkValue *obj, GValue *value) { @@ -147,7 +163,7 @@ gail_adjustment_get_maximum_value (AtkValue *obj, g_value_set_double (value, maximum_value); } -static void +void gail_adjustment_get_minimum_value (AtkValue *obj, GValue *value) { @@ -167,48 +183,20 @@ gail_adjustment_get_minimum_value (AtkValue *obj, g_value_set_double (value, minimum_value); } -static void -gail_adjustment_get_minimum_increment (AtkValue *obj, - GValue *value) +void +gail_adjustment_get_minimum_increment (AtkValue *obj, GValue *value) { - GtkAdjustment* adjustment; - gdouble minimum_increment; + GtkAdjustment *adjustment = GAIL_ADJUSTMENT (obj)->adjustment; - adjustment = GAIL_ADJUSTMENT (obj)->adjustment; if (adjustment == NULL) - { - /* State is defunct */ return; - } - if (adjustment->step_increment != 0 && - adjustment->page_increment != 0) - { - if (ABS (adjustment->step_increment) < ABS (adjustment->page_increment)) - minimum_increment = adjustment->step_increment; - else - minimum_increment = adjustment->page_increment; - } - else if (adjustment->step_increment == 0 && - adjustment->page_increment == 0) - { - minimum_increment = 0; - } - else if (adjustment->step_increment == 0) - { - minimum_increment = adjustment->page_increment; - } - else - { - minimum_increment = adjustment->step_increment; - } - - memset (value, 0, sizeof (GValue)); + memset (value, 0, sizeof (GValue)); g_value_init (value, G_TYPE_DOUBLE); - g_value_set_double (value, minimum_increment); + g_value_set_double (value, GAIL_VALUE_MINIMUM_INCREMENT (adjustment)); } -static gboolean +gboolean gail_adjustment_set_current_value (AtkValue *obj, const GValue *value) { diff --git a/modules/other/gail/gailadjustment.h b/modules/other/gail/gailadjustment.h index 790534dc9..0135b3d91 100644 --- a/modules/other/gail/gailadjustment.h +++ b/modules/other/gail/gailadjustment.h @@ -50,6 +50,22 @@ struct _GailAdjustmentClass AtkObject *gail_adjustment_new (GtkAdjustment *adjustment); +G_GNUC_INTERNAL void +gail_adjustment_get_current_value (AtkValue *obj, + GValue *value); +G_GNUC_INTERNAL void +gail_adjustment_get_maximum_value (AtkValue *obj, + GValue *value); +G_GNUC_INTERNAL void +gail_adjustment_get_minimum_value (AtkValue *obj, + GValue *value); +G_GNUC_INTERNAL void +gail_adjustment_get_minimum_increment (AtkValue *obj, + GValue *value); +G_GNUC_INTERNAL gboolean +gail_adjustment_set_current_value (AtkValue *obj, + const GValue *value); + G_END_DECLS #endif /* __GAIL_ADJUSTMENT_H__ */ diff --git a/modules/other/gail/gailbutton.c b/modules/other/gail/gailbutton.c index 6ecf9cd74..495efce1b 100644 --- a/modules/other/gail/gailbutton.c +++ b/modules/other/gail/gailbutton.c @@ -1021,7 +1021,7 @@ gail_button_get_image_position (AtkImage *image, if (button_image != NULL) { obj = gtk_widget_get_accessible (GTK_WIDGET (button_image)); - atk_component_get_position (ATK_COMPONENT (obj), x, y, coord_type); + atk_component_get_extents (ATK_COMPONENT (obj), x, y, NULL, NULL, coord_type); } else { diff --git a/modules/other/gail/gailimage.c b/modules/other/gail/gailimage.c index 0a33cd1ec..968b1d464 100644 --- a/modules/other/gail/gailimage.c +++ b/modules/other/gail/gailimage.c @@ -179,7 +179,7 @@ gail_image_get_image_position (AtkImage *image, gint *y, AtkCoordType coord_type) { - atk_component_get_position (ATK_COMPONENT (image), x, y, coord_type); + atk_component_get_extents (ATK_COMPONENT (image), x, y, NULL, NULL, coord_type); } static void diff --git a/modules/other/gail/gailimagecell.c b/modules/other/gail/gailimagecell.c index efcded036..5860b723f 100644 --- a/modules/other/gail/gailimagecell.c +++ b/modules/other/gail/gailimagecell.c @@ -150,7 +150,7 @@ gail_image_cell_get_image_position (AtkImage *image, gint *y, AtkCoordType coord_type) { - atk_component_get_position (ATK_COMPONENT (image), x, y, coord_type); + atk_component_get_extents (ATK_COMPONENT (image), x, y, NULL, NULL, coord_type); } static void diff --git a/modules/other/gail/gailnotebook.c b/modules/other/gail/gailnotebook.c index 795af606b..f937abd0e 100644 --- a/modules/other/gail/gailnotebook.c +++ b/modules/other/gail/gailnotebook.c @@ -583,7 +583,9 @@ gail_notebook_check_focus_tab (gpointer data) AtkObject *obj; obj = atk_object_ref_accessible_child (atk_obj, focus_page_num); + G_GNUC_BEGIN_IGNORE_DEPRECATIONS atk_focus_tracker_notify (obj); + G_GNUC_END_IGNORE_DEPRECATIONS g_object_unref (obj); } diff --git a/modules/other/gail/gailnotebookpage.c b/modules/other/gail/gailnotebookpage.c index 444ddac0e..cd4e18d7d 100644 --- a/modules/other/gail/gailnotebookpage.c +++ b/modules/other/gail/gailnotebookpage.c @@ -467,7 +467,7 @@ gail_notebook_page_get_extents (AtkComponent *component, child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0); gail_return_if_fail (child); - atk_component_get_position (ATK_COMPONENT (child), x, y, coord_type); + atk_component_get_extents (ATK_COMPONENT (child), x, y, NULL, NULL, coord_type); g_object_unref (child); } else diff --git a/modules/other/gail/gailpaned.c b/modules/other/gail/gailpaned.c index 5ac2d2340..42ac248da 100644 --- a/modules/other/gail/gailpaned.c +++ b/modules/other/gail/gailpaned.c @@ -22,6 +22,7 @@ #include #include #include "gailpaned.h" +#include "gailvalue.h" static void gail_paned_class_init (GailPanedClass *klass); @@ -44,6 +45,35 @@ static void gail_paned_get_minimum_value (AtkValue *obj, static gboolean gail_paned_set_current_value (AtkValue *obj, const GValue *value); +#ifdef HAVE_ATK_VALUE_2_12 +static void +gail_paned_get_value_and_text (AtkValue *obj, gdouble *value, gchar **text) +{ + GtkWidget *widget = GTK_ACCESSIBLE (obj)->widget; + + if (text != NULL) + *text = NULL; + if (value != NULL) + *value = widget != NULL + ? gtk_paned_get_position (GTK_PANED (widget)) : 0.0; +} + +static AtkRange * +gail_paned_get_range (AtkValue *obj) +{ + GtkWidget *widget = GTK_ACCESSIBLE (obj)->widget; + GtkPaned *paned; + + if (widget == NULL) + return NULL; + paned = GTK_PANED (widget); + return atk_range_new (paned->min_position, paned->max_position, NULL); +} + +GAIL_DEFINE_WIDGET_VALUE_SETTER ( + gail_paned, GTK_PANED, gtk_paned_set_position, (gint) value) +#endif + G_DEFINE_TYPE_WITH_CODE (GailPaned, gail_paned, GAIL_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE, atk_value_interface_init)) @@ -112,6 +142,11 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_maximum_value = gail_paned_get_maximum_value; iface->get_minimum_value = gail_paned_get_minimum_value; iface->set_current_value = gail_paned_set_current_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_paned_get_value_and_text; + iface->get_range = gail_paned_get_range; + iface->set_value = gail_paned_set_value; +#endif } static void diff --git a/modules/other/gail/gailpixmap.c b/modules/other/gail/gailpixmap.c index b8ab4b3da..4f44f93be 100644 --- a/modules/other/gail/gailpixmap.c +++ b/modules/other/gail/gailpixmap.c @@ -103,7 +103,7 @@ gail_pixmap_get_image_position (AtkImage *obj, gint *y, AtkCoordType coord_type) { - atk_component_get_position (ATK_COMPONENT (obj), x, y, coord_type); + atk_component_get_extents (ATK_COMPONENT (obj), x, y, NULL, NULL, coord_type); } static void diff --git a/modules/other/gail/gailprogressbar.c b/modules/other/gail/gailprogressbar.c index d70c139be..6a0df280e 100644 --- a/modules/other/gail/gailprogressbar.c +++ b/modules/other/gail/gailprogressbar.c @@ -26,6 +26,7 @@ #include #include "gailprogressbar.h" +#include "gailvalue.h" #include "gailadjustment.h" static void gail_progress_bar_class_init (GailProgressBarClass *klass); @@ -50,6 +51,13 @@ static void gail_progress_bar_get_minimum_value (AtkValue *obj, static void gail_progress_bar_value_changed (GtkAdjustment *adjustment, gpointer data); +#ifdef HAVE_ATK_VALUE_2_12 +GAIL_DEFINE_ADJUSTMENT_VALUE_READERS ( + gail_progress_bar, + GAIL_VALUE_ADJUSTMENT (GAIL_PROGRESS_BAR (obj)->adjustment), + adjustment->value, adjustment->upper) +#endif + G_DEFINE_TYPE_WITH_CODE (GailProgressBar, gail_progress_bar, GAIL_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE, atk_value_interface_init)) @@ -108,6 +116,10 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_current_value = gail_progress_bar_get_current_value; iface->get_maximum_value = gail_progress_bar_get_maximum_value; iface->get_minimum_value = gail_progress_bar_get_minimum_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_progress_bar_get_value_and_text; + iface->get_range = gail_progress_bar_get_range; +#endif } static void @@ -125,7 +137,7 @@ gail_progress_bar_get_current_value (AtkValue *obj, */ return; - atk_value_get_current_value (ATK_VALUE (progress_bar->adjustment), value); + gail_adjustment_get_current_value (ATK_VALUE (progress_bar->adjustment), value); } static void @@ -143,7 +155,7 @@ gail_progress_bar_get_maximum_value (AtkValue *obj, */ return; - atk_value_get_maximum_value (ATK_VALUE (progress_bar->adjustment), value); + gail_adjustment_get_maximum_value (ATK_VALUE (progress_bar->adjustment), value); } static void @@ -161,7 +173,7 @@ gail_progress_bar_get_minimum_value (AtkValue *obj, */ return; - atk_value_get_minimum_value (ATK_VALUE (progress_bar->adjustment), value); + gail_adjustment_get_minimum_value (ATK_VALUE (progress_bar->adjustment), value); } static void diff --git a/modules/other/gail/gailrange.c b/modules/other/gail/gailrange.c index 9a91bb333..8170d86d0 100644 --- a/modules/other/gail/gailrange.c +++ b/modules/other/gail/gailrange.c @@ -23,6 +23,7 @@ #include #include #include "gailrange.h" +#include "gailvalue.h" #include "gailadjustment.h" #include "gail-private-macros.h" @@ -70,6 +71,28 @@ static gboolean gail_range_set_description (AtkAction *action, gint i, const gchar *desc); +#ifdef HAVE_ATK_VALUE_2_12 +#define GAIL_RANGE_ADJUSTMENT(obj) \ + (GTK_ACCESSIBLE (obj)->widget != NULL \ + ? GAIL_VALUE_ADJUSTMENT (GAIL_RANGE (obj)->adjustment) : NULL) +#define GAIL_RANGE_UPPER(obj, adjustment) \ + (gtk_range_get_restrict_to_fill_level ( \ + GTK_RANGE (GTK_ACCESSIBLE (obj)->widget)) \ + ? MIN ((adjustment)->upper - (adjustment)->page_size, \ + gtk_range_get_fill_level ( \ + GTK_RANGE (GTK_ACCESSIBLE (obj)->widget))) \ + : (adjustment)->upper - (adjustment)->page_size) + +GAIL_DEFINE_ADJUSTMENT_VALUE_READERS ( + gail_range, GAIL_RANGE_ADJUSTMENT (obj), adjustment->value, + GAIL_RANGE_UPPER (obj, adjustment)) +GAIL_DEFINE_ADJUSTMENT_VALUE_INCREMENT ( + gail_range, GAIL_RANGE_ADJUSTMENT (obj), + GAIL_VALUE_MINIMUM_INCREMENT (adjustment)) +GAIL_DEFINE_WIDGET_VALUE_SETTER ( + gail_range, GTK_RANGE, gtk_range_set_value, value) +#endif + G_DEFINE_TYPE_WITH_CODE (GailRange, gail_range, GAIL_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init) G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE, atk_value_interface_init)) @@ -163,6 +186,12 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_minimum_value = gail_range_get_minimum_value; iface->get_minimum_increment = gail_range_get_minimum_increment; iface->set_current_value = gail_range_set_current_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_range_get_value_and_text; + iface->get_range = gail_range_get_range; + iface->get_increment = gail_range_get_increment; + iface->set_value = gail_range_set_value; +#endif } static void @@ -180,7 +209,7 @@ gail_range_get_current_value (AtkValue *obj, */ return; - atk_value_get_current_value (ATK_VALUE (range->adjustment), value); + gail_adjustment_get_current_value (ATK_VALUE (range->adjustment), value); } static void @@ -201,7 +230,7 @@ gail_range_get_maximum_value (AtkValue *obj, */ return; - atk_value_get_maximum_value (ATK_VALUE (range->adjustment), value); + gail_adjustment_get_maximum_value (ATK_VALUE (range->adjustment), value); gtk_range = GTK_RANGE (gtk_accessible_get_widget (GTK_ACCESSIBLE (range))); g_return_if_fail (gtk_range); @@ -231,7 +260,7 @@ gail_range_get_minimum_value (AtkValue *obj, */ return; - atk_value_get_minimum_value (ATK_VALUE (range->adjustment), value); + gail_adjustment_get_minimum_value (ATK_VALUE (range->adjustment), value); } static void @@ -248,7 +277,7 @@ gail_range_get_minimum_increment (AtkValue *obj, GValue *value) */ return; - atk_value_get_minimum_increment (ATK_VALUE (range->adjustment), value); + gail_adjustment_get_minimum_increment (ATK_VALUE (range->adjustment), value); } static gboolean gail_range_set_current_value (AtkValue *obj, @@ -519,5 +548,3 @@ gail_range_set_description (AtkAction *action, else return FALSE; } - - diff --git a/modules/other/gail/gailscalebutton.c b/modules/other/gail/gailscalebutton.c index f04c69046..75915e43d 100644 --- a/modules/other/gail/gailscalebutton.c +++ b/modules/other/gail/gailscalebutton.c @@ -21,6 +21,7 @@ #include #include "gailscalebutton.h" +#include "gailvalue.h" #include "gailadjustment.h" #include "gail-private-macros.h" @@ -65,6 +66,24 @@ static void gail_scale_button_get_minimum_increment (AtkValue *obj, static gboolean gail_scale_button_set_current_value (AtkValue *obj, const GValue *value); +#ifdef HAVE_ATK_VALUE_2_12 +#define GAIL_SCALE_BUTTON_ADJUSTMENT(obj) \ + (GTK_ACCESSIBLE (obj)->widget != NULL \ + ? gtk_scale_button_get_adjustment ( \ + GTK_SCALE_BUTTON (GTK_ACCESSIBLE (obj)->widget)) : NULL) + +GAIL_DEFINE_ADJUSTMENT_VALUE_READERS ( + gail_scale_button, GAIL_SCALE_BUTTON_ADJUSTMENT (obj), + gtk_scale_button_get_value ( + GTK_SCALE_BUTTON (GTK_ACCESSIBLE (obj)->widget)), + adjustment->upper) +GAIL_DEFINE_ADJUSTMENT_VALUE_INCREMENT ( + gail_scale_button, GAIL_SCALE_BUTTON_ADJUSTMENT (obj), + adjustment->step_increment) +GAIL_DEFINE_WIDGET_VALUE_SETTER ( + gail_scale_button, GTK_SCALE_BUTTON, gtk_scale_button_set_value, value) +#endif + G_DEFINE_TYPE_WITH_CODE (GailScaleButton, gail_scale_button, GAIL_TYPE_BUTTON, G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init) G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE, atk_value_interface_init)); @@ -181,6 +200,12 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_minimum_value = gail_scale_button_get_minimum_value; iface->get_minimum_increment = gail_scale_button_get_minimum_increment; iface->set_current_value = gail_scale_button_set_current_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_scale_button_get_value_and_text; + iface->get_range = gail_scale_button_get_range; + iface->get_increment = gail_scale_button_get_increment; + iface->set_value = gail_scale_button_set_value; +#endif } static void @@ -295,5 +320,3 @@ gail_scale_button_notify_gtk (GObject *obj, GAIL_WIDGET_CLASS (gail_scale_button_parent_class)->notify_gtk (obj, pspec); } } - - diff --git a/modules/other/gail/gailspinbutton.c b/modules/other/gail/gailspinbutton.c index 709a413c0..36f2ffa32 100644 --- a/modules/other/gail/gailspinbutton.c +++ b/modules/other/gail/gailspinbutton.c @@ -22,6 +22,7 @@ #include #include #include "gailspinbutton.h" +#include "gailvalue.h" #include "gailadjustment.h" #include "gail-private-macros.h" @@ -49,6 +50,19 @@ static gboolean gail_spin_button_set_current_value (AtkValue *obj, static void gail_spin_button_value_changed (GtkAdjustment *adjustment, gpointer data); +#ifdef HAVE_ATK_VALUE_2_12 +GAIL_DEFINE_ADJUSTMENT_VALUE_READERS ( + gail_spin_button, + GAIL_VALUE_ADJUSTMENT (GAIL_SPIN_BUTTON (obj)->adjustment), + adjustment->value, adjustment->upper) +GAIL_DEFINE_ADJUSTMENT_VALUE_INCREMENT ( + gail_spin_button, + GAIL_VALUE_ADJUSTMENT (GAIL_SPIN_BUTTON (obj)->adjustment), + GAIL_VALUE_MINIMUM_INCREMENT (adjustment)) +GAIL_DEFINE_WIDGET_VALUE_SETTER ( + gail_spin_button, GTK_SPIN_BUTTON, gtk_spin_button_set_value, value) +#endif + G_DEFINE_TYPE_WITH_CODE (GailSpinButton, gail_spin_button, GAIL_TYPE_ENTRY, G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE, atk_value_interface_init)) @@ -110,6 +124,12 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_minimum_value = gail_spin_button_get_minimum_value; iface->get_minimum_increment = gail_spin_button_get_minimum_increment; iface->set_current_value = gail_spin_button_set_current_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_spin_button_get_value_and_text; + iface->get_range = gail_spin_button_get_range; + iface->get_increment = gail_spin_button_get_increment; + iface->set_value = gail_spin_button_set_value; +#endif } static void @@ -127,7 +147,7 @@ gail_spin_button_get_current_value (AtkValue *obj, */ return; - atk_value_get_current_value (ATK_VALUE (spin_button->adjustment), value); + gail_adjustment_get_current_value (ATK_VALUE (spin_button->adjustment), value); } static void @@ -145,7 +165,7 @@ gail_spin_button_get_maximum_value (AtkValue *obj, */ return; - atk_value_get_maximum_value (ATK_VALUE (spin_button->adjustment), value); + gail_adjustment_get_maximum_value (ATK_VALUE (spin_button->adjustment), value); } static void @@ -163,7 +183,7 @@ gail_spin_button_get_minimum_value (AtkValue *obj, */ return; - atk_value_get_minimum_value (ATK_VALUE (spin_button->adjustment), value); + gail_adjustment_get_minimum_value (ATK_VALUE (spin_button->adjustment), value); } static void @@ -180,7 +200,7 @@ gail_spin_button_get_minimum_increment (AtkValue *obj, GValue *value) */ return; - atk_value_get_minimum_increment (ATK_VALUE (spin_button->adjustment), value); + gail_adjustment_get_minimum_increment (ATK_VALUE (spin_button->adjustment), value); } static gboolean @@ -198,7 +218,7 @@ gail_spin_button_set_current_value (AtkValue *obj, */ return FALSE; - return atk_value_set_current_value (ATK_VALUE (spin_button->adjustment), value); + return gail_adjustment_set_current_value (ATK_VALUE (spin_button->adjustment), value); } static void @@ -264,4 +284,3 @@ gail_spin_button_value_changed (GtkAdjustment *adjustment, g_object_notify (G_OBJECT (spin_button), "accessible-value"); } - diff --git a/modules/other/gail/gailvalue.h b/modules/other/gail/gailvalue.h new file mode 100644 index 000000000..498453424 --- /dev/null +++ b/modules/other/gail/gailvalue.h @@ -0,0 +1,56 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +#ifndef __GAIL_VALUE_H__ +#define __GAIL_VALUE_H__ + +#include + +#define GAIL_VALUE_ADJUSTMENT(accessible) \ + ((accessible) != NULL ? GAIL_ADJUSTMENT (accessible)->adjustment : NULL) +#define GAIL_VALUE_MINIMUM_INCREMENT(a) \ + ((a)->step_increment == 0.0 ? (a)->page_increment : \ + (a)->page_increment == 0.0 ? (a)->step_increment : \ + ABS ((a)->step_increment) < ABS ((a)->page_increment) \ + ? (a)->step_increment : (a)->page_increment) + +#ifdef HAVE_ATK_VALUE_2_12 +#define GAIL_DEFINE_ADJUSTMENT_VALUE_READERS(prefix, a_expr, value_expr, max_expr) \ +static void prefix##_get_value_and_text (AtkValue *obj, gdouble *value, gchar **text) \ +{ \ + GtkAdjustment *adjustment = (a_expr); \ + if (text != NULL) *text = NULL; \ + if (value != NULL) *value = adjustment != NULL ? (value_expr) : 0.0; \ +} \ +static AtkRange *prefix##_get_range (AtkValue *obj) \ +{ \ + GtkAdjustment *adjustment = (a_expr); \ + return adjustment != NULL \ + ? atk_range_new (adjustment->lower, (max_expr), NULL) : NULL; \ +} + +#define GAIL_DEFINE_ADJUSTMENT_VALUE_INCREMENT(prefix, a_expr, increment_expr) \ +static gdouble prefix##_get_increment (AtkValue *obj) \ +{ \ + GtkAdjustment *adjustment = (a_expr); \ + return adjustment != NULL ? (increment_expr) : 0.0; \ +} + +#define GAIL_DEFINE_WIDGET_VALUE_SETTER(prefix, cast, setter, value_expr) \ +static void prefix##_set_value (AtkValue *obj, gdouble value) \ +{ \ + GtkWidget *widget = GTK_ACCESSIBLE (obj)->widget; \ + if (widget != NULL) setter (cast (widget), (value_expr)); \ +} +#endif + +#endif /* __GAIL_VALUE_H__ */ diff --git a/modules/other/gail/gailwidget.c b/modules/other/gail/gailwidget.c index f9e82649c..703377c49 100644 --- a/modules/other/gail/gailwidget.c +++ b/modules/other/gail/gailwidget.c @@ -169,8 +169,8 @@ gail_widget_real_initialize (AtkObject *obj, "size_allocate", G_CALLBACK (gail_widget_size_allocate_gtk), NULL); - atk_component_add_focus_handler (ATK_COMPONENT (accessible), - gail_widget_focus_event); + gail_widget_add_focus_handler (ATK_COMPONENT (accessible), + gail_widget_focus_event); /* * Add signal handlers for GTK signals required to support property changes */ diff --git a/modules/other/gail/libgail-util/gailmisc.c b/modules/other/gail/libgail-util/gailmisc.c index 9dc143d07..8213c716a 100644 --- a/modules/other/gail/libgail-util/gailmisc.c +++ b/modules/other/gail/libgail-util/gailmisc.c @@ -251,7 +251,7 @@ gail_misc_layout_get_run_attributes (AtkAttributeSet *attrib_set, if ((pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY)) != NULL) { - value = g_strdup_printf("%s", pango_string->value); + value = g_strdup (pango_string->value); attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FAMILY_NAME, value); diff --git a/modules/other/gail/tests/Makefile.am b/modules/other/gail/tests/Makefile.am index 993249558..0f0e02317 100644 --- a/modules/other/gail/tests/Makefile.am +++ b/modules/other/gail/tests/Makefile.am @@ -12,6 +12,8 @@ module_LTLIBRARIES = \ libferret.la endif +noinst_HEADERS = testvalue.h + noinst_LTLIBRARIES = \ libtestaction.la \ libtestbutton.la \ diff --git a/modules/other/gail/tests/ferret.c b/modules/other/gail/tests/ferret.c index 711852af0..43f9d4bf1 100644 --- a/modules/other/gail/tests/ferret.c +++ b/modules/other/gail/tests/ferret.c @@ -16,6 +16,7 @@ #include #include "testlib.h" +#include "testvalue.h" typedef enum { @@ -1389,7 +1390,8 @@ _print_text_attributes (AtkText *aobject) static gint _print_value (AtkValue *aobject) { - GValue *value_back, val; + GValue val = { 0, }; + GValue *value_back; gint group_num; TabNumber tab_n = VALUE; @@ -1398,11 +1400,11 @@ _print_value (AtkValue *aobject) group_num = _print_groupname(tab_n, VALUE_INTERFACE, "Value Interface"); - atk_value_get_current_value(aobject, value_back); + gail_test_value_get_current_value(aobject, value_back); _print_value_type(group_num, "Value", value_back); - atk_value_get_minimum_value(aobject, value_back); + gail_test_value_get_minimum_value(aobject, value_back); _print_value_type(group_num, "Minimum Value", value_back); - atk_value_get_maximum_value(aobject, value_back); + gail_test_value_get_maximum_value(aobject, value_back); _print_value_type(group_num, "Maximum Value", value_back); return(group_num); } @@ -1445,7 +1447,7 @@ _print_value_type(gint group_num, gchar *type, GValue *value) static void _create_event_watcher (void) { - focus_tracker_id = atk_add_focus_tracker (_print_accessible); + GAIL_TEST_LEGACY_FOCUS_CALL (focus_tracker_id = atk_add_focus_tracker (_print_accessible)); if (track_mouse) { @@ -1512,9 +1514,10 @@ _button_watcher (GSignalInvocationHint *ihint, event = (GdkEventButton *) gp; aobject = gtk_widget_get_accessible (widget); aobject_x = aobject_y = 0; - atk_component_get_position (ATK_COMPONENT (aobject), - &aobject_x, &aobject_y, - ATK_XY_WINDOW); + atk_component_get_extents (ATK_COMPONENT (aobject), + &aobject_x, &aobject_y, + NULL, NULL, + ATK_XY_WINDOW); x = aobject_x + (gint) event->x; y = aobject_y + (gint) event->y; child = atk_component_ref_accessible_at_point (ATK_COMPONENT (aobject), @@ -2686,12 +2689,12 @@ void _toggle_trackfocus(GtkCheckMenuItem *checkmenuitem, if (checkmenuitem->active) { track_focus = TRUE; - focus_tracker_id = atk_add_focus_tracker (_print_accessible); + GAIL_TEST_LEGACY_FOCUS_CALL (focus_tracker_id = atk_add_focus_tracker (_print_accessible)); } else { g_print ("No longer tracking focus.\n"); track_focus = FALSE; - atk_remove_focus_tracker (focus_tracker_id); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_remove_focus_tracker (focus_tracker_id)); } } diff --git a/modules/other/gail/tests/testaction.c b/modules/other/gail/tests/testaction.c index 9d83d1c9e..f1ccb701a 100644 --- a/modules/other/gail/tests/testaction.c +++ b/modules/other/gail/tests/testaction.c @@ -73,7 +73,7 @@ _check_object (AtkObject *obj) static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_object); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_object)); } int diff --git a/modules/other/gail/tests/testbutton.c b/modules/other/gail/tests/testbutton.c index b039e89fa..24a05fa21 100644 --- a/modules/other/gail/tests/testbutton.c +++ b/modules/other/gail/tests/testbutton.c @@ -192,7 +192,7 @@ _print_button_image_info(AtkObject *obj) { static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_object); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_object)); } int diff --git a/modules/other/gail/tests/testcombo.c b/modules/other/gail/tests/testcombo.c index 1602bf9ce..f39c3555f 100644 --- a/modules/other/gail/tests/testcombo.c +++ b/modules/other/gail/tests/testcombo.c @@ -174,7 +174,7 @@ static gint _close_combo_list (gpointer data) static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_combo_box); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_combo_box)); } int diff --git a/modules/other/gail/tests/testcomponent.c b/modules/other/gail/tests/testcomponent.c index a28eccebd..086c4e4db 100644 --- a/modules/other/gail/tests/testcomponent.c +++ b/modules/other/gail/tests/testcomponent.c @@ -1,4 +1,5 @@ #include +#include "testlib.h" static void _check_position (AtkObject *obj); @@ -7,31 +8,17 @@ static void _check_position (AtkObject *obj) AtkObject *parent, *ret_object; gint x, y, width, height; - gint x1, y1, width1, height1; x = y = width = height = 0; - x1 = y1 = width1 = height1 = 0; if (!ATK_IS_COMPONENT (obj)) return; - atk_component_get_extents (ATK_COMPONENT(obj), &x, &y, &width, &height, ATK_XY_SCREEN); - atk_component_get_position (ATK_COMPONENT(obj), &x1, &y1, ATK_XY_SCREEN ); - atk_component_get_size (ATK_COMPONENT(obj), &width1, &height1); - if ((x1 != x) || (y1 != y)) - { - g_print ("atk_component_get_extents and atk_get_position give different" - " values: %d,%d %d,%d\n", x, y, x1, y1); - } - if ((width1 != width) || (height1 != height)) - { - g_print ("atk_component_get_extents and atk_get_size give different" - " values: %d,%d %d,%d\n", width, height, width1, height1); - } - - atk_component_get_position (ATK_COMPONENT(obj), &x1, &y1, ATK_XY_SCREEN); + atk_component_get_extents (ATK_COMPONENT(obj), + &x, &y, &width, &height, + ATK_XY_SCREEN); g_print ("Object Type: %s\n", g_type_name (G_OBJECT_TYPE (obj))); - g_print ("Object at %d, %d on screen\n", x1, y1); + g_print ("Object at %d, %d on screen\n", x, y); g_print ("Object at %d, %d, size: %d, %d\n", x, y, width, height); parent = atk_object_get_parent (obj); @@ -107,7 +94,7 @@ static void _check_position (AtkObject *obj) static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_position); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_position)); } int diff --git a/modules/other/gail/tests/testimage.c b/modules/other/gail/tests/testimage.c index b27e86e5b..e358cd69d 100644 --- a/modules/other/gail/tests/testimage.c +++ b/modules/other/gail/tests/testimage.c @@ -142,7 +142,7 @@ static void _check_objects (AtkObject *obj) static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_objects); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_objects)); } int diff --git a/modules/other/gail/tests/testlib.h b/modules/other/gail/tests/testlib.h index e12fdefd5..10c429f28 100644 --- a/modules/other/gail/tests/testlib.h +++ b/modules/other/gail/tests/testlib.h @@ -68,3 +68,10 @@ gchar *get_arg_of_func (gint window, int string_to_int (const char *the_string); gboolean isVisibleDialog (void); + +/* Legacy ATK focus tracking is distinct from state-change::focused. */ +#define GAIL_TEST_LEGACY_FOCUS_CALL(call) G_STMT_START { \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + call; \ + G_GNUC_END_IGNORE_DEPRECATIONS \ +} G_STMT_END diff --git a/modules/other/gail/tests/testmenuitem.c b/modules/other/gail/tests/testmenuitem.c index c27505b39..2c9aade8b 100644 --- a/modules/other/gail/tests/testmenuitem.c +++ b/modules/other/gail/tests/testmenuitem.c @@ -124,7 +124,7 @@ static gint _do_menu_item_action (gpointer data) static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_object); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_object)); } int diff --git a/modules/other/gail/tests/testnotebook.c b/modules/other/gail/tests/testnotebook.c index fdc663ad8..a8e1d8d9f 100644 --- a/modules/other/gail/tests/testnotebook.c +++ b/modules/other/gail/tests/testnotebook.c @@ -211,7 +211,7 @@ static gint _finish_selection (gpointer data) static void _create_event_watcher (void) { - atk_add_focus_tracker (_do_selection); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_do_selection)); } int diff --git a/modules/other/gail/tests/testobject.c b/modules/other/gail/tests/testobject.c index c11343a84..25f435aa5 100644 --- a/modules/other/gail/tests/testobject.c +++ b/modules/other/gail/tests/testobject.c @@ -163,7 +163,7 @@ static void _print_accessible (AtkObject *obj) * Add a focus handler so we see focus out events as well */ if (ATK_IS_COMPONENT (obj)) - atk_component_add_focus_handler (ATK_COMPONENT (obj), _focus_handler); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_component_add_focus_handler (ATK_COMPONENT (obj), _focus_handler)); g_print ("Object:\n"); _print_type (obj); _print_states (obj); @@ -229,7 +229,7 @@ static void _check_children (AtkObject *obj) if (ATK_IS_COMPONENT (obj)) { - atk_component_add_focus_handler (ATK_COMPONENT (obj), _focus_handler); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_component_add_focus_handler (ATK_COMPONENT (obj), _focus_handler)); layer = atk_component_get_layer (ATK_COMPONENT (obj)); if (role == ATK_ROLE_MENU) g_assert (layer == ATK_LAYER_POPUP); @@ -318,7 +318,7 @@ _create_event_watcher (void) * _print_accessible() will be called for an accessible object when its * widget receives focus. */ - id = atk_add_focus_tracker (_print_accessible); + GAIL_TEST_LEGACY_FOCUS_CALL (id = atk_add_focus_tracker (_print_accessible)); } static void diff --git a/modules/other/gail/tests/testoptionmenu.c b/modules/other/gail/tests/testoptionmenu.c index 01dc64410..7bc13d339 100644 --- a/modules/other/gail/tests/testoptionmenu.c +++ b/modules/other/gail/tests/testoptionmenu.c @@ -143,7 +143,7 @@ static gint _do_menu_item_action (gpointer data) static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_object); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_object)); } int diff --git a/modules/other/gail/tests/testpaned.c b/modules/other/gail/tests/testpaned.c index 300031955..c429979a0 100644 --- a/modules/other/gail/tests/testpaned.c +++ b/modules/other/gail/tests/testpaned.c @@ -2,18 +2,21 @@ #include #include #include +#include "testvalue.h" static gint _test_paned (gpointer data); static void _check_paned (AtkObject *obj); static void _property_change_handler (AtkObject *obj, - AtkPropertyValues *values); + AtkPropertyValues *values, + gpointer user_data G_GNUC_UNUSED); #define NUM_VALID_ROLES 1 static gint last_position; static void _property_change_handler (AtkObject *obj, - AtkPropertyValues *values) + AtkPropertyValues *values, + gpointer user_data G_GNUC_UNUSED) { const gchar *type_name = g_type_name (G_TYPE_FROM_INSTANCE (obj)); const gchar *name = atk_object_get_name (obj); @@ -31,17 +34,17 @@ static void _property_change_handler (AtkObject *obj, value = &val; memset (value, 0, sizeof (GValue)); - atk_value_get_current_value (ATK_VALUE (obj), value); + gail_test_value_get_current_value (ATK_VALUE (obj), value); g_return_if_fail (G_VALUE_HOLDS_INT (value)); position = g_value_get_int (value); g_print ("Position is %d previous position was %d\n", position, last_position); last_position = position; - atk_value_get_minimum_value (ATK_VALUE (obj), value); + gail_test_value_get_minimum_value (ATK_VALUE (obj), value); g_return_if_fail (G_VALUE_HOLDS_INT (value)); position = g_value_get_int (value); g_print ("Minimum Value is %d\n", position); - atk_value_get_maximum_value (ATK_VALUE (obj), value); + gail_test_value_get_maximum_value (ATK_VALUE (obj), value); g_return_if_fail (G_VALUE_HOLDS_INT (value)); position = g_value_get_int (value); g_print ("Maximum Value is %d\n", position); @@ -61,14 +64,14 @@ static gint _test_paned (gpointer data) value = &val; memset (value, 0, sizeof (GValue)); - atk_value_get_current_value (ATK_VALUE (obj), value); + gail_test_value_get_current_value (ATK_VALUE (obj), value); g_return_val_if_fail (G_VALUE_HOLDS_INT (value), FALSE); position = g_value_get_int (value); g_print ("Position is : %d\n", position); last_position = position; position *= 2; g_value_set_int (value, position); - atk_value_set_current_value (ATK_VALUE (obj), value); + (void) gail_test_value_set_current_value (ATK_VALUE (obj), value); times++; } if (times < 4) @@ -102,8 +105,8 @@ static void _check_paned (AtkObject *obj) { done_paned = TRUE; } - atk_object_connect_property_change_handler (paned_obj, - (AtkPropertyChangeHandler*) _property_change_handler); + g_signal_connect (paned_obj, "property-change", + G_CALLBACK (_property_change_handler), NULL); g_timeout_add (2000, _test_paned, paned_obj); } @@ -116,7 +119,7 @@ static void _check_paned (AtkObject *obj) static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_paned); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_paned)); } int diff --git a/modules/other/gail/tests/testprops.c b/modules/other/gail/tests/testprops.c index 4c8e1745e..3a9cf52c8 100644 --- a/modules/other/gail/tests/testprops.c +++ b/modules/other/gail/tests/testprops.c @@ -8,7 +8,8 @@ static void _traverse_children (AtkObject *obj); static void _add_handler (AtkObject *obj); static void _check_properties (AtkObject *obj); static void _property_change_handler (AtkObject *obj, - AtkPropertyValues *values); + AtkPropertyValues *values, + gpointer user_data G_GNUC_UNUSED); static void _state_changed (AtkObject *obj, const gchar *name, gboolean set); @@ -64,7 +65,8 @@ _model_changed (AtkObject *obj) static void _property_change_handler (AtkObject *obj, - AtkPropertyValues *values) + AtkPropertyValues *values, + gpointer user_data G_GNUC_UNUSED) { const gchar *type_name = g_type_name (G_TYPE_FROM_INSTANCE (obj)); const gchar *name = atk_object_get_name (obj); @@ -165,8 +167,8 @@ _add_handler (AtkObject *obj) } if (!found) { - atk_object_connect_property_change_handler (obj, - (AtkPropertyChangeHandler*) _property_change_handler); + g_signal_connect (obj, "property-change", + G_CALLBACK (_property_change_handler), NULL); g_signal_connect (obj, "state-change", (GCallback) _state_changed, NULL); if (ATK_IS_SELECTION (obj)) @@ -206,7 +208,7 @@ _check_properties (AtkObject *obj) static void _create_event_watcher (void) { - id = atk_add_focus_tracker (_check_properties); + GAIL_TEST_LEGACY_FOCUS_CALL (id = atk_add_focus_tracker (_check_properties)); } int diff --git a/modules/other/gail/tests/testselection.c b/modules/other/gail/tests/testselection.c index bf0274b82..89cb6b0e6 100644 --- a/modules/other/gail/tests/testselection.c +++ b/modules/other/gail/tests/testselection.c @@ -1,6 +1,7 @@ #include #include #include +#include "testlib.h" /* * This module tests the selection interface on menu items. @@ -184,7 +185,7 @@ static gint _finish_selection (gpointer data) static void _create_event_watcher (void) { - atk_add_focus_tracker (_do_selection); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_do_selection)); } int diff --git a/modules/other/gail/tests/teststatusbar.c b/modules/other/gail/tests/teststatusbar.c index 5e38279c7..4cd23096d 100644 --- a/modules/other/gail/tests/teststatusbar.c +++ b/modules/other/gail/tests/teststatusbar.c @@ -1,6 +1,7 @@ #include #include #include +#include "testlib.h" /* * To use this test module, run the test program testgtk and click on @@ -11,7 +12,8 @@ static void _check_statusbar (AtkObject *obj); static AtkObject* _find_object (AtkObject* obj, AtkRole role); static void _notify_handler (GObject *obj, GParamSpec *pspec); static void _property_change_handler (AtkObject *obj, - AtkPropertyValues *values); + AtkPropertyValues *values, + gpointer user_data G_GNUC_UNUSED); static AtkObject* _find_object (AtkObject *obj, @@ -49,7 +51,8 @@ _find_object (AtkObject *obj, } static void _property_change_handler (AtkObject *obj, - AtkPropertyValues *values) + AtkPropertyValues *values, + gpointer user_data G_GNUC_UNUSED) { const gchar *type_name = g_type_name (G_TYPE_FROM_INSTANCE (obj)); const gchar *name = atk_object_get_name (obj); @@ -91,8 +94,8 @@ static void _check_statusbar (AtkObject *obj) g_cclosure_new (G_CALLBACK (_notify_handler), NULL, NULL), FALSE); - atk_object_connect_property_change_handler (statusbar, - (AtkPropertyChangeHandler*) _property_change_handler); + g_signal_connect (statusbar, "property-change", + G_CALLBACK (_property_change_handler), NULL); } @@ -113,7 +116,7 @@ _notify_handler (GObject *obj, GParamSpec *pspec) static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_statusbar); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_statusbar)); } int diff --git a/modules/other/gail/tests/testtable.c b/modules/other/gail/tests/testtable.c index 29f1d64e2..bbf30bcc6 100644 --- a/modules/other/gail/tests/testtable.c +++ b/modules/other/gail/tests/testtable.c @@ -42,7 +42,8 @@ static void _notify_table_child_removed (GObject *obj, gint index, AtkObject *child); static void _property_signal_connect (AtkObject *obj); static void _property_change_handler (AtkObject *obj, - AtkPropertyValues *values); + AtkPropertyValues *values, + gpointer user_data G_GNUC_UNUSED); static gboolean tested_set_headers = FALSE; static void test_choice_gui (AtkObject **obj); @@ -195,8 +196,8 @@ static void _check_table (AtkObject *in_obj) } g_table = ATK_TABLE(obj); - atk_object_connect_property_change_handler (obj, - (AtkPropertyChangeHandler*) _property_change_handler); + g_signal_connect (obj, "property-change", + G_CALLBACK (_property_change_handler), NULL); current_obj = obj; /* @@ -533,7 +534,7 @@ _selection_tests(AtkObject *obj) static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_table); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_table)); } int @@ -657,7 +658,8 @@ static void _property_signal_connect (AtkObject *obj) static void _property_change_handler (AtkObject *obj, - AtkPropertyValues *values) + AtkPropertyValues *values, + gpointer user_data G_GNUC_UNUSED) { gchar *obj_text; const gchar *name; diff --git a/modules/other/gail/tests/testtext.c b/modules/other/gail/tests/testtext.c index 7a0daf0c8..bc5c56707 100644 --- a/modules/other/gail/tests/testtext.c +++ b/modules/other/gail/tests/testtext.c @@ -113,13 +113,13 @@ static void _check_text (AtkObject *in_obj) if (!(isVisibleDialog())) setup_gui(obj, runtest); - atk_remove_focus_tracker (id1); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_remove_focus_tracker (id1)); } static void _create_event_watcher (void) { - id1 = atk_add_focus_tracker (_check_text); + GAIL_TEST_LEGACY_FOCUS_CALL (id1 = atk_add_focus_tracker (_check_text)); } int diff --git a/modules/other/gail/tests/testtoplevel.c b/modules/other/gail/tests/testtoplevel.c index 63addd065..024e35583 100644 --- a/modules/other/gail/tests/testtoplevel.c +++ b/modules/other/gail/tests/testtoplevel.c @@ -62,7 +62,7 @@ _check_toplevel (AtkObject *obj) static void _create_event_watcher (void) { - id = atk_add_focus_tracker (_check_toplevel); + GAIL_TEST_LEGACY_FOCUS_CALL (id = atk_add_focus_tracker (_check_toplevel)); } int diff --git a/modules/other/gail/tests/testtreetable.c b/modules/other/gail/tests/testtreetable.c index a5d606e41..0fa2d1046 100644 --- a/modules/other/gail/tests/testtreetable.c +++ b/modules/other/gail/tests/testtreetable.c @@ -342,7 +342,7 @@ _check_expanders (AtkTable *table, static void _create_event_watcher (void) { - atk_add_focus_tracker (_check_table); + GAIL_TEST_LEGACY_FOCUS_CALL (atk_add_focus_tracker (_check_table)); } int diff --git a/modules/other/gail/tests/testvalue.h b/modules/other/gail/tests/testvalue.h new file mode 100644 index 000000000..735278318 --- /dev/null +++ b/modules/other/gail/tests/testvalue.h @@ -0,0 +1,98 @@ +#ifndef __GAIL_TEST_VALUE_H__ +#define __GAIL_TEST_VALUE_H__ + +#include "config.h" + +#include + +#ifdef HAVE_ATK_VALUE_2_12 + +static void +gail_test_value_store_number (AtkValue *value, + GValue *result, + gdouble number) +{ + if (G_VALUE_TYPE (result) == 0) + g_value_init (result, + atk_object_get_role (ATK_OBJECT (value)) == + ATK_ROLE_SPLIT_PANE ? G_TYPE_INT : G_TYPE_DOUBLE); + + if (G_VALUE_HOLDS_INT (result)) + g_value_set_int (result, (gint) number); + else + { + g_return_if_fail (G_VALUE_HOLDS_DOUBLE (result)); + g_value_set_double (result, number); + } +} + +static void +gail_test_value_get_current_value (AtkValue *value, + GValue *result) +{ + gdouble number; + + atk_value_get_value_and_text (value, &number, NULL); + gail_test_value_store_number (value, result, number); +} + +static void +gail_test_value_get_limit (AtkValue *value, + GValue *result, + gboolean upper) +{ + AtkRange *range; + gdouble number; + + range = atk_value_get_range (value); + g_return_if_fail (range != NULL); + + if (upper) + number = atk_range_get_upper_limit (range); + else + number = atk_range_get_lower_limit (range); + + atk_range_free (range); + gail_test_value_store_number (value, result, number); +} + +#define gail_test_value_get_minimum_value(value, result) \ + gail_test_value_get_limit ((value), (result), FALSE) + +#define gail_test_value_get_maximum_value(value, result) \ + gail_test_value_get_limit ((value), (result), TRUE) + +#define GAIL_TEST_VALUE_NUMBER(value) \ + (G_VALUE_HOLDS_INT (value) \ + ? (gdouble) g_value_get_int (value) \ + : g_value_get_double (value)) + +#define gail_test_value_set_current_value(value, new_value) \ + (atk_value_set_value ((value), \ + GAIL_TEST_VALUE_NUMBER (new_value)), \ + TRUE) + +#else + +#define GAIL_TEST_VALUE_IFACE(value) \ + ATK_VALUE_GET_IFACE (value) + +#define gail_test_value_get_current_value(value, result) \ + (GAIL_TEST_VALUE_IFACE (value)->get_current_value ( \ + (value), (result))) + +#define gail_test_value_get_minimum_value(value, result) \ + (GAIL_TEST_VALUE_IFACE (value)->get_minimum_value ( \ + (value), (result))) + +#define gail_test_value_get_maximum_value(value, result) \ + (GAIL_TEST_VALUE_IFACE (value)->get_maximum_value ( \ + (value), (result))) + +#define gail_test_value_set_current_value(value, new_value) \ + (GAIL_TEST_VALUE_IFACE (value)->set_current_value ( \ + (value), (new_value))) + +#endif + +#endif /* __GAIL_TEST_VALUE_H__ */ diff --git a/modules/other/gail/tests/testvalues.c b/modules/other/gail/tests/testvalues.c index 34d91a25a..e7462cb66 100644 --- a/modules/other/gail/tests/testvalues.c +++ b/modules/other/gail/tests/testvalues.c @@ -1,21 +1,25 @@ #include #include #include +#include "testvalue.h" +#include "testlib.h" static void _traverse_children (AtkObject *obj); static void _add_handler (AtkObject *obj); static void _check_values (AtkObject *obj); static void _value_change_handler (AtkObject *obj, - AtkPropertyValues *values); + AtkPropertyValues *values, + gpointer user_data G_GNUC_UNUSED); static guint id; static void _value_change_handler (AtkObject *obj, - AtkPropertyValues *values) + AtkPropertyValues *values, + gpointer user_data G_GNUC_UNUSED) { const gchar *type_name = g_type_name (G_TYPE_FROM_INSTANCE (obj)); - GValue *value_back, val; - + GValue val = { 0, }; + GValue *value_back; value_back = &val; if (!ATK_IS_VALUE (obj)) { @@ -33,17 +37,17 @@ static void _value_change_handler (AtkObject *obj, g_print("Now calling the AtkValue interface functions\n"); - atk_value_get_current_value (ATK_VALUE(obj), value_back); + gail_test_value_get_current_value (ATK_VALUE(obj), value_back); g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back)); g_print ("atk_value_get_current_value returns %f\n", g_value_get_double (value_back) ); - atk_value_get_maximum_value (ATK_VALUE (obj), value_back); + gail_test_value_get_maximum_value (ATK_VALUE (obj), value_back); g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back)); g_print ("atk_value_get_maximum returns %f\n", g_value_get_double (value_back)); - atk_value_get_minimum_value (ATK_VALUE (obj), value_back); + gail_test_value_get_minimum_value (ATK_VALUE (obj), value_back); g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back)); g_print ("atk_value_get_minimum returns %f\n", g_value_get_double (value_back)); @@ -95,15 +99,16 @@ static void _add_handler (AtkObject *obj) } if (!found) { - atk_object_connect_property_change_handler (obj, - (AtkPropertyChangeHandler*) _value_change_handler); + g_signal_connect (obj, "property-change", + G_CALLBACK (_value_change_handler), NULL); g_ptr_array_add (obj_array, obj); } } static void _set_values (AtkObject *obj) { - GValue *value_back, val; + GValue val = { 0, }; + GValue *value_back; static gint count = 0; gdouble double_value; @@ -125,7 +130,7 @@ static void _set_values (AtkObject *obj) { text = "5.7"; atk_editable_text_set_text_contents(ATK_EDITABLE_TEXT(obj),text); g_print("Set text to %s\n",text); - atk_value_get_current_value(ATK_VALUE(obj), value_back); + gail_test_value_get_current_value(ATK_VALUE(obj), value_back); g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back)); g_print("atk_value_get_current_value returns %f\n", g_value_get_double( value_back)); @@ -134,7 +139,7 @@ static void _set_values (AtkObject *obj) { memset (value_back, 0, sizeof (GValue)); g_value_init (value_back, G_TYPE_DOUBLE); g_value_set_double (value_back, 10.0); - if (atk_value_set_current_value (ATK_VALUE (obj), value_back)) + if (gail_test_value_set_current_value (ATK_VALUE (obj), value_back)) { double_value = g_value_get_double (value_back); g_print("atk_value_set_current_value returns %f\n", @@ -179,7 +184,7 @@ static void _check_values (AtkObject *obj) static void _create_event_watcher (void) { - id = atk_add_focus_tracker (_check_values); + GAIL_TEST_LEGACY_FOCUS_CALL (id = atk_add_focus_tracker (_check_values)); } int diff --git a/modules/printbackends/cups/gtkcupsutils.c b/modules/printbackends/cups/gtkcupsutils.c index fdfe21e91..f96b7a5b5 100644 --- a/modules/printbackends/cups/gtkcupsutils.c +++ b/modules/printbackends/cups/gtkcupsutils.c @@ -34,6 +34,11 @@ typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request); +static http_t *gtk_cups_http_connect (const char *host, + int port, + http_encryption_t encryption); +static int gtk_cups_http_reconnect (http_t *http); + static void _connect (GtkCupsRequest *request); static void _post_send (GtkCupsRequest *request); static void _post_write_request (GtkCupsRequest *request); @@ -171,7 +176,7 @@ gtk_cups_request_new_with_username (http_t *connection, else { request->http = NULL; - request->http = httpConnectEncrypt (request->server, + request->http = gtk_cups_http_connect (request->server, ippPort (), cupsEncryption ()); @@ -681,6 +686,29 @@ gtk_cups_request_set_ipp_version (GtkCupsRequest *request, ippSetVersion (request->ipp_request, major, minor); } + +static http_t * +gtk_cups_http_connect (const char *host, + int port, + http_encryption_t encryption) +{ +#ifdef HAVE_HTTPCONNECT2 + return httpConnect2 (host, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL); +#else + return httpConnectEncrypt (host, port, encryption); +#endif +} + +static int +gtk_cups_http_reconnect (http_t *http) +{ +#ifdef HAVE_HTTPRECONNECT2 + return httpReconnect2 (http, 30000, NULL); +#else + return httpReconnect (http); +#endif +} + static void _connect (GtkCupsRequest *request) { @@ -689,7 +717,7 @@ _connect (GtkCupsRequest *request) if (request->http == NULL) { - request->http = httpConnectEncrypt (request->server, + request->http = gtk_cups_http_connect (request->server, ippPort (), cupsEncryption ()); @@ -744,7 +772,7 @@ _post_send (GtkCupsRequest *request) if (httpPost (request->http, request->resource)) { - if (httpReconnect (request->http)) + if (gtk_cups_http_reconnect (request->http)) { request->state = GTK_CUPS_POST_DONE; request->poll_state = GTK_CUPS_HTTP_IDLE; @@ -1046,7 +1074,7 @@ _post_check (GtkCupsRequest *request) } if (auth_result || - httpReconnect (request->http)) + gtk_cups_http_reconnect (request->http)) { /* if the password has been used, reset password_state * so that we ask for a new one next time around @@ -1105,7 +1133,7 @@ _post_check (GtkCupsRequest *request) request->state = GTK_CUPS_POST_CONNECT; /* Reconnect... */ - httpReconnect (request->http); + gtk_cups_http_reconnect (request->http); /* Upgrade with encryption... */ httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED); @@ -1232,7 +1260,7 @@ _get_send (GtkCupsRequest *request) if (httpGet (request->http, request->resource)) { - if (httpReconnect (request->http)) + if (gtk_cups_http_reconnect (request->http)) { request->state = GTK_CUPS_GET_DONE; request->poll_state = GTK_CUPS_HTTP_IDLE; @@ -1341,7 +1369,7 @@ _get_check (GtkCupsRequest *request) } if (auth_result || - httpReconnect (request->http)) + gtk_cups_http_reconnect (request->http)) { /* if the password has been used, reset password_state * so that we ask for a new one next time around @@ -1373,7 +1401,7 @@ _get_check (GtkCupsRequest *request) request->state = GTK_CUPS_GET_CONNECT; /* Reconnect... */ - httpReconnect (request->http); + gtk_cups_http_reconnect (request->http); /* Upgrade with encryption... */ httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED); diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c index e17d97d21..c417ba88e 100644 --- a/modules/printbackends/cups/gtkprintbackendcups.c +++ b/modules/printbackends/cups/gtkprintbackendcups.c @@ -61,7 +61,6 @@ #include "gtkcupsutils.h" - typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass; #define GTK_PRINT_BACKEND_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass)) @@ -144,6 +143,11 @@ struct _GtkPrintBackendCups GHashTable *auth; gchar *username; gboolean authentication_lock; +#ifdef HAVE_CUPS_SECRET_SERVICE + gchar *store_auth_info_uri; + guint secrets_service_available : 1; + guint store_auth_info_allowed : 1; +#endif #ifdef HAVE_CUPS_API_1_6 GDBusConnection *dbus_connection; gchar *avahi_default_printer; @@ -213,10 +217,21 @@ static cairo_surface_t * cups_printer_create_cairo_surface (GtkPrinter static void gtk_print_backend_cups_set_password (GtkPrintBackend *backend, gchar **auth_info_required, gchar **auth_info); +#ifdef HAVE_CUPS_SECRET_SERVICE +static void gtk_print_backend_cups_set_password_full (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info, + gboolean store_auth_info); +static gboolean gtk_print_backend_cups_can_store_auth_info + (GtkPrintBackend *backend); +#endif void overwrite_and_free (gpointer data); static gboolean is_address_local (const gchar *address); static gboolean request_auth_info (gpointer data); +#ifdef HAVE_CUPS_SECRET_SERVICE +static void lookup_auth_info (gpointer data); +#endif #ifdef HAVE_CUPS_API_1_6 static void avahi_request_printer_list (GtkPrintBackendCups *cups_backend); @@ -341,6 +356,12 @@ gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class) backend_class->printer_get_hard_margins = cups_printer_get_hard_margins; backend_class->printer_get_capabilities = cups_printer_get_capabilities; backend_class->set_password = gtk_print_backend_cups_set_password; +#ifdef HAVE_CUPS_SECRET_SERVICE + backend_class->_gtk_reserved1 = + (void (*) (void)) gtk_print_backend_cups_can_store_auth_info; + backend_class->_gtk_reserved2 = + (void (*) (void)) gtk_print_backend_cups_set_password_full; +#endif } static cairo_status_t @@ -489,6 +510,44 @@ cups_free_print_stream_data (CupsPrintStreamData *data) g_free (data); } + +static http_t * +gtk_cups_backend_http_connect (const char *host, + int port) +{ +#ifdef HAVE_HTTPCONNECT2 + return httpConnect2 (host, + port, + NULL, + AF_UNSPEC, + HTTP_ENCRYPT_IF_REQUESTED, + 1, + 30000, + NULL); +#else + return httpConnect (host, port); +#endif +} + +static http_t * +gtk_cups_backend_http_connect_encrypt (const char *host, + int port, + http_encryption_t encryption) +{ +#ifdef HAVE_HTTPCONNECT2 + return httpConnect2 (host, + port, + NULL, + AF_UNSPEC, + encryption, + 1, + 30000, + NULL); +#else + return httpConnectEncrypt (host, port, encryption); +#endif +} + static void cups_print_cb (GtkPrintBackendCups *print_backend, GtkCupsResult *result, @@ -636,7 +695,9 @@ gtk_print_backend_cups_print_stream (GtkPrintBackend *print_backend, { http_t *http; - http = httpConnect (cups_printer->hostname, cups_printer->port); + http = gtk_cups_backend_http_connect ( + cups_printer->hostname, + cups_printer->port); if (http) { request = gtk_cups_request_new_with_username (http, @@ -767,6 +828,11 @@ gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups) backend_cups->requests = NULL; backend_cups->auth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, overwrite_and_free); backend_cups->authentication_lock = FALSE; +#ifdef HAVE_CUPS_SECRET_SERVICE + backend_cups->store_auth_info_uri = NULL; + backend_cups->secrets_service_available = FALSE; + backend_cups->store_auth_info_allowed = FALSE; +#endif backend_cups->covers = NULL; backend_cups->number_of_covers = 0; @@ -813,6 +879,10 @@ gtk_print_backend_cups_finalize (GObject *object) g_free (backend_cups->username); +#ifdef HAVE_CUPS_SECRET_SERVICE + g_free (backend_cups->store_auth_info_uri); +#endif + #ifdef HAVE_CUPS_API_1_6 g_clear_object (&backend_cups->avahi_cancellable); g_free (backend_cups->avahi_default_printer); @@ -932,62 +1002,74 @@ httpGetHostname(http_t *http, } #endif +#ifdef HAVE_CUPS_SECRET_SERVICE +static void cups_secret_store (gchar **auth_info, + gchar **auth_info_labels, + const gchar *printer_uri); + +static void +gtk_print_backend_cups_set_password_full (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info, + gboolean store_auth_info) +#else static void gtk_print_backend_cups_set_password (GtkPrintBackend *backend, gchar **auth_info_required, gchar **auth_info) +#endif { GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend); GList *l; - char dispatch_hostname[HTTP_MAX_URI]; - gchar *key; - gchar *username = NULL; - gchar *hostname = NULL; - gchar *password = NULL; - gint length; + gchar *key = NULL; + const gchar *username = NULL; + const gchar *hostname = NULL; + const gchar *password = NULL; gint i; - length = g_strv_length (auth_info_required); - if (auth_info != NULL) - for (i = 0; i < length; i++) + for (i = 0; auth_info_required[i] != NULL; i++) { if (g_strcmp0 (auth_info_required[i], "username") == 0) - username = g_strdup (auth_info[i]); + username = auth_info[i]; else if (g_strcmp0 (auth_info_required[i], "hostname") == 0) - hostname = g_strdup (auth_info[i]); + hostname = auth_info[i]; else if (g_strcmp0 (auth_info_required[i], "password") == 0) - password = g_strdup (auth_info[i]); + password = auth_info[i]; } if (hostname != NULL && username != NULL && password != NULL) { key = g_strconcat (username, "@", hostname, NULL); - g_hash_table_insert (cups_backend->auth, key, g_strdup (password)); + g_hash_table_replace (cups_backend->auth, key, g_strdup (password)); + + GTK_NOTE (PRINTING, + g_print ("CUPS backend: storing password for %s\n", key)); } g_free (cups_backend->username); cups_backend->username = g_strdup (username); - GTK_NOTE (PRINTING, - g_print ("CUPS backend: storing password for %s\n", key)); +#ifdef HAVE_CUPS_SECRET_SERVICE + if (store_auth_info && + password != NULL && + cups_backend->store_auth_info_uri != NULL) + cups_secret_store (auth_info, + auth_info_required, + cups_backend->store_auth_info_uri); + + g_free (cups_backend->store_auth_info_uri); + cups_backend->store_auth_info_uri = NULL; +#endif for (l = cups_backend->requests; l; l = l->next) { GtkPrintCupsDispatchWatch *dispatch = l->data; - httpGetHostname (dispatch->request->http, dispatch_hostname, sizeof (dispatch_hostname)); - if (is_address_local (dispatch_hostname)) - strcpy (dispatch_hostname, "localhost"); - if (dispatch->request->need_auth_info) { if (auth_info != NULL) - { - dispatch->request->auth_info = g_new0 (gchar *, length + 1); - for (i = 0; i < length; i++) - dispatch->request->auth_info[i] = g_strdup (auth_info[i]); - } + dispatch->request->auth_info = g_strdupv (auth_info); dispatch->backend->authentication_lock = FALSE; dispatch->request->need_auth_info = FALSE; } @@ -1003,6 +1085,33 @@ gtk_print_backend_cups_set_password (GtkPrintBackend *backend, } } + +#ifdef HAVE_CUPS_SECRET_SERVICE +static void +gtk_print_backend_cups_set_password (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info) +{ + gtk_print_backend_cups_set_password_full (backend, + auth_info_required, + auth_info, + FALSE); +} + + +static gboolean +gtk_print_backend_cups_can_store_auth_info (GtkPrintBackend *backend) +{ + GtkPrintBackendCups *cups_backend; + + cups_backend = GTK_PRINT_BACKEND_CUPS (backend); + + return cups_backend->secrets_service_available && + cups_backend->store_auth_info_allowed; +} +#endif + + static gboolean request_password (gpointer data) { @@ -1012,12 +1121,14 @@ request_password (gpointer data) gchar *prompt = NULL; gchar *key = NULL; char hostname[HTTP_MAX_URI]; - gchar **auth_info_required; - gchar **auth_info_default; - gchar **auth_info_display; - gboolean *auth_info_visible; - gint length = 3; - gint i; + const gchar *auth_info_required[4] = + { "hostname", "username", "password", NULL }; + const gchar *auth_info_default[4] = + { NULL, NULL, NULL, NULL }; + const gchar *auth_info_display[4] = + { NULL, NULL, NULL, NULL }; + gboolean auth_info_visible[4] = + { FALSE, TRUE, FALSE, FALSE }; if (dispatch->backend->authentication_lock) return FALSE; @@ -1031,21 +1142,11 @@ request_password (gpointer data) else username = cupsUser (); - auth_info_required = g_new0 (gchar*, length + 1); - auth_info_required[0] = g_strdup ("hostname"); - auth_info_required[1] = g_strdup ("username"); - auth_info_required[2] = g_strdup ("password"); + auth_info_default[0] = hostname; + auth_info_default[1] = username; - auth_info_default = g_new0 (gchar*, length + 1); - auth_info_default[0] = g_strdup (hostname); - auth_info_default[1] = g_strdup (username); - - auth_info_display = g_new0 (gchar*, length + 1); - auth_info_display[1] = g_strdup (_("Username:")); - auth_info_display[2] = g_strdup (_("Password:")); - - auth_info_visible = g_new0 (gboolean, length + 1); - auth_info_visible[1] = TRUE; + auth_info_display[1] = _("Username:"); + auth_info_display[2] = _("Password:"); key = g_strconcat (username, "@", hostname, NULL); password = g_hash_table_lookup (dispatch->backend->auth, key); @@ -1065,10 +1166,10 @@ request_password (gpointer data) { const char *job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name"); const char *printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri"); - char *printer_name = NULL; + const char *printer_name = NULL; if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL) - printer_name = g_strdup (strrchr (printer_uri, '/') + 1); + printer_name = strrchr (printer_uri, '/') + 1; if (dispatch->request->password_state == GTK_CUPS_PASSWORD_NOT_VALID) g_hash_table_remove (dispatch->backend->auth, key); @@ -1112,25 +1213,12 @@ request_password (gpointer data) break; } - g_free (printer_name); - g_signal_emit_by_name (dispatch->backend, "request-password", auth_info_required, auth_info_default, auth_info_display, auth_info_visible, prompt); g_free (prompt); } - for (i = 0; i < length; i++) - { - g_free (auth_info_required[i]); - g_free (auth_info_default[i]); - g_free (auth_info_display[i]); - } - - g_free (auth_info_required); - g_free (auth_info_default); - g_free (auth_info_display); - g_free (auth_info_visible); g_free (key); return FALSE; @@ -1223,6 +1311,845 @@ check_auth_info (gpointer user_data) return TRUE; } + +#ifdef HAVE_CUPS_SECRET_SERVICE + +#define CUPS_SECRET_BUS "org.freedesktop.secrets" +#define CUPS_SECRET_PATH "/org/freedesktop/secrets" +#define CUPS_SECRET_DEFAULT_COLLECTION \ + "/org/freedesktop/secrets/aliases/default" +#define CUPS_SECRET_IFACE(x) "org.freedesktop.Secret." x +#define CUPS_SECRET_TIMEOUT 5000 + +typedef enum +{ + CUPS_SECRET_SEARCH, + CUPS_SECRET_UNLOCK, + CUPS_SECRET_PROMPT, + CUPS_SECRET_PROMPT_WAIT, + CUPS_SECRET_ATTRIBUTES, + CUPS_SECRET_SESSION, + CUPS_SECRET_VALUE, + CUPS_SECRET_CREATE +} CupsSecretStep; + +typedef struct +{ + GDBusConnection *connection; + GtkPrintCupsDispatchWatch *dispatch; + gchar *item_path; + gchar *session_path; + gchar *prompt_path; + gchar **auth_info; + GVariant *store_properties; + gchar *store_password; + guint prompt_subscription; + guint length; + guint prompt_completed : 1; + guint prompt_dismissed : 1; + guint store : 1; + guint create_prompt : 1; + gint password_field; + CupsSecretStep step; +} CupsSecretLookup; + + +static void cups_secret_reply (GObject *, GAsyncResult *, gpointer); + + +static void +cups_secret_call (CupsSecretLookup *lookup, + const gchar *path, + const gchar *interface, + const gchar *method, + GVariant *parameters, + const GVariantType *reply_type, + CupsSecretStep step) +{ + lookup->step = step; + + g_dbus_connection_call (lookup->connection, + CUPS_SECRET_BUS, + path, + interface, + method, + parameters, + reply_type, + G_DBUS_CALL_FLAGS_NONE, + CUPS_SECRET_TIMEOUT, + NULL, + cups_secret_reply, + lookup); +} + + +static void +cups_secret_free (CupsSecretLookup *lookup) +{ + guint i; + + if (lookup->prompt_subscription != 0) + g_dbus_connection_signal_unsubscribe (lookup->connection, + lookup->prompt_subscription); + + if (lookup->session_path != NULL) + g_dbus_connection_call (lookup->connection, + CUPS_SECRET_BUS, + lookup->session_path, + CUPS_SECRET_IFACE ("Session"), + "Close", + NULL, + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + CUPS_SECRET_TIMEOUT, + NULL, + NULL, + NULL); + + if (lookup->auth_info != NULL) + { + for (i = 0; i < lookup->length; i++) + overwrite_and_free (lookup->auth_info[i]); + + g_free (lookup->auth_info); + } + + if (lookup->store_properties != NULL) + g_variant_unref (lookup->store_properties); + + if (lookup->store_password != NULL) + overwrite_and_free (lookup->store_password); + + g_free (lookup->item_path); + g_free (lookup->session_path); + g_free (lookup->prompt_path); + + if (lookup->connection != NULL) + g_object_unref (lookup->connection); + + g_free (lookup); +} + + +static void +cups_secret_done (CupsSecretLookup *lookup, + gboolean found, + GError *error) +{ + GtkPrintCupsDispatchWatch *dispatch; + + if (lookup->store) + { + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("CUPS backend: failed to remember password: %s\n", + error->message)); + g_error_free (error); + } + + cups_secret_free (lookup); + return; + } + + dispatch = lookup->dispatch; + + dispatch->backend->secrets_service_available = + error == NULL; + + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("CUPS backend: Secret Service lookup failed: %s\n", + error->message)); + g_error_free (error); + } + + if (found) + gtk_print_backend_cups_set_password ( + GTK_PRINT_BACKEND (dispatch->backend), + dispatch->request->auth_info_required, + lookup->auth_info); + else + dispatch->backend->authentication_lock = FALSE; + + cups_secret_free (lookup); + + if (found) + check_auth_info (dispatch); + else + request_auth_info (dispatch); +} + + + +static void +cups_secret_get_attributes (CupsSecretLookup *lookup) +{ + cups_secret_call (lookup, + lookup->item_path, + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", + CUPS_SECRET_IFACE ("Item"), + "Attributes"), + G_VARIANT_TYPE ("(v)"), + CUPS_SECRET_ATTRIBUTES); +} + + +static void +cups_secret_open_session (CupsSecretLookup *lookup) +{ + cups_secret_call ( + lookup, + CUPS_SECRET_PATH, + CUPS_SECRET_IFACE ("Service"), + "OpenSession", + g_variant_new ("(sv)", + "plain", + g_variant_new_string ("")), + G_VARIANT_TYPE ("(vo)"), + CUPS_SECRET_SESSION); +} + + +static void +cups_secret_create_item (CupsSecretLookup *lookup) +{ + GVariant *properties; + GVariant *secret; + + if (lookup->store_properties == NULL || + lookup->store_password == NULL || + lookup->session_path == NULL) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + properties = lookup->store_properties; + lookup->store_properties = NULL; + + secret = g_variant_new ( + "(oay@ays)", + lookup->session_path, + NULL, + g_variant_new_bytestring (lookup->store_password), + "text/plain"); + + cups_secret_call ( + lookup, + CUPS_SECRET_DEFAULT_COLLECTION, + CUPS_SECRET_IFACE ("Collection"), + "CreateItem", + g_variant_new ("(@a{sv}@(oayays)b)", + properties, + secret, + TRUE), + G_VARIANT_TYPE ("(oo)"), + CUPS_SECRET_CREATE); +} + + +static void +cups_secret_after_prompt (CupsSecretLookup *lookup, + gboolean dismissed) +{ + if (dismissed) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + if (!lookup->store) + { + cups_secret_get_attributes (lookup); + return; + } + + if (lookup->create_prompt) + { + cups_secret_done (lookup, TRUE, NULL); + return; + } + + cups_secret_open_session (lookup); +} + + + +static void +cups_secret_prompt_completed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + CupsSecretLookup *lookup = user_data; + gboolean dismissed; + + (void) connection; + (void) sender_name; + (void) object_path; + (void) interface_name; + (void) signal_name; + + g_dbus_connection_signal_unsubscribe ( + lookup->connection, + lookup->prompt_subscription); + + lookup->prompt_subscription = 0; + + g_free (lookup->prompt_path); + lookup->prompt_path = NULL; + + if (!g_variant_is_of_type (parameters, + G_VARIANT_TYPE ("(bv)"))) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + g_variant_get (parameters, "(bv)", + &dismissed, NULL); + + if (lookup->step == CUPS_SECRET_PROMPT) + { + lookup->prompt_completed = TRUE; + lookup->prompt_dismissed = dismissed; + return; + } + + cups_secret_after_prompt (lookup, dismissed); +} + + +static void +cups_secret_prompt (CupsSecretLookup *lookup) +{ + lookup->prompt_completed = FALSE; + lookup->prompt_dismissed = FALSE; + + lookup->prompt_subscription = + g_dbus_connection_signal_subscribe ( + lookup->connection, + NULL, + CUPS_SECRET_IFACE ("Prompt"), + "Completed", + lookup->prompt_path, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + cups_secret_prompt_completed, + lookup, + NULL); + + cups_secret_call ( + lookup, + lookup->prompt_path, + CUPS_SECRET_IFACE ("Prompt"), + "Prompt", + g_variant_new ("(s)", "0"), + G_VARIANT_TYPE ("()"), + CUPS_SECRET_PROMPT); +} + + + +static void +cups_secret_reply (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CupsSecretLookup *lookup = user_data; + GError *error = NULL; + GVariant *output; + + output = g_dbus_connection_call_finish ( + G_DBUS_CONNECTION (source_object), res, &error); + + if (output == NULL) + { + cups_secret_done (lookup, FALSE, error); + return; + } + + switch (lookup->step) + { + case CUPS_SECRET_SEARCH: + { + const gchar **unlocked; + const gchar **locked; + const gchar *path; + gboolean is_locked; + + g_variant_get (output, "(^a&o^a&o)", + &unlocked, &locked); + g_variant_unref (output); + + if (unlocked[0] != NULL) + { + path = unlocked[0]; + is_locked = FALSE; + } + else + { + path = locked[0]; + is_locked = TRUE; + } + + if (path != NULL) + lookup->item_path = g_strdup (path); + + g_free (unlocked); + g_free (locked); + + if (lookup->item_path == NULL) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + if (!is_locked) + { + cups_secret_get_attributes (lookup); + return; + } + + { + const gchar *paths[2] = + { lookup->item_path, NULL }; + + cups_secret_call (lookup, + CUPS_SECRET_PATH, + CUPS_SECRET_IFACE ("Service"), + "Unlock", + g_variant_new ("(^ao)", paths), + G_VARIANT_TYPE ("(aoo)"), + CUPS_SECRET_UNLOCK); + } + + return; + } + + case CUPS_SECRET_UNLOCK: + { + g_variant_get (output, "(@aoo)", + NULL, &lookup->prompt_path); + g_variant_unref (output); + + if (strcmp (lookup->prompt_path, "/") == 0) + { + g_free (lookup->prompt_path); + lookup->prompt_path = NULL; + cups_secret_after_prompt (lookup, FALSE); + return; + } + + cups_secret_prompt (lookup); + return; + } + + case CUPS_SECRET_PROMPT: + g_variant_unref (output); + + if (lookup->prompt_completed) + cups_secret_after_prompt (lookup, + lookup->prompt_dismissed); + else + lookup->step = CUPS_SECRET_PROMPT_WAIT; + + return; + + case CUPS_SECRET_PROMPT_WAIT: + break; + + case CUPS_SECRET_ATTRIBUTES: + { + GVariant *attributes; + gchar **required; + const gchar *value; + guint i; + + g_variant_get (output, "(v)", &attributes); + g_variant_unref (output); + + if (!g_variant_is_of_type (attributes, + G_VARIANT_TYPE ("a{ss}"))) + { + g_variant_unref (attributes); + cups_secret_done (lookup, FALSE, NULL); + return; + } + + required = lookup->dispatch->request->auth_info_required; + lookup->auth_info = + g_new0 (gchar *, lookup->length + 1); + lookup->password_field = -1; + + for (i = 0; i < lookup->length; i++) + { + value = NULL; + + if (strcmp (required[i], "password") == 0) + { + lookup->password_field = i; + continue; + } + + if (strcmp (required[i], "username") == 0) + { + if (!g_variant_lookup (attributes, + "user", + "&s", + &value)) + g_variant_lookup (attributes, + "username", + "&s", + &value); + } + else if (strcmp (required[i], "hostname") == 0) + { + if (!g_variant_lookup (attributes, + "server", + "&s", + &value)) + g_variant_lookup (attributes, + "hostname", + "&s", + &value); + } + else if (strcmp (required[i], "domain") == 0) + { + g_variant_lookup (attributes, + "domain", + "&s", + &value); + } + + if (value == NULL) + { + g_variant_unref (attributes); + cups_secret_done (lookup, FALSE, NULL); + return; + } + + lookup->auth_info[i] = g_strdup (value); + } + + g_variant_unref (attributes); + + if (lookup->password_field < 0) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + cups_secret_open_session (lookup); + return; + } + + case CUPS_SECRET_SESSION: + { + g_variant_get (output, "(vo)", + NULL, &lookup->session_path); + g_variant_unref (output); + + if (strcmp (lookup->session_path, "/") == 0) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + if (lookup->store) + { + cups_secret_create_item (lookup); + return; + } + + cups_secret_call ( + lookup, + lookup->item_path, + CUPS_SECRET_IFACE ("Item"), + "GetSecret", + g_variant_new ("(o)", + lookup->session_path), + G_VARIANT_TYPE ("((oayays))"), + CUPS_SECRET_VALUE); + + return; + } + + case CUPS_SECRET_VALUE: + { + GVariant *value; + gconstpointer password; + gsize length; + guint i; + + g_variant_get (output, + "((o@ay@ays))", + NULL, NULL, &value, NULL); + g_variant_unref (output); + + password = g_variant_get_fixed_array ( + value, &length, sizeof (guchar)); + + if (password != NULL) + lookup->auth_info[lookup->password_field] = + g_strndup (password, length); + + g_variant_unref (value); + + for (i = 0; i < lookup->length; i++) + if (lookup->auth_info[i] == NULL) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + cups_secret_done (lookup, TRUE, NULL); + return; + } + + case CUPS_SECRET_CREATE: + { + const gchar *item; + const gchar *prompt; + + g_variant_get (output, "(&o&o)", + &item, &prompt); + + if (strcmp (item, "/") != 0) + { + g_variant_unref (output); + cups_secret_done (lookup, TRUE, NULL); + return; + } + + if (strcmp (prompt, "/") == 0) + { + g_variant_unref (output); + cups_secret_done (lookup, FALSE, NULL); + return; + } + + g_free (lookup->prompt_path); + lookup->prompt_path = g_strdup (prompt); + lookup->create_prompt = TRUE; + + g_variant_unref (output); + + cups_secret_prompt (lookup); + return; + } + } + + g_variant_unref (output); + cups_secret_done (lookup, FALSE, NULL); +} + + +static void +cups_secret_connection (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CupsSecretLookup *lookup = user_data; + GVariantBuilder builder; + GError *error = NULL; + const gchar *uri; + + (void) source_object; + + lookup->connection = g_bus_get_finish (res, &error); + + if (lookup->connection == NULL) + { + cups_secret_done (lookup, FALSE, error); + return; + } + + if (lookup->store) + { + const gchar *paths[2] = + { CUPS_SECRET_DEFAULT_COLLECTION, NULL }; + + cups_secret_call ( + lookup, + CUPS_SECRET_PATH, + CUPS_SECRET_IFACE ("Service"), + "Unlock", + g_variant_new ("(^ao)", paths), + G_VARIANT_TYPE ("(aoo)"), + CUPS_SECRET_UNLOCK); + + return; + } + + uri = gtk_cups_request_ipp_get_string ( + lookup->dispatch->request, + IPP_TAG_URI, + "printer-uri"); + + if (uri == NULL) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + g_variant_builder_init (&builder, + G_VARIANT_TYPE ("a{ss}")); + g_variant_builder_add (&builder, + "{ss}", + "uri", + uri); + + cups_secret_call ( + lookup, + CUPS_SECRET_PATH, + CUPS_SECRET_IFACE ("Service"), + "SearchItems", + g_variant_new ("(@a{ss})", + g_variant_builder_end (&builder)), + G_VARIANT_TYPE ("(aoao)"), + CUPS_SECRET_SEARCH); +} + + +static void +lookup_auth_info (gpointer data) +{ + GtkPrintCupsDispatchWatch *dispatch = data; + CupsSecretLookup *lookup; + gchar **required; + guint i; + + if (dispatch->backend->authentication_lock) + return; + + required = dispatch->request->auth_info_required; + + for (i = 0; required[i] != NULL; i++) + if (strcmp (required[i], "password") == 0) + break; + + if (required[i] == NULL || + gtk_cups_request_ipp_get_string ( + dispatch->request, + IPP_TAG_URI, + "printer-uri") == NULL) + { + request_auth_info (dispatch); + return; + } + + lookup = g_new0 (CupsSecretLookup, 1); + lookup->dispatch = dispatch; + lookup->length = g_strv_length (required); + + dispatch->backend->authentication_lock = TRUE; + + g_bus_get (G_BUS_TYPE_SESSION, + NULL, + cups_secret_connection, + lookup); +} + + +static void +cups_secret_store (gchar **auth_info, + gchar **auth_info_labels, + const gchar *printer_uri) +{ + CupsSecretLookup *lookup; + GVariantBuilder attributes; + GVariantBuilder properties; + const gchar *password = NULL; + const gchar *key; + guint i; + + if (auth_info == NULL || + auth_info_labels == NULL || + printer_uri == NULL) + return; + + g_variant_builder_init (&attributes, + G_VARIANT_TYPE ("a{ss}")); + + g_variant_builder_add (&attributes, + "{ss}", + "uri", + printer_uri); + + for (i = 0; auth_info_labels[i] != NULL; i++) + { + if (auth_info[i] == NULL) + continue; + + key = NULL; + + if (strcmp (auth_info_labels[i], "password") == 0) + { + password = auth_info[i]; + continue; + } + + if (strcmp (auth_info_labels[i], "username") == 0) + key = "user"; + else if (strcmp (auth_info_labels[i], "hostname") == 0) + key = "server"; + else if (strcmp (auth_info_labels[i], "domain") == 0) + key = "domain"; + + if (key != NULL) + g_variant_builder_add (&attributes, + "{ss}", + key, + auth_info[i]); + } + + if (password == NULL) + { + g_variant_builder_clear (&attributes); + return; + } + + g_variant_builder_init (&properties, + G_VARIANT_TYPE ("a{sv}")); + + g_variant_builder_add ( + &properties, + "{sv}", + CUPS_SECRET_IFACE ("Item.Label"), + g_variant_new_string (printer_uri)); + + g_variant_builder_add ( + &properties, + "{sv}", + CUPS_SECRET_IFACE ("Item.Attributes"), + g_variant_builder_end (&attributes)); + + lookup = g_new0 (CupsSecretLookup, 1); + lookup->store = TRUE; + + lookup->store_properties = + g_variant_ref_sink ( + g_variant_builder_end (&properties)); + + lookup->store_password = + g_strdup (password); + + g_bus_get (G_BUS_TYPE_SESSION, + NULL, + cups_secret_connection, + lookup); +} +#endif + + static gboolean request_auth_info (gpointer user_data) { @@ -1230,12 +2157,12 @@ request_auth_info (gpointer user_data) const char *job_title; const char *printer_uri; gchar *prompt = NULL; - char *printer_name = NULL; + const char *printer_name = NULL; gint length; gint i; gboolean *auth_info_visible = NULL; - gchar **auth_info_default = NULL; - gchar **auth_info_display = NULL; + const gchar **auth_info_default = NULL; + const gchar **auth_info_display = NULL; dispatch = (GtkPrintCupsDispatchWatch *) user_data; @@ -1247,35 +2174,35 @@ request_auth_info (gpointer user_data) length = g_strv_length (dispatch->request->auth_info_required); auth_info_visible = g_new0 (gboolean, length); - auth_info_default = g_new0 (gchar *, length + 1); - auth_info_display = g_new0 (gchar *, length + 1); + auth_info_default = g_new0 (const gchar *, length + 1); + auth_info_display = g_new0 (const gchar *, length + 1); for (i = 0; i < length; i++) { if (g_strcmp0 (dispatch->request->auth_info_required[i], "domain") == 0) { - auth_info_display[i] = g_strdup (_("Domain:")); - auth_info_default[i] = g_strdup ("WORKGROUP"); + auth_info_display[i] = _("Domain:"); + auth_info_default[i] = "WORKGROUP"; auth_info_visible[i] = TRUE; } else if (g_strcmp0 (dispatch->request->auth_info_required[i], "username") == 0) { - auth_info_display[i] = g_strdup (_("Username:")); + auth_info_display[i] = _("Username:"); if (dispatch->backend->username != NULL) - auth_info_default[i] = g_strdup (dispatch->backend->username); + auth_info_default[i] = dispatch->backend->username; else - auth_info_default[i] = g_strdup (cupsUser ()); + auth_info_default[i] = cupsUser (); auth_info_visible[i] = TRUE; } else if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0) { - auth_info_display[i] = g_strdup (_("Password:")); + auth_info_display[i] = _("Password:"); auth_info_visible[i] = FALSE; } } if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL) - printer_name = g_strdup (strrchr (printer_uri, '/') + 1); + printer_name = strrchr (printer_uri, '/') + 1; dispatch->backend->authentication_lock = TRUE; @@ -1294,6 +2221,18 @@ request_auth_info (gpointer user_data) prompt = g_strdup ( _("Authentication is required to print this document")); } +#ifdef HAVE_CUPS_SECRET_SERVICE + g_free (dispatch->backend->store_auth_info_uri); + dispatch->backend->store_auth_info_uri = NULL; + + dispatch->backend->store_auth_info_allowed = + dispatch->backend->secrets_service_available && + printer_uri != NULL; + + if (dispatch->backend->store_auth_info_allowed) + dispatch->backend->store_auth_info_uri = g_strdup (printer_uri); +#endif + g_signal_emit_by_name (dispatch->backend, "request-password", dispatch->request->auth_info_required, auth_info_default, @@ -1301,15 +2240,13 @@ request_auth_info (gpointer user_data) auth_info_visible, prompt); - for (i = 0; i < length; i++) - { - g_free (auth_info_default[i]); - g_free (auth_info_display[i]); - } +#ifdef HAVE_CUPS_SECRET_SERVICE + dispatch->backend->store_auth_info_allowed = FALSE; +#endif g_free (auth_info_default); g_free (auth_info_display); - g_free (printer_name); + g_free (auth_info_visible); g_free (prompt); g_idle_add (check_auth_info, user_data); @@ -1514,7 +2451,11 @@ cups_request_execute (GtkPrintBackendCups *print_backend, { dispatch->callback = callback; dispatch->callback_data = user_data; +#ifdef HAVE_CUPS_SECRET_SERVICE + lookup_auth_info (dispatch); +#else request_auth_info (dispatch); +#endif } else { @@ -2555,7 +3496,7 @@ cups_request_avahi_printer_info (const gchar *printer_uri, AvahiService *service; http_t *http; - http = httpConnect (host, port); + http = gtk_cups_backend_http_connect (host, port); if (http) { service = (AvahiService *) g_new0 (AvahiService, 1); @@ -3438,7 +4379,7 @@ cups_request_ppd (GtkPrinter *printer) } } - http = httpConnectEncrypt (cups_printer->hostname, + http = gtk_cups_backend_http_connect_encrypt (cups_printer->hostname, cups_printer->port, cupsEncryption ());