advpack: Forward LaunchINFSectionExA to its Unicode counterpart.
[wine] / dlls / twain / ui.c
1 /*
2 * TWAIN32 Options UI
3 *
4 * Copyright 2006 CodeWeavers, Aric Stewart
5 *
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.
10 *
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.
15 *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26
27 #define NONAMELESSUNION
28 #define NONAMELESSSTRUCT
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "winnls.h"
33 #include "wingdi.h"
34 #include "prsht.h"
35 #include "twain.h"
36 #include "twain_i.h"
37 #include "wine/debug.h"
38 #include "resource.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(twain);
41
42 #ifdef HAVE_SANE
43
44 #define ID_BASE 0x100
45 #define ID_EDIT_BASE 0x1000
46 #define ID_STATIC_BASE 0x2000
47
48 static INT_PTR CALLBACK DialogProc (HWND , UINT , WPARAM , LPARAM );
49 static INT CALLBACK PropSheetProc(HWND, UINT,LPARAM);
50
51 static int create_leading_static(HDC hdc, LPCSTR text,
52         LPDLGITEMTEMPLATEW* template_out, int y, int id)
53 {
54     LPDLGITEMTEMPLATEW tpl =  NULL;
55     INT len;
56     SIZE size;
57     LPBYTE ptr;
58     LONG base;
59
60     *template_out = NULL;
61
62     if (!text)
63         return 0;
64
65     base = GetDialogBaseUnits();
66
67     len = MultiByteToWideChar(CP_ACP,0,text,-1,NULL,0);
68     len *= sizeof(WCHAR);
69     len += sizeof(DLGITEMTEMPLATE);
70     len += 3*sizeof(WORD);
71
72     tpl = HeapAlloc(GetProcessHeap(),0,len);
73     tpl->style=WS_VISIBLE;
74     tpl->dwExtendedStyle = 0;
75     tpl->x = 4;
76     tpl->y = y;
77     tpl->id = ID_BASE;
78
79     GetTextExtentPoint32A(hdc,text,lstrlenA(text),&size);
80
81     tpl->cx =  MulDiv(size.cx,4,LOWORD(base));
82     tpl->cy =  MulDiv(size.cy,8,HIWORD(base)) * 2;
83     ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE);
84     *(LPWORD)ptr = 0xffff;
85     ptr += sizeof(WORD);
86     *(LPWORD)ptr = 0x0082;
87     ptr += sizeof(WORD);
88     ptr += MultiByteToWideChar(CP_ACP,0,text,-1,(LPWSTR)ptr,len) * sizeof(WCHAR);
89     *(LPWORD)ptr = 0x0000;
90
91     *template_out = tpl;
92     return len;
93 }
94
95 static int create_trailing_edit(HDC hdc, LPDLGITEMTEMPLATEW* template_out, int id,
96         int y, LPCSTR text,BOOL is_int)
97 {
98     LPDLGITEMTEMPLATEW tpl =  NULL;
99     INT len;
100     LPBYTE ptr;
101     SIZE size;
102     LONG base;
103     static const char int_base[] = "0000 xxx";
104     static const char float_base[] = "0000.0000 xxx";
105
106     base = GetDialogBaseUnits();
107
108     len = MultiByteToWideChar(CP_ACP,0,text,-1,NULL,0);
109     len *= sizeof(WCHAR);
110     len += sizeof(DLGITEMTEMPLATE);
111     len += 3*sizeof(WORD);
112
113     tpl = HeapAlloc(GetProcessHeap(),0,len);
114     tpl->style=WS_VISIBLE|ES_READONLY|WS_BORDER;
115     tpl->dwExtendedStyle = 0;
116     tpl->x = 1;
117     tpl->y = y;
118     tpl->id = id;
119
120     if (is_int)
121         GetTextExtentPoint32A(hdc,int_base,lstrlenA(int_base),&size);
122     else
123         GetTextExtentPoint32A(hdc,float_base,lstrlenA(float_base),&size);
124
125     tpl->cx =  MulDiv(size.cx*2,4,LOWORD(base));
126     tpl->cy =  MulDiv(size.cy,8,HIWORD(base)) * 2;
127
128     ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE);
129     *(LPWORD)ptr = 0xffff;
130     ptr += sizeof(WORD);
131     *(LPWORD)ptr = 0x0081;
132     ptr += sizeof(WORD);
133     ptr += MultiByteToWideChar(CP_ACP,0,text,-1,(LPWSTR)ptr,len) * sizeof(WCHAR);
134     *(LPWORD)ptr = 0x0000;
135
136     *template_out = tpl;
137     return len;
138 }
139
140
141 static int create_item(activeDS *pSource,HDC hdc, const SANE_Option_Descriptor *opt,
142         INT id, LPDLGITEMTEMPLATEW *template_out, int y, int *cx, int* count)
143 {
144     LPDLGITEMTEMPLATEW tpl = NULL,rc = NULL;
145     WORD class = 0xffff;
146     DWORD styles = WS_VISIBLE;
147     LPBYTE ptr = NULL;
148     LPDLGITEMTEMPLATEW lead_static = NULL;
149     LPDLGITEMTEMPLATEW trail_edit = NULL;
150     DWORD leading_len = 0;
151     DWORD trail_len = 0;
152     DWORD local_len = 0;
153     LPCSTR title = NULL;
154     CHAR buffer[255];
155     int padding = 0;
156     int padding2 = 0;
157     int base_x = 0;
158     LONG base;
159     int ctl_cx = 0;
160     SIZE size;
161
162     GetTextExtentPoint32A(hdc,"X",1,&size);
163     base = GetDialogBaseUnits();
164     base_x = MulDiv(size.cx,4,LOWORD(base));
165
166     if (opt->type == SANE_TYPE_BOOL)
167     {
168         class = 0x0080; /* Button */
169         styles |= BS_AUTOCHECKBOX;
170         local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
171         local_len *= sizeof(WCHAR);
172         title = opt->title;
173     }
174     else if (opt->type == SANE_TYPE_INT)
175     {
176         SANE_Int i;
177
178         sane_control_option(pSource->deviceHandle, id-ID_BASE,
179                 SANE_ACTION_GET_VALUE, &i,NULL);
180
181         sprintf(buffer,"%i",i);
182
183         if (opt->constraint_type == SANE_CONSTRAINT_NONE)
184         {
185             class = 0x0081; /* Edit*/
186             styles |= ES_NUMBER;
187             title = buffer;
188             local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0);
189             local_len *= sizeof(WCHAR);
190         }
191         else if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
192         {
193             class = 0x0084; /* scroll */
194             ctl_cx = 10 * base_x;
195             trail_len += create_trailing_edit(hdc, &trail_edit, id +
196                     ID_EDIT_BASE, y,buffer,TRUE);
197         }
198         else
199         {
200             class= 0x0085; /* Combo */
201             ctl_cx = 10 * base_x;
202             styles |= CBS_DROPDOWNLIST;
203         }
204         leading_len += create_leading_static(hdc, opt->title, &lead_static, y, 
205                 id+ID_STATIC_BASE);
206     }
207     else if (opt->type == SANE_TYPE_FIXED)
208     {
209         SANE_Fixed *i;
210         double dd;
211
212         i = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
213
214         sane_control_option(pSource->deviceHandle, id-ID_BASE,
215                 SANE_ACTION_GET_VALUE, i, NULL);
216
217         dd = SANE_UNFIX(*i);
218         sprintf(buffer,"%f",dd);
219         HeapFree(GetProcessHeap(),0,i);
220
221         if (opt->constraint_type == SANE_CONSTRAINT_NONE)
222         {
223             class = 0x0081; /* Edit */
224             title = buffer;
225             local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0);
226             local_len *= sizeof(WCHAR);
227         }
228         else if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
229         {
230             class= 0x0084; /* scroll */
231             ctl_cx = 10 * base_x;
232             trail_len += create_trailing_edit(hdc, &trail_edit, id +
233                     ID_EDIT_BASE, y,buffer,FALSE);
234         }
235         else
236         {
237             class= 0x0085; /* Combo */
238             ctl_cx = 10 * base_x;
239             styles |= CBS_DROPDOWNLIST;
240         }
241         leading_len += create_leading_static(hdc, opt->title, &lead_static, y,
242                 id+ID_STATIC_BASE);
243     }
244     else if (opt->type == SANE_TYPE_STRING)
245     {
246         if (opt->constraint_type == SANE_CONSTRAINT_NONE)
247         {
248             class = 0x0081; /* Edit*/
249         }
250         else
251         {
252             class= 0x0085; /* Combo */
253             ctl_cx = opt->size * base_x;
254             styles |= CBS_DROPDOWNLIST;
255         }
256         leading_len += create_leading_static(hdc, opt->title, &lead_static, y,
257                 id+ID_STATIC_BASE);
258         sane_control_option(pSource->deviceHandle, id-ID_BASE,
259                 SANE_ACTION_GET_VALUE, buffer,NULL);
260
261         title = buffer;
262         local_len += MultiByteToWideChar(CP_ACP,0,title,-1,NULL,0);
263         local_len *= sizeof(WCHAR);
264     }
265     else if (opt->type == SANE_TYPE_BUTTON)
266     {
267         class = 0x0080; /* Button */
268         local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
269         local_len *= sizeof(WCHAR);
270         title = opt->title;
271     }
272     else if (opt->type == SANE_TYPE_GROUP)
273     {
274         class = 0x0080; /* Button */
275         styles |= BS_GROUPBOX;
276         local_len += MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
277         local_len *= sizeof(WCHAR);
278         title = opt->title;
279     }
280
281     local_len += sizeof(DLGITEMTEMPLATE);
282     if (title)
283         local_len += 3*sizeof(WORD);
284     else
285         local_len += 4*sizeof(WORD);
286
287     if (lead_static)
288     {
289         padding = leading_len % sizeof(DWORD);
290         rc = HeapReAlloc(GetProcessHeap(),0,lead_static,leading_len+local_len + padding);
291         tpl = (LPDLGITEMTEMPLATEW)((LPBYTE)rc + leading_len + padding);
292     }   
293     else
294         rc = tpl = HeapAlloc(GetProcessHeap(),0,local_len);
295
296     tpl->style=styles;
297     tpl->dwExtendedStyle = 0;
298     if (lead_static)
299         tpl->x = lead_static->x + lead_static->cx + 1;
300     else if (opt->type == SANE_TYPE_GROUP)
301         tpl->x = 2;
302     else
303         tpl->x = 4;
304     tpl->y = y;
305     tpl->id = id;
306
307     if (title)
308     {
309         GetTextExtentPoint32A(hdc,title,lstrlenA(title),&size);
310         tpl->cx = size.cx;
311         tpl->cy = size.cy;
312     }
313     else
314     {
315         if (lead_static)
316             tpl->cy = lead_static->cy;
317         else
318             tpl->cy = 15;
319
320         if (!ctl_cx)
321             ctl_cx = 15;
322
323         tpl->cx = ctl_cx;
324     }
325     ptr = (LPBYTE)tpl + sizeof(DLGITEMTEMPLATE);
326     *(LPWORD)ptr = 0xffff;
327     ptr += sizeof(WORD);
328     *(LPWORD)ptr = class;
329     ptr += sizeof(WORD);
330     if (title)
331     {
332         ptr += MultiByteToWideChar(CP_ACP,0,title,-1,(LPWSTR)ptr,local_len) * sizeof(WCHAR);
333     }
334     else
335     {
336         *(LPWORD)ptr = 0x0000;
337         ptr += sizeof(WORD);
338     }
339
340     *((LPWORD)ptr) = 0x0000;
341     ptr += sizeof(WORD);
342
343     if (trail_edit)
344     {
345         trail_edit->x = tpl->cx + tpl->x + 2;
346         *cx = trail_edit->x + trail_edit->cx;
347
348         padding2 = (leading_len + local_len + padding)% sizeof(DWORD);
349
350         rc = HeapReAlloc(GetProcessHeap(),0,rc,leading_len+local_len + padding
351                 +padding2+trail_len);
352
353         memcpy(((LPBYTE)rc) + leading_len + local_len + padding + padding2,
354                 trail_edit,trail_len);
355     }   
356     else
357         *cx = tpl->cx + tpl->x;
358     
359     *template_out = rc;
360     if (leading_len)
361         *count = 2;
362     else
363         *count = 1;
364
365     if (trail_edit)
366         *count+=1;
367
368     return leading_len + local_len + padding + padding2 + trail_len;
369 }
370
371
372 static LPDLGTEMPLATEW create_options_page(HDC hdc, activeDS *pSource, int *from_index,
373                                           BOOL split_tabs)
374 {
375     SANE_Status rc;
376     SANE_Int optcount;
377     int i;
378     INT y = 2;
379     LPDLGTEMPLATEW tpl = NULL;
380     LPBYTE all_controls = NULL;
381     DWORD control_len = 0;
382     int max_cx = 0;
383     int group_max_cx = 0;
384     LPBYTE ptr;
385     int group_offset = -1;
386     INT control_count = 0;
387
388     rc = sane_control_option(pSource->deviceHandle, 0, SANE_ACTION_GET_VALUE, 
389             &optcount, NULL);
390
391     if (rc != SANE_STATUS_GOOD)
392     {
393         ERR("Unable to read number of options\n");
394         return NULL;
395     }
396
397     for (i = *from_index; i < optcount; i++)
398     {
399         LPDLGITEMTEMPLATEW item_tpl = NULL;
400         const SANE_Option_Descriptor *opt;
401         int len;
402         int padding = 0;
403         int x;
404         int count;
405         int hold_for_group = 0;
406
407         opt = sane_get_option_descriptor(pSource->deviceHandle, i);
408         if (opt->type == SANE_TYPE_GROUP && split_tabs)
409         {
410             if (control_len > 0)
411             {
412                 *from_index = i - 1;
413                 goto exit;
414             }
415             else
416             {
417                 *from_index = i;
418                 return NULL;
419             }
420         }
421
422         len = create_item(pSource, hdc, opt, ID_BASE + i, &item_tpl, y, &x,
423                 &count);
424
425         control_count += count;
426
427         if (!len)
428         {
429             continue;
430         }
431
432         hold_for_group = y;
433         y+= item_tpl->cy + 1;
434         max_cx = max(max_cx, x + 2);
435         group_max_cx = max(group_max_cx, x );
436
437         padding = len % sizeof(DWORD);
438
439         if (all_controls)
440         {
441             LPBYTE newone;
442             newone = HeapReAlloc(GetProcessHeap(),0,all_controls,
443                     control_len + len + padding);
444             all_controls = newone;
445             memcpy(all_controls+control_len,item_tpl,len);
446             memset(all_controls+control_len+len,0xca,padding);
447             HeapFree(GetProcessHeap(),0,item_tpl);
448         }
449         else
450         {
451             if (!padding)
452             {
453                 all_controls = (LPBYTE)item_tpl;
454             }
455             else
456             {
457                 all_controls = HeapAlloc(GetProcessHeap(),0,len + padding);
458                 memcpy(all_controls,item_tpl,len);
459                 memset(all_controls+len,0xcb,padding);
460                 HeapFree(GetProcessHeap(),0,item_tpl);
461             }
462         }
463
464         if (opt->type == SANE_TYPE_GROUP)
465         {
466             if (group_offset == -1)
467             {
468                 group_offset  = control_len;
469                 group_max_cx = 0;
470             }
471             else
472             {
473                 LPDLGITEMTEMPLATEW group =
474                     (LPDLGITEMTEMPLATEW)(all_controls+group_offset);
475
476                 group->cy = hold_for_group - group->y;
477                 group->cx = group_max_cx;
478
479                 group = (LPDLGITEMTEMPLATEW)(all_controls+control_len);
480                 group->y += 2;
481                 y+=2;
482                 group_max_cx = 0;
483                 group_offset  = control_len;
484             }
485         }
486
487         control_len += len + padding;
488     }
489
490     if ( group_offset && !split_tabs )
491     {
492         LPDLGITEMTEMPLATEW group =
493             (LPDLGITEMTEMPLATEW)(all_controls+group_offset);
494         group->cy = y - group->y;
495         group->cx = group_max_cx;
496         y+=2;
497     }
498
499     *from_index = i-1;
500 exit:
501
502     tpl = HeapAlloc(GetProcessHeap(),0,sizeof(DLGTEMPLATE) + 3*sizeof(WORD) + 
503             control_len);
504
505     tpl->style = WS_VISIBLE | WS_OVERLAPPEDWINDOW;
506     tpl->dwExtendedStyle = 0;
507     tpl->cdit = control_count;
508     tpl->x = 0;
509     tpl->y = 0;
510     tpl->cx = max_cx + 10;
511     tpl->cy = y + 10;
512     ptr = (LPBYTE)tpl + sizeof(DLGTEMPLATE);
513     *(LPWORD)ptr = 0x0000;
514     ptr+=sizeof(WORD);
515     *(LPWORD)ptr = 0x0000;
516     ptr+=sizeof(WORD);
517     *(LPWORD)ptr = 0x0000;
518     ptr+=sizeof(WORD);
519     memcpy(ptr,all_controls,control_len);
520
521     HeapFree(GetProcessHeap(),0,all_controls);
522
523     return tpl;
524 }
525
526 BOOL DoScannerUI(activeDS *pSource)
527 {
528     HDC hdc;
529     PROPSHEETPAGEW psp[10];
530     int page_count= 0;
531     PROPSHEETHEADERW psh;
532     int index = 1;
533     SANE_Status rc;
534     SANE_Int optcount;
535     UINT psrc;
536     LPWSTR szCaption;
537     DWORD len;
538
539     hdc = GetDC(0);
540
541     memset(&psp,0,sizeof(psp));
542     rc = sane_control_option(pSource->deviceHandle, 0, SANE_ACTION_GET_VALUE, 
543             &optcount, NULL);
544
545     while (index < optcount)
546     {
547         const SANE_Option_Descriptor *opt;
548         psp[page_count].u.pResource = create_options_page(hdc, pSource, &index,
549                 TRUE);
550         opt = sane_get_option_descriptor(pSource->deviceHandle, index);
551
552         if (opt->type == SANE_TYPE_GROUP)
553         {
554             LPWSTR title = NULL;
555             INT len;
556
557             len = MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
558             title = HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR));
559             MultiByteToWideChar(CP_ACP,0,opt->title,-1,title,len);
560
561             psp[page_count].pszTitle = title;
562         }
563
564         if (psp[page_count].u.pResource)
565         {
566             psp[page_count].dwSize = sizeof(PROPSHEETPAGEW);
567             psp[page_count].dwFlags =  PSP_DLGINDIRECT | PSP_USETITLE;
568             psp[page_count].hInstance = DSM_instance;
569             psp[page_count].pfnDlgProc = DialogProc;
570             psp[page_count].lParam = (LPARAM)pSource;
571             page_count ++;
572         }
573        
574         index ++;
575     }
576  
577     len = lstrlenA(device_list[pSource->deviceIndex]->vendor)
578          + lstrlenA(device_list[pSource->deviceIndex]->model) + 2;
579
580     szCaption = HeapAlloc(GetProcessHeap(),0,len *sizeof(WCHAR));
581     MultiByteToWideChar(CP_ACP,0,device_list[pSource->deviceIndex]->vendor,-1,
582             szCaption,len);
583     szCaption[lstrlenA(device_list[pSource->deviceIndex]->vendor)] = ' ';
584     MultiByteToWideChar(CP_ACP,0,device_list[pSource->deviceIndex]->model,-1,
585             &szCaption[lstrlenA(device_list[pSource->deviceIndex]->vendor)+1],len);
586     
587     psh.dwSize = sizeof(PROPSHEETHEADERW);
588     psh.dwFlags = PSH_PROPSHEETPAGE|PSH_PROPTITLE|PSH_USECALLBACK;
589     psh.hwndParent = DSM_parentHWND;
590     psh.hInstance = DSM_instance;
591     psh.u.pszIcon = 0;
592     psh.pszCaption = szCaption;
593     psh.nPages = page_count;
594     psh.u2.nStartPage = 0;
595     psh.u3.ppsp = (LPCPROPSHEETPAGEW) &psp;
596     psh.pfnCallback = PropSheetProc;
597
598     psrc = PropertySheetW(&psh);
599
600     for(index = 0; index < page_count; index ++)
601     {
602         HeapFree(GetProcessHeap(),0,(LPBYTE)psp[index].u.pResource);
603         HeapFree(GetProcessHeap(),0,(LPBYTE)psp[index].pszTitle);
604     }
605     
606     if (psrc == IDOK)
607         return TRUE;
608     else
609         return FALSE;
610 }
611
612 static void UpdateRelevantEdit(HWND hwnd, const SANE_Option_Descriptor *opt, 
613         int index, int position)
614 {
615     CHAR buffer[244];
616     HWND edit_w;
617     CHAR unit[20];
618
619     LoadStringA(DSM_instance, opt->unit, unit,20);
620
621     if (opt->type == SANE_TYPE_INT)
622     {
623         INT si;
624
625         if (opt->constraint.range->quant)
626             si = position * opt->constraint.range->quant;
627         else
628             si = position;
629
630         sprintf(buffer,"%i %s",si,unit);
631
632     }
633     else if  (opt->type == SANE_TYPE_FIXED)
634     {
635         double s_quant, dd;
636
637         s_quant = SANE_UNFIX(opt->constraint.range->quant);
638
639         if (s_quant)
640             dd = position * s_quant;
641         else
642             dd = position * 0.01;
643
644         sprintf(buffer,"%f %s",dd,unit);
645     }
646     else
647         buffer[0] = 0;
648
649     edit_w = GetDlgItem(hwnd,index+ID_BASE+ID_EDIT_BASE);
650     if (edit_w && buffer[0])
651         SetWindowTextA(edit_w,buffer);
652
653 }
654
655 static BOOL UpdateSaneScrollOption(activeDS *pSource, 
656         const SANE_Option_Descriptor *opt, int index, DWORD position)
657 {
658     SANE_Status rc = SANE_STATUS_GOOD;  
659     SANE_Int result = 0;
660
661     if (opt->type == SANE_TYPE_INT)
662     {
663         SANE_Int si;
664
665         if (opt->constraint.range->quant)
666             si = position * opt->constraint.range->quant;
667         else
668             si = position;
669
670         rc = sane_control_option (pSource->deviceHandle,index,
671             SANE_ACTION_SET_VALUE, &si, &result);
672
673     }
674     else if  (opt->type == SANE_TYPE_FIXED)
675     {
676         double s_quant, dd;
677         SANE_Fixed *sf;
678
679         s_quant = SANE_UNFIX(opt->constraint.range->quant);
680
681         if (s_quant)
682             dd = position * s_quant;
683         else
684             dd = position * 0.01;
685
686         sf = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
687
688         *sf = SANE_FIX(dd);
689
690         rc = sane_control_option (pSource->deviceHandle,index,
691             SANE_ACTION_SET_VALUE, sf, &result);
692
693         HeapFree(GetProcessHeap(),0,sf);
694     }
695
696     if(rc == SANE_STATUS_GOOD)
697     {
698         if (result & SANE_INFO_RELOAD_OPTIONS || 
699                 result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
700             return TRUE;
701     }
702     return FALSE;
703 }
704
705 static BOOL UpdateSaneBoolOption(activeDS *pSource, int index, BOOL position)
706 {
707     SANE_Status rc = SANE_STATUS_GOOD;  
708     SANE_Int result = 0;
709     SANE_Bool si;
710
711     si = position;
712
713     rc = sane_control_option (pSource->deviceHandle,index,
714             SANE_ACTION_SET_VALUE, &si, &result);
715
716     if(rc == SANE_STATUS_GOOD)
717     {
718         if (result & SANE_INFO_RELOAD_OPTIONS || 
719                 result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
720             return TRUE;
721     }
722     return FALSE;
723 }
724
725 static BOOL UpdateSaneStringOption(activeDS *pSource, int index, 
726         SANE_String value)
727 {
728     SANE_Status rc = SANE_STATUS_GOOD;  
729     SANE_Int result = 0;
730
731     rc = sane_control_option (pSource->deviceHandle,index,
732             SANE_ACTION_SET_VALUE, value, &result);
733
734     if(rc == SANE_STATUS_GOOD)
735     {
736         if (result & SANE_INFO_RELOAD_OPTIONS || 
737                 result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
738             return TRUE;
739     }
740     return FALSE;
741 }
742
743 static INT_PTR InitializeDialog(HWND hwnd, activeDS *pSource)
744 {
745     SANE_Status rc;
746     SANE_Int optcount;
747     HWND control;
748     int i;
749
750     rc = sane_control_option(pSource->deviceHandle, 0, SANE_ACTION_GET_VALUE, 
751             &optcount, NULL);
752
753     for ( i = 1; i < optcount; i++)
754     {
755         const SANE_Option_Descriptor *opt;
756
757         control = GetDlgItem(hwnd,i+ID_BASE);
758
759         if (!control)
760             continue;
761
762         opt = sane_get_option_descriptor(pSource->deviceHandle, i);
763
764         TRACE("%i %s %i %i\n",i,opt->title,opt->type,opt->constraint_type);
765         
766         if (!SANE_OPTION_IS_ACTIVE(opt->cap))
767             EnableWindow(control,FALSE);
768         else
769             EnableWindow(control,TRUE);
770
771         SendMessageA(control,CB_RESETCONTENT,0,0);
772         /* initialize values */
773         if (opt->type == SANE_TYPE_STRING && opt->constraint_type !=
774                 SANE_CONSTRAINT_NONE)
775         {
776             int j = 0;
777             CHAR buffer[255];
778             while (opt->constraint.string_list[j]!=NULL)
779             {
780                 SendMessageA(control,CB_ADDSTRING,0,
781                         (LPARAM)opt->constraint.string_list[j]);
782                 j++;
783             }
784             sane_control_option(pSource->deviceHandle, i, SANE_ACTION_GET_VALUE, buffer,NULL);
785             SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer);
786         }
787         else if (opt->type == SANE_TYPE_BOOL)
788         {
789             SANE_Bool b;
790             sane_control_option(pSource->deviceHandle, i,
791                     SANE_ACTION_GET_VALUE, &b, NULL);
792             if (b)
793                 SendMessageA(control,BM_SETCHECK,BST_CHECKED,0);
794
795         }
796         else if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
797         {
798             if (opt->type == SANE_TYPE_INT)
799             {
800                 SANE_Int si;
801                 int min,max;
802
803                 min = (SANE_Int)opt->constraint.range->min /
804                     (((SANE_Int)opt->constraint.range->quant)?
805                     (SANE_Int)opt->constraint.range->quant:1);
806
807                 max = (SANE_Int)opt->constraint.range->max /
808                     (((SANE_Int)opt->constraint.range->quant)
809                     ?(SANE_Int)opt->constraint.range->quant:1);
810
811                 SendMessageA(control,SBM_SETRANGE,min,max);
812
813                 sane_control_option(pSource->deviceHandle, i,
814                         SANE_ACTION_GET_VALUE, &si,NULL);
815                 if (opt->constraint.range->quant)
816                     si = si / opt->constraint.range->quant;
817
818                 SendMessageW(control,SBM_SETPOS, si, TRUE);
819                 UpdateRelevantEdit(hwnd, opt, i, si);
820             }
821             else if (opt->type == SANE_TYPE_FIXED)
822             {
823                 SANE_Fixed *sf;
824
825                 double dd;
826                 double s_min,s_max,s_quant;
827                 INT pos;
828                 int min,max;
829
830                 s_min = SANE_UNFIX(opt->constraint.range->min);
831                 s_max = SANE_UNFIX(opt->constraint.range->max);
832                 s_quant = SANE_UNFIX(opt->constraint.range->quant);
833
834                 if (s_quant)
835                 {
836                     min = (s_min / s_quant);
837                     max = (s_max / s_quant);
838                 }
839                 else
840                 {
841                     min = s_min / 0.01;
842                     max = s_max / 0.01;
843                 }
844
845                 SendMessageA(control,SBM_SETRANGE,min,max);
846
847
848                 sf = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
849                 sane_control_option(pSource->deviceHandle, i,
850                         SANE_ACTION_GET_VALUE, sf,NULL);
851
852                 dd = SANE_UNFIX(*sf);
853                 HeapFree(GetProcessHeap(),0,sf);
854
855                 if (s_quant)
856                     pos = (dd / s_quant);
857                 else
858                     pos = dd / 0.01;
859
860                 SendMessageW(control, SBM_SETPOS, pos, TRUE);
861                 
862                 UpdateRelevantEdit(hwnd, opt, i, pos);
863             }
864         }
865     }
866
867     return TRUE;
868 }
869
870 static INT_PTR ProcessScroll(HWND hwnd, activeDS *pSource, WPARAM wParam, 
871         LPARAM lParam)
872 {
873     int index;
874     const SANE_Option_Descriptor *opt;
875     WORD scroll;
876     DWORD position;
877
878     index = GetDlgCtrlID((HWND)lParam)- ID_BASE;
879     if (index < 0)
880         return FALSE;
881
882     opt = sane_get_option_descriptor(pSource->deviceHandle, index);
883
884     if (!opt)
885         return FALSE;
886
887     scroll = LOWORD(wParam);
888
889     switch (scroll)
890     {
891         case SB_THUMBTRACK:
892         case SB_THUMBPOSITION:
893         {
894             SCROLLINFO si;
895             si.cbSize = sizeof(SCROLLINFO);
896             si.fMask = SIF_TRACKPOS;
897             GetScrollInfo((HWND)lParam,SB_CTL, &si);
898             position = si.nTrackPos;
899         }
900             break;
901         case SB_LEFT:
902         case SB_LINELEFT:
903         case SB_PAGELEFT:
904             position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
905             position--;
906             break;
907         case SB_RIGHT:
908         case SB_LINERIGHT:
909         case SB_PAGERIGHT:
910             position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
911             position++;
912             break;
913         default:
914             position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
915     }
916
917     SendMessageW((HWND)lParam,SBM_SETPOS, position, TRUE);
918     position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
919
920     UpdateRelevantEdit(hwnd, opt, index, position);
921     if (UpdateSaneScrollOption(pSource, opt, index, position))
922         InitializeDialog(hwnd,pSource);
923
924     return TRUE;
925 }
926
927
928 static void ButtonClicked(HWND hwnd, activeDS* pSource,INT id, HWND control)
929 {
930     int index;
931     const SANE_Option_Descriptor *opt;
932
933     index = id - ID_BASE;
934     if (index < 0)
935         return;
936
937     opt = sane_get_option_descriptor(pSource->deviceHandle, index);
938
939     if (!opt)
940         return;
941
942     if (opt->type == SANE_TYPE_BOOL)
943     {
944         BOOL r = SendMessageW(control,BM_GETCHECK,0,0)==BST_CHECKED;
945         if (UpdateSaneBoolOption(pSource, index, r))
946                 InitializeDialog(hwnd,pSource);
947     }
948 }
949
950 static void ComboChanged(HWND hwnd, activeDS* pSource,INT id, HWND control)
951 {
952     int index;
953     int selection;
954     int len;
955     const SANE_Option_Descriptor *opt;
956     SANE_String value;
957
958     index = id - ID_BASE;
959     if (index < 0)
960         return;
961
962     opt = sane_get_option_descriptor(pSource->deviceHandle, index);
963
964     if (!opt)
965         return;
966
967     selection = SendMessageW(control,CB_GETCURSEL,0,0);
968     len = SendMessageW(control,CB_GETLBTEXTLEN,selection,0);
969
970     len++;
971     value = HeapAlloc(GetProcessHeap(),0,len);
972     SendMessageA(control,CB_GETLBTEXT,selection,(LPARAM)value);
973
974     if (opt->type == SANE_TYPE_STRING)
975     {
976         if (UpdateSaneStringOption(pSource, index, value))
977                 InitializeDialog(hwnd,pSource);
978     }
979 }
980
981
982 static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
983 {
984     static activeDS *pSource = NULL;
985
986     switch (msg)
987     {
988         case WM_INITDIALOG:
989             pSource = (activeDS*)((LPPROPSHEETPAGEW)lParam)->lParam;
990             return InitializeDialog(hwndDlg, pSource);
991         case WM_HSCROLL:
992             return ProcessScroll(hwndDlg, pSource, wParam, lParam);
993             break;
994         case WM_NOTIFY:
995             {
996                 LPPSHNOTIFY psn = (LPPSHNOTIFY)lParam;
997                 switch (((NMHDR*)lParam)->code)
998                 {
999                     case PSN_APPLY:
1000                         if (psn->lParam == TRUE)
1001                         {
1002                             pSource->currentState = 6;
1003                             pSource->pendingEvent.TWMessage = MSG_XFERREADY;
1004                         }
1005                         break;
1006                     case PSN_QUERYCANCEL:
1007                         pSource->pendingEvent.TWMessage = MSG_CLOSEDSREQ;
1008                         break;
1009                     case PSN_SETACTIVE:
1010                         InitializeDialog(hwndDlg, pSource);
1011                         break;
1012                 }
1013             }
1014         case WM_COMMAND:
1015             {
1016                 switch (HIWORD(wParam))
1017                 {
1018                     case BN_CLICKED:
1019                         ButtonClicked(hwndDlg, pSource,LOWORD(wParam),
1020                                 (HWND)lParam);
1021                         break;
1022                     case CBN_SELCHANGE:
1023                         ComboChanged(hwndDlg, pSource,LOWORD(wParam),
1024                                 (HWND)lParam);
1025                 }
1026             }
1027     }
1028
1029     return FALSE;
1030 }
1031
1032 static int CALLBACK PropSheetProc(HWND hwnd, UINT msg, LPARAM lParam)
1033 {
1034     if (msg == PSCB_INITIALIZED)
1035     {
1036         /* rename OK button to Scan */
1037         HWND scan = GetDlgItem(hwnd,IDOK);
1038         SetWindowTextA(scan,"Scan");
1039     }
1040     return TRUE;
1041 }
1042
1043
1044 static INT_PTR CALLBACK ScanningProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1045 {
1046     return FALSE;
1047 }
1048
1049 HWND ScanningDialogBox(HWND dialog, DWORD progress)
1050 {
1051     if (!dialog)
1052         dialog = CreateDialogW(DSM_instance,
1053                 (LPWSTR)MAKEINTRESOURCE(IDD_DIALOG1), NULL, ScanningProc);
1054
1055     if (progress == -1)
1056     {
1057         EndDialog(dialog,0);
1058         return NULL;
1059     }
1060
1061     RedrawWindow(dialog,NULL,NULL,
1062             RDW_INTERNALPAINT|RDW_UPDATENOW|RDW_ALLCHILDREN);
1063
1064     return dialog;
1065 }
1066
1067 #else  /* HAVE_SANE */
1068
1069 BOOL DoScannerUI(activeDS *pSource)
1070 {
1071     return FALSE;
1072 }
1073
1074 #endif  /* HAVE_SANE */