]>
Commit | Line | Data |
---|---|---|
1 | ///////////////////////////////////////////////////////////////////////////// | |
2 | // Name: src/msw/pen.cpp | |
3 | // Purpose: wxPen | |
4 | // Author: Julian Smart | |
5 | // Modified by: Vadim Zeitlin: refactored wxPen code to wxPenRefData | |
6 | // Created: 04/01/98 | |
7 | // Copyright: (c) Julian Smart | |
8 | // Licence: wxWindows licence | |
9 | ///////////////////////////////////////////////////////////////////////////// | |
10 | ||
11 | // ============================================================================ | |
12 | // declarations | |
13 | // ============================================================================ | |
14 | ||
15 | // ---------------------------------------------------------------------------- | |
16 | // headers | |
17 | // ---------------------------------------------------------------------------- | |
18 | ||
19 | // For compilers that support precompilation, includes "wx.h". | |
20 | #include "wx/wxprec.h" | |
21 | ||
22 | #ifdef __BORLANDC__ | |
23 | #pragma hdrstop | |
24 | #endif | |
25 | ||
26 | #include "wx/pen.h" | |
27 | ||
28 | #ifndef WX_PRECOMP | |
29 | #include "wx/bitmap.h" | |
30 | #include "wx/utils.h" | |
31 | #endif | |
32 | ||
33 | #include "wx/msw/private.h" | |
34 | ||
35 | #define M_PENDATA ((wxPenRefData*)m_refData) | |
36 | ||
37 | // Win32 has ExtCreatePen() but WinCE doesn't | |
38 | #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) | |
39 | #define wxHAVE_EXT_CREATE_PEN | |
40 | #endif | |
41 | ||
42 | // ---------------------------------------------------------------------------- | |
43 | // wxPenRefData: contains information about an HPEN and its handle | |
44 | // ---------------------------------------------------------------------------- | |
45 | ||
46 | class WXDLLEXPORT wxPenRefData : public wxGDIRefData | |
47 | { | |
48 | public: | |
49 | // ctors and dtor | |
50 | // -------------- | |
51 | ||
52 | wxPenRefData(); | |
53 | wxPenRefData(const wxPenRefData& data); | |
54 | wxPenRefData(const wxColour& col, int width, wxPenStyle style); | |
55 | wxPenRefData(const wxBitmap& stipple, int width); | |
56 | virtual ~wxPenRefData(); | |
57 | ||
58 | bool operator==(const wxPenRefData& data) const | |
59 | { | |
60 | // we intentionally don't compare m_hPen fields here | |
61 | return m_style == data.m_style && | |
62 | m_width == data.m_width && | |
63 | m_join == data.m_join && | |
64 | m_cap == data.m_cap && | |
65 | m_colour == data.m_colour && | |
66 | (m_style != wxPENSTYLE_STIPPLE || m_stipple.IsSameAs(data.m_stipple)) && | |
67 | (m_style != wxPENSTYLE_USER_DASH || | |
68 | (m_nbDash == data.m_nbDash && | |
69 | memcmp(m_dash, data.m_dash, m_nbDash*sizeof(wxDash)) == 0)); | |
70 | } | |
71 | ||
72 | ||
73 | // accessors and setters | |
74 | // --------------------- | |
75 | ||
76 | wxColour& GetColour() const { return const_cast<wxColour&>(m_colour); } | |
77 | int GetWidth() const { return m_width; } | |
78 | wxPenStyle GetStyle() const { return m_style; } | |
79 | wxPenJoin GetJoin() const { return m_join; } | |
80 | wxPenCap GetCap() const { return m_cap; } | |
81 | wxDash* GetDash() const { return m_dash; } | |
82 | int GetDashCount() const { return m_nbDash; } | |
83 | wxBitmap* GetStipple() const { return const_cast<wxBitmap *>(&m_stipple); } | |
84 | ||
85 | void SetColour(const wxColour& col) { Free(); m_colour = col; } | |
86 | void SetWidth(int width) { Free(); m_width = width; } | |
87 | void SetStyle(wxPenStyle style) { Free(); m_style = style; } | |
88 | void SetStipple(const wxBitmap& stipple) | |
89 | { | |
90 | Free(); | |
91 | ||
92 | m_style = wxPENSTYLE_STIPPLE; | |
93 | m_stipple = stipple; | |
94 | } | |
95 | ||
96 | void SetDashes(int nb_dashes, const wxDash *dash) | |
97 | { | |
98 | Free(); | |
99 | ||
100 | m_nbDash = nb_dashes; | |
101 | m_dash = const_cast<wxDash *>(dash); | |
102 | } | |
103 | ||
104 | void SetJoin(wxPenJoin join) { Free(); m_join = join; } | |
105 | void SetCap(wxPenCap cap) { Free(); m_cap = cap; } | |
106 | ||
107 | ||
108 | // HPEN management | |
109 | // --------------- | |
110 | ||
111 | // create the HPEN if we don't have it yet | |
112 | bool Alloc(); | |
113 | ||
114 | // get the HPEN creating it on demand | |
115 | WXHPEN GetHPEN() const; | |
116 | ||
117 | // return true if we have a valid HPEN | |
118 | bool HasHPEN() const { return m_hPen != 0; } | |
119 | ||
120 | // return true if we had a valid handle before, false otherwise | |
121 | bool Free(); | |
122 | ||
123 | private: | |
124 | // initialize the fields which have reasonable default values | |
125 | // | |
126 | // doesn't initialize m_width and m_style which must be initialize in ctor | |
127 | void Init() | |
128 | { | |
129 | m_join = wxJOIN_ROUND; | |
130 | m_cap = wxCAP_ROUND; | |
131 | m_nbDash = 0; | |
132 | m_dash = NULL; | |
133 | m_hPen = 0; | |
134 | } | |
135 | ||
136 | int m_width; | |
137 | wxPenStyle m_style; | |
138 | wxPenJoin m_join; | |
139 | wxPenCap m_cap; | |
140 | wxBitmap m_stipple; | |
141 | int m_nbDash; | |
142 | wxDash * m_dash; | |
143 | wxColour m_colour; | |
144 | HPEN m_hPen; | |
145 | ||
146 | wxDECLARE_NO_ASSIGN_CLASS(wxPenRefData); | |
147 | }; | |
148 | ||
149 | // ============================================================================ | |
150 | // implementation | |
151 | // ============================================================================ | |
152 | ||
153 | // ---------------------------------------------------------------------------- | |
154 | // wxPenRefData ctors/dtor | |
155 | // ---------------------------------------------------------------------------- | |
156 | ||
157 | wxPenRefData::wxPenRefData() | |
158 | { | |
159 | Init(); | |
160 | ||
161 | m_style = wxPENSTYLE_SOLID; | |
162 | m_width = 1; | |
163 | } | |
164 | ||
165 | wxPenRefData::wxPenRefData(const wxPenRefData& data) | |
166 | :wxGDIRefData() | |
167 | { | |
168 | m_style = data.m_style; | |
169 | m_width = data.m_width; | |
170 | m_join = data.m_join; | |
171 | m_cap = data.m_cap; | |
172 | m_nbDash = data.m_nbDash; | |
173 | m_dash = data.m_dash; | |
174 | m_colour = data.m_colour; | |
175 | m_hPen = 0; | |
176 | } | |
177 | ||
178 | wxPenRefData::wxPenRefData(const wxColour& col, int width, wxPenStyle style) | |
179 | { | |
180 | Init(); | |
181 | ||
182 | m_style = style; | |
183 | m_width = width; | |
184 | ||
185 | m_colour = col; | |
186 | } | |
187 | ||
188 | wxPenRefData::wxPenRefData(const wxBitmap& stipple, int width) | |
189 | { | |
190 | Init(); | |
191 | ||
192 | m_style = wxPENSTYLE_STIPPLE; | |
193 | m_width = width; | |
194 | ||
195 | m_stipple = stipple; | |
196 | } | |
197 | ||
198 | wxPenRefData::~wxPenRefData() | |
199 | { | |
200 | if ( m_hPen ) | |
201 | ::DeleteObject(m_hPen); | |
202 | } | |
203 | ||
204 | // ---------------------------------------------------------------------------- | |
205 | // wxPenRefData HPEN management | |
206 | // ---------------------------------------------------------------------------- | |
207 | ||
208 | static int ConvertPenStyle(wxPenStyle style) | |
209 | { | |
210 | switch ( style ) | |
211 | { | |
212 | case wxPENSTYLE_SHORT_DASH: | |
213 | case wxPENSTYLE_LONG_DASH: | |
214 | return PS_DASH; | |
215 | ||
216 | case wxPENSTYLE_TRANSPARENT: | |
217 | return PS_NULL; | |
218 | ||
219 | default: | |
220 | wxFAIL_MSG( wxT("unknown pen style") ); | |
221 | // fall through | |
222 | ||
223 | #ifdef wxHAVE_EXT_CREATE_PEN | |
224 | case wxPENSTYLE_DOT: | |
225 | return PS_DOT; | |
226 | ||
227 | case wxPENSTYLE_DOT_DASH: | |
228 | return PS_DASHDOT; | |
229 | ||
230 | case wxPENSTYLE_USER_DASH: | |
231 | return PS_USERSTYLE; | |
232 | ||
233 | case wxPENSTYLE_STIPPLE: | |
234 | case wxPENSTYLE_BDIAGONAL_HATCH: | |
235 | case wxPENSTYLE_CROSSDIAG_HATCH: | |
236 | case wxPENSTYLE_FDIAGONAL_HATCH: | |
237 | case wxPENSTYLE_CROSS_HATCH: | |
238 | case wxPENSTYLE_HORIZONTAL_HATCH: | |
239 | case wxPENSTYLE_VERTICAL_HATCH: | |
240 | case wxPENSTYLE_SOLID: | |
241 | #endif // wxHAVE_EXT_CREATE_PEN | |
242 | ||
243 | return PS_SOLID; | |
244 | } | |
245 | } | |
246 | ||
247 | #ifdef wxHAVE_EXT_CREATE_PEN | |
248 | ||
249 | static int ConvertJoinStyle(wxPenJoin join) | |
250 | { | |
251 | switch( join ) | |
252 | { | |
253 | case wxJOIN_BEVEL: | |
254 | return PS_JOIN_BEVEL; | |
255 | ||
256 | case wxJOIN_MITER: | |
257 | return PS_JOIN_MITER; | |
258 | ||
259 | default: | |
260 | wxFAIL_MSG( wxT("unknown pen join style") ); | |
261 | // fall through | |
262 | ||
263 | case wxJOIN_ROUND: | |
264 | return PS_JOIN_ROUND; | |
265 | } | |
266 | } | |
267 | ||
268 | static int ConvertCapStyle(wxPenCap cap) | |
269 | { | |
270 | switch ( cap ) | |
271 | { | |
272 | case wxCAP_PROJECTING: | |
273 | return PS_ENDCAP_SQUARE; | |
274 | ||
275 | case wxCAP_BUTT: | |
276 | return PS_ENDCAP_FLAT; | |
277 | ||
278 | default: | |
279 | wxFAIL_MSG( wxT("unknown pen cap style") ); | |
280 | // fall through | |
281 | ||
282 | case wxCAP_ROUND: | |
283 | return PS_ENDCAP_ROUND; | |
284 | } | |
285 | } | |
286 | ||
287 | #endif // wxHAVE_EXT_CREATE_PEN | |
288 | ||
289 | bool wxPenRefData::Alloc() | |
290 | { | |
291 | if ( m_hPen ) | |
292 | return false; | |
293 | ||
294 | if ( m_style == wxPENSTYLE_TRANSPARENT ) | |
295 | { | |
296 | m_hPen = (HPEN)::GetStockObject(NULL_PEN); | |
297 | return true; | |
298 | } | |
299 | ||
300 | const COLORREF col = m_colour.GetPixel(); | |
301 | ||
302 | #ifdef wxHAVE_EXT_CREATE_PEN | |
303 | // Only NT can display dashed or dotted lines with width > 1 | |
304 | static const int os = wxGetOsVersion(); | |
305 | if ( os != wxOS_WINDOWS_NT && | |
306 | (m_style == wxPENSTYLE_DOT || | |
307 | m_style == wxPENSTYLE_LONG_DASH || | |
308 | m_style == wxPENSTYLE_SHORT_DASH || | |
309 | m_style == wxPENSTYLE_DOT_DASH || | |
310 | m_style == wxPENSTYLE_USER_DASH) && | |
311 | m_width > 1 ) | |
312 | { | |
313 | m_width = 1; | |
314 | } | |
315 | ||
316 | // check if it's a standard kind of pen which can be created with just | |
317 | // CreatePen() | |
318 | if ( m_join == wxJOIN_ROUND && | |
319 | m_cap == wxCAP_ROUND && | |
320 | m_style != wxPENSTYLE_USER_DASH && | |
321 | m_style != wxPENSTYLE_STIPPLE && | |
322 | (m_width <= 1 || m_style == wxPENSTYLE_SOLID) ) | |
323 | #endif // !wxHAVE_EXT_CREATE_PEN | |
324 | { | |
325 | m_hPen = ::CreatePen(ConvertPenStyle(m_style), m_width, col); | |
326 | } | |
327 | #ifdef wxHAVE_EXT_CREATE_PEN | |
328 | else // need to use ExtCreatePen() | |
329 | { | |
330 | DWORD styleMSW = PS_GEOMETRIC | | |
331 | ConvertPenStyle(m_style) | | |
332 | ConvertJoinStyle(m_join) | | |
333 | ConvertCapStyle(m_cap); | |
334 | ||
335 | LOGBRUSH lb; | |
336 | switch( m_style ) | |
337 | { | |
338 | case wxPENSTYLE_STIPPLE: | |
339 | lb.lbStyle = BS_PATTERN; | |
340 | lb.lbHatch = wxPtrToUInt(m_stipple.GetHBITMAP()); | |
341 | break; | |
342 | ||
343 | case wxPENSTYLE_BDIAGONAL_HATCH: | |
344 | lb.lbStyle = BS_HATCHED; | |
345 | lb.lbHatch = HS_BDIAGONAL; | |
346 | break; | |
347 | ||
348 | case wxPENSTYLE_CROSSDIAG_HATCH: | |
349 | lb.lbStyle = BS_HATCHED; | |
350 | lb.lbHatch = HS_DIAGCROSS; | |
351 | break; | |
352 | ||
353 | case wxPENSTYLE_FDIAGONAL_HATCH: | |
354 | lb.lbStyle = BS_HATCHED; | |
355 | lb.lbHatch = HS_FDIAGONAL; | |
356 | break; | |
357 | ||
358 | case wxPENSTYLE_CROSS_HATCH: | |
359 | lb.lbStyle = BS_HATCHED; | |
360 | lb.lbHatch = HS_CROSS; | |
361 | break; | |
362 | ||
363 | case wxPENSTYLE_HORIZONTAL_HATCH: | |
364 | lb.lbStyle = BS_HATCHED; | |
365 | lb.lbHatch = HS_HORIZONTAL; | |
366 | break; | |
367 | ||
368 | case wxPENSTYLE_VERTICAL_HATCH: | |
369 | lb.lbStyle = BS_HATCHED; | |
370 | lb.lbHatch = HS_VERTICAL; | |
371 | break; | |
372 | ||
373 | default: | |
374 | lb.lbStyle = BS_SOLID; | |
375 | // this should be unnecessary (it's unused) but suppresses the | |
376 | // Purify messages about uninitialized memory read | |
377 | lb.lbHatch = 0; | |
378 | break; | |
379 | } | |
380 | ||
381 | lb.lbColor = col; | |
382 | ||
383 | DWORD *dash; | |
384 | if ( m_style == wxPENSTYLE_USER_DASH && m_nbDash && m_dash ) | |
385 | { | |
386 | dash = new DWORD[m_nbDash]; | |
387 | int rw = m_width > 1 ? m_width : 1; | |
388 | for ( int i = 0; i < m_nbDash; i++ ) | |
389 | dash[i] = m_dash[i] * rw; | |
390 | } | |
391 | else | |
392 | { | |
393 | dash = NULL; | |
394 | } | |
395 | ||
396 | m_hPen = ::ExtCreatePen(styleMSW, m_width, &lb, m_nbDash, (LPDWORD)dash); | |
397 | ||
398 | delete [] dash; | |
399 | } | |
400 | #endif // wxHAVE_EXT_CREATE_PEN | |
401 | ||
402 | return m_hPen != 0; | |
403 | } | |
404 | ||
405 | bool wxPenRefData::Free() | |
406 | { | |
407 | if ( !m_hPen ) | |
408 | return false; | |
409 | ||
410 | ::DeleteObject(m_hPen); | |
411 | m_hPen = 0; | |
412 | ||
413 | return true; | |
414 | } | |
415 | ||
416 | WXHPEN wxPenRefData::GetHPEN() const | |
417 | { | |
418 | if ( !m_hPen ) | |
419 | const_cast<wxPenRefData *>(this)->Alloc(); | |
420 | ||
421 | return (WXHPEN)m_hPen; | |
422 | } | |
423 | ||
424 | // ---------------------------------------------------------------------------- | |
425 | // wxPen | |
426 | // ---------------------------------------------------------------------------- | |
427 | ||
428 | IMPLEMENT_DYNAMIC_CLASS(wxPen, wxGDIObject) | |
429 | ||
430 | wxPen::wxPen(const wxColour& col, int width, wxPenStyle style) | |
431 | { | |
432 | m_refData = new wxPenRefData(col, width, style); | |
433 | } | |
434 | ||
435 | #if FUTURE_WXWIN_COMPATIBILITY_3_0 | |
436 | wxPen::wxPen(const wxColour& colour, int width, int style) | |
437 | { | |
438 | m_refData = new wxPenRefData(colour, width, (wxPenStyle)style); | |
439 | } | |
440 | #endif | |
441 | ||
442 | wxPen::wxPen(const wxBitmap& stipple, int width) | |
443 | { | |
444 | m_refData = new wxPenRefData(stipple, width); | |
445 | } | |
446 | ||
447 | bool wxPen::operator==(const wxPen& pen) const | |
448 | { | |
449 | const wxPenRefData * | |
450 | penData = static_cast<const wxPenRefData *>(pen.m_refData); | |
451 | ||
452 | // an invalid pen is only equal to another invalid pen | |
453 | return m_refData ? penData && *M_PENDATA == *penData : !penData; | |
454 | } | |
455 | ||
456 | bool wxPen::RealizeResource() | |
457 | { | |
458 | return M_PENDATA && M_PENDATA->Alloc(); | |
459 | } | |
460 | ||
461 | WXHANDLE wxPen::GetResourceHandle() const | |
462 | { | |
463 | return M_PENDATA ? M_PENDATA->GetHPEN() : 0; | |
464 | } | |
465 | ||
466 | bool wxPen::FreeResource(bool WXUNUSED(force)) | |
467 | { | |
468 | return M_PENDATA && M_PENDATA->Free(); | |
469 | } | |
470 | ||
471 | bool wxPen::IsFree() const | |
472 | { | |
473 | return M_PENDATA && !M_PENDATA->HasHPEN(); | |
474 | } | |
475 | ||
476 | wxGDIRefData* wxPen::CreateGDIRefData() const | |
477 | { | |
478 | return new wxPenRefData; | |
479 | } | |
480 | ||
481 | wxGDIRefData* wxPen::CloneGDIRefData(const wxGDIRefData* data) const | |
482 | { | |
483 | return new wxPenRefData(*static_cast<const wxPenRefData*>(data)); | |
484 | } | |
485 | ||
486 | void wxPen::SetColour(const wxColour& col) | |
487 | { | |
488 | AllocExclusive(); | |
489 | ||
490 | M_PENDATA->SetColour(col); | |
491 | } | |
492 | ||
493 | void wxPen::SetColour(unsigned char r, unsigned char g, unsigned char b) | |
494 | { | |
495 | SetColour(wxColour(r, g, b)); | |
496 | } | |
497 | ||
498 | void wxPen::SetWidth(int width) | |
499 | { | |
500 | AllocExclusive(); | |
501 | ||
502 | M_PENDATA->SetWidth(width); | |
503 | } | |
504 | ||
505 | void wxPen::SetStyle(wxPenStyle style) | |
506 | { | |
507 | AllocExclusive(); | |
508 | ||
509 | M_PENDATA->SetStyle(style); | |
510 | } | |
511 | ||
512 | void wxPen::SetStipple(const wxBitmap& stipple) | |
513 | { | |
514 | AllocExclusive(); | |
515 | ||
516 | M_PENDATA->SetStipple(stipple); | |
517 | } | |
518 | ||
519 | void wxPen::SetDashes(int nb_dashes, const wxDash *dash) | |
520 | { | |
521 | AllocExclusive(); | |
522 | ||
523 | M_PENDATA->SetDashes(nb_dashes, dash); | |
524 | } | |
525 | ||
526 | void wxPen::SetJoin(wxPenJoin join) | |
527 | { | |
528 | AllocExclusive(); | |
529 | ||
530 | M_PENDATA->SetJoin(join); | |
531 | } | |
532 | ||
533 | void wxPen::SetCap(wxPenCap cap) | |
534 | { | |
535 | AllocExclusive(); | |
536 | ||
537 | M_PENDATA->SetCap(cap); | |
538 | } | |
539 | ||
540 | wxColour wxPen::GetColour() const | |
541 | { | |
542 | wxCHECK_MSG( IsOk(), wxNullColour, wxT("invalid pen") ); | |
543 | ||
544 | return M_PENDATA->GetColour(); | |
545 | } | |
546 | ||
547 | int wxPen::GetWidth() const | |
548 | { | |
549 | wxCHECK_MSG( IsOk(), -1, wxT("invalid pen") ); | |
550 | ||
551 | return M_PENDATA->GetWidth(); | |
552 | } | |
553 | ||
554 | wxPenStyle wxPen::GetStyle() const | |
555 | { | |
556 | wxCHECK_MSG( IsOk(), wxPENSTYLE_INVALID, wxT("invalid pen") ); | |
557 | ||
558 | return M_PENDATA->GetStyle(); | |
559 | } | |
560 | ||
561 | wxPenJoin wxPen::GetJoin() const | |
562 | { | |
563 | wxCHECK_MSG( IsOk(), wxJOIN_INVALID, wxT("invalid pen") ); | |
564 | ||
565 | return M_PENDATA->GetJoin(); | |
566 | } | |
567 | ||
568 | wxPenCap wxPen::GetCap() const | |
569 | { | |
570 | wxCHECK_MSG( IsOk(), wxCAP_INVALID, wxT("invalid pen") ); | |
571 | ||
572 | return M_PENDATA->GetCap(); | |
573 | } | |
574 | ||
575 | int wxPen::GetDashes(wxDash** ptr) const | |
576 | { | |
577 | wxCHECK_MSG( IsOk(), -1, wxT("invalid pen") ); | |
578 | ||
579 | *ptr = M_PENDATA->GetDash(); | |
580 | return M_PENDATA->GetDashCount(); | |
581 | } | |
582 | ||
583 | wxDash* wxPen::GetDash() const | |
584 | { | |
585 | wxCHECK_MSG( IsOk(), NULL, wxT("invalid pen") ); | |
586 | ||
587 | return m_refData ? M_PENDATA->GetDash() : NULL; | |
588 | } | |
589 | ||
590 | int wxPen::GetDashCount() const | |
591 | { | |
592 | wxCHECK_MSG( IsOk(), -1, wxT("invalid pen") ); | |
593 | ||
594 | return m_refData ? M_PENDATA->GetDashCount() : 0; | |
595 | } | |
596 | ||
597 | wxBitmap* wxPen::GetStipple() const | |
598 | { | |
599 | wxCHECK_MSG( IsOk(), NULL, wxT("invalid pen") ); | |
600 | ||
601 | return m_refData ? M_PENDATA->GetStipple() : NULL; | |
602 | } |