msxml3: Implement get_xml.
[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 "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 "sane_i.h"
37 #include "wine/debug.h"
38 #include "resource.h"
39
40 #ifdef SONAME_LIBSANE
41
42 WINE_DEFAULT_DEBUG_CHANNEL(twain);
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(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         psane_control_option(activeDS.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         psane_control_option(activeDS.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         psane_control_option(activeDS.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, int *from_index,
373                                           SANE_Int optcount, BOOL split_tabs)
374 {
375     int i;
376     INT y = 2;
377     LPDLGTEMPLATEW tpl = NULL;
378     LPBYTE all_controls = NULL;
379     DWORD control_len = 0;
380     int max_cx = 0;
381     int group_max_cx = 0;
382     LPBYTE ptr;
383     int group_offset = -1;
384     INT control_count = 0;
385
386     for (i = *from_index; i < optcount; i++)
387     {
388         LPDLGITEMTEMPLATEW item_tpl = NULL;
389         const SANE_Option_Descriptor *opt;
390         int len;
391         int padding = 0;
392         int x;
393         int count;
394         int hold_for_group = 0;
395
396         opt = psane_get_option_descriptor(activeDS.deviceHandle, i);
397         if (!opt)
398             continue;
399         if (opt->type == SANE_TYPE_GROUP && split_tabs)
400         {
401             if (control_len > 0)
402             {
403                 *from_index = i - 1;
404                 goto exit;
405             }
406             else
407             {
408                 *from_index = i;
409                 return NULL;
410             }
411         }
412         if (!SANE_OPTION_IS_ACTIVE (opt->cap))
413             continue;
414
415         len = create_item(hdc, opt, ID_BASE + i, &item_tpl, y, &x, &count);
416
417         control_count += count;
418
419         if (!len)
420         {
421             continue;
422         }
423
424         hold_for_group = y;
425         y+= item_tpl->cy + 1;
426         max_cx = max(max_cx, x + 2);
427         group_max_cx = max(group_max_cx, x );
428
429         padding = len % sizeof(DWORD);
430
431         if (all_controls)
432         {
433             LPBYTE newone;
434             newone = HeapReAlloc(GetProcessHeap(),0,all_controls,
435                     control_len + len + padding);
436             all_controls = newone;
437             memcpy(all_controls+control_len,item_tpl,len);
438             memset(all_controls+control_len+len,0xca,padding);
439             HeapFree(GetProcessHeap(),0,item_tpl);
440         }
441         else
442         {
443             if (!padding)
444             {
445                 all_controls = (LPBYTE)item_tpl;
446             }
447             else
448             {
449                 all_controls = HeapAlloc(GetProcessHeap(),0,len + padding);
450                 memcpy(all_controls,item_tpl,len);
451                 memset(all_controls+len,0xcb,padding);
452                 HeapFree(GetProcessHeap(),0,item_tpl);
453             }
454         }
455
456         if (opt->type == SANE_TYPE_GROUP)
457         {
458             if (group_offset == -1)
459             {
460                 group_offset  = control_len;
461                 group_max_cx = 0;
462             }
463             else
464             {
465                 LPDLGITEMTEMPLATEW group =
466                     (LPDLGITEMTEMPLATEW)(all_controls+group_offset);
467
468                 group->cy = hold_for_group - group->y;
469                 group->cx = group_max_cx;
470
471                 group = (LPDLGITEMTEMPLATEW)(all_controls+control_len);
472                 group->y += 2;
473                 y+=2;
474                 group_max_cx = 0;
475                 group_offset  = control_len;
476             }
477         }
478
479         control_len += len + padding;
480     }
481
482     if ( group_offset && !split_tabs )
483     {
484         LPDLGITEMTEMPLATEW group =
485             (LPDLGITEMTEMPLATEW)(all_controls+group_offset);
486         group->cy = y - group->y;
487         group->cx = group_max_cx;
488         y+=2;
489     }
490
491     *from_index = i-1;
492 exit:
493
494     tpl = HeapAlloc(GetProcessHeap(),0,sizeof(DLGTEMPLATE) + 3*sizeof(WORD) + 
495             control_len);
496
497     tpl->style = WS_VISIBLE | WS_OVERLAPPEDWINDOW;
498     tpl->dwExtendedStyle = 0;
499     tpl->cdit = control_count;
500     tpl->x = 0;
501     tpl->y = 0;
502     tpl->cx = max_cx + 10;
503     tpl->cy = y + 10;
504     ptr = (LPBYTE)tpl + sizeof(DLGTEMPLATE);
505     *(LPWORD)ptr = 0x0000;
506     ptr+=sizeof(WORD);
507     *(LPWORD)ptr = 0x0000;
508     ptr+=sizeof(WORD);
509     *(LPWORD)ptr = 0x0000;
510     ptr+=sizeof(WORD);
511     memcpy(ptr,all_controls,control_len);
512
513     HeapFree(GetProcessHeap(),0,all_controls);
514
515     return tpl;
516 }
517
518 BOOL DoScannerUI(void)
519 {
520     HDC hdc;
521     PROPSHEETPAGEW psp[10];
522     int page_count= 0;
523     PROPSHEETHEADERW psh;
524     int index = 1;
525     SANE_Status rc;
526     SANE_Int optcount;
527     UINT psrc;
528     LPWSTR szCaption;
529     DWORD len;
530
531     hdc = GetDC(0);
532
533     memset(&psp,0,sizeof(psp));
534     rc = psane_control_option(activeDS.deviceHandle, 0, SANE_ACTION_GET_VALUE,
535             &optcount, NULL);
536     if (rc != SANE_STATUS_GOOD)
537     {
538         ERR("Unable to read number of options\n");
539         return FALSE;
540     }
541
542     while (index < optcount)
543     {
544         const SANE_Option_Descriptor *opt;
545         psp[page_count].u.pResource = create_options_page(hdc, &index,
546                 optcount, TRUE);
547         opt = psane_get_option_descriptor(activeDS.deviceHandle, index);
548
549         if (opt->type == SANE_TYPE_GROUP)
550         {
551             LPWSTR title = NULL;
552             INT len;
553
554             len = MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
555             title = HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR));
556             MultiByteToWideChar(CP_ACP,0,opt->title,-1,title,len);
557
558             psp[page_count].pszTitle = title;
559         }
560
561         if (psp[page_count].u.pResource)
562         {
563             psp[page_count].dwSize = sizeof(PROPSHEETPAGEW);
564             psp[page_count].dwFlags =  PSP_DLGINDIRECT | PSP_USETITLE;
565             psp[page_count].hInstance = SANE_instance;
566             psp[page_count].pfnDlgProc = DialogProc;
567             psp[page_count].lParam = (LPARAM)&activeDS;
568             page_count ++;
569         }
570        
571         index ++;
572     }
573  
574     len = lstrlenA(activeDS.identity.Manufacturer)
575          + lstrlenA(activeDS.identity.ProductName) + 2;
576     szCaption = HeapAlloc(GetProcessHeap(),0,len *sizeof(WCHAR));
577     MultiByteToWideChar(CP_ACP,0,activeDS.identity.Manufacturer,-1,
578             szCaption,len);
579     szCaption[lstrlenA(activeDS.identity.Manufacturer)] = ' ';
580     MultiByteToWideChar(CP_ACP,0,activeDS.identity.ProductName,-1,
581             &szCaption[lstrlenA(activeDS.identity.Manufacturer)+1],len);
582     psh.dwSize = sizeof(PROPSHEETHEADERW);
583     psh.dwFlags = PSH_PROPSHEETPAGE|PSH_PROPTITLE|PSH_USECALLBACK;
584     psh.hwndParent = activeDS.hwndOwner;
585     psh.hInstance = SANE_instance;
586     psh.u.pszIcon = 0;
587     psh.pszCaption = szCaption;
588     psh.nPages = page_count;
589     psh.u2.nStartPage = 0;
590     psh.u3.ppsp = (LPCPROPSHEETPAGEW) &psp;
591     psh.pfnCallback = PropSheetProc;
592
593     psrc = PropertySheetW(&psh);
594
595     for(index = 0; index < page_count; index ++)
596     {
597         HeapFree(GetProcessHeap(),0,(LPBYTE)psp[index].u.pResource);
598         HeapFree(GetProcessHeap(),0,(LPBYTE)psp[index].pszTitle);
599     }
600     HeapFree(GetProcessHeap(),0,szCaption);
601     
602     if (psrc == IDOK)
603         return TRUE;
604     else
605         return FALSE;
606 }
607
608 static void UpdateRelevantEdit(HWND hwnd, const SANE_Option_Descriptor *opt, 
609         int index, int position)
610 {
611     CHAR buffer[244];
612     HWND edit_w;
613     CHAR unit[20];
614
615     LoadStringA(SANE_instance, opt->unit, unit,20);
616
617     if (opt->type == SANE_TYPE_INT)
618     {
619         INT si;
620
621         if (opt->constraint.range->quant)
622             si = position * opt->constraint.range->quant;
623         else
624             si = position;
625
626         sprintf(buffer,"%i %s",si,unit);
627
628     }
629     else if  (opt->type == SANE_TYPE_FIXED)
630     {
631         double s_quant, dd;
632
633         s_quant = SANE_UNFIX(opt->constraint.range->quant);
634
635         if (s_quant)
636             dd = position * s_quant;
637         else
638             dd = position * 0.01;
639
640         sprintf(buffer,"%f %s",dd,unit);
641     }
642     else
643         buffer[0] = 0;
644
645     edit_w = GetDlgItem(hwnd,index+ID_BASE+ID_EDIT_BASE);
646     if (edit_w && buffer[0])
647         SetWindowTextA(edit_w,buffer);
648
649 }
650
651 static BOOL UpdateSaneScrollOption(
652         const SANE_Option_Descriptor *opt, int index, DWORD position)
653 {
654     SANE_Status rc = SANE_STATUS_GOOD;  
655     SANE_Int result = 0;
656
657     if (opt->type == SANE_TYPE_INT)
658     {
659         SANE_Int si;
660
661         if (opt->constraint.range->quant)
662             si = position * opt->constraint.range->quant;
663         else
664             si = position;
665
666         rc = psane_control_option (activeDS.deviceHandle,index,
667             SANE_ACTION_SET_VALUE, &si, &result);
668
669     }
670     else if  (opt->type == SANE_TYPE_FIXED)
671     {
672         double s_quant, dd;
673         SANE_Fixed *sf;
674
675         s_quant = SANE_UNFIX(opt->constraint.range->quant);
676
677         if (s_quant)
678             dd = position * s_quant;
679         else
680             dd = position * 0.01;
681
682         sf = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
683
684         *sf = SANE_FIX(dd);
685
686         rc = psane_control_option (activeDS.deviceHandle,index,
687             SANE_ACTION_SET_VALUE, sf, &result);
688
689         HeapFree(GetProcessHeap(),0,sf);
690     }
691
692     if(rc == SANE_STATUS_GOOD)
693     {
694         if (result & SANE_INFO_RELOAD_OPTIONS || 
695                 result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
696             return TRUE;
697     }
698     return FALSE;
699 }
700
701 static BOOL UpdateSaneBoolOption(int index, BOOL position)
702 {
703     SANE_Status rc = SANE_STATUS_GOOD;  
704     SANE_Int result = 0;
705     SANE_Bool si;
706
707     si = position;
708
709     rc = psane_control_option (activeDS.deviceHandle,index,
710             SANE_ACTION_SET_VALUE, &si, &result);
711
712     if(rc == SANE_STATUS_GOOD)
713     {
714         if (result & SANE_INFO_RELOAD_OPTIONS || 
715                 result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
716             return TRUE;
717     }
718     return FALSE;
719 }
720
721 static BOOL UpdateSaneStringOption(int index, SANE_String value)
722 {
723     SANE_Status rc = SANE_STATUS_GOOD;  
724     SANE_Int result = 0;
725
726     rc = psane_control_option (activeDS.deviceHandle,index,
727             SANE_ACTION_SET_VALUE, value, &result);
728
729     if(rc == SANE_STATUS_GOOD)
730     {
731         if (result & SANE_INFO_RELOAD_OPTIONS || 
732                 result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
733             return TRUE;
734     }
735     return FALSE;
736 }
737
738 static INT_PTR InitializeDialog(HWND hwnd)
739 {
740     SANE_Status rc;
741     SANE_Int optcount;
742     HWND control;
743     int i;
744
745     rc = psane_control_option(activeDS.deviceHandle, 0, SANE_ACTION_GET_VALUE,
746             &optcount, NULL);
747     if (rc != SANE_STATUS_GOOD)
748     {
749         ERR("Unable to read number of options\n");
750         return FALSE;
751     }
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 = psane_get_option_descriptor(activeDS.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             psane_control_option(activeDS.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             psane_control_option(activeDS.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                 psane_control_option(activeDS.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                 psane_control_option(activeDS.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, WPARAM wParam, LPARAM lParam)
871 {
872     int index;
873     const SANE_Option_Descriptor *opt;
874     WORD scroll;
875     DWORD position;
876
877     index = GetDlgCtrlID((HWND)lParam)- ID_BASE;
878     if (index < 0)
879         return FALSE;
880
881     opt = psane_get_option_descriptor(activeDS.deviceHandle, index);
882
883     if (!opt)
884         return FALSE;
885
886     scroll = LOWORD(wParam);
887
888     switch (scroll)
889     {
890         case SB_THUMBTRACK:
891         case SB_THUMBPOSITION:
892         {
893             SCROLLINFO si;
894             si.cbSize = sizeof(SCROLLINFO);
895             si.fMask = SIF_TRACKPOS;
896             GetScrollInfo((HWND)lParam,SB_CTL, &si);
897             position = si.nTrackPos;
898         }
899             break;
900         case SB_LEFT:
901         case SB_LINELEFT:
902         case SB_PAGELEFT:
903             position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
904             position--;
905             break;
906         case SB_RIGHT:
907         case SB_LINERIGHT:
908         case SB_PAGERIGHT:
909             position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
910             position++;
911             break;
912         default:
913             position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
914     }
915
916     SendMessageW((HWND)lParam,SBM_SETPOS, position, TRUE);
917     position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
918
919     UpdateRelevantEdit(hwnd, opt, index, position);
920     if (UpdateSaneScrollOption(opt, index, position))
921         InitializeDialog(hwnd);
922
923     return TRUE;
924 }
925
926
927 static void ButtonClicked(HWND hwnd, INT id, HWND control)
928 {
929     int index;
930     const SANE_Option_Descriptor *opt;
931
932     index = id - ID_BASE;
933     if (index < 0)
934         return;
935
936     opt = psane_get_option_descriptor(activeDS.deviceHandle, index);
937
938     if (!opt)
939         return;
940
941     if (opt->type == SANE_TYPE_BOOL)
942     {
943         BOOL r = SendMessageW(control,BM_GETCHECK,0,0)==BST_CHECKED;
944         if (UpdateSaneBoolOption(index, r))
945                 InitializeDialog(hwnd);
946     }
947 }
948
949 static void ComboChanged(HWND hwnd, INT id, HWND control)
950 {
951     int index;
952     int selection;
953     int len;
954     const SANE_Option_Descriptor *opt;
955     SANE_String value;
956
957     index = id - ID_BASE;
958     if (index < 0)
959         return;
960
961     opt = psane_get_option_descriptor(activeDS.deviceHandle, index);
962
963     if (!opt)
964         return;
965
966     selection = SendMessageW(control,CB_GETCURSEL,0,0);
967     len = SendMessageW(control,CB_GETLBTEXTLEN,selection,0);
968
969     len++;
970     value = HeapAlloc(GetProcessHeap(),0,len);
971     SendMessageA(control,CB_GETLBTEXT,selection,(LPARAM)value);
972
973     if (opt->type == SANE_TYPE_STRING)
974     {
975         if (UpdateSaneStringOption(index, value))
976                 InitializeDialog(hwnd);
977     }
978 }
979
980
981 static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
982 {
983     switch (msg)
984     {
985         case WM_INITDIALOG:
986             return InitializeDialog(hwndDlg);
987         case WM_HSCROLL:
988             return ProcessScroll(hwndDlg, wParam, lParam);
989         case WM_NOTIFY:
990             {
991                 LPPSHNOTIFY psn = (LPPSHNOTIFY)lParam;
992                 switch (((NMHDR*)lParam)->code)
993                 {
994                     case PSN_APPLY:
995                         if (psn->lParam == TRUE)
996                         {
997                             activeDS.currentState = 6;
998                             activeDS.pendingEvent.TWMessage = MSG_XFERREADY;
999                         }
1000                         break;
1001                     case PSN_QUERYCANCEL:
1002                         activeDS.pendingEvent.TWMessage = MSG_CLOSEDSREQ;
1003                         break;
1004                     case PSN_SETACTIVE:
1005                         InitializeDialog(hwndDlg);
1006                         break;
1007                 }
1008             }
1009         case WM_COMMAND:
1010             {
1011                 switch (HIWORD(wParam))
1012                 {
1013                     case BN_CLICKED:
1014                         ButtonClicked(hwndDlg,LOWORD(wParam),
1015                                 (HWND)lParam);
1016                         break;
1017                     case CBN_SELCHANGE:
1018                         ComboChanged(hwndDlg,LOWORD(wParam),
1019                                 (HWND)lParam);
1020                 }
1021             }
1022     }
1023
1024     return FALSE;
1025 }
1026
1027 static int CALLBACK PropSheetProc(HWND hwnd, UINT msg, LPARAM lParam)
1028 {
1029     if (msg == PSCB_INITIALIZED)
1030     {
1031         /* rename OK button to Scan */
1032         HWND scan = GetDlgItem(hwnd,IDOK);
1033         SetWindowTextA(scan,"Scan");
1034     }
1035     return TRUE;
1036 }
1037
1038
1039 static INT_PTR CALLBACK ScanningProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1040 {
1041     return FALSE;
1042 }
1043
1044 HWND ScanningDialogBox(HWND dialog, LONG progress)
1045 {
1046     if (!dialog)
1047         dialog = CreateDialogW(SANE_instance,
1048                 (LPWSTR)MAKEINTRESOURCE(IDD_DIALOG1), NULL, ScanningProc);
1049
1050     if (progress == -1)
1051     {
1052         EndDialog(dialog,0);
1053         return NULL;
1054     }
1055
1056     RedrawWindow(dialog,NULL,NULL,
1057             RDW_INTERNALPAINT|RDW_UPDATENOW|RDW_ALLCHILDREN);
1058
1059     return dialog;
1060 }
1061
1062 #else  /* SONAME_LIBSANE */
1063
1064 BOOL DoScannerUI(void)
1065 {
1066     return FALSE;
1067 }
1068
1069 #endif  /* SONAME_LIBSANE */