twain: UI and a few more fixes

Aric Stewart aric at codeweavers.com
Tue Feb 28 08:09:40 CST 2006


-------------- next part --------------
Subject: [PATCH] Continued Twain work, This implements a first pass on a UI interface to the
scanner to allow the user to change settings before starting the scan as well as
giving a basic "Please Wait" dialog while scanning.

also adds handling for SANE_FRAME_GRAY and cleans up so initial global
initialization.

---

 dlls/twain/Makefile.in    |    8 
 dlls/twain/ds_ctrl.c      |   16 +
 dlls/twain/ds_image.c     |   24 +
 dlls/twain/dsm_ctrl.c     |    3 
 dlls/twain/rsrc.rc        |   43 ++
 dlls/twain/twain32_main.c |    2 
 dlls/twain/twain_En.rc    |   40 ++
 dlls/twain/twain_i.h      |   10 
 dlls/twain/ui.c           | 1068 +++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 1205 insertions(+), 9 deletions(-)
 create mode 100644 dlls/twain/rsrc.rc
 create mode 100644 dlls/twain/twain_En.rc
 create mode 100644 dlls/twain/ui.c

applies-to: f604c94a12e985cfafd0d98de33d68b7a2056eb5
cbf303ef55afd77f84d7fc0c76a11657cfef75cb
diff --git a/dlls/twain/Makefile.in b/dlls/twain/Makefile.in
index 6e26243..2c0e9c9 100644
--- a/dlls/twain/Makefile.in
+++ b/dlls/twain/Makefile.in
@@ -3,7 +3,7 @@ TOPOBJDIR = ../..
 SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = twain_32.dll
-IMPORTS   = user32 gdi32 kernel32 ntdll
+IMPORTS   = user32 gdi32 kernel32 ntdll comctl32
 EXTRALIBS = @SANELIBS@
 EXTRAINCL = @SANEINCL@
 
@@ -13,11 +13,15 @@ C_SRCS = \
 	ds_ctrl.c \
 	ds_image.c \
 	dsm_ctrl.c \
-	twain32_main.c
+	twain32_main.c \
+	ui.c
 
 C_SRCS16 = \
 	twain16_main.c
 
+RC_SRCS = \
+	rsrc.rc
+
 SPEC_SRCS16 = twain.spec
 
 @MAKE_DLL_RULES@
