1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/msw/metafile.cpp
3// Purpose:     wxMetafileDC etc.
4// Author:      Julian Smart
5// Modified by: VZ 07.01.00: implemented wxMetaFileDataObject
6// Created:     04/01/98
7// RCS-ID:      $Id: metafile.cpp 46103 2007-05-18 15:14:44Z VZ $
8// Copyright:   (c) Julian Smart
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24    #pragma hdrstop
25#endif
26
27#ifndef WX_PRECOMP
28    #include "wx/utils.h"
29    #include "wx/app.h"
30#endif
31
32#include "wx/metafile.h"
33
34#if wxUSE_METAFILE && !defined(wxMETAFILE_IS_ENH)
35
36#include "wx/clipbrd.h"
37#include "wx/msw/private.h"
38
39#include <stdio.h>
40#include <string.h>
41
42// ----------------------------------------------------------------------------
43// wxWin macros
44// ----------------------------------------------------------------------------
45
46IMPLEMENT_DYNAMIC_CLASS(wxMetafile, wxObject)
47IMPLEMENT_ABSTRACT_CLASS(wxMetafileDC, wxDC)
48
49// ============================================================================
50// implementation
51// ============================================================================
52
53// ----------------------------------------------------------------------------
54// wxMetafileRefData
55// ----------------------------------------------------------------------------
56
57/*
58 * Metafiles
59 * Currently, the only purpose for making a metafile is to put
60 * it on the clipboard.
61 */
62
63wxMetafileRefData::wxMetafileRefData()
64{
65    m_metafile = 0;
66    m_windowsMappingMode = wxMM_ANISOTROPIC;
67    m_width = m_height = 0;
68}
69
70wxMetafileRefData::~wxMetafileRefData()
71{
72    if (m_metafile)
73    {
74        DeleteMetaFile((HMETAFILE) m_metafile);
75        m_metafile = 0;
76    }
77}
78
79// ----------------------------------------------------------------------------
80// wxMetafile
81// ----------------------------------------------------------------------------
82
83wxMetafile::wxMetafile(const wxString& file)
84{
85    m_refData = new wxMetafileRefData;
86
87    M_METAFILEDATA->m_windowsMappingMode = wxMM_ANISOTROPIC;
88    M_METAFILEDATA->m_metafile = 0;
89    if (!file.empty())
90        M_METAFILEDATA->m_metafile = (WXHANDLE) GetMetaFile(file);
91}
92
93wxMetafile::~wxMetafile()
94{
95}
96
97bool wxMetafile::SetClipboard(int width, int height)
98{
99#if !wxUSE_CLIPBOARD
100    return false;
101#else
102    if (!m_refData)
103        return false;
104
105    bool alreadyOpen = wxClipboardOpen();
106    if (!alreadyOpen)
107    {
108        wxOpenClipboard();
109        if (!wxEmptyClipboard())
110            return false;
111    }
112    bool success = wxSetClipboardData(wxDF_METAFILE, this, width,height);
113    if (!alreadyOpen)
114        wxCloseClipboard();
115
116    return success;
117#endif
118}
119
120bool wxMetafile::Play(wxDC *dc)
121{
122    if (!m_refData)
123        return false;
124
125    if (dc->GetHDC() && M_METAFILEDATA->m_metafile)
126    {
127        if ( !::PlayMetaFile(GetHdcOf(*dc), (HMETAFILE)
128                             M_METAFILEDATA->m_metafile) )
129        {
130            wxLogLastError(_T("PlayMetaFile"));
131        }
132    }
133
134    return true;
135}
136
137void wxMetafile::SetHMETAFILE(WXHANDLE mf)
138{
139    if (!m_refData)
140        m_refData = new wxMetafileRefData;
141
142    M_METAFILEDATA->m_metafile = mf;
143}
144
145void wxMetafile::SetWindowsMappingMode(int mm)
146{
147    if (!m_refData)
148        m_refData = new wxMetafileRefData;
149
150    M_METAFILEDATA->m_windowsMappingMode = mm;
151}
152
153// ----------------------------------------------------------------------------
154// Metafile device context
155// ----------------------------------------------------------------------------
156
157// Original constructor that does not takes origin and extent. If you use this,
158// *DO* give origin/extent arguments to wxMakeMetafilePlaceable.
159wxMetafileDC::wxMetafileDC(const wxString& file)
160{
161    m_metaFile = NULL;
162    m_minX = 10000;
163    m_minY = 10000;
164    m_maxX = -10000;
165    m_maxY = -10000;
166    //  m_title = NULL;
167
168    if (!file.IsNull() && wxFileExists(file))
169        wxRemoveFile(file);
170
171    if (!file.IsNull() && (file != wxEmptyString))
172        m_hDC = (WXHDC) CreateMetaFile(file);
173    else
174        m_hDC = (WXHDC) CreateMetaFile(NULL);
175
176    m_ok = (m_hDC != (WXHDC) 0) ;
177
178    // Actual Windows mapping mode, for future reference.
179    m_windowsMappingMode = wxMM_TEXT;
180
181    SetMapMode(wxMM_TEXT); // NOTE: does not set HDC mapmode (this is correct)
182}
183
184// New constructor that takes origin and extent. If you use this, don't
185// give origin/extent arguments to wxMakeMetafilePlaceable.
186wxMetafileDC::wxMetafileDC(const wxString& file, int xext, int yext, int xorg, int yorg)
187{
188    m_minX = 10000;
189    m_minY = 10000;
190    m_maxX = -10000;
191    m_maxY = -10000;
192    if ( !file.empty() && wxFileExists(file) )
193        wxRemoveFile(file);
194    m_hDC = (WXHDC) CreateMetaFile(file.empty() ? NULL : file.c_str());
195
196    m_ok = true;
197
198    ::SetWindowOrgEx((HDC) m_hDC,xorg,yorg, NULL);
199    ::SetWindowExtEx((HDC) m_hDC,xext,yext, NULL);
200
201    // Actual Windows mapping mode, for future reference.
202    m_windowsMappingMode = wxMM_ANISOTROPIC;
203
204    SetMapMode(wxMM_TEXT); // NOTE: does not set HDC mapmode (this is correct)
205}
206
207wxMetafileDC::~wxMetafileDC()
208{
209    m_hDC = 0;
210}
211
212void wxMetafileDC::DoGetTextExtent(const wxString& string,
213                                   wxCoord *x, wxCoord *y,
214                                   wxCoord *descent,
215                                   wxCoord *externalLeading,
216                                   const wxFont *theFont) const
217{
218    const wxFont *fontToUse = theFont;
219    if (!fontToUse)
220        fontToUse = &m_font;
221
222    ScreenHDC dc;
223    SelectInHDC selFont(dc, GetHfontOf(*fontToUse));
224
225    SIZE sizeRect;
226    TEXTMETRIC tm;
227    ::GetTextExtentPoint32(dc, string, string.length(), &sizeRect);
228    ::GetTextMetrics(dc, &tm);
229
230    if ( x )
231        *x = sizeRect.cx;
232    if ( y )
233        *y = sizeRect.cy;
234    if ( descent )
235        *descent = tm.tmDescent;
236    if ( externalLeading )
237        *externalLeading = tm.tmExternalLeading;
238}
239
240void wxMetafileDC::GetTextExtent(const wxString& string, long *x, long *y,
241                                 long *descent, long *externalLeading, wxFont *theFont, bool WXUNUSED(use16bit)) const
242{
243    wxCoord xc, yc, dc, elc;
244    DoGetTextExtent(string, &xc, &yc, &dc, &elc, theFont);
245
246    if ( x )
247        *x = xc;
248    if ( y )
249        *y = yc;
250    if ( descent )
251        *descent = dc;
252    if ( externalLeading )
253        *externalLeading = elc;
254}
255
256void wxMetafileDC::DoGetSize(int *width, int *height) const
257{
258    wxCHECK_RET( m_refData, _T("invalid wxMetafileDC") );
259
260    if ( width )
261        *width = M_METAFILEDATA->m_width;
262    if ( height )
263        *height = M_METAFILEDATA->m_height;
264}
265
266wxMetafile *wxMetafileDC::Close()
267{
268    SelectOldObjects(m_hDC);
269    HANDLE mf = CloseMetaFile((HDC) m_hDC);
270    m_hDC = 0;
271    if (mf)
272    {
273        wxMetafile *wx_mf = new wxMetafile;
274        wx_mf->SetHMETAFILE((WXHANDLE) mf);
275        wx_mf->SetWindowsMappingMode(m_windowsMappingMode);
276        return wx_mf;
277    }
278    return NULL;
279}
280
281void wxMetafileDC::SetMapMode(int mode)
282{
283    m_mappingMode = mode;
284
285    //  int pixel_width = 0;
286    //  int pixel_height = 0;
287    //  int mm_width = 0;
288    //  int mm_height = 0;
289
290    float mm2pixelsX = 10.0;
291    float mm2pixelsY = 10.0;
292
293    switch (mode)
294    {
295        case wxMM_TWIPS:
296            {
297                m_logicalScaleX = (float)(twips2mm * mm2pixelsX);
298                m_logicalScaleY = (float)(twips2mm * mm2pixelsY);
299                break;
300            }
301        case wxMM_POINTS:
302            {
303                m_logicalScaleX = (float)(pt2mm * mm2pixelsX);
304                m_logicalScaleY = (float)(pt2mm * mm2pixelsY);
305                break;
306            }
307        case wxMM_METRIC:
308            {
309                m_logicalScaleX = mm2pixelsX;
310                m_logicalScaleY = mm2pixelsY;
311                break;
312            }
313        case wxMM_LOMETRIC:
314            {
315                m_logicalScaleX = (float)(mm2pixelsX/10.0);
316                m_logicalScaleY = (float)(mm2pixelsY/10.0);
317                break;
318            }
319        default:
320        case wxMM_TEXT:
321            {
322                m_logicalScaleX = 1.0;
323                m_logicalScaleY = 1.0;
324                break;
325            }
326    }
327}
328
329// ----------------------------------------------------------------------------
330// wxMakeMetafilePlaceable
331// ----------------------------------------------------------------------------
332
333#ifdef __WIN32__
334struct RECT32
335{
336  short left;
337  short top;
338  short right;
339  short bottom;
340};
341
342struct mfPLACEABLEHEADER {
343    DWORD    key;
344    short    hmf;
345    RECT32    bbox;
346    WORD    inch;
347    DWORD    reserved;
348    WORD    checksum;
349};
350#else
351struct mfPLACEABLEHEADER {
352    DWORD    key;
353    HANDLE    hmf;
354    RECT    bbox;
355    WORD    inch;
356    DWORD    reserved;
357    WORD    checksum;
358};
359#endif
360
361/*
362 * Pass filename of existing non-placeable metafile, and bounding box.
363 * Adds a placeable metafile header, sets the mapping mode to anisotropic,
364 * and sets the window origin and extent to mimic the wxMM_TEXT mapping mode.
365 *
366 */
367
368bool wxMakeMetafilePlaceable(const wxString& filename, float scale)
369{
370    return wxMakeMetafilePlaceable(filename, 0, 0, 0, 0, scale, false);
371}
372
373bool wxMakeMetafilePlaceable(const wxString& filename, int x1, int y1, int x2, int y2, float scale, bool useOriginAndExtent)
374{
375    // I'm not sure if this is the correct way of suggesting a scale
376    // to the client application, but it's the only way I can find.
377    int unitsPerInch = (int)(576/scale);
378
379    mfPLACEABLEHEADER header;
380    header.key = 0x9AC6CDD7L;
381    header.hmf = 0;
382    header.bbox.left = (int)(x1);
383    header.bbox.top = (int)(y1);
384    header.bbox.right = (int)(x2);
385    header.bbox.bottom = (int)(y2);
386    header.inch = unitsPerInch;
387    header.reserved = 0;
388
389    // Calculate checksum
390    WORD *p;
391    mfPLACEABLEHEADER *pMFHead = &header;
392    for (p =(WORD *)pMFHead,pMFHead -> checksum = 0;
393            p < (WORD *)&pMFHead ->checksum; ++p)
394        pMFHead ->checksum ^= *p;
395
396    FILE *fd = wxFopen(filename.fn_str(), _T("rb"));
397    if (!fd) return false;
398
399    wxChar tempFileBuf[256];
400    wxGetTempFileName(wxT("mf"), tempFileBuf);
401    FILE *fHandle = wxFopen(wxFNCONV(tempFileBuf), _T("wb"));
402    if (!fHandle)
403        return false;
404    fwrite((void *)&header, sizeof(unsigned char), sizeof(mfPLACEABLEHEADER), fHandle);
405
406    // Calculate origin and extent
407    int originX = x1;
408    int originY = y1;
409    int extentX = x2 - x1;
410    int extentY = (y2 - y1);
411
412    // Read metafile header and write
413    METAHEADER metaHeader;
414    fread((void *)&metaHeader, sizeof(unsigned char), sizeof(metaHeader), fd);
415
416    if (useOriginAndExtent)
417        metaHeader.mtSize += 15;
418    else
419        metaHeader.mtSize += 5;
420
421    fwrite((void *)&metaHeader, sizeof(unsigned char), sizeof(metaHeader), fHandle);
422
423    // Write SetMapMode, SetWindowOrigin and SetWindowExt records
424    char modeBuffer[8];
425    char originBuffer[10];
426    char extentBuffer[10];
427    METARECORD *modeRecord = (METARECORD *)&modeBuffer;
428
429    METARECORD *originRecord = (METARECORD *)&originBuffer;
430    METARECORD *extentRecord = (METARECORD *)&extentBuffer;
431
432    modeRecord->rdSize = 4;
433    modeRecord->rdFunction = META_SETMAPMODE;
434    modeRecord->rdParm[0] = MM_ANISOTROPIC;
435
436    originRecord->rdSize = 5;
437    originRecord->rdFunction = META_SETWINDOWORG;
438    originRecord->rdParm[0] = originY;
439    originRecord->rdParm[1] = originX;
440
441    extentRecord->rdSize = 5;
442    extentRecord->rdFunction = META_SETWINDOWEXT;
443    extentRecord->rdParm[0] = extentY;
444    extentRecord->rdParm[1] = extentX;
445
446    fwrite((void *)modeBuffer, sizeof(char), 8, fHandle);
447
448    if (useOriginAndExtent)
449    {
450        fwrite((void *)originBuffer, sizeof(char), 10, fHandle);
451        fwrite((void *)extentBuffer, sizeof(char), 10, fHandle);
452    }
453
454    int ch = -2;
455    while (ch != EOF)
456    {
457        ch = getc(fd);
458        if (ch != EOF)
459        {
460            putc(ch, fHandle);
461        }
462    }
463    fclose(fHandle);
464    fclose(fd);
465    wxRemoveFile(filename);
466    wxCopyFile(tempFileBuf, filename);
467    wxRemoveFile(tempFileBuf);
468    return true;
469}
470
471
472#if wxUSE_DRAG_AND_DROP
473
474// ----------------------------------------------------------------------------
475// wxMetafileDataObject
476// ----------------------------------------------------------------------------
477
478size_t wxMetafileDataObject::GetDataSize() const
479{
480    return sizeof(METAFILEPICT);
481}
482
483bool wxMetafileDataObject::GetDataHere(void *buf) const
484{
485    METAFILEPICT *mfpict = (METAFILEPICT *)buf;
486    const wxMetafile& mf = GetMetafile();
487
488    wxCHECK_MSG( mf.GetHMETAFILE(), false, _T("copying invalid metafile") );
489
490    // doesn't seem to work with any other mapping mode...
491    mfpict->mm   = MM_ANISOTROPIC; //mf.GetWindowsMappingMode();
492    mfpict->xExt = mf.GetWidth();
493    mfpict->yExt = mf.GetHeight();
494
495    // transform the picture size to HIMETRIC units (0.01mm) - as we don't know
496    // what DC the picture will be rendered to, use the default display one
497    PixelToHIMETRIC(&mfpict->xExt, &mfpict->yExt);
498
499    mfpict->hMF  = CopyMetaFile((HMETAFILE)mf.GetHMETAFILE(), NULL);
500
501    return true;
502}
503
504bool wxMetafileDataObject::SetData(size_t WXUNUSED(len), const void *buf)
505{
506    const METAFILEPICT *mfpict = (const METAFILEPICT *)buf;
507
508    wxMetafile mf;
509    mf.SetWindowsMappingMode(mfpict->mm);
510
511    LONG w = mfpict->xExt,
512         h = mfpict->yExt;
513    if ( mfpict->mm == MM_ANISOTROPIC )
514    {
515        // in this case xExt and yExt contain suggested size in HIMETRIC units
516        // (0.01 mm) - transform this to something more reasonable (pixels)
517        HIMETRICToPixel(&w, &h);
518    }
519
520    mf.SetWidth(w);
521    mf.SetHeight(h);
522    mf.SetHMETAFILE((WXHANDLE)mfpict->hMF);
523
524    wxCHECK_MSG( mfpict->hMF, false, _T("pasting invalid metafile") );
525
526    SetMetafile(mf);
527
528    return true;
529}
530
531#endif // wxUSE_DRAG_AND_DROP
532
533#endif // wxUSE_METAFILE
534