driver.c 30.7 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
Alexandre Julliard's avatar
Alexandre Julliard committed
2
 * Exported functions from the PostScript driver.
Alexandre Julliard's avatar
Alexandre Julliard committed
3
 *
4
 * [Ext]DeviceMode, DeviceCapabilities, AdvancedSetupDialog.
Alexandre Julliard's avatar
Alexandre Julliard committed
5 6 7 8 9
 *
 * Will need ExtTextOut for winword6 (urgh!)
 *
 * Copyright 1998  Huw D M Davies
 *
10 11 12 13 14 15 16 17 18 19 20 21
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
22
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
23 24
 */

25 26
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
27 28 29 30 31 32 33 34 35

#include "config.h"

#include <string.h>

#include "wine/debug.h"
#include "psdrv.h"

#include "winuser.h"
36
#include "wine/wingdi16.h"
37
#include "prsht.h"
38
#include "psdlg.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
39

40
WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
41

42 43 44 45 46
/* convert points to paper size units (10th of a millimeter) */
static inline int paper_size_from_points( float size )
{
    return size * 254 / 72;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
47

48
INPUTSLOT *find_slot( PPD *ppd, const PSDRV_DEVMODE *dm )
49 50 51 52 53 54 55 56 57 58
{
    INPUTSLOT *slot;

    LIST_FOR_EACH_ENTRY( slot, &ppd->InputSlots, INPUTSLOT, entry )
        if (slot->WinBin == dm->dmPublic.u1.s1.dmDefaultSource)
            return slot;

    return NULL;
}

59
PAGESIZE *find_pagesize( PPD *ppd, const PSDRV_DEVMODE *dm )
60 61 62 63 64 65 66 67 68 69
{
    PAGESIZE *page;

    LIST_FOR_EACH_ENTRY( page, &ppd->PageSizes, PAGESIZE, entry )
        if (page->WinPage == dm->dmPublic.u1.s1.dmPaperSize)
            return page;

    return NULL;
}

70
DUPLEX *find_duplex( PPD *ppd, const PSDRV_DEVMODE *dm )
71 72 73 74 75 76 77 78 79 80 81 82 83
{
    DUPLEX *duplex;
    WORD win_duplex = dm->dmPublic.dmFields & DM_DUPLEX ? dm->dmPublic.dmDuplex : 0;

    if (win_duplex == 0) return NULL; /* Not capable */

    LIST_FOR_EACH_ENTRY( duplex, &ppd->Duplexes, DUPLEX, entry )
        if (duplex->WinDuplex == win_duplex)
            return duplex;

    return NULL;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
84 85 86 87 88 89 90
/************************************************************************
 *
 *		PSDRV_MergeDevmodes
 *
 * Updates dm1 with some fields from dm2
 *
 */
91
void PSDRV_MergeDevmodes( PSDRV_DEVMODE *dm1, const PSDRV_DEVMODE *dm2, PRINTERINFO *pi )
Alexandre Julliard's avatar
Alexandre Julliard committed
92
{
Alexandre Julliard's avatar
Alexandre Julliard committed
93
    /* some sanity checks here on dm2 */
94

95
    if(dm2->dmPublic.dmFields & DM_ORIENTATION) {
96
        dm1->dmPublic.u1.s1.dmOrientation = dm2->dmPublic.u1.s1.dmOrientation;
97 98 99 100 101 102 103
	TRACE("Changing orientation to %d (%s)\n",
	      dm1->dmPublic.u1.s1.dmOrientation,
	      dm1->dmPublic.u1.s1.dmOrientation == DMORIENT_PORTRAIT ?
	      "Portrait" :
	      (dm1->dmPublic.u1.s1.dmOrientation == DMORIENT_LANDSCAPE ?
	       "Landscape" : "unknown"));
    }
104 105

    /* NB PaperWidth is always < PaperLength */
106 107 108
    if (dm2->dmPublic.dmFields & DM_PAPERSIZE)
    {
        PAGESIZE *page = find_pagesize( pi->ppd, dm2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
109

110 111
        if (page)
        {
112
	    dm1->dmPublic.u1.s1.dmPaperSize = dm2->dmPublic.u1.s1.dmPaperSize;
113 114
	    dm1->dmPublic.u1.s1.dmPaperWidth  = paper_size_from_points( page->PaperDimension->x );
	    dm1->dmPublic.u1.s1.dmPaperLength = paper_size_from_points( page->PaperDimension->y );
115 116
	    dm1->dmPublic.dmFields &= ~(DM_PAPERLENGTH | DM_PAPERWIDTH);
	    dm1->dmPublic.dmFields |= DM_PAPERSIZE;
117 118 119
	    TRACE("Changing page to %s %d x %d\n", page->FullName,
		  dm1->dmPublic.u1.s1.dmPaperWidth,
		  dm1->dmPublic.u1.s1.dmPaperLength );
120 121 122 123 124 125

            if (dm1->dmPublic.dmSize >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR))
            {
                MultiByteToWideChar(CP_ACP, 0, page->FullName, -1, dm1->dmPublic.dmFormName, CCHFORMNAME);
                dm1->dmPublic.dmFields |= DM_FORMNAME;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
126
	}
127 128 129 130 131
        else
            TRACE("Trying to change to unsupported pagesize %d\n", dm2->dmPublic.u1.s1.dmPaperSize);
    }

    else if((dm2->dmPublic.dmFields & DM_PAPERLENGTH) &&
132
       (dm2->dmPublic.dmFields & DM_PAPERWIDTH)) {
133 134
        dm1->dmPublic.u1.s1.dmPaperLength = dm2->dmPublic.u1.s1.dmPaperLength;
        dm1->dmPublic.u1.s1.dmPaperWidth = dm2->dmPublic.u1.s1.dmPaperWidth;
135 136
	TRACE("Changing PaperLength|Width to %dx%d\n",
	      dm2->dmPublic.u1.s1.dmPaperLength,
137
	      dm2->dmPublic.u1.s1.dmPaperWidth);
138 139 140 141 142 143 144 145 146
	dm1->dmPublic.dmFields &= ~DM_PAPERSIZE;
	dm1->dmPublic.dmFields |= (DM_PAPERLENGTH | DM_PAPERWIDTH);
    } else if(dm2->dmPublic.dmFields & (DM_PAPERLENGTH | DM_PAPERWIDTH)) {
      /* You might think that this would be allowed if dm1 is in custom size
	 mode, but apparently Windows reverts to standard paper mode even in
	 this case */
        FIXME("Trying to change only paperlength or paperwidth\n");
	dm1->dmPublic.dmFields &= ~(DM_PAPERLENGTH | DM_PAPERWIDTH);
	dm1->dmPublic.dmFields |= DM_PAPERSIZE;
147 148 149
    }

    if(dm2->dmPublic.dmFields & DM_SCALE) {
150 151
        dm1->dmPublic.u1.s1.dmScale = dm2->dmPublic.u1.s1.dmScale;
	TRACE("Changing Scale to %d\n", dm2->dmPublic.u1.s1.dmScale);
152 153 154
    }

    if(dm2->dmPublic.dmFields & DM_COPIES) {
155 156
        dm1->dmPublic.u1.s1.dmCopies = dm2->dmPublic.u1.s1.dmCopies;
	TRACE("Changing Copies to %d\n", dm2->dmPublic.u1.s1.dmCopies);
157 158
    }

159 160
    if (dm2->dmPublic.dmFields & DM_DEFAULTSOURCE)
    {
161
        INPUTSLOT *slot = find_slot( pi->ppd, dm2 );
162

163
        if (slot)
164
        {
165
	    dm1->dmPublic.u1.s1.dmDefaultSource = dm2->dmPublic.u1.s1.dmDefaultSource;
166
	    TRACE("Changing bin to '%s'\n", slot->FullName);
Alexandre Julliard's avatar
Alexandre Julliard committed
167
	}
168 169
        else
            TRACE("Trying to change to unsupported bin %d\n", dm2->dmPublic.u1.s1.dmDefaultSource);
Alexandre Julliard's avatar
Alexandre Julliard committed
170 171
    }

172
   if (dm2->dmPublic.dmFields & DM_DEFAULTSOURCE )
173
       dm1->dmPublic.u1.s1.dmDefaultSource = dm2->dmPublic.u1.s1.dmDefaultSource;
174
   if (dm2->dmPublic.dmFields & DM_PRINTQUALITY )
175
       dm1->dmPublic.u1.s1.dmPrintQuality = dm2->dmPublic.u1.s1.dmPrintQuality;
176 177
   if (dm2->dmPublic.dmFields & DM_COLOR )
       dm1->dmPublic.dmColor = dm2->dmPublic.dmColor;
178
   if (dm2->dmPublic.dmFields & DM_DUPLEX && pi->ppd->DefaultDuplex && pi->ppd->DefaultDuplex->WinDuplex != 0)
179 180 181 182 183 184 185 186
       dm1->dmPublic.dmDuplex = dm2->dmPublic.dmDuplex;
   if (dm2->dmPublic.dmFields & DM_YRESOLUTION )
       dm1->dmPublic.dmYResolution = dm2->dmPublic.dmYResolution;
   if (dm2->dmPublic.dmFields & DM_TTOPTION )
       dm1->dmPublic.dmTTOption = dm2->dmPublic.dmTTOption;
   if (dm2->dmPublic.dmFields & DM_COLLATE )
       dm1->dmPublic.dmCollate = dm2->dmPublic.dmCollate;
   if (dm2->dmPublic.dmFields & DM_FORMNAME )
187
       lstrcpynW(dm1->dmPublic.dmFormName, dm2->dmPublic.dmFormName, CCHFORMNAME);
188 189 190 191 192 193 194
   if (dm2->dmPublic.dmFields & DM_BITSPERPEL )
       dm1->dmPublic.dmBitsPerPel = dm2->dmPublic.dmBitsPerPel;
   if (dm2->dmPublic.dmFields & DM_PELSWIDTH )
       dm1->dmPublic.dmPelsWidth = dm2->dmPublic.dmPelsWidth;
   if (dm2->dmPublic.dmFields & DM_PELSHEIGHT )
       dm1->dmPublic.dmPelsHeight = dm2->dmPublic.dmPelsHeight;
   if (dm2->dmPublic.dmFields & DM_DISPLAYFLAGS )
195
       dm1->dmPublic.u2.dmDisplayFlags = dm2->dmPublic.u2.dmDisplayFlags;
196 197 198
   if (dm2->dmPublic.dmFields & DM_DISPLAYFREQUENCY )
       dm1->dmPublic.dmDisplayFrequency = dm2->dmPublic.dmDisplayFrequency;
   if (dm2->dmPublic.dmFields & DM_POSITION )
199
       dm1->dmPublic.u1.s2.dmPosition = dm2->dmPublic.u1.s2.dmPosition;
200 201 202 203 204 205 206 207 208 209 210 211 212 213
   if (dm2->dmPublic.dmFields & DM_LOGPIXELS )
       dm1->dmPublic.dmLogPixels = dm2->dmPublic.dmLogPixels;
   if (dm2->dmPublic.dmFields & DM_ICMMETHOD )
       dm1->dmPublic.dmICMMethod = dm2->dmPublic.dmICMMethod;
   if (dm2->dmPublic.dmFields & DM_ICMINTENT )
       dm1->dmPublic.dmICMIntent = dm2->dmPublic.dmICMIntent;
   if (dm2->dmPublic.dmFields & DM_MEDIATYPE )
       dm1->dmPublic.dmMediaType = dm2->dmPublic.dmMediaType;
   if (dm2->dmPublic.dmFields & DM_DITHERTYPE )
       dm1->dmPublic.dmDitherType = dm2->dmPublic.dmDitherType;
   if (dm2->dmPublic.dmFields & DM_PANNINGWIDTH )
       dm1->dmPublic.dmPanningWidth = dm2->dmPublic.dmPanningWidth;
   if (dm2->dmPublic.dmFields & DM_PANNINGHEIGHT )
       dm1->dmPublic.dmPanningHeight = dm2->dmPublic.dmPanningHeight;
Alexandre Julliard's avatar
Alexandre Julliard committed
214 215 216 217 218

    return;
}


Huw Davies's avatar
Huw Davies committed
219 220 221
typedef struct
{
    PRINTERINFO *pi;
222
    PSDRV_DEVMODE *dlgdm;
Huw Davies's avatar
Huw Davies committed
223 224
} PSDRV_DLGINFO;

225 226 227 228 229
/****************************************************************
 *       PSDRV_PaperDlgProc
 *
 * Dialog proc for 'Paper' propsheet
 */
230 231
static INT_PTR CALLBACK PSDRV_PaperDlgProc(HWND hwnd, UINT msg,
                                           WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
232
{
233 234
  static const WCHAR resW[] = {'%','d',0};
  static const WCHAR resxyW[] = {'%','d','x','%','d',0};
235
  PSDRV_DLGINFO *di;
236
  int i, Cursel;
237
  PAGESIZE *ps;
238
  DUPLEX *duplex;
239
  RESOLUTION *res;
Alexandre Julliard's avatar
Alexandre Julliard committed
240

241 242 243
  switch(msg) {
  case WM_INITDIALOG:
    di = (PSDRV_DLGINFO*)((PROPSHEETPAGEA*)lParam)->lParam;
244
    SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)di);
245

246
    i = Cursel = 0;
247
    LIST_FOR_EACH_ENTRY(ps, &di->pi->ppd->PageSizes, PAGESIZE, entry) {
248
      SendDlgItemMessageA(hwnd, IDD_PAPERS, LB_INSERTSTRING, i,
249 250 251
			  (LPARAM)ps->FullName);
      if(di->pi->Devmode->dmPublic.u1.s1.dmPaperSize == ps->WinPage)
	Cursel = i;
252
      i++;
253 254
    }
    SendDlgItemMessageA(hwnd, IDD_PAPERS, LB_SETCURSEL, Cursel, 0);
255

256 257 258 259
    CheckRadioButton(hwnd, IDD_ORIENT_PORTRAIT, IDD_ORIENT_LANDSCAPE,
		     di->pi->Devmode->dmPublic.u1.s1.dmOrientation ==
		     DMORIENT_PORTRAIT ? IDD_ORIENT_PORTRAIT :
		     IDD_ORIENT_LANDSCAPE);
260

261 262
    if (list_empty( &di->pi->ppd->Duplexes ))
    {
263
        ShowWindow(GetDlgItem(hwnd, IDD_DUPLEX), SW_HIDE);
264
        ShowWindow(GetDlgItem(hwnd, IDD_DUPLEX_NAME), SW_HIDE);
265 266 267 268 269 270
    }
    else
    {
        i = Cursel = 0;
        LIST_FOR_EACH_ENTRY( duplex, &di->pi->ppd->Duplexes, DUPLEX, entry )
        {
271 272 273 274
            SendDlgItemMessageA(hwnd, IDD_DUPLEX, CB_INSERTSTRING, i,
                                (LPARAM)(duplex->FullName ? duplex->FullName : duplex->Name));
            if(di->pi->Devmode->dmPublic.dmDuplex == duplex->WinDuplex)
                Cursel = i;
275
            i++;
276 277 278
        }
        SendDlgItemMessageA(hwnd, IDD_DUPLEX, CB_SETCURSEL, Cursel, 0);
    }
279 280 281 282 283 284 285 286 287

    if (list_empty( &di->pi->ppd->Resolutions ))
    {
        int len, res;
        WCHAR buf[256];

        res = di->pi->ppd->DefaultResolution;
        len = sprintfW(buf, resW, res);
        buf[len++] = ' ';
288
        LoadStringW(PSDRV_hInstance, IDS_DPI, buf + len, ARRAY_SIZE(buf) - len);
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
        SendDlgItemMessageW(hwnd, IDD_QUALITY, CB_ADDSTRING, 0, (LPARAM)buf);
        SendDlgItemMessageW(hwnd, IDD_QUALITY, CB_SETITEMDATA, 0, MAKELONG(res, res));
        Cursel = 0;
    }
    else
    {
        int resx, resy;

        Cursel = 0;
        resx = resy = di->pi->ppd->DefaultResolution;

        if (di->pi->Devmode->dmPublic.dmFields & DM_PRINTQUALITY)
            resx = resy = di->pi->Devmode->dmPublic.u1.s1.dmPrintQuality;

        if (di->pi->Devmode->dmPublic.dmFields & DM_YRESOLUTION)
            resy = di->pi->Devmode->dmPublic.dmYResolution;

        if (di->pi->Devmode->dmPublic.dmFields & DM_LOGPIXELS)
            resx = resy = di->pi->Devmode->dmPublic.dmLogPixels;

        LIST_FOR_EACH_ENTRY(res, &di->pi->ppd->Resolutions, RESOLUTION, entry)
        {
            int len;
            WCHAR buf[256];
            DWORD idx;

            if (res->resx == res->resy)
                len = sprintfW(buf, resW, res->resx);
            else
                len = sprintfW(buf, resxyW, res->resx, res->resy);
            buf[len++] = ' ';
320
            LoadStringW(PSDRV_hInstance, IDS_DPI, buf + len, ARRAY_SIZE(buf) - len);
321 322 323 324 325 326 327 328 329
            idx = SendDlgItemMessageW(hwnd, IDD_QUALITY, CB_ADDSTRING, 0, (LPARAM)buf);
            SendDlgItemMessageW(hwnd, IDD_QUALITY, CB_SETITEMDATA, idx, MAKELONG(res->resx, res->resy));

            if (res->resx == resx && res->resy == resy)
                Cursel = idx;
        }
    }
    SendDlgItemMessageW(hwnd, IDD_QUALITY, CB_SETCURSEL, Cursel, 0);

330
    break;
Alexandre Julliard's avatar
Alexandre Julliard committed
331

332
  case WM_COMMAND:
333
    di = (PSDRV_DLGINFO *)GetWindowLongPtrW(hwnd, DWLP_USER);
334 335 336 337
    switch(LOWORD(wParam)) {
    case IDD_PAPERS:
      if(HIWORD(wParam) == LBN_SELCHANGE) {
	Cursel = SendDlgItemMessageA(hwnd, LOWORD(wParam), LB_GETCURSEL, 0, 0);
338 339 340 341 342
        i = 0;
	LIST_FOR_EACH_ENTRY(ps, &di->pi->ppd->PageSizes, PAGESIZE, entry) {
            if(i >= Cursel) break;
            i++;
        }
343 344
        TRACE("Setting pagesize to item %d, WinPage %d (%s), PaperSize %.2fx%.2f\n", Cursel,
              ps->WinPage, ps->FullName, ps->PaperDimension->x, ps->PaperDimension->y);
345
        di->dlgdm->dmPublic.u1.s1.dmPaperSize = ps->WinPage;
346
        di->dlgdm->dmPublic.dmFields |= DM_PAPERSIZE;
347

348 349 350 351
        di->dlgdm->dmPublic.u1.s1.dmPaperWidth  = paper_size_from_points(ps->PaperDimension->x);
        di->dlgdm->dmPublic.u1.s1.dmPaperLength = paper_size_from_points(ps->PaperDimension->y);
        di->dlgdm->dmPublic.dmFields |= DM_PAPERLENGTH | DM_PAPERWIDTH;

352 353 354 355 356
        if (di->dlgdm->dmPublic.dmSize >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR))
        {
            MultiByteToWideChar(CP_ACP, 0, ps->FullName, -1, di->dlgdm->dmPublic.dmFormName, CCHFORMNAME);
            di->dlgdm->dmPublic.dmFields |= DM_FORMNAME;
        }
357
        SendMessageW(GetParent(hwnd), PSM_CHANGED, 0, 0);
358 359 360 361 362
      }
      break;
    case IDD_ORIENT_PORTRAIT:
    case IDD_ORIENT_LANDSCAPE:
      TRACE("Setting orientation to %s\n", wParam == IDD_ORIENT_PORTRAIT ?
363
            "portrait" : "landscape");
364
      di->dlgdm->dmPublic.u1.s1.dmOrientation = wParam == IDD_ORIENT_PORTRAIT ?
365
        DMORIENT_PORTRAIT : DMORIENT_LANDSCAPE;
366
      di->dlgdm->dmPublic.dmFields |= DM_ORIENTATION;
367
      SendMessageW(GetParent(hwnd), PSM_CHANGED, 0, 0);
368
      break;
369 370 371
    case IDD_DUPLEX:
      if(HIWORD(wParam) == CBN_SELCHANGE) {
	Cursel = SendDlgItemMessageA(hwnd, LOWORD(wParam), CB_GETCURSEL, 0, 0);
372 373 374 375 376 377
        i = 0;
        LIST_FOR_EACH_ENTRY( duplex, &di->pi->ppd->Duplexes, DUPLEX, entry )
        {
            if (i >= Cursel) break;
            i++;
        }
378 379
        TRACE("Setting duplex to item %d Winduplex = %d\n", Cursel, duplex->WinDuplex);
        di->dlgdm->dmPublic.dmDuplex = duplex->WinDuplex;
380
        di->dlgdm->dmPublic.dmFields |= DM_DUPLEX;
381
        SendMessageW(GetParent(hwnd), PSM_CHANGED, 0, 0);
382 383
      }
      break;
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

    case IDD_QUALITY:
      if (HIWORD(wParam) == CBN_SELCHANGE)
      {
        LPARAM data;
        int resx, resy;

        Cursel = SendDlgItemMessageW(hwnd, LOWORD(wParam), CB_GETCURSEL, 0, 0);
        data = SendDlgItemMessageW(hwnd, IDD_QUALITY, CB_GETITEMDATA, Cursel, 0);

        resx = LOWORD(data);
        resy = HIWORD(data);
        TRACE("Setting resolution to %dx%d\n", resx, resy);

        di->dlgdm->dmPublic.u1.s1.dmPrintQuality = resx;
        di->dlgdm->dmPublic.dmFields |= DM_PRINTQUALITY;

        di->dlgdm->dmPublic.dmYResolution = resy;
        di->dlgdm->dmPublic.dmFields |= DM_YRESOLUTION;

        if (di->pi->Devmode->dmPublic.dmFields & DM_LOGPIXELS)
        {
            di->dlgdm->dmPublic.dmLogPixels = resx;
            di->dlgdm->dmPublic.dmFields |= DM_LOGPIXELS;
        }

        SendMessageW(GetParent(hwnd), PSM_CHANGED, 0, 0);
      }
      break;
413 414 415 416 417 418
    }
    break;

  case WM_NOTIFY:
   {
    NMHDR *nmhdr = (NMHDR *)lParam;
419
    di = (PSDRV_DLGINFO *)GetWindowLongPtrW(hwnd, DWLP_USER);
420 421
    switch(nmhdr->code) {
    case PSN_APPLY:
422
      *di->pi->Devmode = *di->dlgdm;
423
      SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, PSNRET_NOERROR);
Alexandre Julliard's avatar
Alexandre Julliard committed
424 425 426 427 428
      return TRUE;

    default:
      return FALSE;
    }
429 430
    break;
   }
431

432
  default:
Alexandre Julliard's avatar
Alexandre Julliard committed
433 434
    return FALSE;
  }
435
  return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
436 437 438
}


