]> git.saurik.com Git - wxWidgets.git/blob - wxPython/src/pseudodc.cpp
fixed wxVsnprintf() to write as much as it can if the output buffer is too short
[wxWidgets.git] / wxPython / src / pseudodc.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: common/pseudodc.cpp
3 // Purpose: Implementation of the wxPseudoDC Class
4 // Author: Paul Lanier
5 // Modified by:
6 // Created: 05/25/06
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 //include "wx/wxprec.h"
14
15 #undef DEBUG
16 #include <Python.h>
17 #include "wx/wxPython/wxPython.h"
18 #include "wx/wxPython/pseudodc.h"
19
20 // wxList based class definitions
21 #include <wx/listimpl.cpp>
22 WX_DEFINE_LIST(pdcOpList);
23 WX_DEFINE_LIST(pdcObjectList);
24
25 //----------------------------------------------------------------------------
26 // Helper functions used for drawing greyed out versions of objects
27 //----------------------------------------------------------------------------
28 wxColour &MakeColourGrey(const wxColour &c)
29 {
30 static wxColour rval;
31 rval.Set(byte((230-c.Red())*0.7+c.Red()),
32 byte((230-c.Green())*0.7+c.Green()),
33 byte((230-c.Blue())*0.7+c.Blue()));
34 return rval;
35 }
36 wxBrush &GetGreyBrush(wxBrush &brush)
37 {
38 static wxBrush b;
39 wxColour c;
40 b = brush;
41 c = MakeColourGrey(brush.GetColour());
42 b.SetColour(c);
43 return b;
44 }
45
46 wxPen &GetGreyPen(wxPen &pen)
47 {
48 static wxPen p;
49 wxColour c;
50 p = pen;
51 c = MakeColourGrey(pen.GetColour());
52 p.SetColour(c);
53 return p;
54 }
55
56 void GreyOutImage(wxImage &img)
57 {
58 unsigned char *data = img.GetData();
59 unsigned char r,g,b;
60 unsigned char mr,mg,mb;
61 int i, tst;
62 int len = img.GetHeight()*img.GetWidth()*3;
63 if (img.HasMask())
64 {
65 mr = img.GetMaskRed();
66 mg = img.GetMaskGreen();
67 mb = img.GetMaskBlue();
68 }
69 tst=0;
70 for (i=0;i<len;i+=3)
71 {
72 r=data[i]; g=data[i+1]; b=data[i+2];
73 if (!img.HasMask() ||
74 r!=mr || g!=mg || b!=mb)
75 {
76 if (!tst)
77 {
78 tst=1;
79 }
80 r = (unsigned char)((230.0-r)*0.7+r);
81 g = (unsigned char)((230.0-g)*0.7+g);
82 b = (unsigned char)((230.0-b)*0.7+b);
83 data[i]=r; data[i+1]=g; data[i+2]=b;
84 }
85 }
86 }
87
88 wxIcon &GetGreyIcon(wxIcon &icon)
89 {
90 wxBitmap bmp;
91 bmp.CopyFromIcon(icon);
92 wxImage img = bmp.ConvertToImage();
93 GreyOutImage(img);
94 wxBitmap bmp2(img,32);
95 static wxIcon rval;
96 rval.CopyFromBitmap(bmp2);
97 return rval;
98 }
99
100 wxBitmap &GetGreyBitmap(wxBitmap &bmp)
101 {
102 wxImage img = bmp.ConvertToImage();
103 GreyOutImage(img);
104 static wxBitmap rval(img,32);
105 return rval;
106 }
107
108 // ============================================================================
109 // various pdcOp class implementation methods
110 // ============================================================================
111
112 // ----------------------------------------------------------------------------
113 // pdcDrawPolyPolygonOp constructor
114 // ----------------------------------------------------------------------------
115 pdcDrawPolyPolygonOp::pdcDrawPolyPolygonOp(int n, int count[], wxPoint points[],
116 wxCoord xoffset, wxCoord yoffset, int fillStyle)
117 {
118 m_n=n; m_xoffset=xoffset; m_yoffset=yoffset; m_fillStyle=fillStyle;
119 int total_n=0;
120 if (n)
121 {
122 m_count = new int[n];
123 for(int i=0; i<n; i++)
124 {
125 total_n+=count[i];
126 m_count[i]=count[i];
127 }
128 if (total_n)
129 {
130 m_points = new wxPoint[total_n];
131 for(int j=0; j<total_n; j++)
132 m_points[j] = points[j];
133 }
134 else m_points=NULL;
135 }
136 else
137 {
138 m_points=NULL;
139 m_count=NULL;
140 }
141 m_totaln = total_n;
142 }
143
144 // ----------------------------------------------------------------------------
145 // pdcDrawPolyPolygonOp destructor
146 // ----------------------------------------------------------------------------
147 pdcDrawPolyPolygonOp::~pdcDrawPolyPolygonOp()
148 {
149 if (m_points) delete m_points;
150 if (m_count) delete m_count;
151 m_points=NULL;
152 m_count=NULL;
153 }
154
155 // ----------------------------------------------------------------------------
156 // pdcDrawLinesOp constructor
157 // ----------------------------------------------------------------------------
158 pdcDrawLinesOp::pdcDrawLinesOp(int n, wxPoint points[],
159 wxCoord xoffset, wxCoord yoffset)
160 {
161 m_n=n; m_xoffset=xoffset; m_yoffset=yoffset;
162 if (n)
163 {
164 m_points = new wxPoint[n];
165 for (int i=0; i<n; i++)
166 m_points[i] = points[i];
167 }
168 else m_points=NULL;
169 }
170
171 // ----------------------------------------------------------------------------
172 // pdcDrawLinesOp destructor
173 // ----------------------------------------------------------------------------
174 pdcDrawLinesOp::~pdcDrawLinesOp()
175 {
176 if (m_points) delete m_points;
177 m_points=NULL;
178 }
179
180 // ----------------------------------------------------------------------------
181 // pdcDrawPolygonOp constructor
182 // ----------------------------------------------------------------------------
183 pdcDrawPolygonOp::pdcDrawPolygonOp(int n, wxPoint points[],
184 wxCoord xoffset, wxCoord yoffset, int fillStyle)
185 {
186 m_n=n; m_xoffset=xoffset; m_yoffset=yoffset; m_fillStyle=fillStyle;
187 if (n)
188 {
189 m_points = new wxPoint[n];
190 for (int i=0; i<n; i++)
191 m_points[i] = points[i];
192 }
193 else m_points=NULL;
194 }
195
196 // ----------------------------------------------------------------------------
197 // pdcDrawPolygonOp destructor
198 // ----------------------------------------------------------------------------
199 pdcDrawPolygonOp::~pdcDrawPolygonOp()
200 {
201 if (m_points) delete m_points;
202 m_points=NULL;
203 }
204
205 #if wxUSE_SPLINES
206 // ----------------------------------------------------------------------------
207 // pdcDrawSplineOp constructor
208 // ----------------------------------------------------------------------------
209 pdcDrawSplineOp::pdcDrawSplineOp(int n, wxPoint points[])
210 {
211 m_n=n;
212 if (n)
213 {
214 m_points = new wxPoint[n];
215 for(int i=0; i<n; i++)
216 m_points[i] = points[i];
217 }
218 else m_points=NULL;
219 }
220
221 // ----------------------------------------------------------------------------
222 // pdcDrawSplineOp destructor
223 // ----------------------------------------------------------------------------
224 pdcDrawSplineOp::~pdcDrawSplineOp()
225 {
226 if (m_points) delete m_points;
227 m_points=NULL;
228 }
229 #endif // wxUSE_SPLINES
230
231 // ============================================================================
232 // pdcObject implementation
233 // ============================================================================
234 // ----------------------------------------------------------------------------
235 // DrawToDC - play back the op list to the DC
236 // ----------------------------------------------------------------------------
237 void pdcObject::DrawToDC(wxDC *dc)
238 {
239 pdcOpList::Node *node = m_oplist.GetFirst();
240 while(node)
241 {
242 node->GetData()->DrawToDC(dc, m_greyedout);
243 node = node->GetNext();
244 }
245 }
246
247 // ----------------------------------------------------------------------------
248 // Translate - translate all the operations by some dx,dy
249 // ----------------------------------------------------------------------------
250 void pdcObject::Translate(wxCoord dx, wxCoord dy)
251 {
252 pdcOpList::Node *node = m_oplist.GetFirst();
253 while(node)
254 {
255 node->GetData()->Translate(dx,dy);
256 node = node->GetNext();
257 }
258 if (m_bounded)
259 {
260 m_bounds.x += dx;
261 m_bounds.y += dy;
262 }
263 }
264
265 // ----------------------------------------------------------------------------
266 // SetGreyedOut - set the greyout member and cache grey versions of everything
267 // if greyout is true
268 // ----------------------------------------------------------------------------
269 void pdcObject::SetGreyedOut(bool greyout)
270 {
271 m_greyedout=greyout;
272 if (greyout)
273 {
274 pdcOpList::Node *node = m_oplist.GetFirst();
275 pdcOp *obj;
276 while(node)
277 {
278
279 obj = node->GetData();
280 obj->CacheGrey();
281 node = node->GetNext();
282 }
283 }
284 }
285
286 // ============================================================================
287 // wxPseudoDC implementation
288 // ============================================================================
289
290 // ----------------------------------------------------------------------------
291 // Destructor
292 // ----------------------------------------------------------------------------
293 wxPseudoDC::~wxPseudoDC()
294 {
295 // delete all the nodes in the list
296 RemoveAll();
297
298 }
299
300 // ----------------------------------------------------------------------------
301 // ClearAll - remove all nodes from list
302 // ----------------------------------------------------------------------------
303 void wxPseudoDC::RemoveAll(void)
304 {
305 m_objectlist.Clear();
306 m_currId = -1;
307 m_lastObjNode = NULL;
308 }
309
310 // ----------------------------------------------------------------------------
311 // GetLen - return the number of operations in the current op list
312 // ----------------------------------------------------------------------------
313 int wxPseudoDC::GetLen(void)
314 {
315 pdcObjectList::Node *pt = m_objectlist.GetFirst();
316 int len=0;
317 while (pt)
318 {
319 len += pt->GetData()->GetLen();
320 pt = pt->GetNext();
321 }
322 return len;
323 }
324
325 // ----------------------------------------------------------------------------
326 // FindObjNode - find and return an object node by id. If node doesn't exist
327 // and create is true then create one and return it. Otherwise
328 // return NULL.
329 // ----------------------------------------------------------------------------
330 pdcObjectList::Node *wxPseudoDC::FindObjNode(int id, bool create)
331 {
332 // see if last operation was for same id
333 if (m_lastObjNode && m_lastObjNode->GetData()->GetId() == id)
334 return m_lastObjNode;
335 // if not then search for it
336 pdcObjectList::Node *pt = m_objectlist.GetFirst();
337 while (pt)
338 {
339 if (pt->GetData()->GetId() == id)
340 {
341
342 // cache this node for future operations
343 m_lastObjNode = pt;
344 return pt;
345 }
346 pt = pt->GetNext();
347 }
348 // if create then create and return a new node
349 if (create)
350 {
351 // cache this node for future operations
352 m_lastObjNode = m_objectlist.Append(new pdcObject(id));
353 return m_lastObjNode;
354 }
355 // otherwise just return NULL
356 return NULL;
357 }
358
359 // ----------------------------------------------------------------------------
360 // AddToList - Add a node to the list at the end (preserve draw order)
361 // ----------------------------------------------------------------------------
362 void wxPseudoDC::AddToList(pdcOp *newOp)
363 {
364 pdcObjectList::Node *pt = FindObjNode(m_currId, true);
365 pt->GetData()->AddOp(newOp);
366 }
367
368 // ----------------------------------------------------------------------------
369 // ClearID - remove all the operations associated with a single ID
370 // ----------------------------------------------------------------------------
371 void wxPseudoDC::ClearId(int id)
372 {
373 pdcObjectList::Node *pt = FindObjNode(id);
374 if (pt) pt->GetData()->Clear();
375 }
376
377 // ----------------------------------------------------------------------------
378 // RemoveID - Remove the object node (and all operations) associated with an id
379 // ----------------------------------------------------------------------------
380 void wxPseudoDC::RemoveId(int id)
381 {
382 pdcObjectList::Node *pt = FindObjNode(id);
383 if (pt)
384 {
385 if (m_lastObjNode == pt)
386 m_lastObjNode = NULL;
387 m_objectlist.DeleteNode(pt);
388 }
389 }
390
391 // ----------------------------------------------------------------------------
392 // SetIdBounds - Set the bounding rect for a given id
393 // ----------------------------------------------------------------------------
394 void wxPseudoDC::SetIdBounds(int id, wxRect& rect)
395 {
396 pdcObjectList::Node *pt = FindObjNode(id, true);
397 pt->GetData()->SetBounds(rect);
398 }
399
400 // ----------------------------------------------------------------------------
401 // GetIdBounds - Get the bounding rect for a given id
402 // ----------------------------------------------------------------------------
403 void wxPseudoDC::GetIdBounds(int id, wxRect& rect)
404 {
405 pdcObjectList::Node *pt = FindObjNode(id);
406 if (pt && pt->GetData()->IsBounded())
407 rect = pt->GetData()->GetBounds();
408 else
409 rect.x = rect.y = rect.width = rect.height = 0;
410 }
411
412 // ----------------------------------------------------------------------------
413 // TranslateId - Translate all the operations of a single id
414 // ----------------------------------------------------------------------------
415 void wxPseudoDC::TranslateId(int id, wxCoord dx, wxCoord dy)
416 {
417 pdcObjectList::Node *pt = FindObjNode(id);
418 if (pt) pt->GetData()->Translate(dx,dy);
419 }
420
421 // ----------------------------------------------------------------------------
422 // DrawIdToDC - Draw a specific id to the dc passed in
423 // ----------------------------------------------------------------------------
424 void wxPseudoDC::DrawIdToDC(int id, wxDC *dc)
425 {
426 pdcObjectList::Node *pt = FindObjNode(id);
427 if (pt) pt->GetData()->DrawToDC(dc);
428 }
429
430 // ----------------------------------------------------------------------------
431 // SetIdGreyedOut - Set the greyedout member of id
432 // ----------------------------------------------------------------------------
433 void wxPseudoDC::SetIdGreyedOut(int id, bool greyout)
434 {
435 pdcObjectList::Node *pt = FindObjNode(id);
436 if (pt) pt->GetData()->SetGreyedOut(greyout);
437 }
438
439 // ----------------------------------------------------------------------------
440 // GetIdGreyedOut - Get the greyedout member of id
441 // ----------------------------------------------------------------------------
442 bool wxPseudoDC::GetIdGreyedOut(int id)
443 {
444 pdcObjectList::Node *pt = FindObjNode(id);
445 if (pt) return pt->GetData()->GetGreyedOut();
446 else return false;
447 }
448
449 // ----------------------------------------------------------------------------
450 // FindObjectsByBBox - Return a list of all the ids whose bounding boxes
451 // contain (x,y)
452 // ----------------------------------------------------------------------------
453 PyObject *wxPseudoDC::FindObjectsByBBox(wxCoord x, wxCoord y)
454 {
455 //wxPyBlock_t blocked = wxPyBeginBlockThreads();
456 pdcObjectList::Node *pt = m_objectlist.GetFirst();
457 pdcObject *obj;
458 PyObject* pyList = NULL;
459 pyList = PyList_New(0);
460 wxRect r;
461 while (pt)
462 {
463 obj = pt->GetData();
464 r = obj->GetBounds();
465 if (obj->IsBounded() && r.Contains(x,y))
466 {
467 PyObject* pyObj = PyInt_FromLong((long)obj->GetId());
468 PyList_Insert(pyList, 0, pyObj);
469 Py_DECREF(pyObj);
470 }
471 pt = pt->GetNext();
472 }
473 //wxPyEndBlockThreads(blocked);
474 return pyList;
475 }
476
477 // ----------------------------------------------------------------------------
478 // FindObjects - Return a list of all the ids that draw to (x,y)
479 // ----------------------------------------------------------------------------
480 PyObject *wxPseudoDC::FindObjects(wxCoord x, wxCoord y,
481 wxCoord radius, const wxColor& bg)
482 {
483 //wxPyBlock_t blocked = wxPyBeginBlockThreads();
484 pdcObjectList::Node *pt = m_objectlist.GetFirst();
485 pdcObject *obj;
486 PyObject* pyList = NULL;
487 pyList = PyList_New(0);
488 wxBrush bgbrush(bg);
489 wxPen bgpen(bg);
490 // special case radius = 0
491 if (radius == 0)
492 {
493 wxBitmap bmp(4,4,24);
494 wxMemoryDC memdc;
495 wxColor pix;
496 wxRect viewrect(x-2,y-2,4,4);
497 // setup the memdc for rendering
498 memdc.SelectObject(bmp);
499 memdc.SetBackground(bgbrush);
500 memdc.Clear();
501 memdc.SetDeviceOrigin(2-x,2-y);
502 while (pt)
503 {
504 obj = pt->GetData();
505 if (obj->IsBounded() && obj->GetBounds().Contains(x,y))
506 {
507 // start clean
508 memdc.SetBrush(bgbrush);
509 memdc.SetPen(bgpen);
510 memdc.DrawRectangle(viewrect);
511 // draw the object
512 obj->DrawToDC(&memdc);
513 memdc.GetPixel(x,y,&pix);
514 // clear and update rgn2
515 if (pix != bg)
516 {
517 PyObject* pyObj = PyInt_FromLong((long)obj->GetId());
518 PyList_Insert(pyList, 0, pyObj);
519 Py_DECREF(pyObj);
520 }
521 }
522 pt = pt->GetNext();
523 }
524 memdc.SelectObject(wxNullBitmap);
525 }
526 else
527 {
528 wxRect viewrect(x-radius,y-radius,2*radius,2*radius);
529 wxBitmap maskbmp(2*radius,2*radius,24);
530 wxMemoryDC maskdc;
531 // create bitmap with circle for masking
532 maskdc.SelectObject(maskbmp);
533 maskdc.SetBackground(*wxBLACK_BRUSH);
534 maskdc.Clear();
535 maskdc.SetBrush(*wxWHITE_BRUSH);
536 maskdc.SetPen(*wxWHITE_PEN);
537 maskdc.DrawCircle(radius,radius,radius);
538 // now setup a memdc for rendering our object
539 wxBitmap bmp(2*radius,2*radius,24);
540 wxMemoryDC memdc;
541 memdc.SelectObject(bmp);
542 // set the origin so (x,y) is in the bmp center
543 memdc.SetDeviceOrigin(radius-x,radius-y);
544 // a region will be used to see if the result is empty
545 wxRegion rgn2;
546 while (pt)
547 {
548 obj = pt->GetData();
549 if (obj->IsBounded() && viewrect.Intersects(obj->GetBounds()))
550 {
551 // start clean
552 //memdc.Clear();
553 memdc.SetBrush(bgbrush);
554 memdc.SetPen(bgpen);
555 memdc.DrawRectangle(viewrect);
556 // draw the object
557 obj->DrawToDC(&memdc);
558 // remove background color
559 memdc.SetLogicalFunction(wxXOR);
560 memdc.SetBrush(bgbrush);
561 memdc.SetPen(bgpen);
562 memdc.DrawRectangle(viewrect);
563 memdc.SetLogicalFunction(wxCOPY);
564 #ifdef __WXMAC__
565 // wxAND is not supported on wxMac, but it doesn't seem to
566 // hurt anything to use wxCOPY instead...
567 memdc.Blit(x-radius,y-radius,2*radius,2*radius,&maskdc,0,0,wxCOPY);
568 #else
569 // AND with circle bitmap
570 memdc.Blit(x-radius,y-radius,2*radius,2*radius,&maskdc,0,0,wxAND);
571 #endif
572 // clear and update rgn2
573 memdc.SelectObject(wxNullBitmap);
574 rgn2.Clear();
575 rgn2.Union(bmp, *wxBLACK);
576 //rgn2.Intersect(rgn);
577 memdc.SelectObject(bmp);
578 if (!rgn2.IsEmpty())
579 {
580 PyObject* pyObj = PyInt_FromLong((long)obj->GetId());
581 PyList_Insert(pyList, 0, pyObj);
582 Py_DECREF(pyObj);
583 }
584 }
585 pt = pt->GetNext();
586 }
587 maskdc.SelectObject(wxNullBitmap);
588 memdc.SelectObject(wxNullBitmap);
589 }
590 //wxPyEndBlockThreads(blocked);
591 return pyList;
592 }
593
594 // ----------------------------------------------------------------------------
595 // DrawToDCClipped - play back the op list to the DC but clip any objects
596 // known to be not in rect. This is a coarse level of
597 // clipping to speed things up when lots of objects are off
598 // screen and doesn't affect the dc level clipping
599 // ----------------------------------------------------------------------------
600 void wxPseudoDC::DrawToDCClipped(wxDC *dc, const wxRect& rect)
601 {
602 pdcObjectList::Node *pt = m_objectlist.GetFirst();
603 pdcObject *obj;
604 while (pt)
605 {
606 obj = pt->GetData();
607 if (!obj->IsBounded() || rect.Intersects(obj->GetBounds()))
608 obj->DrawToDC(dc);
609 pt = pt->GetNext();
610 }
611 }
612 void wxPseudoDC::DrawToDCClippedRgn(wxDC *dc, const wxRegion& region)
613 {
614 pdcObjectList::Node *pt = m_objectlist.GetFirst();
615 pdcObject *obj;
616 while (pt)
617 {
618 obj = pt->GetData();
619 if (!obj->IsBounded() ||
620 (region.Contains(obj->GetBounds()) != wxOutRegion))
621 obj->DrawToDC(dc);
622 pt = pt->GetNext();
623 }
624 }
625
626 // ----------------------------------------------------------------------------
627 // DrawToDC - play back the op list to the DC
628 // ----------------------------------------------------------------------------
629 void wxPseudoDC::DrawToDC(wxDC *dc)
630 {
631 pdcObjectList::Node *pt = m_objectlist.GetFirst();
632 while (pt)
633 {
634 pt->GetData()->DrawToDC(dc);
635 pt = pt->GetNext();
636 }
637 }
638