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