439 440
static HPROPSHEETPAGE (WINAPI *pCreatePropertySheetPage) (LPCPROPSHEETPAGEW);
static int (WINAPI *pPropertySheet) (LPCPROPSHEETHEADERW);
Alexandre Julliard's avatar
Alexandre Julliard committed
441

442 443 444 445 446 447 448 449 450 451 452 453
static PRINTERINFO *PSDRV_FindPrinterInfoA(LPCSTR name)
{
    int len = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
    WCHAR *nameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
    PRINTERINFO *pi;

    MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, len );
    pi = PSDRV_FindPrinterInfo( nameW );
    HeapFree( GetProcessHeap(), 0, nameW );

    return pi;
}
454

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
/***********************************************************
 *      DEVMODEdupWtoA
 *
 * Creates an ascii copy of supplied devmode on the process heap
 *
 * Copied from dlls/winspool/info.c until full unicodification
 */
static DEVMODEA *DEVMODEdupWtoA( const DEVMODEW *dmW )
{
    DEVMODEA *dmA;
    DWORD size;
    BOOL formname;
    /* there is no pointer dereference here, if your code checking tool complains it's broken */
    ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;

    if (!dmW) return NULL;
    formname = (dmW->dmSize > off_formname);
    size = dmW->dmSize - CCHDEVICENAME - (formname ? CCHFORMNAME : 0);
    dmA = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra );
    WideCharToMultiByte( CP_ACP, 0, dmW->dmDeviceName, -1, (LPSTR)dmA->dmDeviceName,
                         CCHDEVICENAME, NULL, NULL );
    if (!formname)
    {
        memcpy( &dmA->dmSpecVersion, &dmW->dmSpecVersion,
                dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR) );
    }
    else
    {
        memcpy( &dmA->dmSpecVersion, &dmW->dmSpecVersion,
               off_formname - CCHDEVICENAME * sizeof(WCHAR) );
        WideCharToMultiByte( CP_ACP, 0, dmW->dmFormName, -1, (LPSTR)dmA->dmFormName,
                             CCHFORMNAME, NULL, NULL );
        memcpy( &dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
                (off_formname + CCHFORMNAME * sizeof(WCHAR)) );
    }
    dmA->dmSize = size;
    memcpy( (char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
            dmW->dmDriverExtra );
    return dmA;
}

