4 * Copyright 2006 CodeWeavers, Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define NONAMELESSUNION
32 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(twain
);
38 #define ID_EDIT_BASE 0x1000
39 #define ID_STATIC_BASE 0x2000
41 static INT_PTR CALLBACK
DialogProc (HWND
, UINT
, WPARAM
, LPARAM
);
42 static INT CALLBACK
PropSheetProc(HWND
, UINT
,LPARAM
);
44 static int create_leading_static(HDC hdc
, const WCHAR
*text
,
45 LPDLGITEMTEMPLATEW
* template_out
, int y
, int id
)
47 LPDLGITEMTEMPLATEW tpl
= NULL
;
58 base
= GetDialogBaseUnits();
60 len
= lstrlenW(text
) * sizeof(WCHAR
);
61 len
+= sizeof(DLGITEMTEMPLATE
);
62 len
+= 4*sizeof(WORD
);
65 tpl
->style
=WS_VISIBLE
;
66 tpl
->dwExtendedStyle
= 0;
71 GetTextExtentPoint32W(hdc
,text
,lstrlenW(text
),&size
);
73 tpl
->cx
= MulDiv(size
.cx
,4,LOWORD(base
));
74 tpl
->cy
= MulDiv(size
.cy
,8,HIWORD(base
)) * 2;
75 ptr
= (WORD
*)(tpl
+ 1);
78 lstrcpyW( ptr
, text
);
79 ptr
+= lstrlenW(ptr
) + 1;
86 static int create_trailing_edit(HDC hdc
, LPDLGITEMTEMPLATEW
* template_out
, int id
,
87 int y
, const WCHAR
*text
,BOOL is_int
)
89 LPDLGITEMTEMPLATEW tpl
= NULL
;
94 static const char int_base
[] = "0000 xxx";
95 static const char float_base
[] = "0000.0000 xxx";
97 base
= GetDialogBaseUnits();
99 len
= lstrlenW(text
) * sizeof(WCHAR
);
100 len
+= sizeof(DLGITEMTEMPLATE
);
101 len
+= 4*sizeof(WORD
);
104 tpl
->style
=WS_VISIBLE
|ES_READONLY
|WS_BORDER
;
105 tpl
->dwExtendedStyle
= 0;
111 GetTextExtentPoint32A(hdc
,int_base
,lstrlenA(int_base
),&size
);
113 GetTextExtentPoint32A(hdc
,float_base
,lstrlenA(float_base
),&size
);
115 tpl
->cx
= MulDiv(size
.cx
*2,4,LOWORD(base
));
116 tpl
->cy
= MulDiv(size
.cy
,8,HIWORD(base
)) * 2;
118 ptr
= (WORD
*)(tpl
+ 1);
121 lstrcpyW( ptr
, text
);
122 ptr
+= lstrlenW(ptr
) + 1;
130 static int create_item(HDC hdc
, const struct option_descriptor
*opt
,
131 INT id
, LPDLGITEMTEMPLATEW
*template_out
, int y
, int *cx
, int* count
)
133 LPDLGITEMTEMPLATEW tpl
= NULL
,rc
= NULL
;
135 DWORD styles
= WS_VISIBLE
;
137 LPDLGITEMTEMPLATEW lead_static
= NULL
;
138 LPDLGITEMTEMPLATEW trail_edit
= NULL
;
139 DWORD leading_len
= 0;
142 const WCHAR
*title
= NULL
;
151 GetTextExtentPoint32A(hdc
,"X",1,&size
);
152 base
= GetDialogBaseUnits();
153 base_x
= MulDiv(size
.cx
,4,LOWORD(base
));
158 class = 0x0080; /* Button */
159 styles
|= BS_AUTOCHECKBOX
;
165 sane_option_get_value( id
- ID_BASE
, &i
);
166 swprintf(buffer
, ARRAY_SIZE(buffer
), L
"%i", i
);
168 switch (opt
->constraint_type
)
170 case CONSTRAINT_NONE
:
171 class = 0x0081; /* Edit*/
175 case CONSTRAINT_RANGE
:
176 class = 0x0084; /* scroll */
177 ctl_cx
= 10 * base_x
;
178 trail_len
+= create_trailing_edit(hdc
, &trail_edit
, id
+
179 ID_EDIT_BASE
, y
,buffer
,TRUE
);
182 class= 0x0085; /* Combo */
183 ctl_cx
= 10 * base_x
;
184 styles
|= CBS_DROPDOWNLIST
;
187 leading_len
+= create_leading_static(hdc
, opt
->title
, &lead_static
, y
, id
+ID_STATIC_BASE
);
192 int *i
= calloc( opt
->size
, sizeof(int) );
194 sane_option_get_value( id
- ID_BASE
, i
);
196 swprintf(buffer
, ARRAY_SIZE(buffer
), L
"%f", *i
/ 65536.0);
199 switch (opt
->constraint_type
)
201 case CONSTRAINT_NONE
:
202 class = 0x0081; /* Edit */
205 case CONSTRAINT_RANGE
:
206 class= 0x0084; /* scroll */
207 ctl_cx
= 10 * base_x
;
208 trail_len
+= create_trailing_edit(hdc
, &trail_edit
, id
+ ID_EDIT_BASE
, y
,buffer
,FALSE
);
211 class= 0x0085; /* Combo */
212 ctl_cx
= 10 * base_x
;
213 styles
|= CBS_DROPDOWNLIST
;
216 leading_len
+= create_leading_static(hdc
, opt
->title
, &lead_static
, y
, id
+ID_STATIC_BASE
);
222 switch (opt
->constraint_type
)
224 case CONSTRAINT_NONE
:
225 class = 0x0081; /* Edit*/
228 class= 0x0085; /* Combo */
229 ctl_cx
= opt
->size
* base_x
;
230 styles
|= CBS_DROPDOWNLIST
;
233 leading_len
+= create_leading_static(hdc
, opt
->title
, &lead_static
, y
, id
+ID_STATIC_BASE
);
234 sane_option_get_value( id
- ID_BASE
, str
);
235 MultiByteToWideChar( CP_UNIXCP
, 0, str
, -1, buffer
, ARRAY_SIZE(buffer
) );
240 class = 0x0080; /* Button */
244 class = 0x0080; /* Button */
245 styles
|= BS_GROUPBOX
;
250 local_len
+= sizeof(DLGITEMTEMPLATE
);
251 if (title
) local_len
+= lstrlenW(title
) * sizeof(WCHAR
);
252 local_len
+= 4*sizeof(WORD
);
254 padding
= leading_len
% sizeof(DWORD
);
255 rc
= realloc(lead_static
, leading_len
+ local_len
+ padding
);
256 tpl
= (LPDLGITEMTEMPLATEW
)((LPBYTE
)rc
+ leading_len
+ padding
);
258 tpl
->dwExtendedStyle
= 0;
260 tpl
->x
= lead_static
->x
+ lead_static
->cx
+ 1;
261 else if (opt
->type
== TYPE_GROUP
)
270 GetTextExtentPoint32W(hdc
,title
,lstrlenW(title
),&size
);
277 tpl
->cy
= lead_static
->cy
;
286 ptr
= (WORD
*)(tpl
+ 1);
291 lstrcpyW( ptr
, title
);
292 ptr
+= lstrlenW(ptr
);
299 trail_edit
->x
= tpl
->cx
+ tpl
->x
+ 2;
300 *cx
= trail_edit
->x
+ trail_edit
->cx
;
302 padding2
= (leading_len
+ local_len
+ padding
)% sizeof(DWORD
);
304 rc
= realloc( rc
, leading_len
+ local_len
+ padding
+padding2
+ trail_len
);
306 memcpy(((LPBYTE
)rc
) + leading_len
+ local_len
+ padding
+ padding2
,
307 trail_edit
,trail_len
);
310 *cx
= tpl
->cx
+ tpl
->x
;
322 return leading_len
+ local_len
+ padding
+ padding2
+ trail_len
;
326 static LPDLGTEMPLATEW
create_options_page(HDC hdc
, int *from_index
,
327 int optcount
, BOOL split_tabs
)
331 LPDLGTEMPLATEW tpl
= NULL
;
332 LPBYTE all_controls
= NULL
;
333 DWORD control_len
= 0;
335 int group_max_cx
= 0;
337 int group_offset
= -1;
338 INT control_count
= 0;
340 for (i
= *from_index
; i
< optcount
; i
++)
342 LPDLGITEMTEMPLATEW item_tpl
= NULL
;
343 struct option_descriptor opt
;
348 int hold_for_group
= 0;
351 if (SANE_CALL( option_get_descriptor
, &opt
)) continue;
352 if (opt
.type
== TYPE_GROUP
&& split_tabs
)
368 len
= create_item(hdc
, &opt
, ID_BASE
+ i
, &item_tpl
, y
, &x
, &count
);
370 control_count
+= count
;
378 y
+= item_tpl
->cy
+ 1;
379 max_cx
= max(max_cx
, x
+ 2);
380 group_max_cx
= max(group_max_cx
, x
);
382 padding
= len
% sizeof(DWORD
);
386 all_controls
= realloc(all_controls
, control_len
+ len
+ padding
);
387 memcpy(all_controls
+control_len
,item_tpl
,len
);
388 memset(all_controls
+control_len
+len
,0xca,padding
);
395 all_controls
= (LPBYTE
)item_tpl
;
399 all_controls
= malloc(len
+ padding
);
400 memcpy(all_controls
,item_tpl
,len
);
401 memset(all_controls
+len
,0xcb,padding
);
406 if (opt
.type
== TYPE_GROUP
)
408 if (group_offset
== -1)
410 group_offset
= control_len
;
415 LPDLGITEMTEMPLATEW group
=
416 (LPDLGITEMTEMPLATEW
)(all_controls
+group_offset
);
418 group
->cy
= hold_for_group
- group
->y
;
419 group
->cx
= group_max_cx
;
421 group
= (LPDLGITEMTEMPLATEW
)(all_controls
+control_len
);
425 group_offset
= control_len
;
429 control_len
+= len
+ padding
;
432 if ( group_offset
&& !split_tabs
)
434 LPDLGITEMTEMPLATEW group
=
435 (LPDLGITEMTEMPLATEW
)(all_controls
+group_offset
);
436 group
->cy
= y
- group
->y
;
437 group
->cx
= group_max_cx
;
444 tpl
= malloc(sizeof(DLGTEMPLATE
) + 3*sizeof(WORD
) + control_len
);
446 tpl
->style
= WS_VISIBLE
| WS_OVERLAPPEDWINDOW
;
447 tpl
->dwExtendedStyle
= 0;
448 tpl
->cdit
= control_count
;
451 tpl
->cx
= max_cx
+ 10;
453 ptr
= (LPBYTE
)tpl
+ sizeof(DLGTEMPLATE
);
454 *(LPWORD
)ptr
= 0x0000;
456 *(LPWORD
)ptr
= 0x0000;
458 *(LPWORD
)ptr
= 0x0000;
460 memcpy(ptr
,all_controls
,control_len
);
467 BOOL
DoScannerUI(void)
470 PROPSHEETPAGEW psp
[10];
472 PROPSHEETHEADERW psh
;
482 memset(psp
,0,sizeof(psp
));
483 rc
= sane_option_get_value( 0, &optcount
);
484 if (rc
!= TWCC_SUCCESS
)
486 ERR("Unable to read number of options\n");
490 while (index
< optcount
)
492 struct option_descriptor opt
;
494 psp
[page_count
].u
.pResource
= create_options_page(hdc
, &index
,
497 SANE_CALL( option_get_descriptor
, &opt
);
499 if (opt
.type
== TYPE_GROUP
)
501 psp
[page_count
].pszTitle
= wcsdup( opt
.title
);
504 if (psp
[page_count
].u
.pResource
)
506 psp
[page_count
].dwSize
= sizeof(PROPSHEETPAGEW
);
507 psp
[page_count
].dwFlags
= PSP_DLGINDIRECT
| PSP_USETITLE
;
508 psp
[page_count
].hInstance
= SANE_instance
;
509 psp
[page_count
].pfnDlgProc
= DialogProc
;
510 psp
[page_count
].lParam
= (LPARAM
)&activeDS
;
517 len
= lstrlenA(activeDS
.identity
.Manufacturer
)
518 + lstrlenA(activeDS
.identity
.ProductName
) + 2;
519 szCaption
= malloc(len
*sizeof(WCHAR
));
520 MultiByteToWideChar(CP_ACP
,0,activeDS
.identity
.Manufacturer
,-1,
522 szCaption
[lstrlenA(activeDS
.identity
.Manufacturer
)] = ' ';
523 MultiByteToWideChar(CP_ACP
,0,activeDS
.identity
.ProductName
,-1,
524 &szCaption
[lstrlenA(activeDS
.identity
.Manufacturer
)+1],len
);
525 psh
.dwSize
= sizeof(PROPSHEETHEADERW
);
526 psh
.dwFlags
= PSH_PROPSHEETPAGE
|PSH_PROPTITLE
|PSH_USECALLBACK
;
527 psh
.hwndParent
= activeDS
.hwndOwner
;
528 psh
.hInstance
= SANE_instance
;
530 psh
.pszCaption
= szCaption
;
531 psh
.nPages
= page_count
;
532 psh
.u2
.nStartPage
= 0;
533 psh
.u3
.ppsp
= (LPCPROPSHEETPAGEW
)psp
;
534 psh
.pfnCallback
= PropSheetProc
;
536 psrc
= PropertySheetW(&psh
);
538 for(index
= 0; index
< page_count
; index
++)
540 free((LPBYTE
)psp
[index
].u
.pResource
);
541 free((LPBYTE
)psp
[index
].pszTitle
);
551 static void UpdateRelevantEdit(HWND hwnd
, const struct option_descriptor
*opt
, int position
)
563 if (opt
->constraint
.range
.quant
)
564 si
= position
* opt
->constraint
.range
.quant
;
568 len
= swprintf( buffer
, ARRAY_SIZE(buffer
), L
"%i", si
);
575 if (opt
->constraint
.range
.quant
)
576 dd
= position
* (opt
->constraint
.range
.quant
/ 65536.0);
578 dd
= position
* 0.01;
580 len
= swprintf( buffer
, ARRAY_SIZE(buffer
), L
"%f", dd
);
588 LoadStringW( SANE_instance
, opt
->unit
, buffer
+ len
, ARRAY_SIZE( buffer
) - len
);
590 edit_w
= GetDlgItem(hwnd
,opt
->optno
+ ID_BASE
+ ID_EDIT_BASE
);
591 if (edit_w
) SetWindowTextW(edit_w
,buffer
);
595 static BOOL
UpdateSaneScrollOption(const struct option_descriptor
*opt
, DWORD position
)
604 if (opt
->constraint
.range
.quant
)
605 si
= position
* opt
->constraint
.range
.quant
;
609 sane_option_set_value( opt
->optno
, &si
, &result
);
613 if (opt
->constraint
.range
.quant
)
614 si
= position
* opt
->constraint
.range
.quant
;
616 si
= MulDiv( position
, 65536, 100 );
618 sane_option_set_value( opt
->optno
, &si
, &result
);
627 static INT_PTR
InitializeDialog(HWND hwnd
)
634 rc
= sane_option_get_value( 0, &optcount
);
635 if (rc
!= TWCC_SUCCESS
)
637 ERR("Unable to read number of options\n");
641 for ( i
= 1; i
< optcount
; i
++)
643 struct option_descriptor opt
;
645 control
= GetDlgItem(hwnd
,i
+ID_BASE
);
651 SANE_CALL( option_get_descriptor
, &opt
);
653 TRACE("%i %s %i %i\n",i
,debugstr_w(opt
.title
),opt
.type
,opt
.constraint_type
);
654 EnableWindow(control
,opt
.is_active
);
656 SendMessageA(control
,CB_RESETCONTENT
,0,0);
657 /* initialize values */
658 if (opt
.type
== TYPE_STRING
&& opt
.constraint_type
!= CONSTRAINT_NONE
)
663 for (p
= opt
.constraint
.strings
; *p
; p
+= lstrlenW(p
) + 1)
664 SendMessageW( control
,CB_ADDSTRING
,0, (LPARAM
)p
);
665 sane_option_get_value( i
, buffer
);
666 SendMessageA(control
,CB_SELECTSTRING
,0,(LPARAM
)buffer
);
668 else if (opt
.type
== TYPE_BOOL
)
671 sane_option_get_value( i
, &b
);
673 SendMessageA(control
,BM_SETCHECK
,BST_CHECKED
,0);
676 else if (opt
.type
== TYPE_INT
&& opt
.constraint_type
== CONSTRAINT_WORD_LIST
)
678 int j
, count
= opt
.constraint
.word_list
[0];
681 for (j
=1; j
<=count
; j
++)
683 sprintf(buffer
, "%d", opt
.constraint
.word_list
[j
]);
684 SendMessageA(control
, CB_ADDSTRING
, 0, (LPARAM
)buffer
);
686 sane_option_get_value( i
, &val
);
687 sprintf(buffer
, "%d", val
);
688 SendMessageA(control
,CB_SELECTSTRING
,0,(LPARAM
)buffer
);
690 else if (opt
.constraint_type
== CONSTRAINT_RANGE
)
692 if (opt
.type
== TYPE_INT
)
697 min
= opt
.constraint
.range
.min
/
698 (opt
.constraint
.range
.quant
? opt
.constraint
.range
.quant
: 1);
700 max
= opt
.constraint
.range
.max
/
701 (opt
.constraint
.range
.quant
? opt
.constraint
.range
.quant
: 1);
703 SendMessageA(control
,SBM_SETRANGE
,min
,max
);
705 sane_option_get_value( i
, &si
);
706 if (opt
.constraint
.range
.quant
)
707 si
= si
/ opt
.constraint
.range
.quant
;
709 SendMessageW(control
,SBM_SETPOS
, si
, TRUE
);
710 UpdateRelevantEdit(hwnd
, &opt
, si
);
712 else if (opt
.type
== TYPE_FIXED
)
714 int pos
, min
, max
, *sf
;
716 if (opt
.constraint
.range
.quant
)
718 min
= opt
.constraint
.range
.min
/ opt
.constraint
.range
.quant
;
719 max
= opt
.constraint
.range
.max
/ opt
.constraint
.range
.quant
;
723 min
= MulDiv( opt
.constraint
.range
.min
, 100, 65536 );
724 max
= MulDiv( opt
.constraint
.range
.max
, 100, 65536 );
727 SendMessageA(control
,SBM_SETRANGE
,min
,max
);
730 sf
= calloc( opt
.size
, sizeof(int) );
731 sane_option_get_value( i
, sf
);
733 /* Note that conversion of float -> SANE_Fixed is lossy;
734 * and when you truncate it into an integer, you can get
735 * unfortunate results. This calculation attempts
736 * to mitigate that harm */
737 if (opt
.constraint
.range
.quant
)
738 pos
= *sf
/ opt
.constraint
.range
.quant
;
740 pos
= MulDiv( *sf
, 100, 65536 );
743 SendMessageW(control
, SBM_SETPOS
, pos
, TRUE
);
744 UpdateRelevantEdit(hwnd
, &opt
, pos
);
752 static INT_PTR
ProcessScroll(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
754 struct option_descriptor opt
;
758 opt
.optno
= GetDlgCtrlID((HWND
)lParam
)- ID_BASE
;
762 if (SANE_CALL( option_get_descriptor
, &opt
)) return FALSE
;
764 scroll
= LOWORD(wParam
);
769 case SB_THUMBPOSITION
:
772 si
.cbSize
= sizeof(SCROLLINFO
);
773 si
.fMask
= SIF_TRACKPOS
;
774 GetScrollInfo((HWND
)lParam
,SB_CTL
, &si
);
775 position
= si
.nTrackPos
;
781 position
= SendMessageW((HWND
)lParam
,SBM_GETPOS
,0,0);
787 position
= SendMessageW((HWND
)lParam
,SBM_GETPOS
,0,0);
791 position
= SendMessageW((HWND
)lParam
,SBM_GETPOS
,0,0);
794 SendMessageW((HWND
)lParam
,SBM_SETPOS
, position
, TRUE
);
795 position
= SendMessageW((HWND
)lParam
,SBM_GETPOS
,0,0);
797 UpdateRelevantEdit(hwnd
, &opt
, position
);
798 if (UpdateSaneScrollOption(&opt
, position
))
799 InitializeDialog(hwnd
);
805 static void ButtonClicked(HWND hwnd
, INT id
, HWND control
)
807 struct option_descriptor opt
;
808 BOOL changed
= FALSE
;
810 opt
.optno
= id
- ID_BASE
;
814 if (SANE_CALL( option_get_descriptor
, &opt
)) return;
816 if (opt
.type
== TYPE_BOOL
)
818 BOOL r
= SendMessageW(control
,BM_GETCHECK
,0,0)==BST_CHECKED
;
819 sane_option_set_value( opt
.optno
, &r
, &changed
);
820 if (changed
) InitializeDialog(hwnd
);
824 static void ComboChanged(HWND hwnd
, INT id
, HWND control
)
828 struct option_descriptor opt
;
830 BOOL changed
= FALSE
;
832 opt
.optno
= id
- ID_BASE
;
836 if (SANE_CALL( option_get_descriptor
, &opt
)) return;
838 selection
= SendMessageW(control
,CB_GETCURSEL
,0,0);
839 len
= SendMessageW(control
,CB_GETLBTEXTLEN
,selection
,0);
843 SendMessageA(control
,CB_GETLBTEXT
,selection
,(LPARAM
)value
);
845 if (opt
.type
== TYPE_STRING
)
847 sane_option_set_value( opt
.optno
, value
, &changed
);
849 else if (opt
.type
== TYPE_INT
)
851 int val
= atoi( value
);
852 sane_option_set_value( opt
.optno
, &val
, &changed
);
854 if (changed
) InitializeDialog(hwnd
);
859 static INT_PTR CALLBACK
DialogProc(HWND hwndDlg
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
864 return InitializeDialog(hwndDlg
);
866 return ProcessScroll(hwndDlg
, wParam
, lParam
);
869 LPPSHNOTIFY psn
= (LPPSHNOTIFY
)lParam
;
870 switch (((NMHDR
*)lParam
)->code
)
875 activeDS
.currentState
= 6;
876 SANE_Notify(MSG_XFERREADY
);
879 case PSN_QUERYCANCEL
:
880 SANE_Notify(MSG_CLOSEDSREQ
);
883 InitializeDialog(hwndDlg
);
889 switch (HIWORD(wParam
))
892 ButtonClicked(hwndDlg
,LOWORD(wParam
), (HWND
)lParam
);
895 ComboChanged(hwndDlg
,LOWORD(wParam
), (HWND
)lParam
);
902 static int CALLBACK
PropSheetProc(HWND hwnd
, UINT msg
, LPARAM lParam
)
904 if (msg
== PSCB_INITIALIZED
)
906 /* rename OK button to Scan */
907 HWND scan
= GetDlgItem(hwnd
,IDOK
);
908 SetWindowTextA(scan
,"Scan");
914 static INT_PTR CALLBACK
ScanningProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
919 HWND
ScanningDialogBox(HWND dialog
, LONG progress
)
922 dialog
= CreateDialogW(SANE_instance
,
923 (LPWSTR
)MAKEINTRESOURCE(IDD_DIALOG1
), NULL
, ScanningProc
);
931 RedrawWindow(dialog
,NULL
,NULL
,
932 RDW_INTERNALPAINT
|RDW_UPDATENOW
|RDW_ALLCHILDREN
);