1070 lines
29 KiB
C
1070 lines
29 KiB
C
/*
|
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
#define NONAMELESSUNION
|
|
#define NONAMELESSSTRUCT
|
|
#include "sane_i.h"
|
|
#include "winuser.h"
|
|
#include "winnls.h"
|
|
#include "wingdi.h"
|
|
#include "prsht.h"
|
|
#include "wine/debug.h"
|
|
#include "resource.h"
|
|
#include "wine/unicode.h"
|
|
|
|
#ifdef SONAME_LIBSANE
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(twain);
|
|
|
|
#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(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;
|
|
|
|
psane_control_option(activeDS.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));
|
|
|
|
psane_control_option(activeDS.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);
|
|
psane_control_option(activeDS.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, int *from_index,
|
|
SANE_Int optcount, BOOL split_tabs)
|
|
{
|
|
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;
|
|
|
|
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 = psane_get_option_descriptor(activeDS.deviceHandle, i);
|
|
if (!opt)
|
|
continue;
|
|
if (opt->type == SANE_TYPE_GROUP && split_tabs)
|
|
{
|
|
if (control_len > 0)
|
|
{
|
|
*from_index = i - 1;
|
|
goto exit;
|
|
}
|
|
else
|
|
{
|
|
*from_index = i;
|
|
return NULL;
|
|
}
|
|
}
|
|
if (!SANE_OPTION_IS_ACTIVE (opt->cap))
|
|
continue;
|
|
|
|
len = create_item(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(void)
|
|
{
|
|
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 = psane_control_option(activeDS.deviceHandle, 0, SANE_ACTION_GET_VALUE,
|
|
&optcount, NULL);
|
|
if (rc != SANE_STATUS_GOOD)
|
|
{
|
|
ERR("Unable to read number of options\n");
|
|
return FALSE;
|
|
}
|
|
|
|
while (index < optcount)
|
|
{
|
|
const SANE_Option_Descriptor *opt;
|
|
psp[page_count].u.pResource = create_options_page(hdc, &index,
|
|
optcount, TRUE);
|
|
opt = psane_get_option_descriptor(activeDS.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 = SANE_instance;
|
|
psp[page_count].pfnDlgProc = DialogProc;
|
|
psp[page_count].lParam = (LPARAM)&activeDS;
|
|
page_count ++;
|
|
}
|
|
|
|
index ++;
|
|
}
|
|
|
|
len = lstrlenA(activeDS.identity.Manufacturer)
|
|
+ lstrlenA(activeDS.identity.ProductName) + 2;
|
|
szCaption = HeapAlloc(GetProcessHeap(),0,len *sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP,0,activeDS.identity.Manufacturer,-1,
|
|
szCaption,len);
|
|
szCaption[lstrlenA(activeDS.identity.Manufacturer)] = ' ';
|
|
MultiByteToWideChar(CP_ACP,0,activeDS.identity.ProductName,-1,
|
|
&szCaption[lstrlenA(activeDS.identity.Manufacturer)+1],len);
|
|
psh.dwSize = sizeof(PROPSHEETHEADERW);
|
|
psh.dwFlags = PSH_PROPSHEETPAGE|PSH_PROPTITLE|PSH_USECALLBACK;
|
|
psh.hwndParent = activeDS.hwndOwner;
|
|
psh.hInstance = SANE_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);
|
|
}
|
|
HeapFree(GetProcessHeap(),0,szCaption);
|
|
|
|
if (psrc == IDOK)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
static void UpdateRelevantEdit(HWND hwnd, const SANE_Option_Descriptor *opt,
|
|
int index, int position)
|
|
{
|
|
WCHAR buffer[244];
|
|
HWND edit_w;
|
|
int len;
|
|
|
|
if (opt->type == SANE_TYPE_INT)
|
|
{
|
|
static const WCHAR formatW[] = {'%','i',0};
|
|
INT si;
|
|
|
|
if (opt->constraint.range->quant)
|
|
si = position * opt->constraint.range->quant;
|
|
else
|
|
si = position;
|
|
|
|
len = sprintfW( buffer, formatW, si );
|
|
}
|
|
else if (opt->type == SANE_TYPE_FIXED)
|
|
{
|
|
static const WCHAR formatW[] = {'%','f',0};
|
|
double s_quant, dd;
|
|
|
|
s_quant = SANE_UNFIX(opt->constraint.range->quant);
|
|
|
|
if (s_quant)
|
|
dd = position * s_quant;
|
|
else
|
|
dd = position * 0.01;
|
|
|
|
len = sprintfW( buffer, formatW, dd );
|
|
}
|
|
else return;
|
|
|
|
buffer[len++] = ' ';
|
|
LoadStringW( SANE_instance, opt->unit, buffer + len, sizeof(buffer)/sizeof(WCHAR) - len );
|
|
|
|
edit_w = GetDlgItem(hwnd,index+ID_BASE+ID_EDIT_BASE);
|
|
if (edit_w) SetWindowTextW(edit_w,buffer);
|
|
|
|
}
|
|
|
|
static BOOL UpdateSaneScrollOption(
|
|
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 = psane_control_option (activeDS.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 = psane_control_option (activeDS.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(int index, BOOL position)
|
|
{
|
|
SANE_Status rc = SANE_STATUS_GOOD;
|
|
SANE_Int result = 0;
|
|
SANE_Bool si;
|
|
|
|
si = position;
|
|
|
|
rc = psane_control_option (activeDS.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(int index, SANE_String value)
|
|
{
|
|
SANE_Status rc = SANE_STATUS_GOOD;
|
|
SANE_Int result = 0;
|
|
|
|
rc = psane_control_option (activeDS.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)
|
|
{
|
|
SANE_Status rc;
|
|
SANE_Int optcount;
|
|
HWND control;
|
|
int i;
|
|
|
|
rc = psane_control_option(activeDS.deviceHandle, 0, SANE_ACTION_GET_VALUE,
|
|
&optcount, NULL);
|
|
if (rc != SANE_STATUS_GOOD)
|
|
{
|
|
ERR("Unable to read number of options\n");
|
|
return FALSE;
|
|
}
|
|
|
|
for ( i = 1; i < optcount; i++)
|
|
{
|
|
const SANE_Option_Descriptor *opt;
|
|
|
|
control = GetDlgItem(hwnd,i+ID_BASE);
|
|
|
|
if (!control)
|
|
continue;
|
|
|
|
opt = psane_get_option_descriptor(activeDS.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++;
|
|
}
|
|
psane_control_option(activeDS.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;
|
|
psane_control_option(activeDS.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);
|
|
|
|
psane_control_option(activeDS.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));
|
|
psane_control_option(activeDS.deviceHandle, i,
|
|
SANE_ACTION_GET_VALUE, sf,NULL);
|
|
|
|
dd = SANE_UNFIX(*sf);
|
|
HeapFree(GetProcessHeap(),0,sf);
|
|
|
|
/* Note that conversion of float -> SANE_Fixed is lossy;
|
|
* and when you truncate it into an integer, you can get
|
|
* unfortunate results. This calculation attempts
|
|
* to mitigate that harm */
|
|
if (s_quant)
|
|
pos = ((dd + (s_quant/2.0)) / s_quant);
|
|
else
|
|
pos = (dd + 0.005) / 0.01;
|
|
|
|
SendMessageW(control, SBM_SETPOS, pos, TRUE);
|
|
|
|
UpdateRelevantEdit(hwnd, opt, i, pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static INT_PTR ProcessScroll(HWND hwnd, 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 = psane_get_option_descriptor(activeDS.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(opt, index, position))
|
|
InitializeDialog(hwnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void ButtonClicked(HWND hwnd, INT id, HWND control)
|
|
{
|
|
int index;
|
|
const SANE_Option_Descriptor *opt;
|
|
|
|
index = id - ID_BASE;
|
|
if (index < 0)
|
|
return;
|
|
|
|
opt = psane_get_option_descriptor(activeDS.deviceHandle, index);
|
|
|
|
if (!opt)
|
|
return;
|
|
|
|
if (opt->type == SANE_TYPE_BOOL)
|
|
{
|
|
BOOL r = SendMessageW(control,BM_GETCHECK,0,0)==BST_CHECKED;
|
|
if (UpdateSaneBoolOption(index, r))
|
|
InitializeDialog(hwnd);
|
|
}
|
|
}
|
|
|
|
static void ComboChanged(HWND hwnd, 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 = psane_get_option_descriptor(activeDS.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(index, value))
|
|
InitializeDialog(hwnd);
|
|
}
|
|
}
|
|
|
|
|
|
static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return InitializeDialog(hwndDlg);
|
|
case WM_HSCROLL:
|
|
return ProcessScroll(hwndDlg, wParam, lParam);
|
|
case WM_NOTIFY:
|
|
{
|
|
LPPSHNOTIFY psn = (LPPSHNOTIFY)lParam;
|
|
switch (((NMHDR*)lParam)->code)
|
|
{
|
|
case PSN_APPLY:
|
|
if (psn->lParam)
|
|
{
|
|
activeDS.currentState = 6;
|
|
if (activeDS.windowMessage)
|
|
PostMessageA(activeDS.hwndOwner, activeDS.windowMessage, MSG_XFERREADY, 0);
|
|
}
|
|
break;
|
|
case PSN_QUERYCANCEL:
|
|
if (activeDS.windowMessage)
|
|
PostMessageA(activeDS.hwndOwner, activeDS.windowMessage, MSG_CLOSEDSREQ, 0);
|
|
break;
|
|
case PSN_SETACTIVE:
|
|
InitializeDialog(hwndDlg);
|
|
break;
|
|
}
|
|
}
|
|
case WM_COMMAND:
|
|
switch (HIWORD(wParam))
|
|
{
|
|
case BN_CLICKED:
|
|
ButtonClicked(hwndDlg,LOWORD(wParam), (HWND)lParam);
|
|
break;
|
|
case CBN_SELCHANGE:
|
|
ComboChanged(hwndDlg,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, LONG progress)
|
|
{
|
|
if (!dialog)
|
|
dialog = CreateDialogW(SANE_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 /* SONAME_LIBSANE */
|
|
|
|
BOOL DoScannerUI(void)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#endif /* SONAME_LIBSANE */
|