]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkprintoperation-win32.c
GtkEntry: Sanity check the end_pos value in _get_display_text()
[~andy/gtk] / gtk / gtkprintoperation-win32.c
index 6b6ad5b627d653d6df5fa21818df13395a31bba3..04de8ebbeb178f44d8bd7d2db460199f96162a74 100644 (file)
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #ifndef _MSC_VER
 #define COBJMACROS
 #include "config.h"
 #include <math.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <io.h>
+#include <stdio.h>
 #include <stdlib.h>
+#include <windows.h>
 #include <cairo-win32.h>
 #include <glib.h>
 #include "gtkprintoperation-private.h"
 #include "gtkprint-win32.h"
 #include "gtkintl.h"
 #include "gtkinvisible.h"
-#include "gtkalias.h"
+#include "gtkplug.h"
+#include "gtkstock.h"
+#include "gtk.h"
+#include "gtkwin32embedwidget.h"
 
 #define MAX_PAGE_RANGES 20
 #define STATUS_POLLING_TIME 2000
@@ -53,12 +60,16 @@ typedef struct {
   HANDLE printerHandle;
   int job_id;
   guint timeout_id;
+
+  cairo_surface_t *surface;
+  GtkWidget *embed_widget;
 } GtkPrintOperationWin32;
 
 static void win32_poll_status (GtkPrintOperation *op);
 
 static const GUID myIID_IPrintDialogCallback  = {0x5852a2c3,0x6530,0x11d1,{0xb6,0xa3,0x0,0x0,0xf8,0x75,0x7b,0xf9}};
 
+#if !defined (_MSC_VER) && !defined (HAVE_IPRINTDIALOGCALLBACK)
 #undef INTERFACE
 #define INTERFACE IPrintDialogCallback
 DECLARE_INTERFACE_ (IPrintDialogCallback, IUnknown)
@@ -70,6 +81,7 @@ DECLARE_INTERFACE_ (IPrintDialogCallback, IUnknown)
     STDMETHOD (SelectionChange)(THIS) PURE;
     STDMETHOD (HandleMessage)(THIS_ HWND,UINT,WPARAM,LPARAM,LRESULT*) PURE;
 }; 
+#endif
 
 static UINT got_gdk_events_message;
 
@@ -401,6 +413,42 @@ paper_size_to_win32 (GtkPaperSize *paper_size)
   return 0;
 }
 
