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