Update for bitmap, image on scaling, transparancy,
[wxWidgets.git] / src / gtk1 / dcclient.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dcclient.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // RCS-ID: $Id$
6 // Copyright: (c) 1998 Robert Roebling, Markus Holzem, Chris Breeze
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "dcclient.h"
12 #endif
13
14 #include "wx/dcclient.h"
15 #include "wx/dcmemory.h"
16 #include "wx/image.h"
17 #include <math.h>
18
19 //-----------------------------------------------------------------------------
20 // local data
21 //-----------------------------------------------------------------------------
22
23 #include "bdiag.xbm"
24 #include "fdiag.xbm"
25 #include "cdiag.xbm"
26 #include "horiz.xbm"
27 #include "verti.xbm"
28 #include "cross.xbm"
29 #define num_hatches 6
30
31 static GdkPixmap *hatches[num_hatches];
32 static GdkPixmap **hatch_bitmap = (GdkPixmap **) NULL;
33
34 //-----------------------------------------------------------------------------
35 // constants
36 //-----------------------------------------------------------------------------
37
38 #define RAD2DEG 57.2957795131
39
40 //-----------------------------------------------------------------------------
41 // temporary implementation of the missing GDK function
42 //-----------------------------------------------------------------------------
43 #include "gdk/gdkprivate.h"
44 void gdk_draw_bitmap (GdkDrawable *drawable,
45 GdkGC *gc,
46 GdkDrawable *src,
47 gint xsrc,
48 gint ysrc,
49 gint xdest,
50 gint ydest,
51 gint width,
52 gint height)
53 {
54 GdkWindowPrivate *drawable_private;
55 GdkWindowPrivate *src_private;
56 GdkGCPrivate *gc_private;
57
58 g_return_if_fail (drawable != NULL);
59 g_return_if_fail (src != NULL);
60 g_return_if_fail (gc != NULL);
61
62 drawable_private = (GdkWindowPrivate*) drawable;
63 src_private = (GdkWindowPrivate*) src;
64 if (drawable_private->destroyed || src_private->destroyed)
65 return;
66
67 gc_private = (GdkGCPrivate*) gc;
68
69 if (width == -1) width = src_private->width;
70 if (height == -1) height = src_private->height;
71
72 XCopyPlane( drawable_private->xdisplay,
73 src_private->xwindow,
74 drawable_private->xwindow,
75 gc_private->xgc,
76 xsrc, ysrc,
77 width, height,
78 xdest, ydest,
79 1 );
80 }
81
82 //-----------------------------------------------------------------------------
83 // wxWindowDC
84 //-----------------------------------------------------------------------------
85
86 IMPLEMENT_DYNAMIC_CLASS(wxWindowDC,wxDC)
87
88 wxWindowDC::wxWindowDC()
89 {
90 m_penGC = (GdkGC *) NULL;
91 m_brushGC = (GdkGC *) NULL;
92 m_textGC = (GdkGC *) NULL;
93 m_bgGC = (GdkGC *) NULL;
94 m_cmap = (GdkColormap *) NULL;
95 m_isMemDC = FALSE;
96 }
97
98 wxWindowDC::wxWindowDC( wxWindow *window )
99 {
100 m_penGC = (GdkGC *) NULL;
101 m_brushGC = (GdkGC *) NULL;
102 m_textGC = (GdkGC *) NULL;
103 m_bgGC = (GdkGC *) NULL;
104 m_cmap = (GdkColormap *) NULL;
105
106 if (!window) return;
107 GtkWidget *widget = window->m_wxwindow;
108 if (!widget) return;
109 m_window = widget->window;
110 if (!m_window) return;
111 if (window->m_wxwindow)
112 m_cmap = gtk_widget_get_colormap( window->m_wxwindow );
113 else
114 m_cmap = gtk_widget_get_colormap( window->m_widget );
115
116 m_isMemDC = FALSE;
117
118 SetUpDC();
119
120 }
121
122 wxWindowDC::~wxWindowDC()
123 {
124 Destroy();
125 }
126
127 void wxWindowDC::FloodFill( long WXUNUSED(x), long WXUNUSED(y),
128 const wxColour &WXUNUSED(col), int WXUNUSED(style) )
129 {
130 wxFAIL_MSG( "wxWindowDC::FloodFill not implemented" );
131 }
132
133 bool wxWindowDC::GetPixel( long WXUNUSED(x1), long WXUNUSED(y1), wxColour *WXUNUSED(col) ) const
134 {
135 wxFAIL_MSG( "wxWindowDC::GetPixel not implemented" );
136 return FALSE;
137 }
138
139 void wxWindowDC::DrawLine( long x1, long y1, long x2, long y2 )
140 {
141 wxCHECK_RET( Ok(), "invalid window dc" );
142
143 if (m_pen.GetStyle() != wxTRANSPARENT)
144 {
145 gdk_draw_line( m_window, m_penGC,
146 XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2) );
147
148 CalcBoundingBox(x1, y1);
149 CalcBoundingBox(x2, y2);
150 }
151 }
152
153 void wxWindowDC::CrossHair( long x, long y )
154 {
155 wxCHECK_RET( Ok(), "invalid window dc" );
156
157 if (m_pen.GetStyle() != wxTRANSPARENT)
158 {
159 int w = 0;
160 int h = 0;
161 GetSize( &w, &h );
162 long xx = XLOG2DEV(x);
163 long yy = YLOG2DEV(y);
164 gdk_draw_line( m_window, m_penGC, 0, yy, XLOG2DEVREL(w), yy );
165 gdk_draw_line( m_window, m_penGC, xx, 0, xx, YLOG2DEVREL(h) );
166 }
167 }
168
169 void wxWindowDC::DrawArc( long x1, long y1, long x2, long y2, double xc, double yc )
170 {
171 wxCHECK_RET( Ok(), "invalid window dc" );
172
173 long xx1 = XLOG2DEV(x1);
174 long yy1 = YLOG2DEV(y1);
175 long xx2 = XLOG2DEV(x2);
176 long yy2 = YLOG2DEV(y2);
177 long xxc = XLOG2DEV((long)xc);
178 long yyc = YLOG2DEV((long)yc);
179 double dx = xx1 - xxc;
180 double dy = yy1 - yyc;
181 double radius = sqrt(dx*dx+dy*dy);
182 long r = (long)radius;
183 double radius1, radius2;
184
185 if (xx1 == xx2 && yy1 == yy2)
186 {
187 radius1 = 0.0;
188 radius2 = 360.0;
189 }
190 else
191 if (radius == 0.0)
192 {
193 radius1 = radius2 = 0.0;
194 }
195 else
196 {
197 radius1 = (xx1 - xxc == 0) ?
198 (yy1 - yyc < 0) ? 90.0 : -90.0 :
199 -atan2(double(yy1-yyc), double(xx1-xxc)) * RAD2DEG;
200 radius2 = (xx2 - xxc == 0) ?
201 (yy2 - yyc < 0) ? 90.0 : -90.0 :
202 -atan2(double(yy2-yyc), double(xx2-xxc)) * RAD2DEG;
203 }
204 long alpha1 = long(radius1 * 64.0);
205 long alpha2 = long((radius2 - radius1) * 64.0);
206 while (alpha2 <= 0) alpha2 += 360*64;
207 while (alpha1 > 360*64) alpha1 -= 360*64;
208
209 if (m_brush.GetStyle() != wxTRANSPARENT)
210 gdk_draw_arc( m_window, m_brushGC, TRUE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 );
211
212 if (m_pen.GetStyle() != wxTRANSPARENT)
213 gdk_draw_arc( m_window, m_penGC, FALSE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 );
214
215 CalcBoundingBox (x1, y1);
216 CalcBoundingBox (x2, y2);
217 }
218
219 void wxWindowDC::DrawEllipticArc( long x, long y, long width, long height, double sa, double ea )
220 {
221 wxCHECK_RET( Ok(), "invalid window dc" );
222
223 long xx = XLOG2DEV(x);
224 long yy = YLOG2DEV(y);
225 long ww = m_signX * XLOG2DEVREL(width);
226 long hh = m_signY * YLOG2DEVREL(height);
227
228 // CMB: handle -ve width and/or height
229 if (ww < 0) { ww = -ww; xx = xx - ww; }
230 if (hh < 0) { hh = -hh; yy = yy - hh; }
231
232 long start = long(sa * 64.0);
233 long end = long(ea * 64.0);
234 if (m_brush.GetStyle() != wxTRANSPARENT)
235 gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, start, end );
236
237 if (m_pen.GetStyle() != wxTRANSPARENT)
238 gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, ww, hh, start, end );
239
240 CalcBoundingBox (x, y);
241 CalcBoundingBox (x + width, y + height);
242 }
243
244 void wxWindowDC::DrawPoint( long x, long y )
245 {
246 wxCHECK_RET( Ok(), "invalid window dc" );
247
248 if (m_pen.GetStyle() != wxTRANSPARENT)
249 gdk_draw_point( m_window, m_penGC, XLOG2DEV(x), YLOG2DEV(y) );
250
251 CalcBoundingBox (x, y);
252 }
253
254 void wxWindowDC::DrawLines( int n, wxPoint points[], long xoffset, long yoffset )
255 {
256 wxCHECK_RET( Ok(), "invalid window dc" );
257
258 if (m_pen.GetStyle() == wxTRANSPARENT) return;
259 if (n <= 0) return;
260
261 CalcBoundingBox( points[0].x + xoffset, points[0].y + yoffset );
262
263 for (int i = 0; i < n-1; i++)
264 {
265 long x1 = XLOG2DEV(points[i].x + xoffset);
266 long x2 = XLOG2DEV(points[i+1].x + xoffset);
267 long y1 = YLOG2DEV(points[i].y + yoffset); // oh, what a waste
268 long y2 = YLOG2DEV(points[i+1].y + yoffset);
269 gdk_draw_line( m_window, m_penGC, x1, y1, x2, y2 );
270
271 CalcBoundingBox( points[i+1].x + xoffset, points[i+1].y + yoffset );
272 }
273 }
274
275 void wxWindowDC::DrawLines( wxList *points, long xoffset, long yoffset )
276 {
277 wxCHECK_RET( Ok(), "invalid window dc" );
278
279 if (m_pen.GetStyle() == wxTRANSPARENT) return;
280
281 wxNode *node = points->First();
282 if (!node) return;
283
284 wxPoint *pt = (wxPoint*)node->Data();
285 CalcBoundingBox( pt->x + xoffset, pt->y + yoffset );
286
287 while (node->Next())
288 {
289 wxPoint *point = (wxPoint*)node->Data();
290 wxPoint *npoint = (wxPoint*)node->Next()->Data();
291 long x1 = XLOG2DEV(point->x + xoffset);
292 long x2 = XLOG2DEV(npoint->x + xoffset);
293 long y1 = YLOG2DEV(point->y + yoffset); // and a waste again...
294 long y2 = YLOG2DEV(npoint->y + yoffset);
295 gdk_draw_line( m_window, m_penGC, x1, y1, x2, y2 );
296 node = node->Next();
297
298 CalcBoundingBox( npoint->x + xoffset, npoint->y + yoffset );
299 }
300 }
301
302 void wxWindowDC::DrawPolygon( int n, wxPoint points[], long xoffset, long yoffset, int WXUNUSED(fillStyle) )
303 {
304 wxCHECK_RET( Ok(), "invalid window dc" );
305
306 if (n <= 0) return;
307
308 GdkPoint *gdkpoints = new GdkPoint[n+1];
309 int i;
310 for (i = 0 ; i < n ; i++)
311 {
312 gdkpoints[i].x = XLOG2DEV(points[i].x + xoffset);
313 gdkpoints[i].y = YLOG2DEV(points[i].y + yoffset);
314
315 CalcBoundingBox( points[i].x + xoffset, points[i].y + yoffset );
316 }
317
318 if (m_brush.GetStyle() != wxTRANSPARENT)
319 gdk_draw_polygon (m_window, m_brushGC, TRUE, gdkpoints, n);
320
321 // To do: Fillstyle
322
323 if (m_pen.GetStyle() != wxTRANSPARENT)
324 for (i = 0 ; i < n ; i++)
325 {
326 gdk_draw_line( m_window, m_penGC,
327 gdkpoints[i%n].x,
328 gdkpoints[i%n].y,
329 gdkpoints[(i+1)%n].x,
330 gdkpoints[(i+1)%n].y);
331 }
332
333 delete[] gdkpoints;
334 }
335
336 void wxWindowDC::DrawPolygon( wxList *lines, long xoffset, long yoffset, int WXUNUSED(fillStyle))
337 {
338 wxCHECK_RET( Ok(), "invalid window dc" );
339
340 int n = lines->Number();
341 if (n <= 0) return;
342
343 GdkPoint *gdkpoints = new GdkPoint[n];
344 wxNode *node = lines->First();
345 int cnt = 0;
346 while (node)
347 {
348 wxPoint *p = (wxPoint *) node->Data();
349 gdkpoints[cnt].x = XLOG2DEV(p->x + xoffset);
350 gdkpoints[cnt].y = YLOG2DEV(p->y + yoffset);
351 node = node->Next();
352 cnt++;
353
354 CalcBoundingBox( p->x + xoffset, p->y + yoffset );
355 }
356
357 if (m_brush.GetStyle() != wxTRANSPARENT)
358 gdk_draw_polygon (m_window, m_brushGC, TRUE, gdkpoints, n);
359
360 // To do: Fillstyle
361
362 if (m_pen.GetStyle() != wxTRANSPARENT)
363 {
364 int i;
365 for (i = 0 ; i < n ; i++)
366 {
367 gdk_draw_line( m_window, m_penGC,
368 gdkpoints[i%n].x,
369 gdkpoints[i%n].y,
370 gdkpoints[(i+1)%n].x,
371 gdkpoints[(i+1)%n].y );
372 }
373 }
374 delete[] gdkpoints;
375 }
376
377 void wxWindowDC::DrawRectangle( long x, long y, long width, long height )
378 {
379 wxCHECK_RET( Ok(), "invalid window dc" );
380
381 long xx = XLOG2DEV(x);
382 long yy = YLOG2DEV(y);
383 long ww = m_signX * XLOG2DEVREL(width);
384 long hh = m_signY * YLOG2DEVREL(height);
385
386 // CMB: draw nothing if transformed w or h is 0
387 if (ww == 0 || hh == 0) return;
388
389 // CMB: handle -ve width and/or height
390 if (ww < 0) { ww = -ww; xx = xx - ww; }
391 if (hh < 0) { hh = -hh; yy = yy - hh; }
392
393 if (m_brush.GetStyle() != wxTRANSPARENT)
394 gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh );
395
396 if (m_pen.GetStyle() != wxTRANSPARENT)
397 gdk_draw_rectangle( m_window, m_penGC, FALSE, xx, yy, ww-1, hh-1 );
398
399 CalcBoundingBox( x, y );
400 CalcBoundingBox( x + width, y + height );
401 }
402
403 void wxWindowDC::DrawRoundedRectangle( long x, long y, long width, long height, double radius )
404 {
405 wxCHECK_RET( Ok(), "invalid window dc" );
406
407 if (radius < 0.0) radius = - radius * ((width < height) ? width : height);
408
409 long xx = XLOG2DEV(x);
410 long yy = YLOG2DEV(y);
411 long ww = m_signX * XLOG2DEVREL(width);
412 long hh = m_signY * YLOG2DEVREL(height);
413 long rr = XLOG2DEVREL((long)radius);
414
415 // CMB: handle -ve width and/or height
416 if (ww < 0) { ww = -ww; xx = xx - ww; }
417 if (hh < 0) { hh = -hh; yy = yy - hh; }
418
419 // CMB: if radius is zero use DrawRectangle() instead to avoid
420 // X drawing errors with small radii
421 if (rr == 0)
422 {
423 DrawRectangle( x, y, width, height );
424 return;
425 }
426
427 // CMB: draw nothing if transformed w or h is 0
428 if (ww == 0 || hh == 0) return;
429
430 // CMB: adjust size if outline is drawn otherwise the result is
431 // 1 pixel too wide and high
432 if (m_pen.GetStyle() != wxTRANSPARENT)
433 {
434 ww--;
435 hh--;
436 }
437
438 // CMB: ensure dd is not larger than rectangle otherwise we
439 // get an hour glass shape
440 long dd = 2 * rr;
441 if (dd > ww) dd = ww;
442 if (dd > hh) dd = hh;
443 rr = dd / 2;
444
445 if (m_brush.GetStyle() != wxTRANSPARENT)
446 {
447 gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx+rr, yy, ww-dd+1, hh );
448 gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy+rr, ww, hh-dd+1 );
449 gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, dd, dd, 90*64, 90*64 );
450 gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy, dd, dd, 0, 90*64 );
451 gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 );
452 gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 );
453 }
454
455 if (m_pen.GetStyle() != wxTRANSPARENT)
456 {
457 gdk_draw_line( m_window, m_penGC, xx+rr, yy, xx+ww-rr, yy );
458 gdk_draw_line( m_window, m_penGC, xx+rr, yy+hh, xx+ww-rr, yy+hh );
459 gdk_draw_line( m_window, m_penGC, xx, yy+rr, xx, yy+hh-rr );
460 gdk_draw_line( m_window, m_penGC, xx+ww, yy+rr, xx+ww, yy+hh-rr );
461 gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, dd, dd, 90*64, 90*64 );
462 gdk_draw_arc( m_window, m_penGC, FALSE, xx+ww-dd, yy, dd, dd, 0, 90*64 );
463 gdk_draw_arc( m_window, m_penGC, FALSE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 );
464 gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 );
465 }
466
467 // this ignores the radius
468 CalcBoundingBox( x, y );
469 CalcBoundingBox( x + width, y + height );
470 }
471
472 void wxWindowDC::DrawEllipse( long x, long y, long width, long height )
473 {
474 wxCHECK_RET( Ok(), "invalid window dc" );
475
476 long xx = XLOG2DEV(x);
477 long yy = YLOG2DEV(y);
478 long ww = m_signX * XLOG2DEVREL(width);
479 long hh = m_signY * YLOG2DEVREL(height);
480
481 // CMB: handle -ve width and/or height
482 if (ww < 0) { ww = -ww; xx = xx - ww; }
483 if (hh < 0) { hh = -hh; yy = yy - hh; }
484
485 if (m_brush.GetStyle() != wxTRANSPARENT)
486 gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360*64 );
487
488 if (m_pen.GetStyle() != wxTRANSPARENT)
489 gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, ww, hh, 0, 360*64 );
490
491 CalcBoundingBox( x - width, y - height );
492 CalcBoundingBox( x + width, y + height );
493 }
494
495 bool wxWindowDC::CanDrawBitmap() const
496 {
497 return TRUE;
498 }
499
500 void wxWindowDC::DrawIcon( const wxIcon &icon, long x, long y )
501 {
502 DrawBitmap( icon, x, y, TRUE );
503 }
504
505 void wxWindowDC::DrawBitmap( const wxBitmap &bitmap, long x, long y, bool useMask )
506 {
507 wxCHECK_RET( Ok(), "invalid window dc" );
508
509 if (!bitmap.Ok()) return;
510
511 int xx = XLOG2DEV(x);
512 int yy = YLOG2DEV(y);
513
514 int w = bitmap.GetWidth();
515 int h = bitmap.GetHeight();
516
517 int ww = XLOG2DEVREL(w);
518 int hh = YLOG2DEVREL(h);
519
520 wxBitmap use_bitmap;
521
522 if ((w != ww) || (h != hh))
523 {
524 wxImage image( bitmap );
525 image = image.Scale( ww, hh );
526
527 use_bitmap = image.ConvertToBitmap();
528 }
529 else
530 {
531 use_bitmap = bitmap;
532 }
533
534 GdkBitmap *mask = (GdkBitmap *) NULL;
535 if (use_bitmap.GetMask()) mask = use_bitmap.GetMask()->GetBitmap();
536
537 if (useMask && mask)
538 {
539 gdk_gc_set_clip_mask( m_penGC, mask );
540 gdk_gc_set_clip_origin( m_penGC, xx, yy );
541 }
542
543 GdkPixmap *pm = use_bitmap.GetPixmap();
544 gdk_draw_pixmap( m_window, m_penGC, pm, 0, 0, xx, yy, -1, -1 );
545
546 if (useMask && mask)
547 {
548 gdk_gc_set_clip_mask( m_penGC, (GdkBitmap *) NULL );
549 gdk_gc_set_clip_origin( m_penGC, 0, 0 );
550 }
551
552 CalcBoundingBox( x, y );
553 CalcBoundingBox( x + w, y + h );
554 }
555
556 bool wxWindowDC::Blit( long xdest, long ydest, long width, long height,
557 wxDC *source, long xsrc, long ysrc, int logical_func, bool useMask )
558 {
559 wxCHECK_MSG( Ok(), FALSE, "invalid window dc" );
560
561 CalcBoundingBox( xdest, ydest );
562 CalcBoundingBox( xdest + width, ydest + height );
563
564 int old_logical_func = m_logicalFunction;
565 SetLogicalFunction( logical_func );
566
567 wxClientDC *csrc = (wxClientDC*)source;
568
569 if (csrc->m_isMemDC)
570 {
571 wxMemoryDC* srcDC = (wxMemoryDC*)source;
572 GdkPixmap* pmap = srcDC->m_selected.GetPixmap();
573 if (pmap)
574 {
575 long xx = XLOG2DEV(xdest);
576 long yy = YLOG2DEV(ydest);
577
578 GdkBitmap *mask = (GdkBitmap *) NULL;
579 if (srcDC->m_selected.GetMask()) mask = srcDC->m_selected.GetMask()->GetBitmap();
580
581 if (useMask && mask)
582 {
583 gdk_gc_set_clip_mask( m_penGC, mask );
584 gdk_gc_set_clip_origin( m_penGC, xx, yy );
585 }
586
587 gdk_draw_pixmap( m_window, m_penGC, pmap,
588 source->DeviceToLogicalX(xsrc),
589 source->DeviceToLogicalY(ysrc),
590 xx,
591 yy,
592 source->DeviceToLogicalXRel(width),
593 source->DeviceToLogicalYRel(height) );
594
595 if (useMask && mask)
596 {
597 gdk_gc_set_clip_mask( m_penGC, (GdkBitmap *) NULL );
598 gdk_gc_set_clip_origin( m_penGC, 0, 0 );
599 }
600
601 SetLogicalFunction( old_logical_func );
602 return TRUE;
603 }
604
605 GdkBitmap* bmap = srcDC->m_selected.GetBitmap();
606 if (bmap)
607 {
608 long xx = XLOG2DEV(xdest);
609 long yy = YLOG2DEV(ydest);
610
611 GdkBitmap *mask = (GdkBitmap *) NULL;
612 if (srcDC->m_selected.GetMask()) mask = srcDC->m_selected.GetMask()->GetBitmap();
613
614 if (useMask && mask)
615 {
616 gdk_gc_set_clip_mask( m_penGC, mask );
617 gdk_gc_set_clip_origin( m_penGC, xx, yy );
618 }
619
620 gdk_draw_bitmap( m_window, m_textGC, bmap,
621 source->DeviceToLogicalX(xsrc),
622 source->DeviceToLogicalY(ysrc),
623 xx,
624 yy,
625 source->DeviceToLogicalXRel(width),
626 source->DeviceToLogicalYRel(height) );
627
628 if (useMask && mask)
629 {
630 gdk_gc_set_clip_mask( m_penGC, (GdkBitmap *) NULL );
631 gdk_gc_set_clip_origin( m_penGC, 0, 0 );
632 }
633
634 SetLogicalFunction( old_logical_func );
635 return TRUE;
636 }
637 }
638
639 gdk_window_copy_area ( m_window, m_penGC,
640 XLOG2DEV(xdest), YLOG2DEV(ydest),
641 csrc->GetWindow(),
642 source->DeviceToLogicalX(xsrc),
643 source->DeviceToLogicalY(ysrc),
644 source->DeviceToLogicalXRel(width),
645 source->DeviceToLogicalYRel(height) );
646
647 SetLogicalFunction( old_logical_func );
648 return TRUE;
649 }
650
651 void wxWindowDC::DrawText( const wxString &text, long x, long y, bool WXUNUSED(use16) )
652 {
653 wxCHECK_RET( Ok(), "invalid window dc" );
654
655 GdkFont *font = m_font.GetInternalFont( m_scaleY );
656
657 x = XLOG2DEV(x);
658 y = YLOG2DEV(y);
659
660 // CMB 21/5/98: draw text background if mode is wxSOLID
661 if (m_backgroundMode == wxSOLID)
662 {
663 long width = gdk_string_width( font, text );
664 long height = font->ascent + font->descent;
665 gdk_gc_set_foreground( m_textGC, m_textBackgroundColour.GetColor() );
666 gdk_draw_rectangle( m_window, m_textGC, TRUE, x, y, width, height );
667 gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() );
668 }
669 gdk_draw_string( m_window, font, m_textGC, x, y + font->ascent, text );
670
671 // CMB 17/7/98: simple underline: ignores scaling and underlying
672 // X font's XA_UNDERLINE_POSITION and XA_UNDERLINE_THICKNESS
673 // properties (see wxXt implementation)
674 if (m_font.GetUnderlined())
675 {
676 long width = gdk_string_width( font, text );
677 long ul_y = y + font->ascent;
678 if (font->descent > 0) ul_y++;
679 gdk_draw_line( m_window, m_textGC, x, ul_y, x + width, ul_y);
680 }
681
682 long w, h;
683 GetTextExtent (text, &w, &h);
684 CalcBoundingBox (x + w, y + h);
685 CalcBoundingBox (x, y);
686 }
687
688 bool wxWindowDC::CanGetTextExtent() const
689 {
690 return TRUE;
691 }
692
693 void wxWindowDC::GetTextExtent( const wxString &string, long *width, long *height,
694 long *descent, long *externalLeading,
695 wxFont *theFont, bool WXUNUSED(use16) )
696 {
697 wxCHECK_RET( Ok(), "invalid window dc" );
698
699 wxFont fontToUse = m_font;
700 if (theFont) fontToUse = *theFont;
701
702 GdkFont *font = fontToUse.GetInternalFont( m_scaleY );
703 if (width) (*width) = long(gdk_string_width( font, string ) / m_scaleX);
704 if (height) (*height) = long((font->ascent + font->descent) / m_scaleY);
705 if (descent) (*descent) = long(font->descent / m_scaleY);
706 if (externalLeading) (*externalLeading) = 0; // ??
707 }
708
709 long wxWindowDC::GetCharWidth()
710 {
711 wxCHECK_MSG( Ok(), 0, "invalid window dc" );
712
713 GdkFont *font = m_font.GetInternalFont( m_scaleY );
714 return long(gdk_string_width( font, "H" ) / m_scaleX);
715 }
716
717 long wxWindowDC::GetCharHeight()
718 {
719 wxCHECK_MSG( Ok(), 0, "invalid window dc" );
720
721 GdkFont *font = m_font.GetInternalFont( m_scaleY );
722 return long((font->ascent + font->descent) / m_scaleY);
723 }
724
725 void wxWindowDC::Clear()
726 {
727 wxCHECK_RET( Ok(), "invalid window dc" );
728
729 if (!m_isMemDC)
730 {
731 gdk_window_clear( m_window );
732 }
733 else
734 {
735 int width,height;
736 GetSize( &width, &height );
737 gdk_draw_rectangle( m_window, m_bgGC, TRUE, 0, 0, width, height );
738 }
739 }
740
741 void wxWindowDC::SetFont( const wxFont &font )
742 {
743 wxCHECK_RET( Ok(), "invalid window dc" );
744
745 m_font = font;
746 }
747
748 void wxWindowDC::SetPen( const wxPen &pen )
749 {
750 wxCHECK_RET( Ok(), "invalid window dc" );
751
752 if (m_pen == pen) return;
753
754 m_pen = pen;
755
756 if (!m_pen.Ok()) return;
757
758 gint width = m_pen.GetWidth();
759 // CMB: if width is non-zero scale it with the dc
760 if (width <= 0)
761 {
762 width = 1;
763 }
764 else
765 {
766 // X doesn't allow different width in x and y and so we take
767 // the average
768 double w = 0.5 + (abs(XLOG2DEVREL(width)) + abs(YLOG2DEVREL(width))) / 2.0;
769 width = (int)w;
770 }
771
772 GdkLineStyle lineStyle = GDK_LINE_SOLID;
773 switch (m_pen.GetStyle())
774 {
775 case wxSOLID: { lineStyle = GDK_LINE_SOLID; break; }
776 case wxDOT: { lineStyle = GDK_LINE_ON_OFF_DASH; break; }
777 case wxLONG_DASH: { lineStyle = GDK_LINE_ON_OFF_DASH; break; }
778 case wxSHORT_DASH: { lineStyle = GDK_LINE_ON_OFF_DASH; break; }
779 case wxDOT_DASH: { lineStyle = GDK_LINE_DOUBLE_DASH; break; }
780 }
781
782 GdkCapStyle capStyle = GDK_CAP_ROUND;
783 switch (m_pen.GetCap())
784 {
785 case wxCAP_ROUND: { capStyle = (width <= 1) ? GDK_CAP_NOT_LAST : GDK_CAP_ROUND; break; }
786 case wxCAP_PROJECTING: { capStyle = GDK_CAP_PROJECTING; break; }
787 case wxCAP_BUTT: { capStyle = GDK_CAP_BUTT; break; }
788 }
789
790 GdkJoinStyle joinStyle = GDK_JOIN_ROUND;
791 switch (m_pen.GetJoin())
792 {
793 case wxJOIN_BEVEL: { joinStyle = GDK_JOIN_BEVEL; break; }
794 case wxJOIN_ROUND: { joinStyle = GDK_JOIN_ROUND; break; }
795 case wxJOIN_MITER: { joinStyle = GDK_JOIN_MITER; break; }
796 }
797
798 gdk_gc_set_line_attributes( m_penGC, width, lineStyle, capStyle, joinStyle );
799
800 m_pen.GetColour().CalcPixel( m_cmap );
801 gdk_gc_set_foreground( m_penGC, m_pen.GetColour().GetColor() );
802 }
803
804 void wxWindowDC::SetBrush( const wxBrush &brush )
805 {
806 wxCHECK_RET( Ok(), "invalid window dc" );
807
808 if (m_brush == brush) return;
809
810 m_brush = brush;
811
812 if (!m_brush.Ok()) return;
813
814 m_brush.GetColour().CalcPixel( m_cmap );
815 gdk_gc_set_foreground( m_brushGC, m_brush.GetColour().GetColor() );
816
817 GdkFill fillStyle = GDK_SOLID;
818 switch (m_brush.GetStyle())
819 {
820 case wxSOLID:
821 case wxTRANSPARENT:
822 break;
823 default:
824 fillStyle = GDK_STIPPLED;
825 }
826
827 gdk_gc_set_fill( m_brushGC, fillStyle );
828
829 if (m_brush.GetStyle() == wxSTIPPLE)
830 {
831 gdk_gc_set_stipple( m_brushGC, m_brush.GetStipple()->GetPixmap() );
832 }
833
834 if (IS_HATCH(m_brush.GetStyle()))
835 {
836 int num = m_brush.GetStyle() - wxBDIAGONAL_HATCH;
837 gdk_gc_set_stipple( m_brushGC, hatches[num] );
838 }
839 }
840
841 void wxWindowDC::SetBackground( const wxBrush &brush )
842 {
843 // CMB 21/7/98: Added SetBackground. Sets background brush
844 // for Clear() and bg colour for shapes filled with cross-hatch brush
845
846 wxCHECK_RET( Ok(), "invalid window dc" );
847
848 if (m_backgroundBrush == brush) return;
849
850 m_backgroundBrush = brush;
851
852 if (!m_backgroundBrush.Ok()) return;
853
854 m_backgroundBrush.GetColour().CalcPixel( m_cmap );
855 gdk_gc_set_background( m_brushGC, m_backgroundBrush.GetColour().GetColor() );
856 gdk_gc_set_background( m_penGC, m_backgroundBrush.GetColour().GetColor() );
857 gdk_gc_set_background( m_bgGC, m_backgroundBrush.GetColour().GetColor() );
858 gdk_gc_set_foreground( m_bgGC, m_backgroundBrush.GetColour().GetColor() );
859
860 GdkFill fillStyle = GDK_SOLID;
861 switch (m_backgroundBrush.GetStyle())
862 {
863 case wxSOLID:
864 case wxTRANSPARENT:
865 break;
866 default:
867 fillStyle = GDK_STIPPLED;
868 }
869
870 gdk_gc_set_fill( m_bgGC, fillStyle );
871
872 if (m_backgroundBrush.GetStyle() == wxSTIPPLE)
873 {
874 gdk_gc_set_stipple( m_bgGC, m_backgroundBrush.GetStipple()->GetPixmap() );
875 }
876
877 if (IS_HATCH(m_backgroundBrush.GetStyle()))
878 {
879 int num = m_backgroundBrush.GetStyle() - wxBDIAGONAL_HATCH;
880 gdk_gc_set_stipple( m_bgGC, hatches[num] );
881 }
882 }
883
884 void wxWindowDC::SetLogicalFunction( int function )
885 {
886 wxCHECK_RET( Ok(), "invalid window dc" );
887
888 if (m_logicalFunction == function) return;
889
890 GdkFunction mode = GDK_COPY;
891 switch (function)
892 {
893 case wxXOR: mode = GDK_INVERT; break;
894 case wxINVERT: mode = GDK_INVERT; break;
895 default: break;
896 }
897
898 m_logicalFunction = function;
899 gdk_gc_set_function( m_penGC, mode );
900 gdk_gc_set_function( m_brushGC, mode );
901 gdk_gc_set_function( m_textGC, mode );
902 }
903
904 void wxWindowDC::SetTextForeground( const wxColour &col )
905 {
906 wxCHECK_RET( Ok(), "invalid window dc" );
907
908 if (m_textForegroundColour == col) return;
909
910 m_textForegroundColour = col;
911 if (!m_textForegroundColour.Ok()) return;
912
913 m_textForegroundColour.CalcPixel( m_cmap );
914 gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() );
915 }
916
917 void wxWindowDC::SetTextBackground( const wxColour &col )
918 {
919 wxCHECK_RET( Ok(), "invalid window dc" );
920
921 if (m_textBackgroundColour == col) return;
922
923 m_textBackgroundColour = col;
924 if (!m_textBackgroundColour.Ok()) return;
925
926 m_textBackgroundColour.CalcPixel( m_cmap );
927 gdk_gc_set_background( m_textGC, m_textBackgroundColour.GetColor() );
928 }
929
930 void wxWindowDC::SetBackgroundMode( int mode )
931 {
932 wxCHECK_RET( Ok(), "invalid window dc" );
933
934 m_backgroundMode = mode;
935
936 // CMB 21/7/98: fill style of cross-hatch brushes is affected by
937 // transparent/solid background mode
938
939 if (m_brush.GetStyle() != wxSOLID && m_brush.GetStyle() != wxTRANSPARENT)
940 {
941 gdk_gc_set_fill( m_brushGC,
942 (m_backgroundMode == wxTRANSPARENT) ? GDK_STIPPLED : GDK_OPAQUE_STIPPLED);
943 }
944 }
945
946 void wxWindowDC::SetPalette( const wxPalette& WXUNUSED(palette) )
947 {
948 wxFAIL_MSG( "wxWindowDC::SetPalette not implemented" );
949 }
950
951 void wxWindowDC::SetClippingRegion( long x, long y, long width, long height )
952 {
953 wxCHECK_RET( Ok(), "invalid window dc" );
954
955 wxDC::SetClippingRegion( x, y, width, height );
956
957 GdkRectangle rect;
958 rect.x = XLOG2DEV(x);
959 rect.y = YLOG2DEV(y);
960 rect.width = XLOG2DEVREL(width);
961 rect.height = YLOG2DEVREL(height);
962 gdk_gc_set_clip_rectangle( m_penGC, &rect );
963 gdk_gc_set_clip_rectangle( m_brushGC, &rect );
964 gdk_gc_set_clip_rectangle( m_textGC, &rect );
965 gdk_gc_set_clip_rectangle( m_bgGC, &rect );
966 }
967
968 void wxWindowDC::SetClippingRegion( const wxRegion &region )
969 {
970 wxCHECK_RET( Ok(), "invalid window dc" );
971
972 if (region.Empty())
973 {
974 DestroyClippingRegion();
975 return;
976 }
977
978 gdk_gc_set_clip_region( m_penGC, region.GetRegion() );
979 gdk_gc_set_clip_region( m_brushGC, region.GetRegion() );
980 gdk_gc_set_clip_region( m_textGC, region.GetRegion() );
981 gdk_gc_set_clip_region( m_bgGC, region.GetRegion() );
982 }
983
984 void wxWindowDC::DestroyClippingRegion()
985 {
986 wxCHECK_RET( Ok(), "invalid window dc" );
987
988 wxDC::DestroyClippingRegion();
989
990 gdk_gc_set_clip_rectangle( m_penGC, (GdkRectangle *) NULL );
991 gdk_gc_set_clip_rectangle( m_brushGC, (GdkRectangle *) NULL );
992 gdk_gc_set_clip_rectangle( m_textGC, (GdkRectangle *) NULL );
993 gdk_gc_set_clip_rectangle( m_bgGC, (GdkRectangle *) NULL );
994 }
995
996 void wxWindowDC::SetUpDC()
997 {
998 Destroy();
999 m_ok = TRUE;
1000 m_logicalFunction = wxCOPY;
1001 m_penGC = gdk_gc_new( m_window );
1002 m_brushGC = gdk_gc_new( m_window );
1003 m_textGC = gdk_gc_new( m_window );
1004 m_bgGC = gdk_gc_new( m_window );
1005
1006 wxColour tmp_col( m_textForegroundColour );
1007 m_textForegroundColour = wxNullColour;
1008 SetTextForeground( tmp_col );
1009 tmp_col = m_textBackgroundColour;
1010 m_textBackgroundColour = wxNullColour;
1011 SetTextBackground( tmp_col );
1012
1013 wxPen tmp_pen( m_pen );
1014 m_pen = wxNullPen;
1015 SetPen( tmp_pen );
1016
1017 wxFont tmp_font( m_font );
1018 m_font = wxNullFont;
1019 SetFont( tmp_font );
1020
1021 wxBrush tmp_brush( m_brush );
1022 m_brush = wxNullBrush;
1023 SetBrush( tmp_brush );
1024
1025 tmp_brush = m_backgroundBrush;
1026 m_backgroundBrush = wxNullBrush;
1027 SetBackground( tmp_brush );
1028
1029 if (!hatch_bitmap)
1030 {
1031 hatch_bitmap = hatches;
1032 hatch_bitmap[0] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, bdiag_bits, bdiag_width, bdiag_height );
1033 hatch_bitmap[1] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, cdiag_bits, cdiag_width, cdiag_height );
1034 hatch_bitmap[2] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, fdiag_bits, fdiag_width, fdiag_height );
1035 hatch_bitmap[3] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, cross_bits, cross_width, cross_height );
1036 hatch_bitmap[4] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, horiz_bits, horiz_width, horiz_height );
1037 hatch_bitmap[5] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, verti_bits, verti_width, verti_height );
1038 }
1039 }
1040
1041 void wxWindowDC::Destroy()
1042 {
1043 if (m_penGC) gdk_gc_unref( m_penGC );
1044 m_penGC = (GdkGC*) NULL;
1045 if (m_brushGC) gdk_gc_unref( m_brushGC );
1046 m_brushGC = (GdkGC*) NULL;
1047 if (m_textGC) gdk_gc_unref( m_textGC );
1048 m_textGC = (GdkGC*) NULL;
1049 if (m_bgGC) gdk_gc_unref( m_bgGC );
1050 m_bgGC = (GdkGC*) NULL;
1051 }
1052
1053 GdkWindow *wxWindowDC::GetWindow()
1054 {
1055 return m_window;
1056 }
1057
1058 // ----------------------------------- spline code ----------------------------------------
1059
1060 void wx_quadratic_spline(double a1, double b1, double a2, double b2,
1061 double a3, double b3, double a4, double b4);
1062 void wx_clear_stack();
1063 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
1064 double *y3, double *x4, double *y4);
1065 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
1066 double x4, double y4);
1067 static bool wx_spline_add_point(double x, double y);
1068 static void wx_spline_draw_point_array(wxDC *dc);
1069
1070 wxList wx_spline_point_list;
1071
1072 #define half(z1, z2) ((z1+z2)/2.0)
1073 #define THRESHOLD 5
1074
1075 /* iterative version */
1076
1077 void wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4,
1078 double b4)
1079 {
1080 register double xmid, ymid;
1081 double x1, y1, x2, y2, x3, y3, x4, y4;
1082
1083 wx_clear_stack();
1084 wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4);
1085
1086 while (wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) {
1087 xmid = (double)half(x2, x3);
1088 ymid = (double)half(y2, y3);
1089 if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD &&
1090 fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
1091 wx_spline_add_point( x1, y1 );
1092 wx_spline_add_point( xmid, ymid );
1093 } else {
1094 wx_spline_push(xmid, ymid, (double)half(xmid, x3), (double)half(ymid, y3),
1095 (double)half(x3, x4), (double)half(y3, y4), x4, y4);
1096 wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2),
1097 (double)half(x2, xmid), (double)half(y2, ymid), xmid, ymid);
1098 }
1099 }
1100 }
1101
1102 /* utilities used by spline drawing routines */
1103
1104 typedef struct wx_spline_stack_struct {
1105 double x1, y1, x2, y2, x3, y3, x4, y4;
1106 } Stack;
1107
1108 #define SPLINE_STACK_DEPTH 20
1109 static Stack wx_spline_stack[SPLINE_STACK_DEPTH];
1110 static Stack *wx_stack_top;
1111 static int wx_stack_count;
1112
1113 void wx_clear_stack()
1114 {
1115 wx_stack_top = wx_spline_stack;
1116 wx_stack_count = 0;
1117 }
1118
1119 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
1120 {
1121 wx_stack_top->x1 = x1;
1122 wx_stack_top->y1 = y1;
1123 wx_stack_top->x2 = x2;
1124 wx_stack_top->y2 = y2;
1125 wx_stack_top->x3 = x3;
1126 wx_stack_top->y3 = y3;
1127 wx_stack_top->x4 = x4;
1128 wx_stack_top->y4 = y4;
1129 wx_stack_top++;
1130 wx_stack_count++;
1131 }
1132
1133 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
1134 double *x3, double *y3, double *x4, double *y4)
1135 {
1136 if (wx_stack_count == 0)
1137 return (0);
1138 wx_stack_top--;
1139 wx_stack_count--;
1140 *x1 = wx_stack_top->x1;
1141 *y1 = wx_stack_top->y1;
1142 *x2 = wx_stack_top->x2;
1143 *y2 = wx_stack_top->y2;
1144 *x3 = wx_stack_top->x3;
1145 *y3 = wx_stack_top->y3;
1146 *x4 = wx_stack_top->x4;
1147 *y4 = wx_stack_top->y4;
1148 return (1);
1149 }
1150
1151 static bool wx_spline_add_point(double x, double y)
1152 {
1153 wxPoint *point = new wxPoint ;
1154 point->x = (int) x;
1155 point->y = (int) y;
1156 wx_spline_point_list.Append((wxObject*)point);
1157 return TRUE;
1158 }
1159
1160 static void wx_spline_draw_point_array(wxDC *dc)
1161 {
1162 dc->DrawLines(&wx_spline_point_list, 0, 0 );
1163 wxNode *node = wx_spline_point_list.First();
1164 while (node)
1165 {
1166 wxPoint *point = (wxPoint *)node->Data();
1167 delete point;
1168 delete node;
1169 node = wx_spline_point_list.First();
1170 }
1171 }
1172
1173 void wxWindowDC::DrawSpline( wxList *points )
1174 {
1175 wxCHECK_RET( Ok(), "invalid window dc" );
1176
1177 wxPoint *p;
1178 double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
1179 double x1, y1, x2, y2;
1180
1181 wxNode *node = points->First();
1182 p = (wxPoint *)node->Data();
1183
1184 x1 = p->x;
1185 y1 = p->y;
1186
1187 node = node->Next();
1188 p = (wxPoint *)node->Data();
1189
1190 x2 = p->x;
1191 y2 = p->y;
1192 cx1 = (double)((x1 + x2) / 2);
1193 cy1 = (double)((y1 + y2) / 2);
1194 cx2 = (double)((cx1 + x2) / 2);
1195 cy2 = (double)((cy1 + y2) / 2);
1196
1197 wx_spline_add_point(x1, y1);
1198
1199 while ((node = node->Next()) != NULL)
1200 {
1201 p = (wxPoint *)node->Data();
1202 x1 = x2;
1203 y1 = y2;
1204 x2 = p->x;
1205 y2 = p->y;
1206 cx4 = (double)(x1 + x2) / 2;
1207 cy4 = (double)(y1 + y2) / 2;
1208 cx3 = (double)(x1 + cx4) / 2;
1209 cy3 = (double)(y1 + cy4) / 2;
1210
1211 wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
1212
1213 cx1 = cx4;
1214 cy1 = cy4;
1215 cx2 = (double)(cx1 + x2) / 2;
1216 cy2 = (double)(cy1 + y2) / 2;
1217 }
1218
1219 wx_spline_add_point( cx1, cy1 );
1220 wx_spline_add_point( x2, y2 );
1221
1222 wx_spline_draw_point_array( this );
1223 }
1224
1225
1226 //-----------------------------------------------------------------------------
1227 // wxPaintDC
1228 //-----------------------------------------------------------------------------
1229
1230 IMPLEMENT_DYNAMIC_CLASS(wxPaintDC,wxWindowDC)
1231
1232 wxPaintDC::wxPaintDC()
1233 : wxWindowDC()
1234 {
1235 }
1236
1237 wxPaintDC::wxPaintDC( wxWindow *win )
1238 : wxWindowDC( win )
1239 {
1240 }
1241
1242 //-----------------------------------------------------------------------------
1243 // wxClientDC
1244 //-----------------------------------------------------------------------------
1245
1246 IMPLEMENT_DYNAMIC_CLASS(wxClientDC,wxWindowDC)
1247
1248 wxClientDC::wxClientDC()
1249 : wxWindowDC()
1250 {
1251 }
1252
1253 wxClientDC::wxClientDC( wxWindow *win )
1254 : wxWindowDC( win )
1255 {
1256 }
1257