diff --git a/dlls/twain/ds_ctrl.c b/dlls/twain/ds_ctrl.c
index ff49c28..ed8f993 100644
--- a/dlls/twain/ds_ctrl.c
+++ b/dlls/twain/ds_ctrl.c
@@ -647,11 +647,19 @@ TW_UINT16 TWAIN_EnableDSUserInterface (p
     {
         if (pUserInterface->ShowUI)
         {
+            BOOL rc;
             pSource->currentState = 5; /* Transitions to state 5 */
-            /* FIXME: we should replace xscanimage with our own device UI */
-            system ("xscanimage");
-            pSource->currentState = 6;
-            pSource->pendingEvent.TWMessage = MSG_XFERREADY;
+            rc = DoScannerUI(pSource);
+            if (!rc)
+            {
+                pSource->pendingEvent.TWMessage = MSG_CLOSEDSREQ;
+            }
+            else
+            {
+                sane_get_parameters (pSource->deviceHandle, 
+                        &pSource->sane_param); 
+                pSource->sane_param_valid = TRUE;
+            }
         }
         else
         {
diff --git a/dlls/twain/ds_image.c b/dlls/twain/ds_image.c
index 07ebad8..408562c 100644
--- a/dlls/twain/ds_image.c
+++ b/dlls/twain/ds_image.c
@@ -120,7 +120,7 @@ TW_UINT16 TWAIN_ImageInfoGet (pTW_IDENTI
         TRACE("Bits per Sample %i\n",pSource->sane_param.depth);
         TRACE("Frame Format %i\n",pSource->sane_param.format);
 
-        if (pSource->sane_param.format == 1 /*RGB*/ )
+        if (pSource->sane_param.format == SANE_FRAME_RGB )
         {
             pImageInfo->BitsPerPixel = pSource->sane_param.depth * 3;
             pImageInfo->Compression = TWCP_NONE;
@@ -131,9 +131,18 @@ TW_UINT16 TWAIN_ImageInfoGet (pTW_IDENTI
             pImageInfo->BitsPerSample[2] = pSource->sane_param.depth;
             pImageInfo->PixelType = TWPT_RGB;
         }
+        else if (pSource->sane_param.format == SANE_FRAME_GRAY)
+        {
+            pImageInfo->BitsPerPixel = pSource->sane_param.depth;
+            pImageInfo->Compression = TWCP_NONE;
+            pImageInfo->Planar = TRUE;
+            pImageInfo->SamplesPerPixel = 1;
+            pImageInfo->BitsPerSample[0] = pSource->sane_param.depth;
+            pImageInfo->PixelType = TWPT_GRAY;
+        }
         else
         {
-            ERR("Unhandled source frame type\n");
+            ERR("Unhandled source frame type %i \n",pSource->sane_param.format);
             twRC = TWRC_FAILURE;
             pSource->twCC = TWCC_SEQERROR;
         }
@@ -214,6 +223,12 @@ TW_UINT16 TWAIN_ImageMemXferGet (pTW_IDE
         /* Transfer an image from the source to the application */
         if (pSource->currentState == 6)
         {
+
+            /* trigger scanning dialog */
+            pSource->progressWnd = ScanningDialogBox(NULL,0);
+
+            ScanningDialogBox(pSource->progressWnd,0);
+
             status = sane_start (pSource->deviceHandle);
             if (status != SANE_STATUS_GOOD)
             {
@@ -241,6 +256,7 @@ TW_UINT16 TWAIN_ImageMemXferGet (pTW_IDE
               pSource->sane_param.last_frame);
 
             pSource->currentState = 7;
+
         }
 
         /* access memory buffer */
@@ -286,8 +302,11 @@ TW_UINT16 TWAIN_ImageMemXferGet (pTW_IDE
             pImageMemXfer->YOffset = 0;
             pImageMemXfer->BytesWritten = consumed_len;
 
+            ScanningDialogBox(pSource->progressWnd, consumed_len);
+
             if (status == SANE_STATUS_EOF)
             {
+                ScanningDialogBox(pSource->progressWnd, -1);
                 TRACE("sane_read: %s\n", sane_strstatus (status));
                 sane_cancel (pSource->deviceHandle);
                 twRC = TWRC_XFERDONE;
@@ -296,6 +315,7 @@ TW_UINT16 TWAIN_ImageMemXferGet (pTW_IDE
         }
         else if (status != SANE_STATUS_EOF)
         {
+            ScanningDialogBox(pSource->progressWnd, -1);
             WARN("sane_read: %s\n", sane_strstatus (status));
             sane_cancel (pSource->deviceHandle);
             pSource->twCC = TWCC_OPERATIONERROR;
diff --git a/dlls/twain/dsm_ctrl.c b/dlls/twain/dsm_ctrl.c
index 002a3bf..366bcc8 100644
--- a/dlls/twain/dsm_ctrl.c
+++ b/dlls/twain/dsm_ctrl.c
@@ -34,6 +34,8 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(twain);
 
+const SANE_Device **device_list; 
+
 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
 TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
 {
@@ -304,6 +306,7 @@ TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOr
         newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
         if (newSource)
         {
+            newSource->deviceIndex = i;
             status = sane_open(device_list[i]->name,&newSource->deviceHandle);
             if (status == SANE_STATUS_GOOD)
             {
diff --git a/dlls/twain/rsrc.rc b/dlls/twain/rsrc.rc
new file mode 100644
index 0000000..2e5f14e
--- /dev/null
+++ b/dlls/twain/rsrc.rc
@@ -0,0 +1,43 @@
+/*
+ * Top level resource file for Twain
+ *
+ * Copyright 2006 CodeWeavers, Aric Stewart
+ *
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winnls.h"
+
+#include "resource.h"
+
+/*
+ * Everything that does not depend on language,
+ * like textless bitmaps etc, go into the
+ * neutral language. This will prevent them from
+ * being duplicated for each language.
+ */
+/* #include "wininet_xx.rc" */
+
+/*
+ * Everything specific to any language goes
+ * in one of the specific files.
+ * Note that you can and may override resources
+ * which also have a neutral version. This is to
+ * get localized bitmaps for example.
+ */
+#include "twain_En.rc"
diff --git a/dlls/twain/twain32_main.c b/dlls/twain/twain32_main.c
index e34588c..d64ab8b 100644
--- a/dlls/twain/twain32_main.c
+++ b/dlls/twain/twain32_main.c
@@ -39,6 +39,8 @@ BOOL WINAPI DllMain (HINSTANCE hinstDLL,
         case DLL_PROCESS_ATTACH:
             DisableThreadLibraryCalls(hinstDLL);
             DSM_currentState = 2;
+            DSM_initialized = FALSE;
+            DSM_instance = hinstDLL;
             break;
 
         case DLL_PROCESS_DETACH:
diff --git a/dlls/twain/twain_En.rc b/dlls/twain/twain_En.rc
new file mode 100644
index 0000000..bf8c183
--- /dev/null
+++ b/dlls/twain/twain_En.rc
@@ -0,0 +1,40 @@
+/*
+ * English resources for Twain
+ *
+ * Copyright 2006 CodeWeavers, Aric Stewart
+ *
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+
+IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 186, 46
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE | DS_CENTER | DS_SETFOREGROUND
+CAPTION "Scanning"
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT   "SCANNING.... Please Wait",IDC_STATIC,53,19,85,8
+END
+
+STRINGTABLE DISCARDABLE
+{
+ 0 ""
+ 1 "px"
+ 2 "b"
+ 3 "mm"
+ 4 "dpi"
+ 5 "%"
+ 6 "ns"
+}
diff --git a/dlls/twain/twain_i.h b/dlls/twain/twain_i.h
index 6d98763..7a9ba24 100644
--- a/dlls/twain/twain_i.h
+++ b/dlls/twain/twain_i.h
@@ -42,11 +42,13 @@ typedef struct tagActiveDS
                                                    application */
     TW_UINT16		twCC;			/* condition code */
     HWND		hwndOwner;		/* window handle of the app */
+    HWND        progressWnd;    /* window handle of the scanning window */
 #ifdef HAVE_SANE
     SANE_Handle		deviceHandle;		/* device handle */
     SANE_Parameters     sane_param;             /* parameters about the image
                                                    transferred */
     BOOL                sane_param_valid;  /* true if valid sane_param*/
+    INT                 deviceIndex;    /* index of the current device */
 #endif
     /* Capabiblities */
     TW_UINT16		capXferMech;		/* ICAP_XFERMECH */
@@ -58,8 +60,10 @@ TW_UINT16 DSM_twCC;             /* curre
 TW_HANDLE DSM_parentHWND;       /* window handle of the Source's "parent" */
 TW_UINT32 DSM_sourceId;         /* source id generator */
 TW_UINT16 DSM_currentDevice;    /* keep track of device during enumeration */
+HINSTANCE DSM_instance;
+
 #ifdef HAVE_SANE
-const SANE_Device **device_list;/* a list of all sane devices */
+extern const SANE_Device **device_list;/* a list of all sane devices */
 #endif
 activeDS *activeSources;	/* list of active data sources */
 
@@ -244,4 +248,8 @@ TW_UINT16 TWAIN_AudioNativeXferGet
 TW_UINT16 TWAIN_ICAPXferMech
     (activeDS *pSource, pTW_CAPABILITY pCapability, TW_UINT16 action);
 
+
+/* UI function */
+BOOL DoScannerUI(activeDS *pSource);
+HWND ScanningDialogBox(HWND dialog, DWORD progress);
 #endif
diff --git a/dlls/twain/ui.c b/dlls/twain/ui.c
new file mode 100644
index 0000000..8a8c659
--- /dev/null
+++ b/dlls/twain/ui.c
@@ -0,0 +1,1068 @@
+/*
+* TWAIN32 Options UI
+*
+* Copyright 2006 CodeWeavers, Aric Stewart
+*
+* 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.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "wingdi.h"
+#include "prsht.h"
+#include "twain.h"
+#include "twain_i.h"
+#include "wine/debug.h"
+#include "resource.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(twain);
+
+#define ID_BASE 0x100
+#define ID_EDIT_BASE 0x1000
+#define ID_STATIC_BASE 0x2000
+
+
+INT_PTR CALLBACK DialogProc (HWND , UINT , WPARAM , LPARAM );
+INT CALLBACK PropSheetProc(HWND, UINT,LPARAM);
+
+int create_leading_static(HDC hdc, LPCSTR text, 
+        LPDLGITEMTEMPLATEW* template_out, int y, int id)
+{
+    LPDLGITEMTEMPLATEW tpl =  NULL;
+    INT len;
+    SIZE size;
+    LPBYTE ptr;
+    LONG base;
+
+    *template_out = NULL;
+
+    if (!text)
+        return 0;
+
+    base = GetDialogBaseUnits();
+
+    len = MultiByteToWideChar(CP_ACP,0,text,-1,NULL,0);
+    len *= sizeof(WCHAR);
+    len += sizeof(DLGITEMTEMPLATE);
+    len += 3*sizeof(WORD);
+
+    tpl = HeapAlloc(GetProcessHeap(),0,len);
+    tpl->style=WS_VISIBLE;
+    tpl->dwExtendedStyle = 0;
+    tpl->x = 4;
+    tpl->y = y;
+    tpl->id = ID_BASE;
+
+    GetTextExtentPoint32A(hdc,text,lstrlenA(text),&size);
+
+    tpl->cx =  MulDiv(size.cx,4,LOWORD(base));
+    tpl->cy =  MulDiv(size.cy,8,HIWORD(base)) * 2;
+    ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE);
+    *(LPWORD)ptr = 0xffff;
+    ptr += sizeof(WORD);
+    *(LPWORD)ptr = 0x0082;
+    ptr += sizeof(WORD);
+    ptr += MultiByteToWideChar(CP_ACP,0,text,-1,(LPWSTR)ptr,len) * sizeof(WCHAR);
+    *(LPWORD)ptr = 0x0000;
+
+    *template_out = tpl;
+    return len;
+}
+
+int create_trailing_edit(HDC hdc, LPDLGITEMTEMPLATEW* template_out, int id, 
+        int y, LPCSTR text,BOOL is_int)
+{
+    LPDLGITEMTEMPLATEW tpl =  NULL;
+    INT len;
+    LPBYTE ptr;
+    SIZE size;
+    LONG base;
+    static const char int_base[] = "0000 xxx";
+    static const char float_base[] = "0000.0000 xxx";
+
+    base = GetDialogBaseUnits();
+
+    len = MultiByteToWideChar(CP_ACP,0,text,-1,NULL,0);
+    len *= sizeof(WCHAR);
+    len += sizeof(DLGITEMTEMPLATE);
+    len += 3*sizeof(WORD);
+
+    tpl = HeapAlloc(GetProcessHeap(),0,len);
+    tpl->style=WS_VISIBLE|ES_READONLY|WS_BORDER;
+    tpl->dwExtendedStyle = 0;
+    tpl->x = 1;
+    tpl->y = y;
+    tpl->id = id;
+
+    if (is_int)
+        GetTextExtentPoint32A(hdc,int_base,lstrlenA(int_base),&size);
+    else
+        GetTextExtentPoint32A(hdc,float_base,lstrlenA(float_base),&size);
+
+    tpl->cx =  MulDiv(size.cx*2,4,LOWORD(base));
+    tpl->cy =  MulDiv(size.cy,8,HIWORD(base)) * 2;
+
+    ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE);
+    *(LPWORD)ptr = 0xffff;
+    ptr += sizeof(WORD);
+    *(LPWORD)ptr = 0x0081;
+    ptr += sizeof(WORD);
+    ptr += MultiByteToWideChar(CP_ACP,0,text,-1,(LPWSTR)ptr,len) * sizeof(WCHAR);
+    *(LPWORD)ptr = 0x0000;
+
+    *template_out = tpl;
+    return len;
+}
+
+
+int create_item(activeDS *pSource,HDC hdc, const SANE_Option_Descriptor *opt,
+        INT id, LPDLGITEMTEMPLATEW *template_out, int y, int *cx, int* count)
+{
+    LPDLGITEMTEMPLATEW tpl = NULL,rc = NULL;
+    WORD class = 0xffff;
+    DWORD styles = WS_VISIBLE;
+    LPBYTE ptr = NULL;
+    LPDLGITEMTEMPLATEW lead_static = NULL;
+    LPDLGITEMTEMPLATEW trail_edit = NULL;
+    DWORD leading_len = 0;
+    DWORD trail_len = 0;
+    DWORD local_len = 0;
+    LPCSTR title = NULL;
+    CHAR buffer[255];
+    int padding = 0;
+    int padding2 = 0;
+    int base_x = 0;
+    LONG base;
+    int ctl_cx = 0;
+    SIZE size;
+
+    GetTextExtentPoint32A(hdc,"X",1,&size);
+    base = GetDialogBaseUnits();
+    base_x = MulDiv(size.cx,4,LOWORD(base));
+
+    if (opt->type == SANE_TYPE_BOOL)
+    {
+        class = 0x0080; /* Button */
+        styles |= BS_AUTOCHECKBOX;
+        local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
+        local_len *= sizeof(WCHAR);
+        title = opt->title;
+    }
+    else if (opt->type == SANE_TYPE_INT)
+    {
+        SANE_Int i;
+
+        sane_control_option(pSource->deviceHandle, id-ID_BASE,
+                SANE_ACTION_GET_VALUE, &i,NULL);
+
+        sprintf(buffer,"%i",i);
+
+        if (opt->constraint_type == SANE_CONSTRAINT_NONE)
+        {
+            class = 0x0081; /* Edit*/
+            styles |= ES_NUMBER;
+            title = buffer;
+            local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0);
+            local_len *= sizeof(WCHAR);
+        }
+        else if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
+        {
+            class = 0x0084; /* scroll */
+            ctl_cx = 10 * base_x;
+            trail_len += create_trailing_edit(hdc, &trail_edit, id +
+                    ID_EDIT_BASE, y,buffer,TRUE);
+        }
+        else
+        {
+            class= 0x0085; /* Combo */
+            ctl_cx = 10 * base_x;
+            styles |= CBS_DROPDOWNLIST;
+        }
+        leading_len += create_leading_static(hdc, opt->title, &lead_static, y, 
+                id+ID_STATIC_BASE);
+    }
+    else if (opt->type == SANE_TYPE_FIXED)
+    {
+        SANE_Fixed *i;
+        double dd;
+
+        i = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
+
+        sane_control_option(pSource->deviceHandle, id-ID_BASE,
+                SANE_ACTION_GET_VALUE, i, NULL);
+
+        dd = SANE_UNFIX(*i);
+        sprintf(buffer,"%f",dd);
+        HeapFree(GetProcessHeap(),0,i);
+
+        if (opt->constraint_type == SANE_CONSTRAINT_NONE)
+        {
+            class = 0x0081; /* Edit */
+            title = buffer;
+            local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0);
+            local_len *= sizeof(WCHAR);
+        }
+        else if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
+        {
+            class= 0x0084; /* scroll */
+            ctl_cx = 10 * base_x;
+            trail_len += create_trailing_edit(hdc, &trail_edit, id +
+                    ID_EDIT_BASE, y,buffer,FALSE);
+        }
+        else
+        {
+            class= 0x0085; /* Combo */
+            ctl_cx = 10 * base_x;
+            styles |= CBS_DROPDOWNLIST;
+        }
+        leading_len += create_leading_static(hdc, opt->title, &lead_static, y,
+                id+ID_STATIC_BASE);
+    }
+    else if (opt->type == SANE_TYPE_STRING)
+    {
+        if (opt->constraint_type == SANE_CONSTRAINT_NONE)
+        {
+            class = 0x0081; /* Edit*/
+        }
+        else
+        {
+            class= 0x0085; /* Combo */
+            ctl_cx = opt->size * base_x;
+            styles |= CBS_DROPDOWNLIST;
+        }
+        leading_len += create_leading_static(hdc, opt->title, &lead_static, y,
+                id+ID_STATIC_BASE);
+        sane_control_option(pSource->deviceHandle, id-ID_BASE,
+                SANE_ACTION_GET_VALUE, buffer,NULL);
+
+        title = buffer;
+        local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0);
+        local_len *= sizeof(WCHAR);
+    }
+    else if (opt->type == SANE_TYPE_BUTTON)
+    {
+        class = 0x0080; /* Button */
+        local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
+        local_len *= sizeof(WCHAR);
+        title = opt->title;
+    }
+    else if (opt->type == SANE_TYPE_GROUP)
+    {
+        class = 0x0080; /* Button */
+        styles |= BS_GROUPBOX;
+        local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
+        local_len *= sizeof(WCHAR);
+        title = opt->title;
+    }
+
+    local_len += sizeof(DLGITEMTEMPLATE);
+    if (title)
+        local_len += 3*sizeof(WORD);
+    else
+        local_len += 4*sizeof(WORD);
+
+    if (lead_static)
+    {
+        padding = leading_len % sizeof(DWORD);
+        rc = HeapReAlloc(GetProcessHeap(),0,lead_static,leading_len+local_len + padding);
+        tpl = (LPDLGITEMTEMPLATEW)((LPBYTE)rc + leading_len + padding);
+    }   
+    else
+        rc = tpl = HeapAlloc(GetProcessHeap(),0,local_len);
+
+    tpl->style=styles;
+    tpl->dwExtendedStyle = 0;
+    if (lead_static)
+        tpl->x = lead_static->x + lead_static->cx + 1;
+    else if (opt->type == SANE_TYPE_GROUP)
+        tpl->x = 2;
+    else
+        tpl->x = 4;
+    tpl->y = y;
+    tpl->id = id;
+
+    if (title)
+    {
+        GetTextExtentPoint32A(hdc,title,lstrlenA(title),&size);
+        tpl->cx = size.cx;
+        tpl->cy = size.cy;
+    }
+    else
+    {
+        if (lead_static)
+            tpl->cy = lead_static->cy;
+        else
+            tpl->cy = 15;
+
+        if (!ctl_cx)
+            ctl_cx = 15;
+
+        tpl->cx = ctl_cx;
+    }
+    ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE);
+    *(LPWORD)ptr = 0xffff;
+    ptr += sizeof(WORD);
+    *(LPWORD)ptr = class;
+    ptr += sizeof(WORD);
+    if (title)
+    {
+        ptr += MultiByteToWideChar(CP_ACP,0,title,-1,(LPWSTR)ptr,local_len) * sizeof(WCHAR);
+    }
+    else
+    {
+        *(LPWORD)ptr = 0x0000;
+        ptr += sizeof(WORD);
+    }
+
+    *((LPWORD)ptr) = 0x0000;
+    ptr += sizeof(WORD);
+
+    if (trail_edit)
+    {
+        trail_edit->x = tpl->cx + tpl->x + 2;
+        *cx = trail_edit->x + trail_edit->cx;
+
+        padding2 = (leading_len + local_len + padding)% sizeof(DWORD);
+
+        rc = HeapReAlloc(GetProcessHeap(),0,rc,leading_len+local_len + padding
+                +padding2+trail_len);
+
+        memcpy(((LPBYTE)rc) + leading_len + local_len + padding + padding2,
+                trail_edit,trail_len);
+    }   
+    else
+        *cx = tpl->cx + tpl->x;
+    
+    *template_out = rc;
+    if (leading_len)
+        *count = 2;
+    else
+        *count = 1;
+
+    if (trail_edit)
+        *count+=1;
+
+    return leading_len + local_len + padding + padding2 + trail_len;
+}
+
+LPDLGTEMPLATEW create_options_page(HDC hdc, activeDS *pSource, int *from_index,
+        BOOL split_tabs)
+{
+#ifndef HAVE_SANE
+    return NULL;
+#else
+    SANE_Status rc;
+    SANE_Int optcount;
+    int i;
+    INT y = 2;
+    LPDLGTEMPLATEW tpl = NULL;
+    LPBYTE all_controls = NULL;
+    DWORD control_len = 0;
+    int max_cx = 0;
+    int group_max_cx = 0;
+    LPBYTE ptr;
+    int group_offset = -1;
+    INT control_count = 0;
+
+    rc = sane_control_option(pSource->deviceHandle, 0, SANE_ACTION_GET_VALUE, 
+            &optcount, NULL);
+
+    if (rc != SANE_STATUS_GOOD)
+    {
+        ERR("Unable to read number of options\n");
+        return NULL;
+    }
+
+    for (i = *from_index; i < optcount; i++)
+    {
+        LPDLGITEMTEMPLATEW item_tpl = NULL;
+        const SANE_Option_Descriptor *opt;
+        int len;
+        int padding = 0;
+        int x;
+        int count;
+        int hold_for_group = 0;
+
+        opt = sane_get_option_descriptor(pSource->deviceHandle, i);
+        if (opt->type == SANE_TYPE_GROUP && split_tabs)
+        {
+            if (control_len > 0)
+            {
+                *from_index = i - 1;
+                goto exit;
+            }
+            else
+            {
+                *from_index = i;
+                return NULL;
+            }
+        }
+
+        len = create_item(pSource, hdc, opt, ID_BASE + i, &item_tpl, y, &x,
+                &count);
+
+        control_count += count;
+
+        if (!len)
+        {
+            continue;
+        }
+
+        hold_for_group = y;
+        y+= item_tpl->cy + 1;
+        max_cx = max(max_cx, x + 2);
+        group_max_cx = max(group_max_cx, x );
+
+        padding = len % sizeof(DWORD);
+
+        if (all_controls)
+        {
+            LPBYTE newone;
+            newone = HeapReAlloc(GetProcessHeap(),0,all_controls,
+                    control_len + len + padding);
+            all_controls = newone;
+            memcpy(all_controls+control_len,item_tpl,len);
+            memset(all_controls+control_len+len,0xca,padding);
+            HeapFree(GetProcessHeap(),0,item_tpl);
+        }
+        else
+        {
+            if (!padding)
+            {
+                all_controls = (LPBYTE)item_tpl;
+            }
+            else
+            {
+                all_controls = HeapAlloc(GetProcessHeap(),0,len + padding);
+                memcpy(all_controls,item_tpl,len);
+                memset(all_controls+len,0xcb,padding);
+                HeapFree(GetProcessHeap(),0,item_tpl);
+            }
+        }
+
+        if (opt->type == SANE_TYPE_GROUP)
+        {
+            if (group_offset == -1)
+            {
+                group_offset  = control_len;
+                group_max_cx = 0;
+            }
+            else
+            {
+                LPDLGITEMTEMPLATEW group =
+                    (LPDLGITEMTEMPLATEW)(all_controls+group_offset);
+
+                group->cy = hold_for_group - group->y;
+                group->cx = group_max_cx;
+
+                group = (LPDLGITEMTEMPLATEW)(all_controls+control_len);
+                group->y += 2;
+                y+=2;
+                group_max_cx = 0;
+                group_offset  = control_len;
+            }
+        }
+
+        control_len += len + padding;
+    }
+
+    if ( group_offset && !split_tabs )
+    {
+        LPDLGITEMTEMPLATEW group =
+            (LPDLGITEMTEMPLATEW)(all_controls+group_offset);
+        group->cy = y - group->y;
+        group->cx = group_max_cx;
+        y+=2;
+    }
+
+    *from_index = i-1;
+exit:
+
+    tpl = HeapAlloc(GetProcessHeap(),0,sizeof(DLGTEMPLATE) + 3*sizeof(WORD) + 
+            control_len);
+
+    tpl->style = WS_VISIBLE | WS_OVERLAPPEDWINDOW;
+    tpl->dwExtendedStyle = 0;
+    tpl->cdit = control_count;
+    tpl->x = 0;
+    tpl->y = 0;
+    tpl->cx = max_cx + 10;
+    tpl->cy = y + 10;
+    ptr = (LPBYTE)tpl + sizeof(DLGTEMPLATE);
+    *(LPWORD)ptr = 0x0000;
+    ptr+=sizeof(WORD);
+    *(LPWORD)ptr = 0x0000;
+    ptr+=sizeof(WORD);
+    *(LPWORD)ptr = 0x0000;
+    ptr+=sizeof(WORD);
+    memcpy(ptr,all_controls,control_len);
+
+    HeapFree(GetProcessHeap(),0,all_controls);
+
+    return tpl;
+#endif
+}
+
+
+BOOL DoScannerUI(activeDS *pSource)
+{
+    HDC hdc;
+    PROPSHEETPAGEW psp[10];
+    int page_count= 0;
+    PROPSHEETHEADERW psh;
+    int index = 1;
+    SANE_Status rc;
+    SANE_Int optcount;
+    UINT psrc;
+    LPWSTR szCaption;
+    DWORD len;
+
+    hdc = GetDC(0);
+
+    memset(&psp,0,sizeof(psp));
+    rc = sane_control_option(pSource->deviceHandle, 0, SANE_ACTION_GET_VALUE, 
+            &optcount, NULL);
+
+    while (index < optcount)
+    {
+        const SANE_Option_Descriptor *opt;
+        psp[page_count].u.pResource = create_options_page(hdc, pSource, &index,
+                TRUE);
+        opt = sane_get_option_descriptor(pSource->deviceHandle, index);
+
+        if (opt->type == SANE_TYPE_GROUP)
+        {
+            LPWSTR title = NULL;
+            INT len;
+
+            len = MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
+            title = HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR));
+            MultiByteToWideChar(CP_ACP,0,opt->title,-1,title,len);
+
+            psp[page_count].pszTitle = title;
+        }
+
+        if (psp[page_count].u.pResource)
+        {
+            psp[page_count].dwSize = sizeof(PROPSHEETPAGEW);
+            psp[page_count].dwFlags =  PSP_DLGINDIRECT | PSP_USETITLE;
+            psp[page_count].hInstance = DSM_instance;
+            psp[page_count].pfnDlgProc = DialogProc;
+            psp[page_count].lParam = (LPARAM)pSource;
+            page_count ++;
+        }
+       
+        index ++;
+    }
+ 
+    len = lstrlenA(device_list[pSource->deviceIndex]->vendor)
+         + lstrlenA(device_list[pSource->deviceIndex]->model) + 2;
+
+    szCaption = HeapAlloc(GetProcessHeap(),0,len *sizeof(WCHAR));
+    MultiByteToWideChar(CP_ACP,0,device_list[pSource->deviceIndex]->vendor,-1,
+            szCaption,len);
+    szCaption[lstrlenA(device_list[pSource->deviceIndex]->vendor)] = ' ';
+    MultiByteToWideChar(CP_ACP,0,device_list[pSource->deviceIndex]->model,-1,
+            &szCaption[lstrlenA(device_list[pSource->deviceIndex]->vendor)+1],len);
+    
+    psh.dwSize = sizeof(PROPSHEETHEADERW);
+    psh.dwFlags = PSH_PROPSHEETPAGE|PSH_PROPTITLE|PSH_USECALLBACK;
+    psh.hwndParent = DSM_parentHWND;
+    psh.hInstance = DSM_instance;
+    psh.u.pszIcon = 0;
+    psh.pszCaption = szCaption;
+    psh.nPages = page_count;
+    psh.u2.nStartPage = 0;
+    psh.u3.ppsp = (LPCPROPSHEETPAGEW) &psp;
+    psh.pfnCallback = PropSheetProc;
+
+    psrc = PropertySheetW(&psh);
+
+    for(index = 0; index < page_count; index ++)
+    {
+        HeapFree(GetProcessHeap(),0,(LPBYTE)psp[index].u.pResource);
+        HeapFree(GetProcessHeap(),0,(LPBYTE)psp[index].pszTitle);
+    }
+    
+    if (psrc == IDOK)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+static void UpdateReleventEdit(HWND hwnd, const SANE_Option_Descriptor *opt, 
+        int index, int position)
+{
+    CHAR buffer[244];
+    HWND edit_w;
+    CHAR unit[20];
+
+    LoadStringA(DSM_instance, opt->unit, unit,20);
+
+    if (opt->type == SANE_TYPE_INT)
+    {
+        INT si;
+
+        if (opt->constraint.range->quant)
+            si = position * opt->constraint.range->quant;
+        else
+            si = position;
+
+        sprintf(buffer,"%i %s",si,unit);
+
+    }
+    else if  (opt->type == SANE_TYPE_FIXED)
+    {
+        double s_quant, dd;
+
+        s_quant = SANE_UNFIX(opt->constraint.range->quant);
+
+        if (s_quant)
+            dd = position * s_quant;
+        else
+            dd = position * 0.01;
+
+        sprintf(buffer,"%f %s",dd,unit);
+    }
+    else
+        buffer[0] = 0;
+
+    edit_w = GetDlgItem(hwnd,index+ID_BASE+ID_EDIT_BASE);
+    if (edit_w && buffer[0])
+        SetWindowTextA(edit_w,buffer);
+
+}
+
+static BOOL UpdateSaneScrollOption(activeDS *pSource, 
+        const SANE_Option_Descriptor *opt, int index, DWORD position)
+{
+    SANE_Status rc = SANE_STATUS_GOOD;  
+    SANE_Int result = 0;
+
+    if (opt->type == SANE_TYPE_INT)
+    {
+        SANE_Int si;
+
+        if (opt->constraint.range->quant)
+            si = position * opt->constraint.range->quant;
+        else
+            si = position;
+
+        rc = sane_control_option (pSource->deviceHandle,index,
+            SANE_ACTION_SET_VALUE, &si, &result);
+
+    }
+    else if  (opt->type == SANE_TYPE_FIXED)
+    {
+        double s_quant, dd;
+        SANE_Fixed *sf;
+
+        s_quant = SANE_UNFIX(opt->constraint.range->quant);
+
+        if (s_quant)
+            dd = position * s_quant;
+        else
+            dd = position * 0.01;
+
+        sf = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
+
+        *sf = SANE_FIX(dd);
+
+        rc = sane_control_option (pSource->deviceHandle,index,
+            SANE_ACTION_SET_VALUE, sf, &result);
+
+        HeapFree(GetProcessHeap(),0,sf);
+    }
+
+    if(rc == SANE_STATUS_GOOD)
+    {
+        if (result & SANE_INFO_RELOAD_OPTIONS || 
+                result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
+            return TRUE;
+    }
+    return FALSE;
+}
+
+static BOOL UpdateSaneBoolOption(activeDS *pSource, int index, BOOL position)
+{
+    SANE_Status rc = SANE_STATUS_GOOD;  
+    SANE_Int result = 0;
+    SANE_Bool si;
+
+    si = position;
+
+    rc = sane_control_option (pSource->deviceHandle,index,
+            SANE_ACTION_SET_VALUE, &si, &result);
+
+    if(rc == SANE_STATUS_GOOD)
+    {
+        if (result & SANE_INFO_RELOAD_OPTIONS || 
+                result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
+            return TRUE;
+    }
+    return FALSE;
+}
+
+static BOOL UpdateSaneStringOption(activeDS *pSource, int index, 
+        SANE_String value)
+{
+    SANE_Status rc = SANE_STATUS_GOOD;  
+    SANE_Int result = 0;
+
+    rc = sane_control_option (pSource->deviceHandle,index,
+            SANE_ACTION_SET_VALUE, value, &result);
+
+    if(rc == SANE_STATUS_GOOD)
+    {
+        if (result & SANE_INFO_RELOAD_OPTIONS || 
+                result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
+            return TRUE;
+    }
+    return FALSE;
+}
+
+static INT_PTR InitializeDialog(HWND hwnd, activeDS *pSource)
+{
+    SANE_Status rc;
+    SANE_Int optcount;
+    HWND control;
+    int i;
+
+    rc = sane_control_option(pSource->deviceHandle, 0, SANE_ACTION_GET_VALUE, 
+            &optcount, NULL);
+
+    for ( i = 1; i < optcount; i++)
+    {
+        const SANE_Option_Descriptor *opt;
+
+        control = GetDlgItem(hwnd,i+ID_BASE);
+
+        if (!control)
+            continue;
+
+        opt = sane_get_option_descriptor(pSource->deviceHandle, i);
+
+        TRACE("%i %s %i %i\n",i,opt->title,opt->type,opt->constraint_type);
+        
+        if (!SANE_OPTION_IS_ACTIVE(opt->cap))
+            EnableWindow(control,FALSE);
+        else
+            EnableWindow(control,TRUE);
+
+        SendMessageA(control,CB_RESETCONTENT,0,0);
+        /* initialize values */
+        if (opt->type == SANE_TYPE_STRING && opt->constraint_type !=
+                SANE_CONSTRAINT_NONE)
+        {
+            int j = 0;
+            CHAR buffer[255];
+            while (opt->constraint.string_list[j]!=NULL)
+            {
+                SendMessageA(control,CB_ADDSTRING,0,
+                        (LPARAM)opt->constraint.string_list[j]);
+                j++;
+            }
+            sane_control_option(pSource->deviceHandle, i, SANE_ACTION_GET_VALUE, buffer,NULL);
+            SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer);
+        }
+        else if (opt->type == SANE_TYPE_BOOL)
+        {
+            SANE_Bool b;
+            sane_control_option(pSource->deviceHandle, i,
+                    SANE_ACTION_GET_VALUE, &b, NULL);
+            if (b)
+                SendMessageA(control,BM_SETCHECK,BST_CHECKED,0);
+
+        }
+        else if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
+        {
+            if (opt->type == SANE_TYPE_INT)
+            {
+                SANE_Int si;
+                int min,max;
+
+                min = (SANE_Int)opt->constraint.range->min /
+                    (((SANE_Int)opt->constraint.range->quant)?
+                    (SANE_Int)opt->constraint.range->quant:1);
+
+                max = (SANE_Int)opt->constraint.range->max /
+                    (((SANE_Int)opt->constraint.range->quant)
+                    ?(SANE_Int)opt->constraint.range->quant:1);
+
+                SendMessageA(control,SBM_SETRANGE,min,max);
+
+                sane_control_option(pSource->deviceHandle, i,
+                        SANE_ACTION_GET_VALUE, &si,NULL);
+                if (opt->constraint.range->quant)
+                    si = si / opt->constraint.range->quant;
+
+                SendMessageW(control,SBM_SETPOS, si, TRUE);
+                UpdateReleventEdit(hwnd, opt, i, si);
+            }
+            else if (opt->type == SANE_TYPE_FIXED)
+            {
+                SANE_Fixed *sf;
+
+                double dd;
+                double s_min,s_max,s_quant;
+                INT pos;
+                int min,max;
+
+                s_min = SANE_UNFIX(opt->constraint.range->min);
+                s_max = SANE_UNFIX(opt->constraint.range->max);
+                s_quant = SANE_UNFIX(opt->constraint.range->quant);
+
+                if (s_quant)
+                {
+                    min = (s_min / s_quant);
+                    max = (s_max / s_quant);
+                }
+                else
+                {
+                    min = s_min / 0.01;
+                    max = s_max / 0.01;
+                }
+
+                SendMessageA(control,SBM_SETRANGE,min,max);
+
+
+                sf = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
+                sane_control_option(pSource->deviceHandle, i,
+                        SANE_ACTION_GET_VALUE, sf,NULL);
+
+                dd = SANE_UNFIX(*sf);
+                HeapFree(GetProcessHeap(),0,sf);
+
+                if (s_quant)
+                    pos = (dd / s_quant);
+                else
+                    pos = dd / 0.01;
+
+                SendMessageW(control, SBM_SETPOS, pos, TRUE);
+                
+                UpdateReleventEdit(hwnd, opt, i, pos);
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+static INT_PTR ProcessScroll(HWND hwnd, activeDS *pSource, WPARAM wParam, 
+        LPARAM lParam)
+{
+    int index;
+    const SANE_Option_Descriptor *opt;
+    WORD scroll;
+    DWORD position;
+
+    index = GetDlgCtrlID((HWND)lParam)- ID_BASE;
+    if (index < 0)
+        return FALSE;
+
+    opt = sane_get_option_descriptor(pSource->deviceHandle, index);
+
+    if (!opt)
+        return FALSE;
+
+    scroll = LOWORD(wParam);
+
+    switch (scroll)
+    {
+        case SB_THUMBTRACK:
+        case SB_THUMBPOSITION:
+        {
+            SCROLLINFO si;
+            si.cbSize = sizeof(SCROLLINFO);
+            si.fMask = SIF_TRACKPOS;
+            GetScrollInfo((HWND)lParam,SB_CTL, &si);
+            position = si.nTrackPos;
+        }
+            break;
+        case SB_LEFT:
+        case SB_LINELEFT:
+        case SB_PAGELEFT:
+            position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
+            position--;
+            break;
+        case SB_RIGHT:
+        case SB_LINERIGHT:
+        case SB_PAGERIGHT:
+            position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
+            position++;
+            break;
+        default:
+            position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
+    }
+
+    SendMessageW((HWND)lParam,SBM_SETPOS, position, TRUE);
+    position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
+
+    UpdateReleventEdit(hwnd, opt, index, position);
+    if (UpdateSaneScrollOption(pSource, opt, index, position))
+        InitializeDialog(hwnd,pSource);
+
+    return TRUE;
+}
+
+
+static void ButtonClicked(HWND hwnd, activeDS* pSource,INT id, HWND control)
+{
+    int index;
+    const SANE_Option_Descriptor *opt;
+
+    index = id - ID_BASE;
+    if (index < 0)
+        return;
+
+    opt = sane_get_option_descriptor(pSource->deviceHandle, index);
+
+    if (!opt)
+        return;
+
+    if (opt->type == SANE_TYPE_BOOL)
+    {
+        BOOL r = SendMessageW(control,BM_GETCHECK,0,0)==BST_CHECKED;
+        if (UpdateSaneBoolOption(pSource, index, r))
+                InitializeDialog(hwnd,pSource);
+    }
+}
+
+static void ComboChanged(HWND hwnd, activeDS* pSource,INT id, HWND control)
+{
+    int index;
+    int selection;
+    int len;
+    const SANE_Option_Descriptor *opt;
+    SANE_String value;
+
+    index = id - ID_BASE;
+    if (index < 0)
+        return;
+
+    opt = sane_get_option_descriptor(pSource->deviceHandle, index);
+
+    if (!opt)
+        return;
+
+    selection = SendMessageW(control,CB_GETCURSEL,0,0);
+    len = SendMessageW(control,CB_GETLBTEXTLEN,selection,0);
+
+    len++;
+    value = HeapAlloc(GetProcessHeap(),0,len);
+    SendMessageA(control,CB_GETLBTEXT,selection,(LPARAM)value);
+
+    if (opt->type == SANE_TYPE_STRING)
+    {
+        if (UpdateSaneStringOption(pSource, index, value))
+                InitializeDialog(hwnd,pSource);
+    }
+}
+
+
+INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    static activeDS *pSource = NULL;
+
+    switch (msg)
+    {
+        case WM_INITDIALOG:
+            pSource = (activeDS*)((LPPROPSHEETPAGEW)lParam)->lParam;
+            return InitializeDialog(hwndDlg, pSource);
+        case WM_HSCROLL:
+            return ProcessScroll(hwndDlg, pSource, wParam, lParam);
+            break;
+        case WM_NOTIFY:
+            {
+                LPPSHNOTIFY psn = (LPPSHNOTIFY)lParam;
+                switch (((NMHDR*)lParam)->code)
+                {
+                    case PSN_APPLY:
+                        if (psn->lParam == TRUE)
+                        {
+                            pSource->currentState = 6;
+                            pSource->pendingEvent.TWMessage = MSG_XFERREADY;
+                        }
+                        break;
+                    case PSN_QUERYCANCEL:
+                        pSource->pendingEvent.TWMessage = MSG_CLOSEDSREQ;
+                        break;
+                    case PSN_SETACTIVE:
+                        InitializeDialog(hwndDlg, pSource);
+                        break;
+                }
+            }
+        case WM_COMMAND:
+            {
+                switch (HIWORD(wParam))
+                {
+                    case BN_CLICKED:
+                        ButtonClicked(hwndDlg, pSource,LOWORD(wParam),
+                                (HWND)lParam);
+                        break;
+                    case CBN_SELCHANGE:
+                        ComboChanged(hwndDlg, pSource,LOWORD(wParam),
+                                (HWND)lParam);
+                }
+            }
+    }
+
+    return FALSE;
+}
+
+int CALLBACK PropSheetProc(HWND hwnd, UINT msg, LPARAM lParam)
+{
+    if (msg == PSCB_INITIALIZED)
+    {
+        /* rename OK button to Scan */
+        HWND scan = GetDlgItem(hwnd,IDOK);
+        SetWindowTextA(scan,"Scan");
+    }
+    return TRUE;
+}
+
+
+INT_PTR CALLBACK ScanningProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    return FALSE;
+}
+
+HWND ScanningDialogBox(HWND dialog, DWORD progress)
+{
+    if (!dialog)
+        dialog = CreateDialogW(DSM_instance,
+                (LPWSTR)MAKEINTRESOURCE(IDD_DIALOG1), NULL, ScanningProc);
+
+    if (progress == -1)
+    {
+        EndDialog(dialog,0);
+        return NULL;
+    }
+
+    RedrawWindow(dialog,NULL,NULL,
+            RDW_INTERNALPAINT|RDW_UPDATENOW|RDW_ALLCHILDREN);
+
+    return dialog;
+}
diff --git a/dlls/twain/resource.h b/dlls/twain/resource.h
new file mode 100644
index 0000000..70d5c3e
--- /dev/null
+++ b/dlls/twain/resource.h
@@ -0,0 +1,23 @@
+/*
+ * Twain resource definitions
+ *
+ * Copyright 2006 CodeWeavers, Aric Stewart
+ *
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define IDD_DIALOG1 0x400
+
+#define IDC_STATIC  0x401
---
0.99.9i


More information about the wine-patches mailing list