From 8358c63e15b64f7e11a238e2b375121108148479 Mon Sep 17 00:00:00 2001 From: Aric Stewart Date: Wed, 22 Mar 2006 13:34:22 -0600 Subject: [PATCH] twain: Add a property sheet UI for scanning. --- dlls/twain/.gitignore | 1 + dlls/twain/Makefile.in | 8 +- dlls/twain/ds_ctrl.c | 17 +- dlls/twain/ds_image.c | 10 + dlls/twain/dsm_ctrl.c | 1 + dlls/twain/resource.h | 23 + dlls/twain/rsrc.rc | 28 + dlls/twain/twain32_main.c | 1 + dlls/twain/twain_En.rc | 40 ++ dlls/twain/twain_i.h | 8 + dlls/twain/ui.c | 1074 +++++++++++++++++++++++++++++++++++++ 11 files changed, 1205 insertions(+), 6 deletions(-) create mode 100644 dlls/twain/resource.h create mode 100644 dlls/twain/rsrc.rc create mode 100644 dlls/twain/twain_En.rc create mode 100644 dlls/twain/ui.c diff --git a/dlls/twain/.gitignore b/dlls/twain/.gitignore index f3c7a7c5da6..581860b6baa 100644 --- a/dlls/twain/.gitignore +++ b/dlls/twain/.gitignore @@ -1 +1,2 @@ Makefile +rsrc.res diff --git a/dlls/twain/Makefile.in b/dlls/twain/Makefile.in index 6e26243a7c2..c4550ced711 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 = comctl32 user32 gdi32 kernel32 ntdll 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 ff49c28745f..50805a522fc 100644 --- a/dlls/twain/ds_ctrl.c +++ b/dlls/twain/ds_ctrl.c @@ -647,11 +647,20 @@ TW_UINT16 TWAIN_EnableDSUserInterface (pTW_IDENTITY pOrigin, { 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; + } +#ifdef HAVE_SANE + else + { + sane_get_parameters (pSource->deviceHandle, &pSource->sane_param); + pSource->sane_param_valid = TRUE; + } +#endif } else { diff --git a/dlls/twain/ds_image.c b/dlls/twain/ds_image.c index dd37cf0f5b5..92ec70046cd 100644 --- a/dlls/twain/ds_image.c +++ b/dlls/twain/ds_image.c @@ -223,6 +223,12 @@ TW_UINT16 TWAIN_ImageMemXferGet (pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, /* 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) { @@ -295,8 +301,11 @@ TW_UINT16 TWAIN_ImageMemXferGet (pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, 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; @@ -305,6 +314,7 @@ TW_UINT16 TWAIN_ImageMemXferGet (pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, } 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 002a3bfddcd..dce7add51b4 100644 --- a/dlls/twain/dsm_ctrl.c +++ b/dlls/twain/dsm_ctrl.c @@ -304,6 +304,7 @@ TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData) 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/resource.h b/dlls/twain/resource.h new file mode 100644 index 00000000000..70d5c3e8c28 --- /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 diff --git a/dlls/twain/rsrc.rc b/dlls/twain/rsrc.rc new file mode 100644 index 00000000000..455cac25d4c --- /dev/null +++ b/dlls/twain/rsrc.rc @@ -0,0 +1,28 @@ +/* + * 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" + +#include "twain_En.rc" diff --git a/dlls/twain/twain32_main.c b/dlls/twain/twain32_main.c index e34588c6d8d..df59afc22f6 100644 --- a/dlls/twain/twain32_main.c +++ b/dlls/twain/twain32_main.c @@ -39,6 +39,7 @@ BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hinstDLL); DSM_currentState = 2; + 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 00000000000..bf8c183c0cf --- /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 6d98763a63f..d2202c4ab0d 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,6 +60,8 @@ TW_UINT16 DSM_twCC; /* current condition code of Source Manager */ 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 */ #endif @@ -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 00000000000..d925512069a --- /dev/null +++ b/dlls/twain/ui.c @@ -0,0 +1,1074 @@ +/* +* 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 +#include +#include + +#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); + +#ifdef HAVE_SANE + +#define ID_BASE 0x100 +#define ID_EDIT_BASE 0x1000 +#define ID_STATIC_BASE 0x2000 + +static INT_PTR CALLBACK DialogProc (HWND , UINT , WPARAM , LPARAM ); +static INT CALLBACK PropSheetProc(HWND, UINT,LPARAM); + +static 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; +} + +static 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; +} + + +static 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; +} + + +static LPDLGTEMPLATEW create_options_page(HDC hdc, activeDS *pSource, int *from_index, + BOOL split_tabs) +{ + 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; +} + +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 UpdateRelevantEdit(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); + UpdateRelevantEdit(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); + + UpdateRelevantEdit(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); + + UpdateRelevantEdit(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); + } +} + + +static 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; +} + +static 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; +} + + +static 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; +} + +#else /* HAVE_SANE */ + +BOOL DoScannerUI(activeDS *pSource) +{ + return FALSE; +} + +#endif /* HAVE_SANE */