dinput: Keep the list of all the dinput devices created for each IDIrectInput object.
[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                                           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 = psane_control_option(activeDS.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 = psane_get_option_descriptor(activeDS.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(hdc, opt, ID_BASE + i, &item_tpl, y, &x, &count);
423
424         control_count += count;
425
426         if (!len)
427         {
428             continue;
429         }
430
431         hold_for_group = y;
432         y+= item_tpl->cy + 1;
433         max_cx = max(max_cx, x + 2);
434         group_max_cx = max(group_max_cx, x );
435
436         padding = len % sizeof(DWORD);
437
438         if (all_controls)
439         {
440             LPBYTE newone;
441             newone = HeapReAlloc(GetProcessHeap(),0,all_controls,
442                     control_len + len + padding);
443             all_controls = newone;
444             memcpy(all_controls+control_len,item_tpl,len);
445             memset(all_controls+control_len+len,0xca,padding);
446             HeapFree(GetProcessHeap(),0,item_tpl);
447         }
448         else
449         {
450             if (!padding)
451             {
452                 all_controls = (LPBYTE)item_tpl;
453             }
454             else
455             {
456                 all_controls = HeapAlloc(GetProcessHeap(),0,len + padding);
457                 memcpy(all_controls,item_tpl,len);
458                 memset(all_controls+len,0xcb,padding);
459                 HeapFree(GetProcessHeap(),0,item_tpl);
460             }
461         }
462
463         if (opt->type == SANE_TYPE_GROUP)
464         {
465             if (group_offset == -1)
466             {
467                 group_offset  = control_len;
468                 group_max_cx = 0;
469             }
470             else
471             {
472                 LPDLGITEMTEMPLATEW group =
473                     (LPDLGITEMTEMPLATEW)(all_controls+group_offset);
474
475                 group->cy = hold_for_group - group->y;
476                 group->cx = group_max_cx;
477
478                 group = (LPDLGITEMTEMPLATEW)(all_controls+control_len);
479                 group->y += 2;
480                 y+=2;
481                 group_max_cx = 0;
482                 group_offset  = control_len;
483             }
484         }
485
486         control_len += len + padding;
487     }
488
489     if ( group_offset && !split_tabs )
490     {
491         LPDLGITEMTEMPLATEW group =
492             (LPDLGITEMTEMPLATEW)(all_controls+group_offset);
493         group->cy = y - group->y;
494         group->cx = group_max_cx;
495         y+=2;
496     }
497
498     *from_index = i-1;
499 exit:
500
501     tpl = HeapAlloc(GetProcessHeap(),0,sizeof(DLGTEMPLATE) + 3*sizeof(WORD) + 
502             control_len);
503
504     tpl->style = WS_VISIBLE | WS_OVERLAPPEDWINDOW;
505     tpl->dwExtendedStyle = 0;
506     tpl->cdit = control_count;
507     tpl->x = 0;
508     tpl->y = 0;
509     tpl->cx = max_cx + 10;
510     tpl->cy = y + 10;
511     ptr = (LPBYTE)tpl + sizeof(DLGTEMPLATE);
512     *(LPWORD)ptr = 0x0000;
513     ptr+=sizeof(WORD);
514     *(LPWORD)ptr = 0x0000;
515     ptr+=sizeof(WORD);
516     *(LPWORD)ptr = 0x0000;
517     ptr+=sizeof(WORD);
518     memcpy(ptr,all_controls,control_len);
519
520     HeapFree(GetProcessHeap(),0,all_controls);
521
522     return tpl;
523 }
524
525 BOOL DoScannerUI(void)
526 {
527     HDC hdc;
528     PROPSHEETPAGEW psp[10];
529     int page_count= 0;
530     PROPSHEETHEADERW psh;
531     int index = 1;
532     SANE_Status rc;
533     SANE_Int optcount;
534     UINT psrc;
535     LPWSTR szCaption;
536     DWORD len;
537
538     hdc = GetDC(0);
539
540     memset(&psp,0,sizeof(psp));
541     rc = psane_control_option(activeDS.deviceHandle, 0, SANE_ACTION_GET_VALUE,
542             &optcount, NULL);
543
544     while (index < optcount)
545     {
546         const SANE_Option_Descriptor *opt;
547         psp[page_count].u.pResource = create_options_page(hdc, &index,
548                 TRUE);
549         opt = psane_get_option_descriptor(activeDS.deviceHandle, index);
550
551         if (opt->type == SANE_TYPE_GROUP)
552         {
553             LPWSTR title = NULL;
554             INT len;
555
556             len = MultiByteToWideChar(CP_ACP,0,opt->title,-1,NULL,0);
557             title = HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR));
558             MultiByteToWideChar(CP_ACP,0,opt->title,-1,title,len);
559
560             psp[page_count].pszTitle = title;
561         }
562
563         if (psp[page_count].u.pResource)
564         {
565             psp[page_count].dwSize = sizeof(PROPSHEETPAGEW);
566             psp[page_count].dwFlags =  PSP_DLGINDIRECT | PSP_USETITLE;
567             psp[page_count].hInstance = SANE_instance;
568             psp[page_count].pfnDlgProc = DialogProc;
569             psp[page_count].lParam = (LPARAM)&activeDS;
570             page_count ++;
571         }
572        
573         index ++;
574     }
575  
576     len = lstrlenA(activeDS.identity.Manufacturer)
577          + lstrlenA(activeDS.identity.ProductName) + 2;
578     szCaption = HeapAlloc(GetProcessHeap(),0,len *sizeof(WCHAR));
579     MultiByteToWideChar(CP_ACP,0,activeDS.identity.Manufacturer,-1,
580             szCaption,len);
581     szCaption[lstrlenA(activeDS.identity.Manufacturer)] = ' ';
582     MultiByteToWideChar(CP_ACP,0,activeDS.identity.ProductName,-1,
583             &szCaption[lstrlenA(activeDS.identity.Manufacturer)+1],len);
584     psh.dwSize = sizeof(PROPSHEETHEADERW);
585     psh.dwFlags = PSH_PROPSHEETPAGE|PSH_PROPTITLE|PSH_USECALLBACK;
586     psh.hwndParent = activeDS.hwndOwner;
587     psh.hInstance = SANE_instance;
588     psh.u.pszIcon = 0;
589     psh.pszCaption = szCaption;
590     psh.nPages = page_count;
591     psh.u2.nStartPage = 0;
592     psh.u3.ppsp = (LPCPROPSHEETPAGEW) &psp;
593     psh.pfnCallback = PropSheetProc;
594
595     psrc = PropertySheetW(&psh);
596
597     for(index = 0; index < page_count; index ++)
598     {
599         HeapFree(GetProcessHeap(),0,(LPBYTE)psp[index].u.pResource);
600         HeapFree(GetProcessHeap(),0,(LPBYTE)psp[index].pszTitle);
601     }
602     
603     if (psrc == IDOK)
604         return TRUE;
605     else
606         return FALSE;
607 }
608
609 static void UpdateRelevantEdit(HWND hwnd, const SANE_Option_Descriptor *opt, 
610         int index, int position)
611 {
612     CHAR buffer[244];
613     HWND edit_w;
614     CHAR unit[20];
615
616     LoadStringA(SANE_instance, opt->unit, unit,20);
617
618     if (opt->type == SANE_TYPE_INT)
619     {
620         INT si;
621
622         if (opt->constraint.range->quant)
623             si = position * opt->constraint.range->quant;
624         else
625             si = position;
626
627         sprintf(buffer,"%i %s",si,unit);
628
629     }
630     else if  (opt->type == SANE_TYPE_FIXED)
631     {
632         double s_quant, dd;
633
634         s_quant = SANE_UNFIX(opt->constraint.range->quant);
635
636         if (s_quant)
637             dd = position * s_quant;
638         else
639             dd = position * 0.01;
640
641         sprintf(buffer,"%f %s",dd,unit);
642     }
643     else
644         buffer[0] = 0;
645
646     edit_w = GetDlgItem(hwnd,index+ID_BASE+ID_EDIT_BASE);
647     if (edit_w && buffer[0])
648         SetWindowTextA(edit_w,buffer);
649
650 }
651
652 static BOOL UpdateSaneScrollOption(
653         const SANE_Option_Descriptor *opt, int index, DWORD position)
654 {
655     SANE_Status rc = SANE_STATUS_GOOD;  
656     SANE_Int result = 0;
657
658     if (opt->type == SANE_TYPE_INT)
659     {
660         SANE_Int si;
661
662         if (opt->constraint.range->quant)
663             si = position * opt->constraint.range->quant;
664         else
665             si = position;
666
667         rc = psane_control_option (activeDS.deviceHandle,index,
668             SANE_ACTION_SET_VALUE, &si, &result);
669
670     }
671     else if  (opt->type == SANE_TYPE_FIXED)
672     {
673         double s_quant, dd;
674         SANE_Fixed *sf;
675
676         s_quant = SANE_UNFIX(opt->constraint.range->quant);
677
678         if (s_quant)
679             dd = position * s_quant;
680         else
681             dd = position * 0.01;
682
683         sf = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
684
685         *sf = SANE_FIX(dd);
686
687         rc = psane_control_option (activeDS.deviceHandle,index,
688             SANE_ACTION_SET_VALUE, sf, &result);
689
690         HeapFree(GetProcessHeap(),0,sf);
691     }
692
693     if(rc == SANE_STATUS_GOOD)
694     {
695         if (result & SANE_INFO_RELOAD_OPTIONS || 
696                 result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
697             return TRUE;
698     }
699     return FALSE;
700 }
701
702 static BOOL UpdateSaneBoolOption(int index, BOOL position)
703 {
704     SANE_Status rc = SANE_STATUS_GOOD;  
705     SANE_Int result = 0;
706     SANE_Bool si;
707
708     si = position;
709
710     rc = psane_control_option (activeDS.deviceHandle,index,
711             SANE_ACTION_SET_VALUE, &si, &result);
712
713     if(rc == SANE_STATUS_GOOD)
714     {
715         if (result & SANE_INFO_RELOAD_OPTIONS || 
716                 result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
717             return TRUE;
718     }
719     return FALSE;
720 }
721
722 static BOOL UpdateSaneStringOption(int index, SANE_String value)
723 {
724     SANE_Status rc = SANE_STATUS_GOOD;  
725     SANE_Int result = 0;
726
727     rc = psane_control_option (activeDS.deviceHandle,index,
728             SANE_ACTION_SET_VALUE, value, &result);
729
730     if(rc == SANE_STATUS_GOOD)
731     {
732         if (result & SANE_INFO_RELOAD_OPTIONS || 
733                 result & SANE_INFO_RELOAD_PARAMS || result & SANE_INFO_INEXACT) 
734             return TRUE;
735     }
736     return FALSE;
737 }
738
739 static INT_PTR InitializeDialog(HWND hwnd)
740 {
741     SANE_Status rc;
742     SANE_Int optcount;
743     HWND control;
744     int i;
745
746     rc = psane_control_option(activeDS.deviceHandle, 0, SANE_ACTION_GET_VALUE,
747             &optcount, NULL);
748
749     for ( i = 1; i < optcount; i++)
750     {
751         const SANE_Option_Descriptor *opt;
752
753         control = GetDlgItem(hwnd,i+ID_BASE);
754
755         if (!control)
756             continue;
757
758         opt = psane_get_option_descriptor(activeDS.deviceHandle, i);
759
760         TRACE("%i %s %i %i\n",i,opt->title,opt->type,opt->constraint_type);
761         
762         if (!SANE_OPTION_IS_ACTIVE(opt->cap))
763             EnableWindow(control,FALSE);
764         else
765             EnableWindow(control,TRUE);
766
767         SendMessageA(control,CB_RESETCONTENT,0,0);
768         /* initialize values */
769         if (opt->type == SANE_TYPE_STRING && opt->constraint_type !=
770                 SANE_CONSTRAINT_NONE)
771         {
772             int j = 0;
773             CHAR buffer[255];
774             while (opt->constraint.string_list[j]!=NULL)
775             {
776                 SendMessageA(control,CB_ADDSTRING,0,
777                         (LPARAM)opt->constraint.string_list[j]);
778                 j++;
779             }
780             psane_control_option(activeDS.deviceHandle, i, SANE_ACTION_GET_VALUE, buffer,NULL);
781             SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer);
782         }
783         else if (opt->type == SANE_TYPE_BOOL)
784         {
785             SANE_Bool b;
786             psane_control_option(activeDS.deviceHandle, i,
787                     SANE_ACTION_GET_VALUE, &b, NULL);
788             if (b)
789                 SendMessageA(control,BM_SETCHECK,BST_CHECKED,0);
790
791         }
792         else if (opt->constraint_type == SANE_CONSTRAINT_RANGE)
793         {
794             if (opt->type == SANE_TYPE_INT)
795             {
796                 SANE_Int si;
797                 int min,max;
798
799                 min = (SANE_Int)opt->constraint.range->min /
800                     (((SANE_Int)opt->constraint.range->quant)?
801                     (SANE_Int)opt->constraint.range->quant:1);
802
803                 max = (SANE_Int)opt->constraint.range->max /
804                     (((SANE_Int)opt->constraint.range->quant)
805                     ?(SANE_Int)opt->constraint.range->quant:1);
806
807                 SendMessageA(control,SBM_SETRANGE,min,max);
808
809                 psane_control_option(activeDS.deviceHandle, i,
810                         SANE_ACTION_GET_VALUE, &si,NULL);
811                 if (opt->constraint.range->quant)
812                     si = si / opt->constraint.range->quant;
813
814                 SendMessageW(control,SBM_SETPOS, si, TRUE);
815                 UpdateRelevantEdit(hwnd, opt, i, si);
816             }
817             else if (opt->type == SANE_TYPE_FIXED)
818             {
819                 SANE_Fixed *sf;
820
821                 double dd;
822                 double s_min,s_max,s_quant;
823                 INT pos;
824                 int min,max;
825
826                 s_min = SANE_UNFIX(opt->constraint.range->min);
827                 s_max = SANE_UNFIX(opt->constraint.range->max);
828                 s_quant = SANE_UNFIX(opt->constraint.range->quant);
829
830                 if (s_quant)
831                 {
832                     min = (s_min / s_quant);
833                     max = (s_max / s_quant);
834                 }
835                 else
836                 {
837                     min = s_min / 0.01;
838                     max = s_max / 0.01;
839                 }
840
841                 SendMessageA(control,SBM_SETRANGE,min,max);
842
843
844                 sf = HeapAlloc(GetProcessHeap(),0,opt->size*sizeof(SANE_Word));
845                 psane_control_option(activeDS.deviceHandle, i,
846                         SANE_ACTION_GET_VALUE, sf,NULL);
847
848                 dd = SANE_UNFIX(*sf);
849                 HeapFree(GetProcessHeap(),0,sf);
850
851                 if (s_quant)
852                     pos = (dd / s_quant);
853                 else
854                     pos = dd / 0.01;
855
856                 SendMessageW(control, SBM_SETPOS, pos, TRUE);
857                 
858                 UpdateRelevantEdit(hwnd, opt, i, pos);
859             }
860         }
861     }
862
863     return TRUE;
864 }
865
866 static INT_PTR ProcessScroll(HWND hwnd, WPARAM wParam, LPARAM lParam)
867 {
868     int index;
869     const SANE_Option_Descriptor *opt;
870     WORD scroll;
871     DWORD position;
872
873     index = GetDlgCtrlID((HWND)lParam)- ID_BASE;
874     if (index < 0)
875         return FALSE;
876
877     opt = psane_get_option_descriptor(activeDS.deviceHandle, index);
878
879     if (!opt)
880         return FALSE;
881
882     scroll = LOWORD(wParam);
883
884     switch (scroll)
885     {
886         case SB_THUMBTRACK:
887         case SB_THUMBPOSITION:
888         {
889             SCROLLINFO si;
890             si.cbSize = sizeof(SCROLLINFO);
891             si.fMask = SIF_TRACKPOS;
892             GetScrollInfo((HWND)lParam,SB_CTL, &si);
893             position = si.nTrackPos;
894         }
895             break;
896         case SB_LEFT:
897         case SB_LINELEFT:
898         case SB_PAGELEFT:
899             position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
900             position--;
901             break;
902         case SB_RIGHT:
903         case SB_LINERIGHT:
904         case SB_PAGERIGHT:
905             position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
906             position++;
907             break;
908         default:
909             position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
910     }
911
912     SendMessageW((HWND)lParam,SBM_SETPOS, position, TRUE);
913     position = SendMessageW((HWND)lParam,SBM_GETPOS,0,0);
914
915     UpdateRelevantEdit(hwnd, opt, index, position);
916     if (UpdateSaneScrollOption(opt, index, position))
917         InitializeDialog(hwnd);
918
919     return TRUE;
920 }
921
922
923 static void ButtonClicked(HWND hwnd, INT id, HWND control)
924 {
925     int index;
926     const SANE_Option_Descriptor *opt;
927
928     index = id - ID_BASE;
929     if (index < 0)
930         return;
931
932     opt = psane_get_option_descriptor(activeDS.deviceHandle, index);
933
934     if (!opt)
935         return;
936
937     if (opt->type == SANE_TYPE_BOOL)
938     {
939         BOOL r = SendMessageW(control,BM_GETCHECK,0,0)==BST_CHECKED;
940         if (UpdateSaneBoolOption(index, r))
941                 InitializeDialog(hwnd);
942     }
943 }
944
945 static void ComboChanged(HWND hwnd, INT id, HWND control)
946 {
947     int index;
948     int selection;
949     int len;
950     const SANE_Option_Descriptor *opt;
951     SANE_String value;
952
953     index = id - ID_BASE;
954     if (index < 0)
955         return;
956
957     opt = psane_get_option_descriptor(activeDS.deviceHandle, index);
958
959     if (!opt)
960         return;
961
962     selection = SendMessageW(control,CB_GETCURSEL,0,0);
963     len = SendMessageW(control,CB_GETLBTEXTLEN,selection,0);
964
965     len++;
966     value = HeapAlloc(GetProcessHeap(),0,len);
967     SendMessageA(control,CB_GETLBTEXT,selection,(LPARAM)value);
968
969     if (opt->type == SANE_TYPE_STRING)
970     {
971         if (UpdateSaneStringOption(index, value))
972                 InitializeDialog(hwnd);
973     }
974 }
975
976
977 static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
978 {
979     switch (msg)
980     {
981         case WM_INITDIALOG:
982             return InitializeDialog(hwndDlg);
983         case WM_HSCROLL:
984             return ProcessScroll(hwndDlg, wParam, lParam);
985             break;
986         case WM_NOTIFY:
987             {
988                 LPPSHNOTIFY psn = (LPPSHNOTIFY)lParam;
989                 switch (((NMHDR*)lParam)->code)
990                 {
991                     case PSN_APPLY:
992                         if (psn->lParam == TRUE)
993                         {
994                             activeDS.currentState = 6;
995                             activeDS.pendingEvent.TWMessage = MSG_XFERREADY;
996                         }
997                         break;
998                     case PSN_QUERYCANCEL:
999                         activeDS.pendingEvent.TWMessage = MSG_CLOSEDSREQ;
1000                         break;
1001                     case PSN_SETACTIVE:
1002                         InitializeDialog(hwndDlg);
1003                         break;
1004                 }
1005             }
1006         case WM_COMMAND:
1007             {
1008                 switch (HIWORD(wParam))
1009                 {
1010                     case BN_CLICKED:
1011                         ButtonClicked(hwndDlg,LOWORD(wParam),
1012                                 (HWND)lParam);
1013                         break;
1014                     case CBN_SELCHANGE:
1015                         ComboChanged(hwndDlg,LOWORD(wParam),
1016                                 (HWND)lParam);
1017                 }
1018             }
1019     }
1020
1021     return FALSE;
1022 }
1023
1024 static int CALLBACK PropSheetProc(HWND hwnd, UINT msg, LPARAM lParam)
1025 {
1026     if (msg == PSCB_INITIALIZED)
1027     {
1028         /* rename OK button to Scan */
1029         HWND scan = GetDlgItem(hwnd,IDOK);
1030         SetWindowTextA(scan,"Scan");
1031     }
1032     return TRUE;
1033 }
1034
1035
1036 static INT_PTR CALLBACK ScanningProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1037 {
1038     return FALSE;
1039 }
1040
1041 HWND ScanningDialogBox(HWND dialog, LONG progress)
1042 {
1043     if (!dialog)
1044         dialog = CreateDialogW(SANE_instance,
1045                 (LPWSTR)MAKEINTRESOURCE(IDD_DIALOG1), NULL, ScanningProc);
1046
1047     if (progress == -1)
1048     {
1049         EndDialog(dialog,0);
1050         return NULL;
1051     }
1052
1053     RedrawWindow(dialog,NULL,NULL,
1054             RDW_INTERNALPAINT|RDW_UPDATENOW|RDW_ALLCHILDREN);
1055
1056     return dialog;
1057 }
1058
1059 #else  /* SONAME_LIBSANE */
1060
1061 BOOL DoScannerUI(void)
1062 {
1063     return FALSE;
1064 }
1065
1066 #endif  /* SONAME_LIBSANE */