]> git.saurik.com Git - wxWidgets.git/blob - user/wxLayout/wxllist.cpp
6e972e3ebceea49aabf39ccb4fafa856057130c9
[wxWidgets.git] / user / wxLayout / wxllist.cpp
1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
3 * *
4 * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
5 * *
6 * $Id$
7 *******************************************************************/
8
9 /*
10
11 */
12
13 #ifdef __GNUG__
14 #pragma implementation "wxllist.h"
15 #endif
16
17 //#include "Mpch.h"
18
19
20 #include "wx/wxprec.h"
21 #ifdef __BORLANDC__
22 # pragma hdrstop
23 #endif
24
25 #ifdef M_PREFIX
26 # include "gui/wxllist.h"
27 #else
28 # include "wxllist.h"
29 #endif
30
31 #ifndef USE_PCH
32 # include "iostream.h"
33 # include <wx/dc.h>
34 # include <wx/dcps.h>
35 # include <wx/print.h>
36 # include <wx/log.h>
37 #endif
38
39 #include <ctype.h>
40
41 /// This should never really get created
42 #define WXLLIST_TEMPFILE "__wxllist.tmp"
43
44 #ifdef WXLAYOUT_DEBUG
45
46 # define TypewxString(t) g_aTypewxStrings[t]
47 # define WXLO_DEBUG(x) wxLogDebug x
48
49 static const char *g_aTypewxStrings[] =
50 {
51 "invalid", "text", "cmd", "icon"
52 };
53 void
54 wxLayoutObject::Debug(void)
55 {
56 WXLO_DEBUG(("%s",g_aTypewxStrings[GetType()]));
57 }
58 #else
59 # define TypewxString(t) ""
60 # define WXLO_DEBUG(x)
61 #endif
62
63
64 /// Cursors smaller than this disappear in XOR drawing mode
65 #define WXLO_MINIMUM_CURSOR_WIDTH 4
66
67 /// Use this character to estimate a cursor size when none is available.
68 #define WXLO_CURSORCHAR "E"
69 /** @name Helper functions */
70 //@{
71 /// allows me to compare to wxPoints
72 bool operator ==(wxPoint const &p1, wxPoint const &p2)
73 {
74 return p1.x == p2.x && p1.y == p2.y;
75 }
76
77 /// allows me to compare to wxPoints
78 bool operator !=(wxPoint const &p1, wxPoint const &p2)
79 {
80 return p1.x != p2.x || p1.y != p2.y;
81 }
82
83 /// allows me to compare to wxPoints
84 bool operator <=(wxPoint const &p1, wxPoint const &p2)
85 {
86 return p1.y < p2.y || (p1.y == p2.y && p1.x <= p2.x);
87 }
88
89 /// grows a wxRect so that it includes the given point
90
91 static
92 void GrowRect(wxRect &r, const wxPoint & p)
93 {
94 if(r.x > p.x)
95 r.x = p.x;
96 else if(r.x + r.width < p.x)
97 r.width = p.x - r.x;
98
99 if(r.y > p.y)
100 r.y = p.y;
101 else if(r.y + r.height < p.y)
102 r.height = p.y - r.y;
103 }
104
105 /// returns true if the point is in the rectangle
106 static
107 bool Contains(const wxRect &r, const wxPoint &p)
108 {
109 return r.x <= p.x && r.y <= p.y && (r.x+r.width) >= p.x && (r.y + r.height) >= p.y;
110 }
111 //@}
112
113 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
114
115 wxLayoutObjectText
116
117 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
118
119 wxLayoutObjectText::wxLayoutObjectText(const wxString &txt)
120 {
121 m_Text = txt;
122 m_Width = 0;
123 m_Height = 0;
124 m_Top = 0;
125 m_Bottom = 0;
126 }
127
128 wxLayoutObject *
129 wxLayoutObjectText::Copy(void)
130 {
131 wxLayoutObjectText *obj = new wxLayoutObjectText(m_Text);
132 obj->m_Width = m_Width;
133 obj->m_Height = m_Height;
134 obj->m_Top = m_Top;
135 obj->m_Bottom = m_Bottom;
136 obj->SetUserData(m_UserData);
137 return obj;
138 }
139
140 wxPoint
141 wxLayoutObjectText::GetSize(CoordType *top, CoordType *bottom) const
142 {
143
144 *top = m_Top; *bottom = m_Bottom;
145 return wxPoint(m_Width, m_Height);
146 }
147
148 void
149 wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &coords)
150 {
151 dc.DrawText(m_Text, coords.x, coords.y-m_Top);
152 }
153
154 CoordType
155 wxLayoutObjectText::GetOffsetScreen(wxDC &dc, CoordType xpos) const
156 {
157 CoordType
158 offs = 1,
159 maxlen = m_Text.Length();
160 long
161 width = 0,
162 height, descent = 0l;
163
164 if(xpos == 0) return 0; // easy
165
166 while(width < xpos && offs < maxlen)
167 {
168 dc.GetTextExtent(m_Text.substr(0,offs),
169 &width, &height, &descent);
170 offs++;
171 }
172 /* We have to substract 1 to compensate for the offs++, and another
173 one because we don't want to position the cursor behind the
174 object what we clicked on, but before - otherwise it looks
175 funny. */
176 return (xpos > 2) ? offs-2 : 0;
177 }
178
179 void
180 wxLayoutObjectText::Layout(wxDC &dc)
181 {
182 long descent = 0l;
183
184 dc.GetTextExtent(m_Text,&m_Width, &m_Height, &descent);
185 m_Bottom = descent;
186 m_Top = m_Height - m_Bottom;
187 }
188
189 #ifdef WXLAYOUT_DEBUG
190 void
191 wxLayoutObjectText::Debug(void)
192 {
193 wxLayoutObject::Debug();
194 WXLO_DEBUG((" `%s`", m_Text.c_str()));
195 }
196 #endif
197
198 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
199
200 wxLayoutObjectIcon
201
202 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
203
204 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon)
205 {
206 m_Icon = new wxBitmap(icon);
207 }
208
209 wxLayoutObject *
210 wxLayoutObjectIcon::Copy(void)
211 {
212 wxLayoutObjectIcon *obj = new wxLayoutObjectIcon(new
213 wxBitmap(*m_Icon));
214 obj->SetUserData(m_UserData);
215 return obj;
216 }
217
218 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon)
219 {
220 m_Icon = icon;
221 }
222
223 void
224 wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &coords)
225 {
226 dc.DrawBitmap(*m_Icon, coords.x, coords.y-m_Icon->GetHeight(),
227 (m_Icon->GetMask() == NULL) ? FALSE : TRUE);
228 }
229
230 void
231 wxLayoutObjectIcon::Layout(wxDC & /* dc */)
232 {
233 }
234
235 wxPoint
236 wxLayoutObjectIcon::GetSize(CoordType *top, CoordType *bottom) const
237 {
238 *top = m_Icon->GetHeight();
239 *bottom = 0;
240 return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight());
241 }
242
243
244
245 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
246
247 wxLayoutObjectIcon
248
249 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
250
251 wxLayoutObjectCmd::wxLayoutObjectCmd(int size, int family, int style, int
252 weight, bool underline,
253 wxColour &fg, wxColour &bg)
254
255 {
256 m_font = new wxFont(size,family,style,weight,underline);
257 m_ColourFG = fg;
258 m_ColourBG = bg;
259 }
260
261 wxLayoutObject *
262 wxLayoutObjectCmd::Copy(void)
263 {
264 wxLayoutStyleInfo si;
265 GetStyle(&si);
266
267 wxLayoutObjectCmd *obj = new wxLayoutObjectCmd(
268 si.size, si.family, si.style, si.weight, si.underline,
269 m_ColourFG, m_ColourBG);
270 obj->SetUserData(m_UserData);
271 return obj;
272 }
273
274
275 wxLayoutObjectCmd::~wxLayoutObjectCmd()
276 {
277 delete m_font;
278 }
279
280 void
281 wxLayoutObjectCmd::GetStyle(wxLayoutStyleInfo *si) const
282 {
283 si->size = m_font->GetPointSize();
284 si->family = m_font->GetFamily();
285 si->style = m_font->GetStyle();
286 si->underline = m_font->GetUnderlined();
287 si->weight = m_font->GetWeight();
288
289 si->fg_red = m_ColourFG.Red();
290 si->fg_green = m_ColourFG.Green();
291 si->fg_blue = m_ColourFG.Blue();
292 si->bg_red = m_ColourBG.Red();
293 si->bg_green = m_ColourBG.Green();
294 si->bg_blue = m_ColourBG.Blue();
295 }
296
297 void
298 wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const & /* coords */)
299 {
300 wxASSERT(m_font);
301 dc.SetFont(*m_font);
302 dc.SetTextForeground(m_ColourFG);
303 dc.SetTextBackground(m_ColourBG);
304 }
305
306 void
307 wxLayoutObjectCmd::Layout(wxDC &dc)
308 {
309 // this get called, so that recalculation uses right font sizes
310 Draw(dc, wxPoint(0,0));
311 }
312
313
314 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
315
316 The wxLayoutLine object
317
318 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
319
320 wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist)
321 {
322 m_LineNumber = 0;
323 m_Width = m_Height = 0;
324 m_Length = 0;
325 m_Dirty = true;
326 m_Previous = prev;
327 m_Next = NULL;
328 RecalculatePosition(llist);
329 if(m_Previous)
330 {
331 m_LineNumber = m_Previous->GetLineNumber()+1;
332 m_Next = m_Previous->GetNextLine();
333 m_Previous->m_Next = this;
334 m_Height = m_Previous->GetHeight();
335 }
336 if(m_Next)
337 {
338 m_Next->m_Previous = this;
339 m_Next->MoveLines(+1);
340 m_Next->RecalculatePositions(1,llist);
341 }
342 }
343
344 wxLayoutLine::~wxLayoutLine()
345 {
346 // kbList cleans itself
347 }
348
349 wxPoint
350 wxLayoutLine::RecalculatePosition(wxLayoutList *llist)
351 {
352 if(m_Previous)
353 m_Position = m_Previous->GetPosition() +
354 wxPoint(0,m_Previous->GetHeight());
355 else
356 m_Position = wxPoint(0,0);
357 llist->SetUpdateRect(m_Position);
358 return m_Position;
359 }
360
361 void
362 wxLayoutLine::RecalculatePositions(int recurse, wxLayoutList *llist)
363 {
364 wxASSERT(recurse >= 0);
365 wxPoint pos = m_Position;
366 CoordType height = m_Height;
367
368 // WXLO_TRACE("RecalculatePositions()");
369 RecalculatePosition(llist);
370 if(m_Next)
371 {
372 if(recurse > 0)
373 m_Next->RecalculatePositions(--recurse, llist);
374 else if(pos != m_Position || m_Height != height)
375 m_Next->RecalculatePositions(0, llist);
376 }
377 }
378
379 wxLayoutObjectList::iterator
380 wxLayoutLine::FindObject(CoordType xpos, CoordType *offset) const
381 {
382 wxASSERT(xpos >= 0);
383 wxASSERT(offset);
384 wxLayoutObjectList::iterator
385 i,
386 found = NULLIT;
387 CoordType x = 0, len;
388
389 /* We search through the objects. As we don't like returning the
390 object that the cursor is behind, we just remember such an
391 object in "found" so we can return it if there is really no
392 further object following it. */
393 for(i = m_ObjectList.begin(); i != NULLIT; i++)
394 {
395 len = (**i).GetLength();
396 if( x <= xpos && xpos <= x + len )
397 {
398 *offset = xpos-x;
399 if(xpos == x + len) // is there another object behind?
400 found = i;
401 else // we are really inside this object
402 return i;
403 }
404 x += (**i).GetLength();
405 }
406 return found; // ==NULL if really none found
407 }
408
409 wxLayoutObjectList::iterator
410 wxLayoutLine::FindObjectScreen(wxDC &dc, CoordType xpos, CoordType *cxpos) const
411 {
412 wxASSERT(cxpos);
413 wxASSERT(cxpos);
414 wxLayoutObjectList::iterator i;
415 CoordType x = 0, cx = 0, width;
416
417 for(i = m_ObjectList.begin(); i != NULLIT; i++)
418 {
419 (**i).Layout(dc);
420 width = (**i).GetWidth();
421 if( x <= xpos && xpos <= x + width )
422 {
423 *cxpos = cx + (**i).GetOffsetScreen(dc, xpos-x);
424 wxLogDebug("wxLayoutLine::FindObjectScreen: cursor xpos = %ld", *cxpos);
425 return i;
426 }
427 x += (**i).GetWidth();
428 cx += (**i).GetLength();
429 }
430 // behind last object:
431 *cxpos = cx;
432 return m_ObjectList.tail();
433 }
434
435 bool
436 wxLayoutLine::Insert(CoordType xpos, wxLayoutObject *obj)
437 {
438 wxASSERT(xpos >= 0);
439 wxASSERT(obj != NULL);
440 CoordType offset;
441 wxLOiterator i = FindObject(xpos, &offset);
442 if(i == NULLIT)
443 {
444 if(xpos == 0 ) // aha, empty line!
445 {
446 m_ObjectList.push_back(obj);
447 m_Length += obj->GetLength();
448 return true;
449 }
450 else
451 return false;
452 }
453
454 CoordType len = (**i).GetLength();
455 if(offset == 0 /*&& i != m_ObjectList.begin()*/) // why?
456 { // insert before this object
457 m_ObjectList.insert(i,obj);
458 m_Length += obj->GetLength();
459 return true;
460 }
461 if(offset == len )
462 {
463 if( i == m_ObjectList.tail()) // last object?
464 m_ObjectList.push_back(obj);
465 else
466 { // insert after current object
467 i++;
468 m_ObjectList.insert(i,obj);
469 }
470 m_Length += obj->GetLength();
471 return true;
472 }
473 /* Otherwise we need to split the current object.
474 Fortunately this can only be a text object. */
475 wxASSERT((**i).GetType() == WXLO_TYPE_TEXT);
476 wxString left, right;
477 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
478 left = tobj->GetText().substr(0,offset);
479 right = tobj->GetText().substr(offset,len-offset);
480 // current text object gets set to right half
481 tobj->GetText() = right; // set new text
482 // before it we insert the new object
483 m_ObjectList.insert(i,obj);
484 m_Length += obj->GetLength();
485 // and before that we insert the left half
486 m_ObjectList.insert(i,new wxLayoutObjectText(left));
487 return true;
488 }
489
490 bool
491 wxLayoutLine::Insert(CoordType xpos, wxString text)
492 {
493 wxASSERT(xpos >= 0);
494 CoordType offset;
495 wxLOiterator i = FindObject(xpos, &offset);
496 if(i != NULLIT && (**i).GetType() == WXLO_TYPE_TEXT)
497 {
498 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
499 tobj->GetText().insert(offset, text);
500 m_Length += text.Length();
501
502 return true;
503 }
504 else
505 return Insert(xpos, new wxLayoutObjectText(text));
506 }
507
508 CoordType
509 wxLayoutLine::Delete(CoordType xpos, CoordType npos)
510 {
511 CoordType offset, len;
512
513 wxASSERT(xpos >= 0);
514 wxASSERT(npos >= 0);
515 wxLOiterator i = FindObject(xpos, &offset);
516 while(npos > 0)
517 {
518 if(i == NULLIT) return npos;
519 // now delete from that object:
520 if((**i).GetType() != WXLO_TYPE_TEXT)
521 {
522 if(offset != 0) // at end of line after a non-text object
523 return npos;
524 // always len == 1:
525 len = (**i).GetLength();
526 m_Length -= len;
527 npos -= len;
528 m_ObjectList.erase(i);
529 }
530 else
531 {
532 // tidy up: remove empty text objects
533 if((**i).GetLength() == 0)
534 {
535 m_ObjectList.erase(i);
536 continue;
537 }
538 // Text object:
539 CoordType max = (**i).GetLength() - offset;
540 if(npos < max) max = npos;
541 if(max == 0)
542 {
543 if(xpos == GetLength())
544 return npos;
545 else
546 { // at the end of an object
547 // move to begin of next object:
548 i++; offset = 0;
549 continue; // start over
550 }
551 }
552 npos -= max;
553 m_Length -= max;
554 if(offset == 0 && max == (**i).GetLength())
555 m_ObjectList.erase(i); // remove the whole object
556 else
557 ((wxLayoutObjectText *)(*i))->GetText().Remove(offset,max);
558 }
559 }
560 return npos;
561 }
562
563 bool
564 wxLayoutLine::DeleteWord(CoordType xpos)
565 {
566 wxASSERT(xpos >= 0);
567 CoordType offset;
568
569 wxLOiterator i = FindObject(xpos, &offset);
570
571 for(;;)
572 {
573 if(i == NULLIT) return false;
574 if((**i).GetType() != WXLO_TYPE_TEXT)
575 {
576 // This should only happen when at end of line, behind a non-text
577 // object:
578 if(offset == (**i).GetLength()) return false;
579 m_Length -= (**i).GetLength(); // -1
580 m_ObjectList.erase(i);
581 return true; // we are done
582 }
583 else
584 { // text object:
585 if(offset == (**i).GetLength()) // at end of object
586 {
587 i++; offset = 0;
588 continue;
589 }
590 wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
591 size_t count = 0;
592 wxString str = tobj->GetText();
593 str = str.substr(offset,str.Length()-offset);
594 // Find out how many positions we need to delete:
595 // 1. eat leading space
596 while(isspace(str.c_str()[count])) count++;
597 // 2. eat the word itself:
598 while(isalnum(str.c_str()[count])) count++;
599 // now delete it:
600 wxASSERT(count+offset <= (size_t) (**i).GetLength());
601 ((wxLayoutObjectText *)*i)->GetText().erase(offset,count);
602 m_Length -= count;
603 return true;
604 }
605 }
606 wxASSERT(0); // we should never arrive here
607 }
608
609 wxLayoutLine *
610 wxLayoutLine::DeleteLine(bool update, wxLayoutList *llist)
611 {
612 if(m_Next) m_Next->m_Previous = m_Previous;
613 if(m_Previous) m_Previous->m_Next = m_Next;
614 if(update)
615 {
616 m_Next->MoveLines(-1);
617 m_Next->RecalculatePositions(1, llist);
618 }
619 wxLayoutLine *next = m_Next;
620 delete this;
621 return next;
622 }
623
624 void
625 wxLayoutLine::Draw(wxDC &dc,
626 wxLayoutList *llist,
627 const wxPoint & offset) const
628 {
629 wxLayoutObjectList::iterator i;
630 wxPoint pos = offset;
631 pos = pos + GetPosition();
632
633 pos.y += m_BaseLine;
634
635 for(i = m_ObjectList.begin(); i != NULLIT; i++)
636 {
637 (**i).Draw(dc, pos);
638 pos.x += (**i).GetWidth();
639 }
640 }
641
642 void
643 wxLayoutLine::Layout(wxDC &dc,
644 wxLayoutList *llist,
645 wxPoint *cursorPos,
646 wxPoint *cursorSize,
647 int cx)
648 {
649 wxLayoutObjectList::iterator i;
650
651 CoordType
652 oldHeight = m_Height;
653 CoordType
654 topHeight, bottomHeight; // above and below baseline
655 CoordType
656 objHeight = 0,
657 objTopHeight, objBottomHeight;
658 CoordType
659 len, count = 0;
660 m_Height = 0; m_BaseLine = 0;
661 m_Width = 0;
662 topHeight = 0; bottomHeight = 0;
663 wxPoint size;
664 bool cursorFound = false;
665
666 if(cursorPos)
667 {
668 *cursorPos = m_Position;
669 if(cursorSize) *cursorSize = wxPoint(0,0);
670 }
671
672 for(i = m_ObjectList.begin(); i != NULLIT; i++)
673 {
674 (**i).Layout(dc);
675 size = (**i).GetSize(&objTopHeight, &objBottomHeight);
676
677 if(cursorPos && ! cursorFound)
678 { // we need to check whether the text cursor is here
679 len = (**i).GetLength();
680 if(count <= cx && count+len > cx)
681 {
682 if((**i).GetType() == WXLO_TYPE_TEXT)
683 {
684 len = cx - count; // pos in object
685 CoordType width, height, descent;
686 dc.GetTextExtent((*(wxLayoutObjectText*)*i).GetText().substr(0,len),
687 &width, &height, &descent);
688 cursorPos->x += width;
689 cursorPos->y = m_Position.y;
690 wxString str;
691 if(len < (**i).GetLength())
692 str = (*(wxLayoutObjectText*)*i).GetText().substr(len,1);
693 else
694 str = WXLO_CURSORCHAR;
695 dc.GetTextExtent(str, &width, &height, &descent);
696 wxASSERT(cursorSize);
697 // Just in case some joker inserted an empty string object:
698 if(width == 0) width = WXLO_MINIMUM_CURSOR_WIDTH;
699 if(height == 0) height = objHeight;
700 cursorSize->x = width;
701 cursorSize->y = height;
702 cursorFound = true; // no more checks
703 }
704 else
705 { // on some other object
706 CoordType top, bottom; // unused
707 *cursorSize = (**i).GetSize(&top,&bottom);
708 cursorPos->y = m_Position.y;
709 cursorFound = true; // no more checks
710 }
711 }
712 else
713 {
714 count += len;
715 cursorPos->x += (**i).GetWidth();
716 }
717 } // cursor finding
718 objHeight = size.y;
719 m_Width += size.x;
720 if(objHeight > m_Height) m_Height = objHeight;
721 if(objTopHeight > topHeight) topHeight = objTopHeight;
722 if(objBottomHeight > bottomHeight) bottomHeight = objBottomHeight;
723 }
724 if(topHeight + bottomHeight > m_Height) m_Height =
725 topHeight+bottomHeight;
726 m_BaseLine = topHeight;
727
728 if(m_Height == 0)
729 {
730 if(GetPreviousLine()) // empty line
731 {
732 m_Height = GetPreviousLine()->GetHeight();
733 m_BaseLine = GetPreviousLine()->m_BaseLine;
734 }
735 else
736 {
737 CoordType width, height, descent;
738 dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
739 m_Height = height;
740 m_BaseLine = m_Height - descent;
741 }
742 }
743
744
745 // tell next line about coordinate change
746 if(m_Next && objHeight != oldHeight)
747 m_Next->RecalculatePositions(0, llist);
748
749 // We need to check whether we found a valid cursor size:
750 if(cursorPos)
751 {
752 // this might be the case if the cursor is at the end of the
753 // line or on a command object:
754 if(cursorSize->y < WXLO_MINIMUM_CURSOR_WIDTH)
755 {
756 CoordType width, height, descent;
757 dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
758 cursorSize->x = width;
759 cursorSize->y = height;
760 }
761 if(m_BaseLine >= cursorSize->y) // the normal case anyway
762 cursorPos->y += m_BaseLine-cursorSize->y;
763 }
764 }
765
766
767 wxLayoutLine *
768 wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist)
769 {
770 wxASSERT(xpos >= 0);
771
772 if(xpos == 0)
773 { // insert an empty line before this one
774 wxLayoutLine *prev = new wxLayoutLine(m_Previous, llist);
775 if(m_Previous == NULL)
776 { // We were in first line, need to link in new empty line
777 // before this.
778 prev->m_Next = this;
779 m_Previous = prev;
780 m_Previous->m_Height = GetHeight(); // this is a wild guess
781 }
782 MoveLines(+1);
783 if(m_Next)
784 m_Next->RecalculatePositions(1, llist);
785 return this;
786 }
787
788 CoordType offset;
789 wxLOiterator i = FindObject(xpos, &offset);
790 if(i == NULLIT)
791 // must be at the end of the line then
792 return new wxLayoutLine(this, llist);
793 // split this line:
794
795 wxLayoutLine *newLine = new wxLayoutLine(this, llist);
796 // split object at i:
797 if((**i).GetType() == WXLO_TYPE_TEXT && offset != 0)
798 {
799 wxString left, right;
800 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
801 left = tobj->GetText().substr(0,offset);
802 right = tobj->GetText().substr(offset,tobj->GetLength()-offset);
803 // current text object gets set to left half
804 tobj->GetText() = left; // set new text
805 newLine->Append(new wxLayoutObjectText(right));
806 m_Length -= right.Length();
807 i++; // don't move this object to the new list
808 }
809 else
810 if(offset > 0)
811 i++; // move objects from here to new list
812
813 while(i != m_ObjectList.end())
814 {
815 newLine->Append(*i);
816 m_Length -= (**i).GetLength();
817 m_ObjectList.remove(i); // remove without deleting it
818 }
819 if(m_Next)
820 m_Next->RecalculatePositions(2, llist);
821 return newLine;
822 }
823
824
825 void
826 wxLayoutLine::MergeNextLine(wxLayoutList *llist)
827 {
828 wxASSERT(GetNextLine());
829 wxLayoutObjectList &list = GetNextLine()->m_ObjectList;
830 wxLOiterator i;
831
832 for(i = list.begin(); i != list.end();)
833 {
834 Append(*i);
835 list.remove(i); // remove without deleting it
836 }
837 wxASSERT(list.empty());
838 wxLayoutLine *oldnext = GetNextLine();
839 SetNext(GetNextLine()->GetNextLine());
840 delete oldnext;
841 RecalculatePositions(1, llist);
842 }
843
844 CoordType
845 wxLayoutLine::GetWrapPosition(CoordType column)
846 {
847 CoordType offset;
848 wxLOiterator i = FindObject(column, &offset);
849 if(i == NULLIT) return -1; // cannot wrap
850
851 // go backwards through the list and look for space in text objects
852 do
853 {
854 if((**i).GetType() == WXLO_TYPE_TEXT)
855 {
856 do
857 {
858 if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset]))
859 return column;
860 else
861 {
862 offset--;
863 column--;
864 }
865 }while(offset != -1);
866 i--; // move on to previous object
867 }
868 else
869 {
870 column -= (**i).GetLength();
871 i--;
872 }
873 if( i != NULLIT)
874 offset = (**i).GetLength();
875 }while(i != NULLIT);
876 /* If we reached the begin of the list and have more than one
877 object, that one is longer than the margin, so break behind
878 it. */
879 CoordType pos = 0;
880 i = m_ObjectList.begin();
881 while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
882 {
883 pos += (**i).GetLength();
884 i++;
885 }
886 if(i == NULLIT) return -1; //why should this happen?
887 pos += (**i).GetLength();
888 i++;
889 while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
890 {
891 pos += (**i).GetLength();
892 i++;
893 }
894 if(i == NULLIT) return -1; //this is possible, if there is only one text object
895 // now we are at the second text object:
896 pos -= (**i).GetLength();
897 return pos; // in front of it
898 }
899
900
901 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
902
903 The wxLayoutList object
904
905 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
906
907 wxLayoutList::wxLayoutList()
908 {
909 m_DefaultSetting = NULL;
910 m_FirstLine = NULL;
911 m_ColourFG = *wxBLACK;
912 m_ColourBG = *wxWHITE;
913 InvalidateUpdateRect();
914 Clear();
915 }
916
917 wxLayoutList::~wxLayoutList()
918 {
919 InternalClear();
920 m_FirstLine->DeleteLine(false, this);
921 }
922
923 void
924 wxLayoutList::Empty(void)
925 {
926 while(m_FirstLine)
927 m_FirstLine = m_FirstLine->DeleteLine(false, this);
928
929 m_CursorPos = wxPoint(0,0);
930 m_CursorScreenPos = wxPoint(0,0);
931 m_CursorSize = wxPoint(0,0);
932 m_FirstLine = new wxLayoutLine(NULL, this); // empty first line
933 m_CursorLine = m_FirstLine;
934 InvalidateUpdateRect();
935 }
936
937
938 void
939 wxLayoutList::InternalClear(void)
940 {
941 Empty();
942 if(m_DefaultSetting)
943 {
944 delete m_DefaultSetting;
945 m_DefaultSetting = NULL;
946 }
947 }
948
949 void
950 wxLayoutList::SetFont(int family, int size, int style, int weight,
951 int underline, wxColour *fg,
952 wxColour *bg)
953 {
954 if(family != -1) m_FontFamily = family;
955 if(size != -1) m_FontPtSize = size;
956 if(style != -1) m_FontStyle = style;
957 if(weight != -1) m_FontWeight = weight;
958 if(underline != -1) m_FontUnderline = underline != 0;
959
960 if(fg != NULL) m_ColourFG = *fg;
961 if(bg != NULL) m_ColourBG = *bg;
962
963 Insert(
964 new wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,m_FontWeight,m_FontUnderline,
965 m_ColourFG, m_ColourBG));
966 }
967
968 void
969 wxLayoutList::SetFont(int family, int size, int style, int weight,
970 int underline, char const *fg, char const *bg)
971
972 {
973 wxColour
974 *cfg = NULL,
975 *cbg = NULL;
976
977 if( fg )
978 cfg = wxTheColourDatabase->FindColour(fg);
979 if( bg )
980 cbg = wxTheColourDatabase->FindColour(bg);
981
982 SetFont(family,size,style,weight,underline,cfg,cbg);
983 }
984
985 void
986 wxLayoutList::Clear(int family, int size, int style, int weight,
987 int /* underline */, wxColour *fg, wxColour *bg)
988 {
989 InternalClear();
990
991 // set defaults
992 m_FontPtSize = size;
993 m_FontUnderline = false;
994 m_FontFamily = family;
995 m_FontStyle = style;
996 m_FontWeight = weight;
997 if(fg) m_ColourFG = *fg;
998 if(bg) m_ColourBG = *bg;
999
1000 m_ColourFG = *wxBLACK;
1001 m_ColourBG = *wxWHITE;
1002
1003 if(m_DefaultSetting)
1004 delete m_DefaultSetting;
1005
1006 m_DefaultSetting = new
1007 wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,
1008 m_FontWeight,m_FontUnderline,
1009 m_ColourFG, m_ColourBG);
1010 }
1011
1012
1013
1014 bool
1015 wxLayoutList::MoveCursorTo(wxPoint const &p)
1016 {
1017 SetUpdateRect(m_CursorScreenPos);
1018 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1019 wxLayoutLine *line = m_FirstLine;
1020 while(line && line->GetLineNumber() != p.y)
1021 line = line->GetNextLine();
1022 if(line && line->GetLineNumber() == p.y) // found it
1023 {
1024 m_CursorPos.y = p.y;
1025 m_CursorLine = line;
1026 CoordType len = line->GetLength();
1027 if(len >= p.x)
1028 {
1029 m_CursorPos.x = p.x;
1030 return true;
1031 }
1032 else
1033 {
1034 m_CursorPos.x = len;
1035 return false;
1036 }
1037 }
1038 return false;
1039 }
1040
1041 bool
1042 wxLayoutList::MoveCursorVertically(int n)
1043 {
1044 SetUpdateRect(m_CursorScreenPos);
1045 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1046 bool rc;
1047 if(n < 0) // move up
1048 {
1049 if(m_CursorLine == m_FirstLine) return false;
1050 while(n < 0 && m_CursorLine)
1051 {
1052 m_CursorLine = m_CursorLine->GetPreviousLine();
1053 m_CursorPos.y--;
1054 n++;
1055 }
1056 if(! m_CursorLine)
1057 {
1058 m_CursorLine = m_FirstLine;
1059 m_CursorPos.y = 0;
1060 rc = false;
1061 }
1062 else
1063 {
1064 if(m_CursorPos.x > m_CursorLine->GetLength())
1065 m_CursorPos.x = m_CursorLine->GetLength();
1066 rc = true;
1067 }
1068 }
1069 else // move down
1070 {
1071 wxLayoutLine *last = m_CursorLine;
1072 if(! m_CursorLine->GetNextLine()) return false;
1073 while(n > 0 && m_CursorLine)
1074 {
1075 n--;
1076 m_CursorPos.y ++;
1077 m_CursorLine = m_CursorLine->GetNextLine();
1078 }
1079 if(! m_CursorLine)
1080 {
1081 m_CursorLine = last;
1082 m_CursorPos.y ++;
1083 rc = false;
1084 }
1085 else
1086 {
1087 if(m_CursorPos.x > m_CursorLine->GetLength())
1088 m_CursorPos.x = m_CursorLine->GetLength();
1089 rc = true;
1090 }
1091 }
1092 return rc;
1093 }
1094
1095 bool
1096 wxLayoutList::MoveCursorHorizontally(int n)
1097 {
1098 SetUpdateRect(m_CursorScreenPos);
1099 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1100 int move;
1101 while(n < 0)
1102 {
1103 if(m_CursorPos.x == 0) // at begin of line
1104 {
1105 if(! MoveCursorVertically(-1))
1106 break;
1107 MoveCursorToEndOfLine();
1108 n++;
1109 continue;
1110 }
1111 move = -n;
1112 if(move > m_CursorPos.x) move = m_CursorPos.x;
1113 m_CursorPos.x -= move; n += move;
1114 }
1115
1116 while(n > 0)
1117 {
1118 int len = m_CursorLine->GetLength();
1119 if(m_CursorPos.x == len) // at end of line
1120 {
1121 if(! MoveCursorVertically(1))
1122 break;
1123 MoveCursorToBeginOfLine();
1124 n--;
1125 continue;
1126 }
1127 move = n;
1128 if( move >= len-m_CursorPos.x) move = len-m_CursorPos.x;
1129 m_CursorPos.x += move;
1130 n -= move;
1131 }
1132 return n == 0;
1133 }
1134
1135 bool
1136 wxLayoutList::Insert(wxString const &text)
1137 {
1138 wxASSERT(m_CursorLine);
1139 SetUpdateRect(m_CursorScreenPos);
1140 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1141 m_CursorLine->Insert(m_CursorPos.x, text);
1142 m_CursorPos.x += text.Length();
1143 return true;
1144 }
1145
1146 bool
1147 wxLayoutList::Insert(wxLayoutObject *obj)
1148 {
1149 wxASSERT(m_CursorLine);
1150 SetUpdateRect(m_CursorScreenPos);
1151 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1152 m_CursorLine->Insert(m_CursorPos.x, obj);
1153 m_CursorPos.x += obj->GetLength();
1154 return true;
1155 }
1156
1157 bool
1158 wxLayoutList::LineBreak(void)
1159 {
1160 wxASSERT(m_CursorLine);
1161 bool setFirst = (m_CursorLine == m_FirstLine && m_CursorPos.x == 0);
1162 SetUpdateRect(m_CursorScreenPos);
1163 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1164 m_CursorLine = m_CursorLine->Break(m_CursorPos.x, this);
1165 if(setFirst) // we were at beginning of first line
1166 m_FirstLine = m_CursorLine->GetPreviousLine();
1167 m_CursorPos.y++;
1168 m_CursorPos.x = 0;
1169 return true;
1170 }
1171
1172 bool
1173 wxLayoutList::WrapLine(CoordType column)
1174 {
1175 if(m_CursorPos.x <= column || column < 1)
1176 return false; // do nothing yet
1177 else
1178 {
1179 CoordType xpos = m_CursorLine->GetWrapPosition(column);
1180 if(xpos == -1)
1181 return false; // cannot break line
1182 //else:
1183 CoordType newpos = m_CursorPos.x - xpos - 1;
1184 m_CursorPos.x = xpos;
1185 SetUpdateRect(m_CursorScreenPos);
1186 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1187 LineBreak();
1188 Delete(1); // delete the space
1189 m_CursorPos.x = newpos;
1190 return true;
1191 }
1192 }
1193
1194 bool
1195 wxLayoutList::Delete(CoordType npos)
1196 {
1197 wxASSERT(m_CursorLine);
1198 SetUpdateRect(m_CursorScreenPos);
1199 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1200 CoordType left;
1201 do
1202 {
1203 left = m_CursorLine->Delete(m_CursorPos.x, npos);
1204 if(left == 0)
1205 return true;
1206 // More to delete, continue on next line.
1207 // First, check if line is empty:
1208 if(m_CursorLine->GetLength() == 0)
1209 { // in this case, updating could probably be optimised
1210 #ifdef WXLO_DEBUG
1211 wxASSERT(DeleteLines(1) == 0);
1212 #else
1213 DeleteLines(1);
1214 #endif
1215
1216 left--;
1217 }
1218 else
1219 {
1220 // Need to join next line
1221 if(! m_CursorLine->GetNextLine())
1222 break; // cannot
1223 else
1224 {
1225 m_CursorLine->MergeNextLine(this);
1226 left--;
1227 }
1228 }
1229 }
1230 while(left);
1231 return left == 0;
1232 }
1233
1234 int
1235 wxLayoutList::DeleteLines(int n)
1236 {
1237 wxASSERT(m_CursorLine);
1238 wxLayoutLine *line;
1239 SetUpdateRect(m_CursorScreenPos);
1240 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1241 while(n > 0)
1242 {
1243 if(!m_CursorLine->GetNextLine())
1244 { // we cannot delete this line, but we can clear it
1245 MoveCursorToBeginOfLine();
1246 DeleteToEndOfLine();
1247 return n-1;
1248 }
1249 //else:
1250 line = m_CursorLine;
1251 m_CursorLine = m_CursorLine->DeleteLine(true, this);
1252 n--;
1253 if(line == m_FirstLine) m_FirstLine = m_CursorLine;
1254 wxASSERT(m_FirstLine);
1255 wxASSERT(m_CursorLine);
1256 }
1257 m_CursorLine->RecalculatePositions(2, this);
1258 return n;
1259 }
1260
1261 void
1262 wxLayoutList::Recalculate(wxDC &dc, CoordType bottom)
1263 {
1264 wxLayoutLine *line = m_FirstLine;
1265
1266 // first, make sure everything is calculated - this might not be
1267 // needed, optimise it later
1268 m_DefaultSetting->Layout(dc);
1269 while(line)
1270 {
1271 line->RecalculatePosition(this); // so we don't need to do it all the time
1272 // little condition to speed up redrawing:
1273 if(bottom != -1 && line->GetPosition().y > bottom) break;
1274 line = line->GetNextLine();
1275 }
1276 }
1277
1278 void
1279 wxLayoutList::UpdateCursorScreenPos(wxDC &dc)
1280 {
1281 wxASSERT(m_CursorLine);
1282 m_CursorLine->Layout(dc, this, (wxPoint *)&m_CursorScreenPos, (wxPoint *)&m_CursorSize, m_CursorPos.x);
1283 }
1284
1285 wxPoint
1286 wxLayoutList::GetCursorScreenPos(wxDC &dc)
1287 {
1288 UpdateCursorScreenPos(dc);
1289 return m_CursorScreenPos;
1290 }
1291
1292 void
1293 wxLayoutList::Layout(wxDC &dc, CoordType bottom)
1294 {
1295 wxLayoutLine *line = m_FirstLine;
1296
1297 // first, make sure everything is calculated - this might not be
1298 // needed, optimise it later
1299 m_DefaultSetting->Layout(dc);
1300 while(line)
1301 {
1302 if(line == m_CursorLine)
1303 line->Layout(dc, this, (wxPoint *)&m_CursorScreenPos, (wxPoint *)&m_CursorSize, m_CursorPos.x);
1304 else
1305 line->Layout(dc, this);
1306 // little condition to speed up redrawing:
1307 if(bottom != -1 && line->GetPosition().y > bottom) break;
1308 line = line->GetNextLine();
1309 }
1310
1311 ///FIXME: disabled for now
1312 #if 0
1313 // can only be 0 if we are on the first line and have no next line
1314 wxASSERT(m_CursorSize.x != 0 || (m_CursorLine &&
1315 m_CursorLine->GetNextLine() == NULL &&
1316 m_CursorLine == m_FirstLine));
1317 #endif
1318 SetUpdateRect(m_CursorScreenPos);
1319 SetUpdateRect(m_CursorScreenPos+m_CursorSize);
1320 }
1321
1322 void
1323 wxLayoutList::Draw(wxDC &dc, wxPoint const &offset,
1324 CoordType top, CoordType bottom)
1325 {
1326 wxLayoutLine *line = m_FirstLine;
1327
1328 Layout(dc, bottom);
1329 m_DefaultSetting->Draw(dc, wxPoint(0,0));
1330 wxBrush brush(m_ColourBG, wxSOLID);
1331 dc.SetBrush(brush);
1332
1333 while(line)
1334 {
1335 // only draw if between top and bottom:
1336 if((top == -1 || line->GetPosition().y >= top))
1337 line->Draw(dc, this, offset);
1338 // little condition to speed up redrawing:
1339 if(bottom != -1 && line->GetPosition().y + line->GetHeight() > bottom) break;
1340 line = line->GetNextLine();
1341 }
1342 // can only be 0 if we are on the first line and have no next line
1343 wxASSERT(m_CursorSize.x != 0 || (m_CursorLine &&
1344 m_CursorLine->GetNextLine() == NULL &&
1345 m_CursorLine == m_FirstLine));
1346 InvalidateUpdateRect();
1347 }
1348
1349 wxLayoutObject *
1350 wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos, wxPoint *cursorPos)
1351 {
1352 // First, find the right line:
1353 wxLayoutLine *line = m_FirstLine;
1354 wxPoint p;
1355
1356 // we need to run a layout here to get font sizes right :-(
1357 m_DefaultSetting->Layout(dc);
1358 while(line)
1359 {
1360 p = line->GetPosition();
1361 if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y)
1362 break;
1363 line->Layout(dc, this);
1364 line = line->GetNextLine();
1365 }
1366 if(line == NULL) return NULL; // not found
1367 if(cursorPos) cursorPos->y = line->GetLineNumber();
1368 // Now, find the object in the line:
1369 wxLOiterator i = line->FindObjectScreen(dc, pos.x, & cursorPos->x);
1370 return (i == NULLIT) ? NULL : *i;
1371
1372 }
1373
1374 wxPoint
1375 wxLayoutList::GetSize(void) const
1376 {
1377 wxLayoutLine
1378 *line = m_FirstLine,
1379 *last = line;
1380 if(! line)
1381 return wxPoint(0,0);
1382
1383 wxPoint maxPoint(0,0);
1384
1385 // find last line:
1386 while(line)
1387 {
1388 if(line->GetWidth() > maxPoint.x)
1389 maxPoint.x = line->GetWidth();
1390 last = line;
1391 line = line->GetNextLine();
1392 }
1393
1394 maxPoint.y = last->GetPosition().y + last->GetHeight();
1395 return maxPoint;
1396 }
1397
1398 void
1399 wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate)
1400 {
1401 wxPoint coords;
1402 coords = m_CursorScreenPos;
1403 coords.x += translate.x;
1404 coords.y += translate.y;
1405
1406 #ifdef WXLAYOUT_DEBUG
1407 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
1408 (long)m_CursorPos.x, (long)m_CursorPos.y,
1409 (long)coords.x, (long)coords.y,
1410 (long)m_CursorSize.x, (long)m_CursorSize.y,
1411 (long)m_CursorLine->GetLineNumber(),
1412 (long)m_CursorLine->GetLength()));
1413 #endif
1414
1415 dc.SetBrush(*wxBLACK_BRUSH);
1416 dc.SetLogicalFunction(wxXOR);
1417 dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
1418 if(active)
1419 dc.DrawRectangle(coords.x, coords.y, m_CursorSize.x,
1420 m_CursorSize.y);
1421 else
1422 dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
1423 coords.x+m_CursorSize.x, coords.y+m_CursorSize.y-1);
1424 dc.SetLogicalFunction(wxCOPY);
1425 //dc.SetBrush(wxNullBrush);
1426 }
1427
1428 /** Called by the objects to update the update rectangle.
1429 @param p a point to include in it
1430 */
1431 void
1432 wxLayoutList::SetUpdateRect(const wxPoint &p)
1433 {
1434 if(m_UpdateRectValid)
1435 GrowRect(m_UpdateRect, p);
1436 else
1437 {
1438 m_UpdateRect.x = p.x;
1439 m_UpdateRect.y = p.y;
1440 m_UpdateRect.width = 4; // large enough to avoid surprises from
1441 m_UpdateRect.height = 4;// wxGTK :-)
1442 m_UpdateRectValid = true;
1443 }
1444 }
1445
1446 void
1447 wxLayoutList::StartSelection(void)
1448 {
1449 wxLogDebug("Starting selection at %ld/%ld", m_CursorPos.x, m_CursorPos.y);
1450 m_Selection.m_CursorA = m_CursorPos;
1451 }
1452
1453 void
1454 wxLayoutList::EndSelection(void)
1455 {
1456 wxLogDebug("Ending selection at %ld/%ld", m_CursorPos.x, m_CursorPos.y);
1457 m_Selection.m_CursorB = m_CursorPos;
1458 }
1459
1460 bool
1461 wxLayoutList::IsSelected(const wxPoint &cursor)
1462 {
1463 return m_Selection.m_CursorA <= cursor
1464 && cursor <= m_Selection.m_CursorB;
1465 }
1466
1467
1468 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1469
1470 wxLayoutPrintout
1471
1472 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1473
1474 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist,
1475 wxString const & title)
1476 :wxPrintout(title)
1477 {
1478 m_llist = llist;
1479 m_title = title;
1480 }
1481
1482 float
1483 wxLayoutPrintout::ScaleDC(wxDC *dc)
1484 {
1485 // The following bit is taken from the printing sample, let's see
1486 // whether it works for us.
1487
1488 /* You might use THIS code to set the printer DC to ROUGHLY reflect
1489 * the screen text size. This page also draws lines of actual length 5cm
1490 * on the page.
1491 */
1492 // Get the logical pixels per inch of screen and printer
1493 int ppiScreenX, ppiScreenY;
1494 GetPPIScreen(&ppiScreenX, &ppiScreenY);
1495 int ppiPrinterX, ppiPrinterY;
1496 GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
1497
1498 if(ppiScreenX == 0) // not yet set, need to guess
1499 {
1500 ppiScreenX = 100;
1501 ppiScreenY = 100;
1502 }
1503 if(ppiPrinterX == 0) // not yet set, need to guess
1504 {
1505 ppiPrinterX = 72;
1506 ppiPrinterY = 72;
1507 }
1508
1509 // This scales the DC so that the printout roughly represents the
1510 // the screen scaling. The text point size _should_ be the right size
1511 // but in fact is too small for some reason. This is a detail that will
1512 // need to be addressed at some point but can be fudged for the
1513 // moment.
1514 float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
1515
1516 // Now we have to check in case our real page size is reduced
1517 // (e.g. because we're drawing to a print preview memory DC)
1518 int pageWidth, pageHeight;
1519 int w, h;
1520 dc->GetSize(&w, &h);
1521 GetPageSizePixels(&pageWidth, &pageHeight);
1522 if(pageWidth != 0) // doesn't work always
1523 {
1524 // If printer pageWidth == current DC width, then this doesn't
1525 // change. But w might be the preview bitmap width, so scale down.
1526 scale = scale * (float)(w/(float)pageWidth);
1527 }
1528 dc->SetUserScale(scale, scale);
1529 return scale;
1530 }
1531
1532 bool wxLayoutPrintout::OnPrintPage(int page)
1533 {
1534 wxDC *dc = GetDC();
1535
1536 ScaleDC(dc);
1537
1538 if (dc)
1539 {
1540 int top, bottom;
1541 top = (page - 1)*m_PrintoutHeight;
1542 bottom = top + m_PrintoutHeight;
1543 // SetDeviceOrigin() doesn't work here, so we need to manually
1544 // translate all coordinates.
1545 wxPoint translate(m_Offset.x,m_Offset.y-top);
1546 m_llist->Draw(*dc, translate, top, bottom);
1547 return true;
1548 }
1549 else
1550 return false;
1551 }
1552
1553 void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
1554 {
1555 /* We allocate a temporary wxDC for printing, so that we can
1556 determine the correct paper size and scaling. We don't actually
1557 print anything on it. */
1558 #ifdef __WXMSW__
1559 wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false);
1560 #else
1561 wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
1562 #endif
1563
1564 float scale = ScaleDC(&psdc);
1565
1566 psdc.GetSize(&m_PageWidth, &m_PageHeight);
1567 // This sets a left/top origin of 15% and 20%:
1568 m_Offset = wxPoint((15*m_PageWidth)/100, m_PageHeight/20);
1569
1570 // This is the length of the printable area.
1571 m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
1572 m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height
1573
1574
1575 m_NumOfPages = 1 +
1576 (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight));
1577
1578 *minPage = 1;
1579 *maxPage = m_NumOfPages;
1580
1581 *selPageFrom = 1;
1582 *selPageTo = m_NumOfPages;
1583 wxRemoveFile(WXLLIST_TEMPFILE);
1584 }
1585
1586 bool wxLayoutPrintout::HasPage(int pageNum)
1587 {
1588 return pageNum <= m_NumOfPages;
1589 }
1590
1591 /*
1592 Stupid wxWindows doesn't draw proper ellipses, so we comment this
1593 out. It's a waste of paper anyway.
1594 */
1595 #if 0
1596 void
1597 wxLayoutPrintout::DrawHeader(wxDC &dc,
1598 wxPoint topleft, wxPoint bottomright,
1599 int pageno)
1600 {
1601 // make backups of all essential parameters
1602 const wxBrush& brush = dc.GetBrush();
1603 const wxPen& pen = dc.GetPen();
1604 const wxFont& font = dc.GetFont();
1605
1606 dc.SetBrush(*wxWHITE_BRUSH);
1607 dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
1608 dc.DrawRoundedRectangle(topleft.x,
1609 topleft.y,bottomright.x-topleft.x,
1610 bottomright.y-topleft.y);
1611 dc.SetBrush(*wxBLACK_BRUSH);
1612 wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10,
1613 wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
1614 dc.SetFont(myfont);
1615
1616 wxString page;
1617 page = "9999/9999 "; // many pages...
1618 long w,h;
1619 dc.GetTextExtent(page,&w,&h);
1620 page.Printf("%d/%d", pageno, (int) m_NumOfPages);
1621 dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
1622 dc.GetTextExtent("XXXX", &w,&h);
1623 dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
1624
1625 // restore settings
1626 dc.SetPen(pen);
1627 dc.SetBrush(brush);
1628 dc.SetFont(font);
1629 }
1630 #endif
1631
1632