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