496 497 498 499 500
 /******************************************************************
 *         PSDRV_ExtDeviceMode
 *
 *  Retrieves or modifies device-initialization information for the PostScript
 *  driver, or displays a driver-supplied dialog box for configuring the driver.
Alexandre Julliard's avatar
Alexandre Julliard committed
501
 *
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
 * PARAMETERS
 *  lpszDriver  -- Driver name
 *  hwnd        -- Parent window for the dialog box
 *  lpdmOutput  -- Address of a DEVMODE structure for writing initialization information
 *  lpszDevice  -- Device name
 *  lpszPort    -- Port name
 *  lpdmInput   -- Address of a DEVMODE structure for reading initialization information
 *  lpProfile   -- Name of initialization file, defaults to WIN.INI if NULL
 *  wMode      -- Operation to perform.  Can be a combination if > 0.
 *      (0)             -- Returns number of bytes required by DEVMODE structure
 *      DM_UPDATE (1)   -- Write current settings to environment and initialization file
 *      DM_COPY (2)     -- Write current settings to lpdmOutput
 *      DM_PROMPT (4)   -- Presents the driver's modal dialog box (USER.240)
 *      DM_MODIFY (8)   -- Changes current settings according to lpdmInput before any other operation
 *
 * RETURNS
 *  Returns size of DEVMODE structure if wMode is 0.  Otherwise, IDOK is returned for success
 *  for both dialog and non-dialog operations.  IDCANCEL is returned if the dialog box was cancelled.
 *  A return value less than zero is returned if a non-dialog operation fails.
 *  
 * BUGS
 *
 * Just returns default devmode at the moment.  No use of initialization file.
Alexandre Julliard's avatar
Alexandre Julliard committed
525
 */
