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