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