526 527 528
INT PSDRV_ExtDeviceMode(LPSTR lpszDriver, HWND hwnd, LPDEVMODEA lpdmOutput,
                        LPSTR lpszDevice, LPSTR lpszPort, LPDEVMODEA lpdmInput,
                        LPSTR lpszProfile, DWORD dwMode)
Alexandre Julliard's avatar
Alexandre Julliard committed
529
{
530
  PRINTERINFO *pi = PSDRV_FindPrinterInfoA(lpszDevice);
531
  if(!pi) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
532

533
  TRACE("(Driver=%s, hwnd=%p, devOut=%p, Device='%s', Port='%s', devIn=%p, Profile='%s', Mode=%04x)\n",
534
  lpszDriver, hwnd, lpdmOutput, lpszDevice, lpszPort, lpdmInput, debugstr_a(lpszProfile), dwMode);
Alexandre Julliard's avatar
Alexandre Julliard committed
535

536 537
  /* If dwMode == 0, return size of DEVMODE structure */
  if(!dwMode)
538
      return pi->Devmode->dmPublic.dmSize + pi->Devmode->dmPublic.dmDriverExtra - CCHDEVICENAME - CCHFORMNAME;
539

540
  /* If DM_MODIFY is set, change settings in accordance with lpdmInput */
541 542 543
  if((dwMode & DM_MODIFY) && lpdmInput)
  {
    DEVMODEW *dmW = GdiConvertToDevmodeW( lpdmInput );
544
    TRACE("DM_MODIFY set. devIn->dmFields = %08x\n", lpdmInput->dmFields);
545 546
    if (dmW) PSDRV_MergeDevmodes(pi->Devmode, (PSDRV_DEVMODE *)dmW, pi);
    HeapFree( GetProcessHeap(), 0, dmW );
Alexandre Julliard's avatar
Alexandre Julliard committed
547 548
  }

549 550
  /* If DM_PROMPT is set, present modal dialog box */
  if(dwMode & DM_PROMPT) {
551
    HINSTANCE hinstComctl32;
552
    HPROPSHEETPAGE hpsp[1];
553 554
    PROPSHEETPAGEW psp;
    PROPSHEETHEADERW psh;
Huw Davies's avatar
Huw Davies committed
555
    PSDRV_DLGINFO di;
556
    PSDRV_DEVMODE dlgdm;
557
    WCHAR SetupW[64];
558
    static const WCHAR PAPERW[] = {'P','A','P','E','R','\0'};
559

560
    LoadStringW(PSDRV_hInstance, IDS_SETUP, SetupW, ARRAY_SIZE(SetupW));
561 562
    hinstComctl32 = LoadLibraryA("comctl32.dll");
    pCreatePropertySheetPage = (void*)GetProcAddress(hinstComctl32,
563
						    "CreatePropertySheetPageW");
564
    pPropertySheet = (void*)GetProcAddress(hinstComctl32, "PropertySheetW");
565
    memset(&psp,0,sizeof(psp));
Huw Davies's avatar
Huw Davies committed
566 567 568
    dlgdm = *pi->Devmode;
    di.pi = pi;
    di.dlgdm = &dlgdm;
569
    psp.dwSize = sizeof(psp);
570
    psp.hInstance = PSDRV_hInstance;
571
    psp.u.pszTemplate = PAPERW;
572 573
    psp.u2.pszIcon = NULL;
    psp.pfnDlgProc = PSDRV_PaperDlgProc;
Huw Davies's avatar
Huw Davies committed
574
    psp.lParam = (LPARAM)&di;
575 576 577 578
    hpsp[0] = pCreatePropertySheetPage(&psp);

    memset(&psh, 0, sizeof(psh));
    psh.dwSize = sizeof(psh);
579
    psh.pszCaption = SetupW;
580
    psh.nPages = 1;
581
    psh.hwndParent = hwnd;
582
    psh.u3.phpage = hpsp;
583

584
    pPropertySheet(&psh);
585

586
  }
587 588 589
  
  /* If DM_UPDATE is set, should write settings to environment and initialization file */
  if(dwMode & DM_UPDATE)
590 591
    FIXME("Mode DM_UPDATE.  Just do the same as DM_COPY\n");

592 593
  /* If DM_COPY is set, should write settings to lpdmOutput */
  if((dwMode & DM_COPY) || (dwMode & DM_UPDATE)) {
594
    if (lpdmOutput)
595 596 597 598 599
    {
        DEVMODEA *dmA = DEVMODEdupWtoA( &pi->Devmode->dmPublic );
        if (dmA) memcpy( lpdmOutput, dmA, dmA->dmSize + dmA->dmDriverExtra );
        HeapFree( GetProcessHeap(), 0, dmA );
    }
600 601
    else
        FIXME("lpdmOutput is NULL what should we do??\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
602
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
603 604
  return IDOK;
}
605
/***********************************************************************
606 607 608 609 610 611 612 613 614 615 616
 *	PSDRV_DeviceCapabilities	
 *
 *      Retrieves the capabilities of a printer device driver.
 *
 * Parameters
 *      lpszDriver -- printer driver name
 *      lpszDevice -- printer name
 *      lpszPort -- port name
 *      fwCapability -- device capability
 *      lpszOutput -- output buffer
 *      lpDevMode -- device data buffer
Alexandre Julliard's avatar
Alexandre Julliard committed
617
 *
618 619
 * Returns
 *      Result depends on the setting of fwCapability.  -1 indicates failure.
Alexandre Julliard's avatar
Alexandre Julliard committed
620
 */
621 622
DWORD PSDRV_DeviceCapabilities(LPSTR lpszDriver, LPCSTR lpszDevice, LPCSTR lpszPort,
                               WORD fwCapability, LPSTR lpszOutput, LPDEVMODEA lpDevMode)
Alexandre Julliard's avatar
Alexandre Julliard committed
623
{
Alexandre Julliard's avatar
Alexandre Julliard committed
624
  PRINTERINFO *pi;
625
  DEVMODEW *lpdm;
626
  DWORD ret;
627
  pi = PSDRV_FindPrinterInfoA(lpszDevice);
628

629 630
  TRACE("%s %s %s, %u, %p, %p\n", debugstr_a(lpszDriver), debugstr_a(lpszDevice),
        debugstr_a(lpszPort), fwCapability, lpszOutput, lpDevMode);
631 632

  if (!pi) {
633 634 635
      ERR("no printer info for %s %s, return 0!\n",
          debugstr_a(lpszDriver), debugstr_a(lpszDevice));
      return 0;
636 637
  }

638 639
  lpdm = &pi->Devmode->dmPublic;
  if (lpDevMode) lpdm = GdiConvertToDevmodeW( lpDevMode );
Alexandre Julliard's avatar
Alexandre Julliard committed
640 641 642 643

  switch(fwCapability) {

  case DC_PAPERS:
Alexandre Julliard's avatar
Alexandre Julliard committed
644 645 646 647 648
    {
      PAGESIZE *ps;
      WORD *wp = (WORD *)lpszOutput;
      int i = 0;

649 650
      LIST_FOR_EACH_ENTRY(ps, &pi->ppd->PageSizes, PAGESIZE, entry)
      {
651
        TRACE("DC_PAPERS: %u\n", ps->WinPage);
652
        i++;
Alexandre Julliard's avatar
Alexandre Julliard committed
653 654
	if(lpszOutput != NULL)
	  *wp++ = ps->WinPage;
655
      }
656 657
      ret = i;
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
658
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
659 660

  case DC_PAPERSIZE:
Alexandre Julliard's avatar
Alexandre Julliard committed
661 662 663 664
    {
      PAGESIZE *ps;
      POINT16 *pt = (POINT16 *)lpszOutput;
      int i = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
665

666 667
      LIST_FOR_EACH_ENTRY(ps, &pi->ppd->PageSizes, PAGESIZE, entry)
      {
668
        TRACE("DC_PAPERSIZE: %f x %f\n", ps->PaperDimension->x, ps->PaperDimension->y);
669
        i++;
Alexandre Julliard's avatar
Alexandre Julliard committed
670
	if(lpszOutput != NULL) {
671 672
          pt->x = paper_size_from_points( ps->PaperDimension->x );
          pt->y = paper_size_from_points( ps->PaperDimension->y );
Alexandre Julliard's avatar
Alexandre Julliard committed
673 674
	  pt++;
	}
675
      }
676 677
      ret = i;
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
678
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
679 680

  case DC_PAPERNAMES:
Alexandre Julliard's avatar
Alexandre Julliard committed
681 682 683 684 685
    {
      PAGESIZE *ps;
      char *cp = lpszOutput;
      int i = 0;

686 687
      LIST_FOR_EACH_ENTRY(ps, &pi->ppd->PageSizes, PAGESIZE, entry)
      {
688
        TRACE("DC_PAPERNAMES: %s\n", debugstr_a(ps->FullName));
689
        i++;
Alexandre Julliard's avatar
Alexandre Julliard committed
690
	if(lpszOutput != NULL) {
691
	  lstrcpynA(cp, ps->FullName, 64);
Alexandre Julliard's avatar
Alexandre Julliard committed
692 693
	  cp += 64;
	}
694
      }
695 696
      ret = i;
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
697
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
698 699

  case DC_ORIENTATION:
700 701
    ret = pi->ppd->LandscapeOrientation ? pi->ppd->LandscapeOrientation : 90;
    break;
Alexandre Julliard's avatar
Alexandre Julliard committed
702 703 704 705 706 707 708

  case DC_BINS:
    {
      INPUTSLOT *slot;
      WORD *wp = (WORD *)lpszOutput;
      int i = 0;

709 710 711 712 713 714
      LIST_FOR_EACH_ENTRY( slot, &pi->ppd->InputSlots, INPUTSLOT, entry )
      {
          i++;
          if (lpszOutput != NULL)
              *wp++ = slot->WinBin;
      }
715 716
      ret = i;
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
717 718 719 720 721 722 723
    }

  case DC_BINNAMES:
    {
      INPUTSLOT *slot;
      char *cp = lpszOutput;
      int i = 0;
724

725 726 727 728 729 730 731 732 733
      LIST_FOR_EACH_ENTRY( slot, &pi->ppd->InputSlots, INPUTSLOT, entry )
      {
          i++;
          if (lpszOutput != NULL)
          {
              lstrcpynA( cp, slot->FullName, 24 );
              cp += 24;
          }
      }
734 735
      ret = i;
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
736
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
737

738 739
  case DC_BINADJUST:
    FIXME("DC_BINADJUST: stub.\n");
740 741
    ret = DCBA_FACEUPNONE;
    break;
742

Alexandre Julliard's avatar
Alexandre Julliard committed
743
  case DC_ENUMRESOLUTIONS:
744
    {
745 746 747
        RESOLUTION *res;
        LONG *lp = (LONG *)lpszOutput;
        int i = 0;
748

749 750 751 752 753 754 755 756 757 758 759 760
        LIST_FOR_EACH_ENTRY(res, &pi->ppd->Resolutions, RESOLUTION, entry)
        {
            i++;
            if (lpszOutput != NULL)
            {
                lp[0] = res->resx;
                lp[1] = res->resy;
                lp += 2;
            }
        }
        ret = i;
        break;
761 762
    }

763
  /* Windows returns 9999 too */
764
  case DC_COPIES:
765
    TRACE("DC_COPIES: returning 9999\n");
766 767
    ret = 9999;
    break;
768 769

  case DC_DRIVER:
770 771
    ret = lpdm->dmDriverVersion;
    break;
Alexandre Julliard's avatar
Alexandre Julliard committed
772

773 774
  case DC_DATATYPE_PRODUCED:
    FIXME("DATA_TYPE_PRODUCED: stub.\n");
775 776
    ret = -1; /* simulate that the driver supports 'RAW' */
    break;
777 778

  case DC_DUPLEX:
779 780 781
    ret = 0;
    if(pi->ppd->DefaultDuplex && pi->ppd->DefaultDuplex->WinDuplex != 0)
      ret = 1;
782
    TRACE("DC_DUPLEX: returning %d\n", ret);
783
    break;
784 785 786

  case DC_EMF_COMPLIANT:
    FIXME("DC_EMF_COMPLIANT: stub.\n");
787 788
    ret = -1; /* simulate that the driver do not support EMF */
    break;
789 790

  case DC_EXTRA:
791 792
    ret = lpdm->dmDriverExtra;
    break;
793 794

  case DC_FIELDS:
795 796
    ret = lpdm->dmFields;
    break;
797 798 799

  case DC_FILEDEPENDENCIES:
    FIXME("DC_FILEDEPENDENCIES: stub.\n");
800 801
    ret = 0;
    break;
802 803 804 805

  case DC_MAXEXTENT:
    {
      PAGESIZE *ps;
806
      float x = 0, y = 0;
807

808 809
      LIST_FOR_EACH_ENTRY(ps, &pi->ppd->PageSizes, PAGESIZE, entry)
      {
810 811
          if (ps->PaperDimension->x > x) x = ps->PaperDimension->x;
          if (ps->PaperDimension->y > y) y = ps->PaperDimension->y;
812
      }
813 814
      ret = MAKELONG( paper_size_from_points(x), paper_size_from_points(y) );
      break;
815 816 817 818 819
    }

  case DC_MINEXTENT:
    {
      PAGESIZE *ps;
820
      float x = 1e6, y = 1e6;
821

822 823
      LIST_FOR_EACH_ENTRY(ps, &pi->ppd->PageSizes, PAGESIZE, entry)
      {
824 825
          if (ps->PaperDimension->x < x) x = ps->PaperDimension->x;
          if (ps->PaperDimension->y < y) y = ps->PaperDimension->y;
826
      }
827 828
      ret = MAKELONG( paper_size_from_points(x), paper_size_from_points(y) );
      break;
829 830 831
    }

  case DC_SIZE:
832 833
    ret = lpdm->dmSize;
    break;
834 835 836

  case DC_TRUETYPE:
    FIXME("DC_TRUETYPE: stub\n");
837 838
    ret = DCTT_SUBDEV;
    break;
839 840

  case DC_VERSION:
841 842
    ret = lpdm->dmSpecVersion;
    break;
Alexandre Julliard's avatar
Alexandre Julliard committed
843

844
  /* We'll just return false here, very few printers can collate anyway */
845
  case DC_COLLATE:
846
    TRACE("DC_COLLATE: returning FALSE\n");
847 848
    ret = FALSE;
    break;
849

850 851
  /* Printer supports colour printing - 1 if yes, 0 if no (Win2k/XP only) */
  case DC_COLORDEVICE:
852
    ret = pi->ppd->ColorDevice != CD_False;
853
    break;
854

855 856 857
  /* Identification number of the printer manufacturer for use with ICM (Win9x only) */
  case DC_MANUFACTURER:
    FIXME("DC_MANUFACTURER: stub\n");
858 859 860
    ret = -1;
    break;

861 862 863
  /* Identification number of the printer model for use with ICM (Win9x only) */
  case DC_MODEL:
    FIXME("DC_MODEL: stub\n");
864 865 866
    ret = -1;
    break;

867 868 869
  /* Nonzero if the printer supports stapling, zero otherwise (Win2k/XP only) */
  case DC_STAPLE: /* WINVER >= 0x0500 */
    FIXME("DC_STAPLE: stub\n");
870 871 872
    ret = -1;
    break;

873 874 875 876 877 878
  /* Returns an array of 64-character string buffers containing the names of the paper forms
   * available for use, unless pOutput is NULL.  The return value is the number of paper forms.
   * (Win2k/XP only)
   */
  case DC_MEDIAREADY: /* WINVER >= 0x0500 */
    FIXME("DC_MEDIAREADY: stub\n");
879 880
    ret = -1;
    break;
881 882 883 884 885 886 887

  /* Returns an array of 64-character string buffers containing the names of the supported
   * media types, unless pOutput is NULL.  The return value is the number of supported.
   * media types (XP only)
   */
  case DC_MEDIATYPENAMES: /* WINVER >= 0x0501 */
    FIXME("DC_MEDIATYPENAMES: stub\n");
888 889 890
    ret = -1;
    break;

891 892 893 894 895
  /* Returns an array of DWORD values which represent the supported media types, unless
   * pOutput is NULL.  The return value is the number of supported media types. (XP only)
   */
  case DC_MEDIATYPES: /* WINVER >= 0x0501 */
    FIXME("DC_MEDIATYPES: stub\n");
896 897
    ret = -1;
    break;
898 899 900 901 902 903 904

  /* Returns an array of DWORD values, each representing a supported number of document
   * pages per printed page, unless pOutput is NULL.  The return value is the number of
   * array entries. (Win2k/XP only)
   */
  case DC_NUP:
    FIXME("DC_NUP: stub\n");
905 906 907
    ret = -1;
    break;

908 909 910 911 912 913 914
  /* Returns an array of 32-character string buffers containing a list of printer description
   * languages supported by the printer, unless pOutput is NULL.  The return value is
   * number of array entries. (Win2k/XP only)
   */
   
  case DC_PERSONALITY: /* WINVER >= 0x0500 */
    FIXME("DC_PERSONALITY: stub\n");
915 916 917
    ret = -1;
    break;

918 919 920
  /* Returns the amount of printer memory in kilobytes. (Win2k/XP only) */
  case DC_PRINTERMEM: /* WINVER >= 0x0500 */
    FIXME("DC_PRINTERMEM: stub\n");
921 922 923
    ret = -1;
    break;

924 925 926
  /* Returns the printer's print rate in PRINTRATEUNIT units. (Win2k/XP only) */
  case DC_PRINTRATE: /* WINVER >= 0x0500 */
    FIXME("DC_PRINTRATE: stub\n");
927 928 929
    ret = -1;
    break;

930 931 932
  /* Returns the printer's print rate in pages per minute. (Win2k/XP only) */
  case DC_PRINTRATEPPM: /* WINVER >= 0x0500 */
    FIXME("DC_PRINTRATEPPM: stub\n");
933 934 935
    ret = -1;
    break;

936 937 938 939 940
  /* Returns the printer rate unit used for DC_PRINTRATE, which is one of
   * PRINTRATEUNIT_{CPS,IPM,LPM,PPM} (Win2k/XP only)
   */  
  case DC_PRINTRATEUNIT: /* WINVER >= 0x0500 */
    FIXME("DC_PRINTRATEUNIT: stub\n");
941 942 943
    ret = -1;
    break;

Alexandre Julliard's avatar
Alexandre Julliard committed
944
  default:
945
    FIXME("Unsupported capability %d\n", fwCapability);
946
    ret = -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
947
  }
948

949
  if (lpDevMode) HeapFree( GetProcessHeap(), 0, lpdm );
950
  return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
951 952
}

953 954 955 956 957 958 959 960 961 962 963 964 965 966 967
#if 0
typedef struct {
  DWORD nPages;
  DWORD Unknown;
  HPROPSHEETPAGE hPages[10];
} EDMPS;

INT PSDRV_ExtDeviceModePropSheet(HWND hwnd, LPSTR lpszDevice, LPSTR lpszPort,
				 LPVOID pPropSheet)
{
    EDMPS *ps = pPropSheet;
    PROPSHEETPAGE psp;

    psp->dwSize = sizeof(psp);
    psp->hInstance = 0x1234;
968

969
    ps->nPages = 1;
970

971 972 973
}

#endif