+static gchar*
+get_default_printer (void)
+{
+  wchar_t *win32_printer_name = NULL;
+  gchar *printer_name = NULL;
+  DWORD needed;
+
+  GetDefaultPrinterW (NULL, &needed);
+  win32_printer_name = g_malloc ((gsize) needed * sizeof (wchar_t));
+  if (!GetDefaultPrinterW (win32_printer_name, &needed))
+    {
+      g_free (win32_printer_name);
+      return NULL;
+    }
+  printer_name = g_utf16_to_utf8 (win32_printer_name, -1, NULL, NULL, NULL);
+  g_free (win32_printer_name);
+
+  return printer_name;
+}
+
+static void
+set_hard_margins (GtkPrintOperation *op)
+{
+  double top, bottom, left, right;
+  GtkPrintOperationWin32 *op_win32 = op->priv->platform_data;
+
+  top = GetDeviceCaps (op_win32->hdc, PHYSICALOFFSETY);
+  bottom = GetDeviceCaps (op_win32->hdc, PHYSICALHEIGHT)
+      - GetDeviceCaps (op_win32->hdc, VERTRES) - top;
+  left = GetDeviceCaps (op_win32->hdc, PHYSICALOFFSETX);
+  right = GetDeviceCaps (op_win32->hdc, PHYSICALWIDTH)
+      - GetDeviceCaps (op_win32->hdc, HORZRES) - left;
+
+  _gtk_print_context_set_hard_margins (op->priv->print_context, top, bottom, left, right);
+}
+
 void
 win32_start_page (GtkPrintOperation *op,
                  GtkPrintContext *print_context,
@@ -409,6 +457,7 @@ win32_start_page (GtkPrintOperation *op,
   GtkPrintOperationWin32 *op_win32 = op->priv->platform_data;
   LPDEVMODEW devmode;
   GtkPaperSize *paper_size;
+  double x_off, y_off;
 
   devmode = GlobalLock (op_win32->devmode);
   
@@ -424,6 +473,8 @@ win32_start_page (GtkPrintOperation *op,
     {
       devmode->dmPaperSize = DMPAPER_USER;
       devmode->dmFields |= DM_PAPERWIDTH | DM_PAPERLENGTH;
+
+      /* Lengths in DEVMODE are in tenths of a millimeter */
       devmode->dmPaperWidth = gtk_paper_size_get_width (paper_size, GTK_UNIT_MM) * 10.0;
       devmode->dmPaperLength = gtk_paper_size_get_height (paper_size, GTK_UNIT_MM) * 10.0;
     }
@@ -431,6 +482,11 @@ win32_start_page (GtkPrintOperation *op,
   ResetDCW (op_win32->hdc, devmode);
   
   GlobalUnlock (op_win32->devmode);
+
+  set_hard_margins (op);
+  x_off = GetDeviceCaps (op_win32->hdc, PHYSICALOFFSETX);
+  y_off = GetDeviceCaps (op_win32->hdc, PHYSICALOFFSETY);
+  cairo_surface_set_device_offset (op_win32->surface, -x_off, -y_off);
   
   StartPage (op_win32->hdc);
 }
@@ -440,6 +496,9 @@ win32_end_page (GtkPrintOperation *op,
                GtkPrintContext *print_context)
 {
   GtkPrintOperationWin32 *op_win32 = op->priv->platform_data;
+
+  cairo_surface_show_page (op_win32->surface);
+
   EndPage (op_win32->hdc);
 }
 
@@ -455,7 +514,7 @@ win32_poll_status_timeout (GtkPrintOperation *op)
   win32_poll_status (op);
 
   if (!gtk_print_operation_is_finished (op))
-    op_win32->timeout_id = g_timeout_add (STATUS_POLLING_TIME,
+    op_win32->timeout_id = gdk_threads_add_timeout (STATUS_POLLING_TIME,
                                          (GSourceFunc)win32_poll_status_timeout,
                                          op);
   g_object_unref (op);
@@ -464,32 +523,40 @@ win32_poll_status_timeout (GtkPrintOperation *op)
 
 
 static void
-win32_end_run (GtkPrintOperation *op)
+win32_end_run (GtkPrintOperation *op,
+              gboolean           wait,
+              gboolean           cancelled)
 {
   GtkPrintOperationWin32 *op_win32 = op->priv->platform_data;
   LPDEVNAMES devnames;
   HANDLE printerHandle = 0;
+
+  cairo_surface_finish (op_win32->surface);
   
   EndDoc (op_win32->hdc);
-  devnames = GlobalLock (op_win32->devnames);
-  if (!OpenPrinterW (((gunichar2 *)devnames) + devnames->wDeviceOffset,
-                    &printerHandle, NULL))
-    printerHandle = 0;
-  GlobalUnlock (op_win32->devnames);
+
+  if (op->priv->track_print_status)
+    {
+      devnames = GlobalLock (op_win32->devnames);
+      if (!OpenPrinterW (((gunichar2 *)devnames) + devnames->wDeviceOffset,
+                        &printerHandle, NULL))
+       printerHandle = 0;
+      GlobalUnlock (op_win32->devnames);
+    }
   
-  GlobalFree(op_win32->devmode);
-  GlobalFree(op_win32->devnames);
+  GlobalFree (op_win32->devmode);
+  GlobalFree (op_win32->devnames);
 
-  cairo_surface_destroy (op->priv->surface);
-  op->priv->surface = NULL;
+  cairo_surface_destroy (op_win32->surface);
+  op_win32->surface = NULL;
 
-  DeleteDC(op_win32->hdc);
+  DeleteDC (op_win32->hdc);
   
   if (printerHandle != 0)
     {
       op_win32->printerHandle = printerHandle;
       win32_poll_status (op);
-      op_win32->timeout_id = g_timeout_add (STATUS_POLLING_TIME,
+      op_win32->timeout_id = gdk_threads_add_timeout (STATUS_POLLING_TIME,
                                            (GSourceFunc)win32_poll_status_timeout,
                                            op);
     }
@@ -518,8 +585,9 @@ win32_poll_status (GtkPrintOperation *op)
   status_str = NULL;
   if (ret)
     {
+      DWORD win32_status;
       job_info = (JOB_INFO_1W *)data;
-      DWORD win32_status = job_info->Status;
+      win32_status = job_info->Status;
 
       if (job_info->pStatus)
        status_str = g_utf16_to_utf8 (job_info->pStatus, 
@@ -585,10 +653,9 @@ static HWND
 get_parent_hwnd (GtkWidget *widget)
 {
   gtk_widget_realize (widget);
-  return gdk_win32_drawable_get_handle (widget->window);
+  return gdk_win32_window_get_handle (gtk_widget_get_window (widget));
 }
 
-
 static void
 devnames_to_settings (GtkPrintSettings *settings,
                      HANDLE hDevNames)
@@ -646,18 +713,19 @@ devmode_to_settings (GtkPrintSettings *settings,
                                     -1, NULL, NULL, NULL);
       if (form_name == NULL || form_name[0] == 0)
        form_name = g_strdup (_("Custom size"));
+
+      /* Lengths in DEVMODE are in tenths of a millimeter */
       paper_size = gtk_paper_size_new_custom (form_name,
                                              form_name,
-                                             devmode->dmPaperWidth * 10.0,
-                                             devmode->dmPaperLength * 10.0,
+                                             devmode->dmPaperWidth / 10.0,
+                                             devmode->dmPaperLength / 10.0,
                                              GTK_UNIT_MM);
       gtk_print_settings_set_paper_size (settings, paper_size);
       gtk_paper_size_free (paper_size);
     }
   
   if (devmode->dmFields & DM_SCALE)
-    gtk_print_settings_set_scale (settings,
-                                 devmode->dmScale / 100.0);
+    gtk_print_settings_set_scale (settings, devmode->dmScale);
   
   if (devmode->dmFields & DM_COPIES)
     gtk_print_settings_set_n_copies (settings,
@@ -737,7 +805,7 @@ devmode_to_settings (GtkPrintSettings *settings,
     }
   
   if (devmode->dmFields & DM_COLOR)
-    gtk_print_settings_set_use_color (settings, devmode->dmFields == DMCOLOR_COLOR);
+    gtk_print_settings_set_use_color (settings, devmode->dmColor == DMCOLOR_COLOR);
   
   if (devmode->dmFields & DM_DUPLEX)
     {
@@ -819,7 +887,7 @@ static void
 dialog_to_print_settings (GtkPrintOperation *op,
                          LPPRINTDLGEXW printdlgex)
 {
-  int i;
+  guint i;
   GtkPrintSettings *settings;
 
   settings = gtk_print_settings_new ();
@@ -891,8 +959,8 @@ devmode_from_settings (GtkPrintSettings *settings,
     {
       devmode->dmDriverExtra = extras_len;
       memcpy (((char *)devmode) + sizeof (DEVMODEW), extras, extras_len);
-      g_free (extras);
     }
+  g_free (extras);
   if (gtk_print_settings_has_key (settings, GTK_PRINT_SETTINGS_WIN32_DRIVER_VERSION))
     devmode->dmDriverVersion = gtk_print_settings_get_int (settings, GTK_PRINT_SETTINGS_WIN32_DRIVER_VERSION);
   
@@ -929,8 +997,10 @@ devmode_from_settings (GtkPrintSettings *settings,
        {
          devmode->dmPaperSize = DMPAPER_USER;
          devmode->dmFields |= DM_PAPERWIDTH | DM_PAPERLENGTH;
-         devmode->dmPaperWidth = gtk_paper_size_get_width (paper_size, GTK_UNIT_MM) / 10.0;
-         devmode->dmPaperLength = gtk_paper_size_get_height (paper_size, GTK_UNIT_MM) / 10.0;
+
+          /* Lengths in DEVMODE are in tenths of a millimeter */
+         devmode->dmPaperWidth = gtk_paper_size_get_width (paper_size, GTK_UNIT_MM) * 10.0;
+         devmode->dmPaperLength = gtk_paper_size_get_height (paper_size, GTK_UNIT_MM) * 10.0;
        }
       gtk_paper_size_free (paper_size);
     }
@@ -938,7 +1008,7 @@ devmode_from_settings (GtkPrintSettings *settings,
   if (gtk_print_settings_has_key (settings, GTK_PRINT_SETTINGS_SCALE))
     {
       devmode->dmFields |= DM_SCALE;
-      devmode->dmScale = gtk_print_settings_get_scale (settings) * 100;
+      devmode->dmScale = gtk_print_settings_get_scale (settings);
     }
   
   if (gtk_print_settings_has_key (settings, GTK_PRINT_SETTINGS_N_COPIES))
@@ -1148,7 +1218,7 @@ dialog_from_print_settings (GtkPrintOperation *op,
   
   printer = gtk_print_settings_get_printer (settings);
   if (printer)
-    printdlgex->hDevNames = gtk_print_win32_devnames_from_printer_name (printer);
+    printdlgex->hDevNames = gtk_print_win32_devnames_to_win32_from_printer_name (printer);
   
   printdlgex->hDevMode = devmode_from_settings (settings,
                                                op->priv->default_page_setup);
@@ -1262,11 +1332,318 @@ print_callback_new  (void)
   return &callback->iPrintDialogCallback;
 }
 
+static  void
+plug_grab_notify (GtkWidget        *widget,
+                 gboolean          was_grabbed,
+                 GtkPrintOperation *op)
+{
+  EnableWindow (GetAncestor (GDK_WINDOW_HWND (gtk_widget_get_window (widget)), GA_ROOT),
+               was_grabbed);
+}
+
+
+static BOOL CALLBACK
+pageDlgProc (HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+  GtkPrintOperation *op;
+  GtkPrintOperationWin32 *op_win32;
+  
+  if (message == WM_INITDIALOG)
+    {
+      PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lparam;
+      GtkWidget *plug;
+
+      op = GTK_PRINT_OPERATION ((gpointer)page->lParam);
+      op_win32 = op->priv->platform_data;
+
+      SetWindowLongPtrW (wnd, GWLP_USERDATA, (LONG_PTR)op);
+      
+      plug = _gtk_win32_embed_widget_new (wnd);
+      gtk_window_set_modal (GTK_WINDOW (plug), TRUE);
+      op_win32->embed_widget = plug;
+      gtk_container_add (GTK_CONTAINER (plug), op->priv->custom_widget);
+      gtk_widget_show (op->priv->custom_widget);
+      gtk_widget_show (plug);
+      gdk_window_focus (gtk_widget_get_window (plug), GDK_CURRENT_TIME);
+
+      /* This dialog is modal, so we grab the embed widget */
+      gtk_grab_add (plug);
+
+      /* When we lose the grab we need to disable the print dialog */
+      g_signal_connect (plug, "grab-notify", G_CALLBACK (plug_grab_notify), op);
+      return FALSE;
+    }
+  else if (message == WM_DESTROY)
+    {
+      op = GTK_PRINT_OPERATION (GetWindowLongPtrW (wnd, GWLP_USERDATA));
+      op_win32 = op->priv->platform_data;
+      
+      g_signal_emit_by_name (op, "custom-widget-apply", op->priv->custom_widget);
+      gtk_widget_destroy (op_win32->embed_widget);
+      op_win32->embed_widget = NULL;
+      op->priv->custom_widget = NULL;
+    }
+  else 
+    {
+      op = GTK_PRINT_OPERATION (GetWindowLongPtrW (wnd, GWLP_USERDATA));
+      op_win32 = op->priv->platform_data;
+
+      return _gtk_win32_embed_widget_dialog_procedure (GTK_WIN32_EMBED_WIDGET (op_win32->embed_widget),
+                                                      wnd, message, wparam, lparam);
+    }
+  
+  return FALSE;
+}
+
+static HPROPSHEETPAGE
+create_application_page (GtkPrintOperation *op)
+{
+  HPROPSHEETPAGE hpage;
+  PROPSHEETPAGEW page;
+  DLGTEMPLATE *template;
+  HGLOBAL htemplate;
+  LONG base_units;
+  WORD baseunitX, baseunitY;
+  WORD *array;
+  GtkRequisition requisition;
+  const char *tab_label;
+
+  /* Make the template the size of the custom widget size request */
+  gtk_widget_get_preferred_size (op->priv->custom_widget,
+                                 &requisition, NULL);
+
+  base_units = GetDialogBaseUnits ();
+  baseunitX = LOWORD (base_units);
+  baseunitY = HIWORD (base_units);
+  
+  htemplate = GlobalAlloc (GMEM_MOVEABLE, 
+                          sizeof (DLGTEMPLATE) + sizeof (WORD) * 3);
+  template = GlobalLock (htemplate);
+  template->style = WS_CHILDWINDOW | DS_CONTROL;
+  template->dwExtendedStyle = WS_EX_CONTROLPARENT;
+  template->cdit = 0;
+  template->x = MulDiv (0, 4, baseunitX);
+  template->y = MulDiv (0, 8, baseunitY);
+  template->cx = MulDiv (requisition.width, 4, baseunitX);
+  template->cy = MulDiv (requisition.height, 8, baseunitY);
+  
+  array = (WORD *) (template+1);
+  *array++ = 0; /* menu */
+  *array++ = 0; /* class */
+  *array++ = 0; /* title */
+  
+  memset (&page, 0, sizeof (page));
+  page.dwSize = sizeof (page);
+  page.dwFlags = PSP_DLGINDIRECT | PSP_USETITLE | PSP_PREMATURE;
+  page.hInstance = GetModuleHandle (NULL);
+  page.pResource = template;
+  
+  tab_label = op->priv->custom_tab_label;
+  if (tab_label == NULL)
+    tab_label = g_get_application_name ();
+  if (tab_label == NULL)
+    tab_label = _("Application");
+  page.pszTitle = g_utf8_to_utf16 (tab_label, 
+                                  -1, NULL, NULL, NULL);
+  page.pfnDlgProc = pageDlgProc;
+  page.pfnCallback = NULL;
+  page.lParam = (LPARAM) op;
+  hpage = CreatePropertySheetPageW (&page);
+  
+  GlobalUnlock (htemplate);
+  
+  /* TODO: We're leaking htemplate here... */
+  
+  return hpage;
+}
+
+static GtkPageSetup *
+create_page_setup (GtkPrintOperation *op)
+{
+  GtkPrintOperationPrivate *priv = op->priv;
+  GtkPageSetup *page_setup;
+  GtkPrintSettings *settings;
+  
+  if (priv->default_page_setup)
+    page_setup = gtk_page_setup_copy (priv->default_page_setup);
+  else
+    page_setup = gtk_page_setup_new ();
+
+  settings = priv->print_settings;
+  if (settings)
+    {
+      GtkPaperSize *paper_size;
+      
+      if (gtk_print_settings_has_key (settings, GTK_PRINT_SETTINGS_ORIENTATION))
+       gtk_page_setup_set_orientation (page_setup,
+                                       gtk_print_settings_get_orientation (settings));
+
+
+      paper_size = gtk_print_settings_get_paper_size (settings);
+      if (paper_size)
+       {
+         gtk_page_setup_set_paper_size (page_setup, paper_size);
+         gtk_paper_size_free (paper_size);
+       }
+
+      /* TODO: Margins? */
+    }
+  
+  return page_setup;
+}
+
 GtkPrintOperationResult
-_gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
-                                                 GtkWindow *parent,
-                                                 gboolean *do_print,
-                                                 GError **error)
+gtk_print_operation_run_without_dialog (GtkPrintOperation *op,
+                                       gboolean          *do_print)
+{
+  GtkPrintOperationResult result;
+  GtkPrintOperationWin32 *op_win32;
+  GtkPrintOperationPrivate *priv;
+  GtkPrintSettings *settings;
+  GtkPageSetup *page_setup;
+  DOCINFOW docinfo;
+  HGLOBAL hDevMode = NULL;
+  HGLOBAL hDevNames = NULL;
+  HDC hDC = NULL;
+  const char *printer = NULL;
+  double dpi_x, dpi_y;
+  int job_id;
+  cairo_t *cr;
+  DEVNAMES *pdn;
+  DEVMODEW *pdm;
+
+  *do_print = FALSE;
+
+  priv = op->priv;
+  settings = priv->print_settings;
+  
+  op_win32 = g_new0 (GtkPrintOperationWin32, 1);
+  priv->platform_data = op_win32;
+  priv->free_platform_data = (GDestroyNotify) op_win32_free;
+  printer = gtk_print_settings_get_printer (settings);
+
+  if (!printer)
+    {
+      /* No printer selected. Get the system default printer and store
+       * it in settings.
+       */
+      gchar *tmp_printer = get_default_printer ();
+      if (!tmp_printer)
+       {
+         result = GTK_PRINT_OPERATION_RESULT_ERROR;
+         g_set_error_literal (&priv->error,
+                              GTK_PRINT_ERROR,
+                              GTK_PRINT_ERROR_INTERNAL_ERROR,
+                              _("No printer found"));
+         goto out;
+       }
+      gtk_print_settings_set_printer (settings, tmp_printer);
+      printer = gtk_print_settings_get_printer (settings);
+      g_free (tmp_printer);
+    }
+
+  hDevNames = gtk_print_win32_devnames_to_win32_from_printer_name (printer);
+  hDevMode = devmode_from_settings (settings, op->priv->default_page_setup);
+
+  /* Create a printer DC for the print settings and page setup provided. */
+  pdn = GlobalLock (hDevNames);
+  pdm = GlobalLock (hDevMode);
+  hDC = CreateDCW ((wchar_t*)pdn + pdn->wDriverOffset,
+                  (wchar_t*)pdn + pdn->wDeviceOffset,
+                  (wchar_t*)pdn + pdn->wOutputOffset,
+                  pdm );
+  GlobalUnlock (hDevNames);
+  GlobalUnlock (hDevMode);
+
+  if (!hDC)
+    {
+      result = GTK_PRINT_OPERATION_RESULT_ERROR;
+      g_set_error_literal (&priv->error,
+                          GTK_PRINT_ERROR,
+                          GTK_PRINT_ERROR_INTERNAL_ERROR,
+                          _("Invalid argument to CreateDC"));
+      goto out;
+    }
+  
+  priv->print_context = _gtk_print_context_new (op);
+  page_setup = create_page_setup (op);
+  _gtk_print_context_set_page_setup (priv->print_context, page_setup);
+  g_object_unref (page_setup);
+
+  *do_print = TRUE;
+
+  op_win32->surface = cairo_win32_printing_surface_create (hDC);
+  dpi_x = (double) GetDeviceCaps (hDC, LOGPIXELSX);
+  dpi_y = (double) GetDeviceCaps (hDC, LOGPIXELSY);
+
+  cr = cairo_create (op_win32->surface);
+  gtk_print_context_set_cairo_context (priv->print_context, cr, dpi_x, dpi_y);
+  cairo_destroy (cr);
+
+  set_hard_margins (op);
+
+  memset (&docinfo, 0, sizeof (DOCINFOW));
+  docinfo.cbSize = sizeof (DOCINFOW); 
+  docinfo.lpszDocName = g_utf8_to_utf16 (op->priv->job_name, -1, NULL, NULL, NULL); 
+  docinfo.lpszOutput = NULL; 
+  docinfo.lpszDatatype = NULL; 
+  docinfo.fwType = 0; 
+
+  job_id = StartDocW (hDC, &docinfo); 
+  g_free ((void *)docinfo.lpszDocName);
+  if (job_id <= 0)
+    { 
+      result = GTK_PRINT_OPERATION_RESULT_ERROR;
+      g_set_error_literal (&priv->error,
+                          GTK_PRINT_ERROR,
+                          GTK_PRINT_ERROR_GENERAL,
+                          _("Error from StartDoc"));
+      *do_print = FALSE;
+      cairo_surface_destroy (op_win32->surface);
+      op_win32->surface = NULL;
+      goto out; 
+    }
+
+  result = GTK_PRINT_OPERATION_RESULT_APPLY;
+  op_win32->hdc = hDC;
+  op_win32->devmode = hDevMode;
+  op_win32->devnames = hDevNames;
+  op_win32->job_id = job_id;
+  op->priv->print_pages = gtk_print_settings_get_print_pages (op->priv->print_settings);
+  op->priv->num_page_ranges = 0;
+  if (op->priv->print_pages == GTK_PRINT_PAGES_RANGES)
+    op->priv->page_ranges = gtk_print_settings_get_page_ranges (op->priv->print_settings,
+                                                               &op->priv->num_page_ranges);
+  op->priv->manual_num_copies = 1;
+  op->priv->manual_collation = FALSE;
+  op->priv->manual_reverse = FALSE;
+  op->priv->manual_orientation = FALSE;
+  op->priv->manual_scale = 1.0;
+  op->priv->manual_page_set = GTK_PAGE_SET_ALL;
+  op->priv->manual_number_up = 1;
+  op->priv->manual_number_up_layout = GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM;
+
+  op->priv->start_page = win32_start_page;
+  op->priv->end_page = win32_end_page;
+  op->priv->end_run = win32_end_run;
+  
+ out:
+  if (!*do_print && hDC != NULL)
+    DeleteDC (hDC);
+
+  if (!*do_print && hDevMode != NULL)
+    GlobalFree (hDevMode);
+
+  if (!*do_print && hDevNames != NULL)
+    GlobalFree (hDevNames);
+
+  return result;
+}
+
+GtkPrintOperationResult
+gtk_print_operation_run_with_dialog (GtkPrintOperation *op,
+                                    GtkWindow         *parent,
+                                    gboolean          *do_print)
 {
   HRESULT hResult;
   LPPRINTDLGEXW printdlgex = NULL;
@@ -1275,10 +1652,18 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
   GtkWidget *invisible = NULL;
   GtkPrintOperationResult result;
   GtkPrintOperationWin32 *op_win32;
+  GtkPrintOperationPrivate *priv;
   IPrintDialogCallback *callback;
+  HPROPSHEETPAGE prop_page;
   
   *do_print = FALSE;
 
+  priv = op->priv;
+  
+  op_win32 = g_new0 (GtkPrintOperationWin32, 1);
+  priv->platform_data = op_win32;
+  priv->free_platform_data = (GDestroyNotify) op_win32_free;
+  
   if (parent == NULL)
     {
       invisible = gtk_invisible_new ();
@@ -1291,14 +1676,14 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
   if (!printdlgex)
     {
       result = GTK_PRINT_OPERATION_RESULT_ERROR;
-      g_set_error (error,
-                  GTK_PRINT_ERROR,
-                  GTK_PRINT_ERROR_NOMEM,
-                  _("Not enough free memory"));
+      g_set_error_literal (&priv->error,
+                           GTK_PRINT_ERROR,
+                           GTK_PRINT_ERROR_NOMEM,
+                           _("Not enough free memory"));
       goto out;
     }      
 
-  printdlgex->lStructSize = sizeof(PRINTDLGEXW);
+  printdlgex->lStructSize = sizeof (PRINTDLGEXW);
   printdlgex->hwndOwner = parentHWnd;
   printdlgex->hDevMode = NULL;
   printdlgex->hDevNames = NULL;
@@ -1314,10 +1699,10 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
   if (!page_ranges) 
     {
       result = GTK_PRINT_OPERATION_RESULT_ERROR;
-      g_set_error (error,
-                  GTK_PRINT_ERROR,
-                  GTK_PRINT_ERROR_NOMEM,
-                  _("Not enough free memory"));
+      g_set_error_literal (&priv->error,
+                           GTK_PRINT_ERROR,
+                           GTK_PRINT_ERROR_NOMEM,
+                           _("Not enough free memory"));
       goto out;
     }
 
@@ -1333,8 +1718,18 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
   printdlgex->hInstance = 0;
   printdlgex->lpPrintTemplateName = NULL;
   printdlgex->lpCallback = NULL;
-  printdlgex->nPropertyPages = 0;
-  printdlgex->lphPropertyPages = NULL;
+
+  g_signal_emit_by_name (op, "create-custom-widget",
+                        &op->priv->custom_widget);
+  if (op->priv->custom_widget) {
+    prop_page = create_application_page (op);
+    printdlgex->nPropertyPages = 1;
+    printdlgex->lphPropertyPages = &prop_page;
+  } else {
+    printdlgex->nPropertyPages = 0;
+    printdlgex->lphPropertyPages = NULL;
+  }
+  
   printdlgex->nStartPage = START_PAGE_GENERAL;
   printdlgex->dwResultAction = 0;
 
@@ -1343,8 +1738,8 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
   callback = print_callback_new ();
   printdlgex->lpCallback = (IUnknown *)callback;
   got_gdk_events_message = RegisterWindowMessage ("GDK_WIN32_GOT_EVENTS");
-  
-  hResult = PrintDlgExW(printdlgex);
+
+  hResult = PrintDlgExW (printdlgex);
   IUnknown_Release ((IUnknown *)callback);
   gdk_win32_set_modal_dialog_libgtk_only (NULL);
 
@@ -1352,30 +1747,30 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
     {
       result = GTK_PRINT_OPERATION_RESULT_ERROR;
       if (hResult == E_OUTOFMEMORY)
-       g_set_error (error,
-                    GTK_PRINT_ERROR,
-                    GTK_PRINT_ERROR_NOMEM,
-                    _("Not enough free memory"));
+       g_set_error_literal (&priv->error,
+                             GTK_PRINT_ERROR,
+                             GTK_PRINT_ERROR_NOMEM,
+                             _("Not enough free memory"));
       else if (hResult == E_INVALIDARG)
-       g_set_error (error,
-                    GTK_PRINT_ERROR,
-                    GTK_PRINT_ERROR_INTERNAL_ERROR,
-                    _("Invalid argument to PrintDlgEx"));
+       g_set_error_literal (&priv->error,
+                             GTK_PRINT_ERROR,
+                             GTK_PRINT_ERROR_INTERNAL_ERROR,
+                             _("Invalid argument to PrintDlgEx"));
       else if (hResult == E_POINTER)
-       g_set_error (error,
-                    GTK_PRINT_ERROR,
-                    GTK_PRINT_ERROR_INTERNAL_ERROR,
-                    _("Invalid pointer to PrintDlgEx"));
+       g_set_error_literal (&priv->error,
+                             GTK_PRINT_ERROR,
+                             GTK_PRINT_ERROR_INTERNAL_ERROR,
+                             _("Invalid pointer to PrintDlgEx"));
       else if (hResult == E_HANDLE)
-       g_set_error (error,
-                    GTK_PRINT_ERROR,
-                    GTK_PRINT_ERROR_INTERNAL_ERROR,
-                    _("Invalid handle to PrintDlgEx"));
+       g_set_error_literal (&priv->error,
+                             GTK_PRINT_ERROR,
+                             GTK_PRINT_ERROR_INTERNAL_ERROR,
+                             _("Invalid handle to PrintDlgEx"));
       else /* E_FAIL */
-       g_set_error (error,
-                    GTK_PRINT_ERROR,
-                    GTK_PRINT_ERROR_GENERAL,
-                    _("Unspecified error"));
+       g_set_error_literal (&priv->error,
+                             GTK_PRINT_ERROR,
+                             GTK_PRINT_ERROR_GENERAL,
+                             _("Unspecified error"));
       goto out;
     }
 
@@ -1392,38 +1787,50 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
     {
       DOCINFOW docinfo;
       int job_id;
-
+      double dpi_x, dpi_y;
+      cairo_t *cr;
+      GtkPageSetup *page_setup;
+
+      priv->print_context = _gtk_print_context_new (op);
+      page_setup = create_page_setup (op);
+      _gtk_print_context_set_page_setup (priv->print_context, page_setup);
+      g_object_unref (page_setup);
+      
       *do_print = TRUE;
 
-      op->priv->surface = cairo_win32_surface_create (printdlgex->hDC);
-      op->priv->dpi_x = (double)GetDeviceCaps (printdlgex->hDC, LOGPIXELSX);
-      op->priv->dpi_y = (double)GetDeviceCaps (printdlgex->hDC, LOGPIXELSY);
+      op_win32->surface = cairo_win32_printing_surface_create (printdlgex->hDC);
+
+      dpi_x = (double)GetDeviceCaps (printdlgex->hDC, LOGPIXELSX);
+      dpi_y = (double)GetDeviceCaps (printdlgex->hDC, LOGPIXELSY);
+
+      cr = cairo_create (op_win32->surface);
+      gtk_print_context_set_cairo_context (priv->print_context, cr, dpi_x, dpi_y);
+      cairo_destroy (cr);
 
-      memset( &docinfo, 0, sizeof (DOCINFOW));
+      set_hard_margins (op);
+
+      memset ( &docinfo, 0, sizeof (DOCINFOW));
       docinfo.cbSize = sizeof (DOCINFOW); 
       docinfo.lpszDocName = g_utf8_to_utf16 (op->priv->job_name, -1, NULL, NULL, NULL); 
       docinfo.lpszOutput = (LPCWSTR) NULL; 
       docinfo.lpszDatatype = (LPCWSTR) NULL; 
       docinfo.fwType = 0; 
 
-      job_id = StartDocW(printdlgex->hDC, &docinfo); 
+      job_id = StartDocW (printdlgex->hDC, &docinfo); 
       g_free ((void *)docinfo.lpszDocName);
       if (job_id <= 0) 
-       { 
+       {
          result = GTK_PRINT_OPERATION_RESULT_ERROR;
-         g_set_error (error,
-                      GTK_PRINT_ERROR,
-                      GTK_PRINT_ERROR_GENERAL,
-                    _("Error from StartDoc"));
+         g_set_error_literal (&priv->error,
+                               GTK_PRINT_ERROR,
+                               GTK_PRINT_ERROR_GENERAL,
+                               _("Error from StartDoc"));
          *do_print = FALSE;
-         cairo_surface_destroy (op->priv->surface);
-         op->priv->surface = NULL;
+         cairo_surface_destroy (op_win32->surface);
+         op_win32->surface = NULL;
          goto out; 
        } 
       
-      op_win32 = g_new (GtkPrintOperationWin32, 1);
-      op->priv->platform_data = op_win32;
-      op->priv->free_platform_data = (GDestroyNotify) op_win32_free;
       op_win32->hdc = printdlgex->hDC;
       op_win32->devmode = printdlgex->hDevMode;
       op_win32->devnames = printdlgex->hDevNames;
@@ -1440,6 +1847,8 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
       op->priv->manual_orientation = FALSE;
       op->priv->manual_scale = 1.0;
       op->priv->manual_page_set = GTK_PAGE_SET_ALL;
+      op->priv->manual_number_up = 1;
+      op->priv->manual_number_up_layout = GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM;
     }
 
   op->priv->start_page = win32_start_page;
@@ -1447,11 +1856,14 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
   op->priv->end_run = win32_end_run;
   
   out:
+  if (!*do_print && printdlgex && printdlgex->hDC != NULL)
+    DeleteDC (printdlgex->hDC);
+
   if (!*do_print && printdlgex && printdlgex->hDevMode != NULL) 
-    GlobalFree(printdlgex->hDevMode); 
+    GlobalFree (printdlgex->hDevMode); 
 
   if (!*do_print && printdlgex && printdlgex->hDevNames != NULL) 
-    GlobalFree(printdlgex->hDevNames); 
+    GlobalFree (printdlgex->hDevNames); 
 
   if (page_ranges)
     GlobalFree (page_ranges);
@@ -1465,18 +1877,114 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
   return result;
 }
 
-void 
-_gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation          *op,
-                                                        GtkWindow                  *parent,
-                                                       GtkPrintOperationPrintFunc  print_cb)
+GtkPrintOperationResult
+_gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
+                                                 gboolean           show_dialog,
+                                                 GtkWindow         *parent,
+                                                 gboolean          *do_print)
 {
-  gboolean do_print;
-
-  _gtk_print_operation_platform_backend_run_dialog (op, parent, &do_print, NULL);
-  if (do_print)
-    print_cb (op);
+  if (show_dialog)
+    return gtk_print_operation_run_with_dialog (op, parent, do_print);
   else
-    _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED_ABORTED, NULL);
+    return gtk_print_operation_run_without_dialog (op, do_print);
+}
+
+void
+_gtk_print_operation_platform_backend_launch_preview (GtkPrintOperation *op,
+                                                     cairo_surface_t   *surface,
+                                                     GtkWindow         *parent,
+                                                     const gchar       *filename)
+{
+  HDC dc;
+  HENHMETAFILE metafile;
+  
+  dc = cairo_win32_surface_get_dc (surface);
+  cairo_surface_destroy (surface);
+  metafile = CloseEnhMetaFile (dc);
+  DeleteEnhMetaFile (metafile);
+  
+  ShellExecuteW (NULL, L"open", (gunichar2 *)filename, NULL, NULL, SW_SHOW);
+}
+
+void
+_gtk_print_operation_platform_backend_preview_start_page (GtkPrintOperation *op,
+                                                         cairo_surface_t *surface,
+                                                         cairo_t *cr)
+{
+  HDC dc = cairo_win32_surface_get_dc (surface);
+  StartPage (dc);
+}
+
+void
+_gtk_print_operation_platform_backend_preview_end_page (GtkPrintOperation *op,
+                                                       cairo_surface_t *surface,
+                                                       cairo_t *cr)
+{
+  HDC dc;
+
+  cairo_surface_show_page (surface);
+
+  /* TODO: Enhanced metafiles don't support multiple pages.
+   */
+  dc = cairo_win32_surface_get_dc (surface);
+  EndPage (dc);
+}
+
+cairo_surface_t *
+_gtk_print_operation_platform_backend_create_preview_surface (GtkPrintOperation *op,
+                                                             GtkPageSetup      *page_setup,
+                                                             gdouble           *dpi_x,
+                                                             gdouble           *dpi_y,
+                                                             gchar            **target)
+{
+  GtkPaperSize *paper_size;
+  HDC metafile_dc;
+  RECT rect;
+  char *template;
+  char *filename;
+  gunichar2 *filename_utf16;
+  int fd;
+
+  template = g_build_filename (g_get_tmp_dir (), "prXXXXXX", NULL);
+  fd = g_mkstemp (template);
+  close (fd);
+
+  filename = g_strconcat (template, ".emf", NULL);
+  g_free (template);
+  
+  filename_utf16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+  g_free (filename);
+
+  paper_size = gtk_page_setup_get_paper_size (page_setup);
+
+  /* The rectangle dimensions are given in hundredths of a millimeter */
+  rect.left = 0;
+  rect.right = 100.0 * gtk_paper_size_get_width (paper_size, GTK_UNIT_MM);
+  rect.top = 0;
+  rect.bottom = 100.0 * gtk_paper_size_get_height (paper_size, GTK_UNIT_MM);
+  
+  metafile_dc = CreateEnhMetaFileW (NULL, filename_utf16,
+                                   &rect, L"Gtk+\0Print Preview\0\0");
+  if (metafile_dc == NULL)
+    {
+      g_warning ("Can't create metafile");
+      return NULL;
+    }
+
+  *target = (char *)filename_utf16;
+  
+  *dpi_x = (double)GetDeviceCaps (metafile_dc, LOGPIXELSX);
+  *dpi_y = (double)GetDeviceCaps (metafile_dc, LOGPIXELSY);
+
+  return cairo_win32_printing_surface_create (metafile_dc);
+}
+
+void
+_gtk_print_operation_platform_backend_resize_preview_surface (GtkPrintOperation *op,
+                                                             GtkPageSetup      *page_setup,
+                                                             cairo_surface_t   *surface)
+{
+  /* TODO: Implement */
 }
 
 GtkPageSetup *
@@ -1506,7 +2014,7 @@ gtk_print_run_page_setup_dialog (GtkWindow        *parent,
   
   memset (pagesetupdlg, 0, sizeof (PAGESETUPDLGW));
 
-  pagesetupdlg->lStructSize = sizeof(PAGESETUPDLGW);
+  pagesetupdlg->lStructSize = sizeof (PAGESETUPDLGW);
 
   if (parent != NULL)
     pagesetupdlg->hwndOwner = get_parent_hwnd (GTK_WIDGET (parent));
@@ -1518,10 +2026,10 @@ gtk_print_run_page_setup_dialog (GtkWindow        *parent,
   pagesetupdlg->hDevNames = NULL;
   printer = gtk_print_settings_get_printer (settings);
   if (printer)
-    pagesetupdlg->hDevNames = gtk_print_win32_devnames_from_printer_name (printer);
+    pagesetupdlg->hDevNames = gtk_print_win32_devnames_to_win32_from_printer_name (printer);
 
-  GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IMEASURE|LOCALE_RETURN_NUMBER,
-                (LPWSTR)&measure_system, sizeof (DWORD));
+  GetLocaleInfoW (LOCALE_USER_DEFAULT, LOCALE_IMEASURE|LOCALE_RETURN_NUMBER,
+                 (LPWSTR)&measure_system, sizeof (DWORD));
 
   if (measure_system == 0)
     {
@@ -1569,9 +2077,6 @@ gtk_print_run_page_setup_dialog (GtkWindow        *parent,
        devmode_to_settings (settings, pagesetupdlg->hDevMode);
     }
   
-  if (free_settings)
-    g_object_unref (settings);
-
   if (res)
     {
       gtk_page_setup_set_orientation (page_setup, 
@@ -1608,6 +2113,9 @@ gtk_print_run_page_setup_dialog (GtkWindow        *parent,
                                        unit);
     }
   
+  if (free_settings)
+    g_object_unref (settings);
+
   return page_setup;
 }
 
@@ -1618,9 +2126,9 @@ gtk_print_run_page_setup_dialog_async (GtkWindow            *parent,
                                       GtkPageSetupDoneFunc  done_cb,
                                       gpointer              data)
 {
-  GtkPageSetup *page_setup;
+  GtkPageSetup *new_page_setup;
 
-  page_setup = gtk_print_run_page_setup_dialog (parent, page_setup, settings);
-  done_cb (page_setup, data);
-  g_object_unref (page_setup);
+  new_page_setup = gtk_print_run_page_setup_dialog (parent, page_setup, settings);
+  done_cb (new_page_setup, data);
+  g_object_unref (new_page_setup);
 }