]> git.saurik.com Git - wxWidgets.git/blob - user/wxLayout/wxllist.cpp
4596fc72059280fbfbedeec15a7de55b76a8785e
[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 <wx/wxprec.h>
18
19 #ifdef __BORLANDC__
20 # pragma hdrstop
21 #endif
22
23 #include "Mpch.h"
24
25 #ifdef M_BASEDIR
26 # include "gui/wxllist.h"
27 # include "gui/wxlparser.h"
28 # define SHOW_SELECTIONS 1
29 #else
30 # include "wxllist.h"
31 # include "wxlparser.h"
32 # define SHOW_SELECTIONS 1
33 #endif
34
35 #ifndef USE_PCH
36 # include <iostream.h>
37
38 # include <wx/dc.h>
39 # include <wx/dcps.h>
40 # include <wx/print.h>
41 # include <wx/log.h>
42 # include <wx/filefn.h>
43 #endif
44
45 #ifdef WXLAYOUT_USE_CARET
46 # include <wx/caret.h>
47 #endif // WXLAYOUT_USE_CARET
48
49 #include <ctype.h>
50
51 /// This should never really get created
52 #define WXLLIST_TEMPFILE "__wxllist.tmp"
53
54 #ifdef WXLAYOUT_DEBUG
55
56 # define TypeString(t) g_aTypeStrings[t]
57 # define WXLO_DEBUG(x) wxLogDebug x
58
59 static const char *g_aTypeStrings[] =
60 {
61 "invalid", "text", "cmd", "icon"
62 };
63 void
64 wxLayoutObject::Debug(void)
65 {
66 WXLO_DEBUG(("%s",g_aTypeStrings[GetType()]));
67 }
68 #else
69 # define TypeString(t) ""
70 # define WXLO_DEBUG(x)
71 #endif
72
73 // FIXME under MSW, this constant is needed to make the thing properly redraw
74 // itself - I don't know where the size calculation error is and I can't
75 // waste time looking for it right now. Search for occurences of
76 // MSW_CORRECTION to find all the places where I did it.
77 #ifdef __WXMSW__
78 static const int MSW_CORRECTION = 5;
79 #else
80 static const int MSW_CORRECTION = 0;
81 #endif
82
83 /// Cursors smaller than this disappear in XOR drawing mode
84 #define WXLO_MINIMUM_CURSOR_WIDTH 4
85
86 /// Use this character to estimate a cursor size when none is available.
87 #define WXLO_CURSORCHAR "E"
88 /** @name Helper functions */
89 //@{
90 /// allows me to compare to wxPoints
91 bool operator <=(wxPoint const &p1, wxPoint const &p2)
92 {
93 return p1.y < p2.y || (p1.y == p2.y && p1.x <= p2.x);
94 }
95
96 /// grows a wxRect so that it includes the given point
97
98 static
99 void GrowRect(wxRect &r, CoordType x, CoordType y)
100 {
101 if(r.x > x)
102 r.x = x;
103 else if(r.x + r.width < x)
104 r.width = x - r.x;
105
106 if(r.y > y)
107 r.y = y;
108 else if(r.y + r.height < y)
109 r.height = y - r.y;
110 }
111
112 #if 0
113 // unused
114 /// returns true if the point is in the rectangle
115 static
116 bool Contains(const wxRect &r, const wxPoint &p)
117 {
118 return r.x <= p.x && r.y <= p.y && (r.x+r.width) >= p.x && (r.y + r.height) >= p.y;
119 }
120 #endif
121
122
123 //@}
124
125
126 void ReadString(wxString &to, wxString &from)
127 {
128 to = "";
129 const char *cptr = from.c_str();
130 while(*cptr && *cptr != '\n')
131 to += *cptr++;
132 if(*cptr) cptr++;
133 from = cptr;
134 }
135
136 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
137
138 wxLayoutObject
139
140 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
141
142 /* static */
143 wxLayoutObject *
144 wxLayoutObject::Read(wxString &istr)
145 {
146 wxString tmp;
147 ReadString(tmp, istr);
148 int type = -1;
149 sscanf(tmp.c_str(),"%d", &type);
150
151 switch(type)
152 {
153 case WXLO_TYPE_TEXT:
154 return wxLayoutObjectText::Read(istr);
155 case WXLO_TYPE_CMD:
156 return wxLayoutObjectCmd::Read(istr);
157 case WXLO_TYPE_ICON:
158 return wxLayoutObjectIcon::Read(istr);
159 default:
160 return NULL;
161 }
162 }
163
164 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
165
166 wxLayoutObjectText
167
168 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
169
170 wxLayoutObjectText::wxLayoutObjectText(const wxString &txt)
171 {
172 m_Text = txt;
173 m_Width = 0;
174 m_Height = 0;
175 m_Top = 0;
176 m_Bottom = 0;
177 }
178
179 wxLayoutObject *
180 wxLayoutObjectText::Copy(void)
181 {
182 wxLayoutObjectText *obj = new wxLayoutObjectText(m_Text);
183 obj->m_Width = m_Width;
184 obj->m_Height = m_Height;
185 obj->m_Top = m_Top;
186 obj->m_Bottom = m_Bottom;
187 obj->SetUserData(m_UserData);
188 return obj;
189 }
190
191
192 void
193 wxLayoutObjectText::Write(wxString &ostr)
194 {
195 ostr << (int) WXLO_TYPE_TEXT << '\n'
196 << m_Text << '\n';
197 }
198 /* static */
199 wxLayoutObjectText *
200 wxLayoutObjectText::Read(wxString &istr)
201 {
202 wxString text;
203 ReadString(text, istr);
204
205 return new wxLayoutObjectText(text);
206 }
207
208 wxPoint
209 wxLayoutObjectText::GetSize(CoordType *top, CoordType *bottom) const
210 {
211
212 *top = m_Top; *bottom = m_Bottom;
213 return wxPoint(m_Width, m_Height);
214 }
215
216 void
217 wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &coords,
218 wxLayoutList *wxllist,
219 CoordType begin, CoordType end)
220 {
221 if( end <= 0)
222 dc.DrawText(m_Text, coords.x, coords.y-m_Top);
223 else
224 {
225 // highlight the bit between begin and len
226 wxString str;
227 CoordType
228 xpos = coords.x,
229 ypos = coords.y-m_Top;
230 long width, height, descent;
231
232 if(begin < 0) begin = 0;
233 if( end > (signed)m_Text.Length() )
234 end = m_Text.Length();
235
236 str = m_Text.Mid(0, begin);
237 dc.DrawText(str, xpos, ypos);
238 dc.GetTextExtent(str, &width, &height, &descent);
239 xpos += width;
240 wxllist->StartHighlighting(dc);
241 str = m_Text.Mid(begin, end-begin);
242 dc.DrawText(str, xpos, ypos);
243 dc.GetTextExtent(str, &width, &height, &descent);
244 xpos += width;
245 wxllist->EndHighlighting(dc);
246 str = m_Text.Mid(end, m_Text.Length()-end);
247 dc.DrawText(str, xpos, ypos);
248 }
249 }
250
251 CoordType
252 wxLayoutObjectText::GetOffsetScreen(wxDC &dc, CoordType xpos) const
253 {
254 CoordType
255 offs = 1,
256 maxlen = m_Text.Length();
257 long
258 width = 0,
259 height, descent = 0l;
260
261 if(xpos == 0) return 0; // easy
262
263 while(width < xpos && offs < maxlen)
264 {
265 dc.GetTextExtent(m_Text.substr(0,offs),
266 &width, &height, &descent);
267 offs++;
268 }
269 /* We have to substract 1 to compensate for the offs++, and another
270 one because we don't want to position the cursor behind the
271 object what we clicked on, but before - otherwise it looks
272 funny. */
273 return (xpos > 2) ? offs-2 : 0;
274 }
275
276 void
277 wxLayoutObjectText::Layout(wxDC &dc, class wxLayoutList *llist)
278 {
279 long descent = 0l;
280
281 // now this is done in wxLayoutLine::Layout(), but this code might be
282 // reenabled later - in principle, it's more efficient
283 #if 0
284 CoordType widthOld = m_Width,
285 heightOld = m_Height;
286 #endif // 0
287
288 dc.GetTextExtent(m_Text, &m_Width, &m_Height, &descent);
289
290 #if 0
291 if ( widthOld != m_Width || heightOld != m_Height )
292 {
293 // as the text length changed, it must be refreshed
294 wxLayoutLine *line = GetLine();
295
296 wxCHECK_RET( line, "wxLayoutObjectText can't refresh itself" );
297
298 // as our size changed, we need to repaint the part which was appended
299 wxPoint position(line->GetPosition());
300
301 // this is not the most efficient way (we repaint the whole line), but
302 // it's not too slow and is *simple*
303 if ( widthOld < m_Width )
304 widthOld = m_Width;
305 if ( heightOld < m_Height )
306 heightOld = m_Height;
307
308 llist->SetUpdateRect(position.x + widthOld + MSW_CORRECTION,
309 position.y + heightOld + MSW_CORRECTION);
310 }
311 #endif // 0
312
313 m_Bottom = descent;
314 m_Top = m_Height - m_Bottom;
315 }
316
317
318 #ifdef WXLAYOUT_DEBUG
319 void
320 wxLayoutObjectText::Debug(void)
321 {
322 wxLayoutObject::Debug();
323 WXLO_DEBUG((" `%s`", m_Text.c_str()));
324 }
325 #endif
326
327 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
328
329 wxLayoutObjectIcon
330
331 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
332
333 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon)
334 {
335 m_Icon = new wxBitmap(icon);
336 }
337
338
339 void
340 wxLayoutObjectIcon::Write(wxString &ostr)
341 {
342 /* Exports icon through a temporary file. */
343
344 wxString file = wxGetTempFileName("wxloexport");
345
346 ostr << WXLO_TYPE_ICON << '\n'
347 << file << '\n';
348 m_Icon->SaveFile(file, WXLO_BITMAP_FORMAT);
349 }
350 /* static */
351 wxLayoutObjectIcon *
352 wxLayoutObjectIcon::Read(wxString &istr)
353 {
354 wxString file;
355 ReadString(file, istr);
356
357 if(! wxFileExists(file))
358 return NULL;
359 wxLayoutObjectIcon *obj = new wxLayoutObjectIcon;
360
361 if(!obj->m_Icon->LoadFile(file, WXLO_BITMAP_FORMAT))
362 {
363 delete obj;
364 return NULL;
365 }
366 else
367 return obj;
368 }
369
370 wxLayoutObject *
371 wxLayoutObjectIcon::Copy(void)
372 {
373 wxLayoutObjectIcon *obj = new wxLayoutObjectIcon(new
374 wxBitmap(*m_Icon));
375 obj->SetUserData(m_UserData);
376 return obj;
377 }
378
379 wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon)
380 {
381 m_Icon = icon;
382 }
383
384 void
385 wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &coords,
386 wxLayoutList *wxllist,
387 CoordType begin, CoordType /* len */)
388 {
389 dc.DrawBitmap(*m_Icon, coords.x, coords.y-m_Icon->GetHeight(),
390 (m_Icon->GetMask() == NULL) ? FALSE : TRUE);
391 }
392
393 void
394 wxLayoutObjectIcon::Layout(wxDC & /* dc */, class wxLayoutList * )
395 {
396 }
397
398 wxPoint
399 wxLayoutObjectIcon::GetSize(CoordType *top, CoordType *bottom) const
400 {
401 *top = m_Icon->GetHeight();
402 *bottom = 0;
403 return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight());
404 }
405
406
407
408 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
409
410 wxLayoutObjectCmd
411
412 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
413
414
415 wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily,
416 int isize,
417 int istyle,
418 int iweight,
419 int iul,
420 wxColour *fg,
421 wxColour *bg)
422 {
423 family = ifamily; size = isize;
424 style = istyle; weight = iweight;
425 underline = iul != 0;
426 if(fg)
427 {
428 m_fg = *fg;
429 m_fg_valid = TRUE;
430 }
431 else
432 m_fg = *wxBLACK;
433 if(bg)
434 {
435 m_bg = *bg;
436 m_bg_valid = TRUE;
437 }
438 else
439 m_bg = *wxWHITE;
440 }
441
442 #define COPY_SI_(what) if(right.what != -1) what = right.what;
443
444 wxLayoutStyleInfo &
445 wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo &right)
446 {
447 COPY_SI_(family);
448 COPY_SI_(style);
449 COPY_SI_(size);
450 COPY_SI_(weight);
451 COPY_SI_(underline);
452 if(right.m_fg_valid) m_fg = right.m_fg;
453 if(right.m_bg_valid) m_bg = right.m_bg;
454 return *this;
455 }
456
457 wxLayoutObjectCmd::wxLayoutObjectCmd(int family, int size, int style, int
458 weight, int underline,
459 wxColour *fg, wxColour *bg)
460
461 {
462 m_StyleInfo = new wxLayoutStyleInfo(family, size,style,weight,underline,fg,bg);
463 }
464
465 wxLayoutObject *
466 wxLayoutObjectCmd::Copy(void)
467 {
468 wxLayoutObjectCmd *obj = new wxLayoutObjectCmd(
469 m_StyleInfo->size,
470 m_StyleInfo->family,
471 m_StyleInfo->style,
472 m_StyleInfo->weight,
473 m_StyleInfo->underline,
474 m_StyleInfo->m_fg_valid ?
475 &m_StyleInfo->m_fg : NULL,
476 m_StyleInfo->m_bg_valid ?
477 &m_StyleInfo->m_bg : NULL);
478 obj->SetUserData(m_UserData);
479 return obj;
480 }
481
482 void
483 wxLayoutObjectCmd::Write(wxString &ostr)
484 {
485 ostr << WXLO_TYPE_CMD << '\n'
486 << m_StyleInfo->size << '\n'
487 << m_StyleInfo->family << '\n'
488 << m_StyleInfo->style << '\n'
489 << m_StyleInfo->weight << '\n'
490 << m_StyleInfo->underline << '\n'
491 << m_StyleInfo->m_fg_valid << '\n'
492 << m_StyleInfo->m_bg_valid << '\n';
493 if(m_StyleInfo->m_fg_valid)
494 {
495 ostr << m_StyleInfo->m_fg.Red() << '\n'
496 << m_StyleInfo->m_fg.Green() << '\n'
497 << m_StyleInfo->m_fg.Blue() << '\n';
498 }
499 if(m_StyleInfo->m_bg_valid)
500 {
501 ostr << m_StyleInfo->m_bg.Red() << '\n'
502 << m_StyleInfo->m_bg.Green() << '\n'
503 << m_StyleInfo->m_bg.Blue() << '\n';
504 }
505 }
506 /* static */
507 wxLayoutObjectCmd *
508 wxLayoutObjectCmd::Read(wxString &istr)
509 {
510 wxLayoutObjectCmd *obj = new wxLayoutObjectCmd;
511
512 wxString tmp;
513 ReadString(tmp, istr);
514 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->size);
515 ReadString(tmp, istr);
516 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->family);
517 ReadString(tmp, istr);
518 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->style);
519 ReadString(tmp, istr);
520 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->weight);
521 ReadString(tmp, istr);
522 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->underline);
523 ReadString(tmp, istr);
524 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_fg_valid);
525 ReadString(tmp, istr);
526 sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_bg_valid);
527 if(obj->m_StyleInfo->m_fg_valid)
528 {
529 int red, green, blue;
530 ReadString(tmp, istr);
531 sscanf(tmp.c_str(),"%d", &red);
532 ReadString(tmp, istr);
533 sscanf(tmp.c_str(),"%d", &green);
534 ReadString(tmp, istr);
535 sscanf(tmp.c_str(),"%d", &blue);
536 obj->m_StyleInfo->m_fg = wxColour(red, green, blue);
537 }
538 if(obj->m_StyleInfo->m_bg_valid)
539 {
540 int red, green, blue;
541 ReadString(tmp, istr);
542 sscanf(tmp.c_str(),"%d", &red);
543 ReadString(tmp, istr);
544 sscanf(tmp.c_str(),"%d", &green);
545 ReadString(tmp, istr);
546 sscanf(tmp.c_str(),"%d", &blue);
547 obj->m_StyleInfo->m_bg = wxColour(red, green, blue);
548 }
549 return obj;
550 }
551
552
553 wxLayoutObjectCmd::~wxLayoutObjectCmd()
554 {
555 delete m_StyleInfo;
556 }
557
558 wxLayoutStyleInfo *
559 wxLayoutObjectCmd::GetStyle(void) const
560 {
561 return m_StyleInfo;
562 }
563
564 void
565 wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const & /* coords */,
566 wxLayoutList *wxllist,
567 CoordType begin, CoordType /* len */)
568 {
569 wxASSERT(m_StyleInfo);
570 wxllist->ApplyStyle(m_StyleInfo, dc);
571 }
572
573 void
574 wxLayoutObjectCmd::Layout(wxDC &dc, class wxLayoutList * llist)
575 {
576 // this get called, so that recalculation uses right font sizes
577 Draw(dc, wxPoint(0,0), llist);
578 }
579
580
581 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
582
583 The wxLayoutLine object
584
585 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
586
587 wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist)
588 {
589 m_LineNumber = 0;
590 m_Width = m_Height = 0;
591 m_Length = 0;
592 MarkDirty(0);
593 m_Previous = prev;
594 m_Next = NULL;
595 RecalculatePosition(llist);
596 if(m_Previous)
597 {
598 m_LineNumber = m_Previous->GetLineNumber()+1;
599 m_Next = m_Previous->GetNextLine();
600 m_Previous->m_Next = this;
601 }
602 if(m_Next)
603 {
604 m_Next->m_Previous = this;
605 m_Next->MoveLines(+1);
606 m_Next->RecalculatePositions(1,llist);
607 }
608 }
609
610 wxLayoutLine::~wxLayoutLine()
611 {
612 // kbList cleans itself
613 }
614
615 wxPoint
616 wxLayoutLine::RecalculatePosition(wxLayoutList *llist)
617 {
618 wxASSERT(m_Previous || GetLineNumber() == 0);
619
620 wxPoint posOld(m_Position);
621
622 if(m_Previous)
623 {
624 m_Position = m_Previous->GetPosition();
625 m_Position.y += m_Previous->GetHeight();
626 }
627 else
628 m_Position = wxPoint(0,0);
629
630 if ( m_Position != posOld )
631 {
632 // the whole line moved and must be repainted
633 llist->SetUpdateRect(m_Position);
634 llist->SetUpdateRect(m_Position.x + GetWidth() + MSW_CORRECTION,
635 m_Position.y + GetHeight() + MSW_CORRECTION);
636 llist->SetUpdateRect(posOld);
637 llist->SetUpdateRect(posOld.x + GetWidth() + MSW_CORRECTION,
638 posOld.y + GetHeight() + MSW_CORRECTION);
639 }
640
641 return m_Position;
642 }
643
644 void
645 wxLayoutLine::RecalculatePositions(int recurse, wxLayoutList *llist)
646 {
647 //FIXME: is this really needed? We run Layout() anyway.
648 // Recursing here, drives computation time up exponentially, as
649 // each line will cause all following lines to be recalculated.
650 // Yes, or linenumbers go wrong.
651
652 wxASSERT(recurse >= 0);
653 wxPoint pos = m_Position;
654 CoordType height = m_Height;
655
656 // WXLO_TRACE("RecalculatePositions()");
657 RecalculatePosition(llist);
658 if(m_Next)
659 {
660 if(recurse > 0)
661 m_Next->RecalculatePositions(--recurse, llist);
662 else if(pos != m_Position || m_Height != height)
663 m_Next->RecalculatePositions(0, llist);
664 }
665 }
666
667 wxLayoutObjectList::iterator
668 wxLayoutLine::FindObject(CoordType xpos, CoordType *offset) const
669 {
670 wxASSERT(xpos >= 0);
671 wxASSERT(offset);
672 wxLayoutObjectList::iterator
673 i,
674 found = NULLIT;
675 CoordType x = 0, len;
676
677 /* We search through the objects. As we don't like returning the
678 object that the cursor is behind, we just remember such an
679 object in "found" so we can return it if there is really no
680 further object following it. */
681 for(i = m_ObjectList.begin(); i != NULLIT; i++)
682 {
683 len = (**i).GetLength();
684 if( x <= xpos && xpos <= x + len )
685 {
686 *offset = xpos-x;
687 if(xpos == x + len) // is there another object behind?
688 found = i;
689 else // we are really inside this object
690 return i;
691 }
692 x += (**i).GetLength();
693 }
694 return found; // ==NULL if really none found
695 }
696
697 wxLayoutObjectList::iterator
698 wxLayoutLine::FindObjectScreen(wxDC &dc,
699 CoordType xpos, CoordType *cxpos,
700 bool *found) const
701 {
702 wxASSERT(cxpos);
703 wxASSERT(cxpos);
704 wxLayoutObjectList::iterator i;
705 CoordType x = 0, cx = 0, width;
706
707 for(i = m_ObjectList.begin(); i != NULLIT; i++)
708 {
709 width = (**i).GetWidth();
710 if( x <= xpos && xpos <= x + width )
711 {
712 *cxpos = cx + (**i).GetOffsetScreen(dc, xpos-x);
713 if(found) *found = true;
714 return i;
715 }
716 x += (**i).GetWidth();
717 cx += (**i).GetLength();
718 }
719 // behind last object:
720 *cxpos = cx;
721 if(found) *found = false;
722 return m_ObjectList.tail();
723 }
724
725 /** Finds text in this line.
726 @param needle the text to find
727 @param xpos the position where to start the search
728 @return the cursoor coord where it was found or -1
729 */
730 CoordType
731 wxLayoutLine::FindText(const wxString &needle, CoordType xpos) const
732 {
733 int
734 cpos = 0,
735 relpos = -1;
736 wxString const *text;
737
738 for(wxLOiterator i = m_ObjectList.begin(); i != m_ObjectList.end(); i++)
739 {
740 if(cpos >= xpos) // search from here!
741 {
742 if((**i).GetType() == WXLO_TYPE_TEXT)
743 {
744 text = & ((wxLayoutObjectText*)(*i))->GetText();
745 relpos = text->Find(needle);
746 if(relpos >= cpos-xpos) // -1 if not found
747 {
748 return cpos+relpos;
749 }
750 }
751 cpos += (**i).GetLength();
752 }
753 }
754 return -1; // not found
755 }
756
757 bool
758 wxLayoutLine::Insert(CoordType xpos, wxLayoutObject *obj)
759 {
760 wxASSERT(xpos >= 0);
761 wxASSERT(obj != NULL);
762
763 MarkDirty(xpos);
764
765 CoordType offset;
766 wxLOiterator i = FindObject(xpos, &offset);
767 if(i == NULLIT)
768 {
769 if(xpos == 0 ) // aha, empty line!
770 {
771 m_ObjectList.push_back(obj);
772 m_Length += obj->GetLength();
773 return true;
774 }
775 else
776 return false;
777 }
778
779 CoordType len = (**i).GetLength();
780 if(offset == 0 /*&& i != m_ObjectList.begin()*/) // why?
781 { // insert before this object
782 m_ObjectList.insert(i,obj);
783 m_Length += obj->GetLength();
784 return true;
785 }
786 if(offset == len )
787 {
788 if( i == m_ObjectList.tail()) // last object?
789 m_ObjectList.push_back(obj);
790 else
791 { // insert after current object
792 i++;
793 m_ObjectList.insert(i,obj);
794 }
795 m_Length += obj->GetLength();
796 return true;
797 }
798 /* Otherwise we need to split the current object.
799 Fortunately this can only be a text object. */
800 wxASSERT((**i).GetType() == WXLO_TYPE_TEXT);
801 wxString left, right;
802 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
803 left = tobj->GetText().substr(0,offset);
804 right = tobj->GetText().substr(offset,len-offset);
805 // current text object gets set to right half
806 tobj->GetText() = right; // set new text
807 // before it we insert the new object
808 m_ObjectList.insert(i,obj);
809 m_Length += obj->GetLength();
810 // and before that we insert the left half
811 m_ObjectList.insert(i,new wxLayoutObjectText(left));
812 return true;
813 }
814
815 bool
816 wxLayoutLine::Insert(CoordType xpos, const wxString& text)
817 {
818 wxASSERT(xpos >= 0);
819
820 MarkDirty(xpos);
821
822 CoordType offset;
823 wxLOiterator i = FindObject(xpos, &offset);
824 if(i != NULLIT && (**i).GetType() == WXLO_TYPE_TEXT)
825 {
826 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
827 tobj->GetText().insert(offset, text);
828 m_Length += text.Length();
829 }
830 else
831 {
832 if ( !Insert(xpos, new wxLayoutObjectText(text)) )
833 return false;
834 }
835
836 return true;
837 }
838
839 CoordType
840 wxLayoutLine::Delete(CoordType xpos, CoordType npos)
841 {
842 CoordType offset, len;
843
844 wxASSERT(xpos >= 0);
845 wxASSERT(npos >= 0);
846 MarkDirty(xpos);
847 wxLOiterator i = FindObject(xpos, &offset);
848 while(npos > 0)
849 {
850 if(i == NULLIT) return npos;
851 // now delete from that object:
852 if((**i).GetType() != WXLO_TYPE_TEXT)
853 {
854 if(offset != 0) // at end of line after a non-text object
855 return npos;
856 // always len == 1:
857 len = (**i).GetLength();
858 m_Length -= len;
859 npos -= len;
860 m_ObjectList.erase(i);
861 }
862 else
863 {
864 // tidy up: remove empty text objects
865 if((**i).GetLength() == 0)
866 {
867 m_ObjectList.erase(i);
868 continue;
869 }
870 // Text object:
871 CoordType max = (**i).GetLength() - offset;
872 if(npos < max) max = npos;
873 if(max == 0)
874 {
875 if(xpos == GetLength())
876 return npos;
877 else
878 { // at the end of an object
879 // move to begin of next object:
880 i++; offset = 0;
881 continue; // start over
882 }
883 }
884 npos -= max;
885 m_Length -= max;
886 if(offset == 0 && max == (**i).GetLength())
887 m_ObjectList.erase(i); // remove the whole object
888 else
889 ((wxLayoutObjectText *)(*i))->GetText().Remove(offset,max);
890 }
891 }
892
893 return npos;
894 }
895
896 bool
897 wxLayoutLine::DeleteWord(CoordType xpos)
898 {
899 wxASSERT(xpos >= 0);
900 CoordType offset;
901 MarkDirty(xpos);
902
903 wxLOiterator i = FindObject(xpos, &offset);
904
905 for(;;)
906 {
907 if(i == NULLIT) return false;
908 if((**i).GetType() != WXLO_TYPE_TEXT)
909 {
910 // This should only happen when at end of line, behind a non-text
911 // object:
912 if(offset == (**i).GetLength()) return false;
913 m_Length -= (**i).GetLength(); // -1
914 m_ObjectList.erase(i);
915 return true; // we are done
916 }
917 else
918 { // text object:
919 if(offset == (**i).GetLength()) // at end of object
920 {
921 i++; offset = 0;
922 continue;
923 }
924 wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
925 size_t count = 0;
926 wxString str = tobj->GetText();
927 str = str.substr(offset,str.Length()-offset);
928 // Find out how many positions we need to delete:
929 // 1. eat leading space
930 while(isspace(str.c_str()[count])) count++;
931 // 2. eat the word itself:
932 while(isalnum(str.c_str()[count])) count++;
933 // now delete it:
934 wxASSERT(count+offset <= (size_t) (**i).GetLength());
935 ((wxLayoutObjectText *)*i)->GetText().erase(offset,count);
936 m_Length -= count;
937 return true;
938 }
939 }
940
941 wxFAIL_MSG("unreachable");
942 }
943
944 wxLayoutLine *
945 wxLayoutLine::DeleteLine(bool update, wxLayoutList *llist)
946 {
947 if(m_Next) m_Next->m_Previous = m_Previous;
948 if(m_Previous) m_Previous->m_Next = m_Next;
949 if(update)
950 {
951 m_Next->MoveLines(-1);
952 m_Next->RecalculatePositions(1, llist);
953 }
954 wxLayoutLine *next = m_Next;
955 delete this;
956 return next;
957 }
958
959 void
960 wxLayoutLine::Draw(wxDC &dc,
961 wxLayoutList *llist,
962 const wxPoint & offset) const
963 {
964 wxLayoutObjectList::iterator i;
965 wxPoint pos = offset;
966 pos = pos + GetPosition();
967
968 pos.y += m_BaseLine;
969
970 CoordType xpos = 0; // cursorpos, lenght of line
971
972 CoordType from, to, tempto;
973 //FIXME This doesn't work yet, needs updating afterr default
974 //settings for list or a wxLayoutObjectCmd have changed:
975 //llist->ApplyStyle(&((wxLayoutLine *)this)->m_StyleInfo, dc);
976 int highlight = llist->IsSelected(this, &from, &to);
977 // WXLO_DEBUG(("highlight=%d", highlight ));
978 if(highlight == 1) // we need to draw the whole line inverted!
979 llist->StartHighlighting(dc);
980 else
981 llist->EndHighlighting(dc);
982
983 for(i = m_ObjectList.begin(); i != NULLIT; i++)
984 {
985 if(highlight == -1) // partially highlight line
986 {
987 // parts of the line need highlighting
988 tempto = xpos+(**i).GetLength();
989 (**i).Draw(dc, pos, llist, from-xpos, to-xpos);
990 }
991 else
992 (**i).Draw(dc, pos, llist);
993 pos.x += (**i).GetWidth();
994 xpos += (**i).GetLength();
995 }
996 }
997
998 void
999 wxLayoutLine::Layout(wxDC &dc,
1000 wxLayoutList *llist,
1001 wxPoint *cursorPos,
1002 wxPoint *cursorSize,
1003 int cx)
1004 {
1005 wxLayoutObjectList::iterator i;
1006
1007 // when a line becomes dirty, we redraw it from the place where it was
1008 // changed till the end of line (because the following wxLayoutObjects are
1009 // moved when the preceding one changes) - calculate the update rectangle.
1010 CoordType updateTop = m_Position.y,
1011 updateLeft = -1,
1012 updateWidth = m_Width,
1013 updateHeight = m_Height;
1014
1015 CoordType
1016 topHeight = 0,
1017 bottomHeight = 0; // above and below baseline
1018 CoordType
1019 objTopHeight, objBottomHeight; // above and below baseline
1020 CoordType
1021 len, count = 0;
1022
1023 CoordType heightOld = m_Height;
1024
1025 m_Height = 0;
1026 m_Width = 0;
1027 m_BaseLine = 0;
1028
1029 bool cursorFound = false;
1030
1031 if(cursorPos)
1032 {
1033 *cursorPos = m_Position;
1034 if(cursorSize) *cursorSize = wxPoint(0,0);
1035 }
1036
1037 //FIXME This doesn't work yet, needs updating afterr default
1038 //settings for list or a wxLayoutObjectCmd have changed:
1039 //llist->ApplyStyle(&m_StyleInfo, dc);
1040 for(i = m_ObjectList.begin(); i != NULLIT; i++)
1041 {
1042 wxLayoutObject *obj = *i;
1043 obj->Layout(dc, llist);
1044 wxPoint sizeObj = obj->GetSize(&objTopHeight, &objBottomHeight);
1045
1046 if(cursorPos && ! cursorFound)
1047 {
1048 // we need to check whether the text cursor is here
1049 len = obj->GetLength();
1050 if(count <= cx && count+len > cx)
1051 {
1052 if(obj->GetType() == WXLO_TYPE_TEXT)
1053 {
1054 len = cx - count; // pos in object
1055 CoordType width, height, descent;
1056 dc.GetTextExtent((*(wxLayoutObjectText*)*i).GetText().substr(0,len),
1057 &width, &height, &descent);
1058 cursorPos->x += width;
1059 cursorPos->y = m_Position.y;
1060 wxString str;
1061 if(len < obj->GetLength())
1062 str = (*(wxLayoutObjectText*)*i).GetText().substr(len,1);
1063 else
1064 str = WXLO_CURSORCHAR;
1065 dc.GetTextExtent(str, &width, &height, &descent);
1066 wxASSERT(cursorSize);
1067 // Just in case some joker inserted an empty string object:
1068 if(width == 0) width = WXLO_MINIMUM_CURSOR_WIDTH;
1069 if(height == 0) height = sizeObj.y;
1070 cursorSize->x = width;
1071 cursorSize->y = height;
1072 cursorFound = true; // no more checks
1073 }
1074 else
1075 {
1076 // on some other object
1077 CoordType top, bottom; // unused
1078 *cursorSize = obj->GetSize(&top,&bottom);
1079 cursorPos->y = m_Position.y;
1080 cursorFound = true; // no more checks
1081 }
1082 }
1083 else
1084 {
1085 count += len;
1086 cursorPos->x += obj->GetWidth();
1087 }
1088 } // cursor finding
1089
1090 m_Width += sizeObj.x;
1091 if(sizeObj.y > m_Height)
1092 {
1093 m_Height = sizeObj.y;
1094 }
1095
1096 if(objTopHeight > topHeight)
1097 topHeight = objTopHeight;
1098 if(objBottomHeight > bottomHeight)
1099 bottomHeight = objBottomHeight;
1100 }
1101
1102 if ( IsDirty() )
1103 {
1104 if ( updateHeight < m_Height )
1105 updateHeight = m_Height;
1106 if ( updateWidth < m_Width )
1107 updateWidth = m_Width;
1108
1109 // update all line if we don't know where to start from
1110 if ( updateLeft == -1 )
1111 updateLeft = 0;
1112
1113 llist->SetUpdateRect(updateLeft, updateTop);
1114 llist->SetUpdateRect(updateLeft + updateWidth + MSW_CORRECTION,
1115 updateTop + updateHeight + MSW_CORRECTION);
1116 }
1117
1118 if(topHeight + bottomHeight > m_Height)
1119 {
1120 m_Height = topHeight+bottomHeight;
1121 }
1122
1123 m_BaseLine = topHeight;
1124
1125 if(m_Height == 0)
1126 {
1127 CoordType width, height, descent;
1128 dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
1129 m_Height = height;
1130 m_BaseLine = m_Height - descent;
1131 }
1132
1133 // tell next line about coordinate change
1134 if(m_Next && m_Height != heightOld)
1135 {
1136 // FIXME isn't this done in RecalculatePositions() below anyhow?
1137 m_Next->RecalculatePositions(0, llist);
1138 }
1139
1140 // We need to check whether we found a valid cursor size:
1141 if(cursorPos)
1142 {
1143 // this might be the case if the cursor is at the end of the
1144 // line or on a command object:
1145 if(cursorSize->y < WXLO_MINIMUM_CURSOR_WIDTH)
1146 {
1147 CoordType width, height, descent;
1148 dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
1149 cursorSize->x = width;
1150 cursorSize->y = height;
1151 }
1152 if(m_BaseLine >= cursorSize->y) // the normal case anyway
1153 cursorPos->y += m_BaseLine-cursorSize->y;
1154 }
1155
1156 RecalculatePositions(1, llist);
1157
1158 MarkClean();
1159 }
1160
1161
1162 wxLayoutLine *
1163 wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist)
1164 {
1165 wxASSERT(xpos >= 0);
1166
1167 MarkDirty(xpos);
1168
1169 /* If we are at the begin of a line, we want to move all other
1170 lines down and stay with the cursor where we are. However, if we
1171 are in an empty line, we want to move down with it. */
1172 if(xpos == 0 && GetLength() > 0)
1173 { // insert an empty line before this one
1174 wxLayoutLine *prev = new wxLayoutLine(m_Previous, llist);
1175 if(m_Previous == NULL)
1176 { // We were in first line, need to link in new empty line
1177 // before this.
1178 prev->m_Next = this;
1179 m_Previous = prev;
1180 m_Previous->m_Height = 0; // this is a wild guess
1181 }
1182 if(m_Next)
1183 m_Next->RecalculatePositions(1, llist);
1184 return m_Previous;
1185 }
1186
1187 CoordType offset;
1188 wxLOiterator i = FindObject(xpos, &offset);
1189 if(i == NULLIT)
1190 // must be at the end of the line then
1191 return new wxLayoutLine(this, llist);
1192 // split this line:
1193
1194 wxLayoutLine *newLine = new wxLayoutLine(this, llist);
1195 // split object at i:
1196 if((**i).GetType() == WXLO_TYPE_TEXT && offset != 0)
1197 {
1198 wxString left, right;
1199 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
1200 left = tobj->GetText().substr(0,offset);
1201 right = tobj->GetText().substr(offset,tobj->GetLength()-offset);
1202 // current text object gets set to left half
1203 tobj->GetText() = left; // set new text
1204 newLine->Append(new wxLayoutObjectText(right));
1205 m_Length -= right.Length();
1206 i++; // don't move this object to the new list
1207 }
1208 else
1209 {
1210 if(offset > 0)
1211 i++; // move objects from here to new list
1212 }
1213
1214 while(i != m_ObjectList.end())
1215 {
1216 wxLayoutObject *obj = *i;
1217 newLine->Append(obj);
1218 m_Length -= obj->GetLength();
1219
1220 m_ObjectList.remove(i); // remove without deleting it
1221 }
1222 if(m_Next)
1223 m_Next->RecalculatePositions(2, llist);
1224 return newLine;
1225 }
1226
1227
1228 void
1229 wxLayoutLine::MergeNextLine(wxLayoutList *llist)
1230 {
1231 wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge");
1232 wxLayoutObjectList &list = GetNextLine()->m_ObjectList;
1233 wxLOiterator i;
1234
1235 MarkDirty(GetWidth());
1236
1237 wxLayoutObject *last = NULL;
1238 for(i = list.begin(); i != list.end();)
1239 {
1240 wxLayoutObject *current = *i;
1241
1242 // merge text objects together for efficiency
1243 if ( last && last->GetType() == WXLO_TYPE_TEXT &&
1244 current->GetType() == WXLO_TYPE_TEXT )
1245 {
1246 wxLayoutObjectText *textObj = (wxLayoutObjectText *)last;
1247 wxString text(textObj->GetText());
1248 text += ((wxLayoutObjectText *)current)->GetText();
1249 textObj->SetText(text);
1250
1251 list.erase(i); // remove and delete it
1252 }
1253 else
1254 {
1255 // just append the object "as was"
1256 Append(current);
1257
1258 list.remove(i); // remove without deleting it
1259 }
1260 }
1261 wxASSERT(list.empty());
1262
1263 wxLayoutLine *oldnext = GetNextLine();
1264 wxLayoutLine *nextLine = oldnext->GetNextLine();
1265 SetNext(nextLine);
1266 if ( nextLine )
1267 {
1268 nextLine->MoveLines(-1);
1269 }
1270 else
1271 {
1272 // this is now done in Delete(), but if this function is ever called
1273 // from elsewhere, we might have to move refresh code back here (in
1274 // order not to duplicate it)
1275 #if 0
1276 wxPoint pos(oldnext->GetPosition());
1277 llist->SetUpdateRect(pos);
1278 llist->SetUpdateRect(pos.x + oldnext->GetWidth() + MSW_CORRECTION,
1279 pos.y + oldnext->GetHeight() + MSW_CORRECTION);
1280 #endif // 0
1281 }
1282
1283 delete oldnext;
1284 }
1285
1286 CoordType
1287 wxLayoutLine::GetWrapPosition(CoordType column)
1288 {
1289 CoordType offset;
1290 wxLOiterator i = FindObject(column, &offset);
1291 if(i == NULLIT) return -1; // cannot wrap
1292
1293 // go backwards through the list and look for space in text objects
1294 do
1295 {
1296 if((**i).GetType() == WXLO_TYPE_TEXT)
1297 {
1298 do
1299 {
1300 if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset]))
1301 return column;
1302 else
1303 {
1304 offset--;
1305 column--;
1306 }
1307 }while(offset != -1);
1308 i--; // move on to previous object
1309 }
1310 else
1311 {
1312 column -= (**i).GetLength();
1313 i--;
1314 }
1315 if( i != NULLIT)
1316 offset = (**i).GetLength();
1317 }while(i != NULLIT);
1318 /* If we reached the begin of the list and have more than one
1319 object, that one is longer than the margin, so break behind
1320 it. */
1321 CoordType pos = 0;
1322 i = m_ObjectList.begin();
1323 while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
1324 {
1325 pos += (**i).GetLength();
1326 i++;
1327 }
1328 if(i == NULLIT) return -1; //why should this happen?
1329 pos += (**i).GetLength();
1330 i++;
1331 while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
1332 {
1333 pos += (**i).GetLength();
1334 i++;
1335 }
1336 if(i == NULLIT) return -1; //this is possible, if there is only one text object
1337 // now we are at the second text object:
1338 pos -= (**i).GetLength();
1339 return pos; // in front of it
1340 }
1341
1342
1343 #ifdef WXLAYOUT_DEBUG
1344 void
1345 wxLayoutLine::Debug(void)
1346 {
1347 wxString tmp;
1348 wxPoint pos = GetPosition();
1349 WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld",
1350 (long int) GetLineNumber(),
1351 (long int) pos.x, (long int) pos.y,
1352 (long int) GetHeight()));
1353 if(m_ObjectList.begin() != NULLIT)
1354 (**m_ObjectList.begin()).Debug();
1355
1356 }
1357 #endif
1358
1359 void
1360 wxLayoutLine::Copy(wxLayoutList *llist,
1361 CoordType from,
1362 CoordType to)
1363 {
1364 CoordType firstOffset, lastOffset;
1365
1366 if(to == -1) to = GetLength();
1367 if(from == to) return;
1368
1369 wxLOiterator first = FindObject(from, &firstOffset);
1370 wxLOiterator last = FindObject(to, &lastOffset);
1371
1372 // Common special case: only one object
1373 if( first != NULLIT && last != NULLIT && *first == *last )
1374 {
1375 if( (**first).GetType() == WXLO_TYPE_TEXT )
1376 {
1377 llist->Insert(new wxLayoutObjectText(
1378 ((wxLayoutObjectText
1379 *)*first)->GetText().substr(firstOffset,
1380 lastOffset-firstOffset))
1381 );
1382 return;
1383 }
1384 else // what can we do?
1385 {
1386 if(lastOffset > firstOffset) // i.e. +1 :-)
1387 llist->Insert( (**first).Copy() );
1388 return;
1389 }
1390 }
1391
1392 // If we reach here, we can safely copy the whole first object from
1393 // the firstOffset position on:
1394 if((**first).GetType() == WXLO_TYPE_TEXT && firstOffset != 0)
1395 {
1396 llist->Insert(new wxLayoutObjectText(
1397 ((wxLayoutObjectText *)*first)->GetText().substr(firstOffset))
1398 );
1399 }
1400 else if(firstOffset == 0)
1401 llist->Insert( (**first).Copy() );
1402 // else nothing to copy :-(
1403
1404 // Now we copy all objects before the last one:
1405 wxLOiterator i = first; i++;
1406 for( ; i != last; i++)
1407 llist->Insert( (**i).Copy() );
1408
1409 // And now the last object:
1410 if(lastOffset != 0)
1411 {
1412 if( (**last).GetType() == WXLO_TYPE_TEXT )
1413 {
1414 llist->Insert(new wxLayoutObjectText(
1415 ((wxLayoutObjectText *)*last)->GetText().substr(0,lastOffset))
1416 );
1417 }
1418 else
1419 llist->Insert( (**last).Copy() );
1420 }
1421 }
1422
1423
1424 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1425
1426 The wxLayoutList object
1427
1428 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1429
1430 wxLayoutList::wxLayoutList()
1431 {
1432 #ifdef WXLAYOUT_USE_CARET
1433 m_caret = NULL;
1434 #endif // WXLAYOUT_USE_CARET
1435
1436 m_FirstLine = NULL;
1437 InvalidateUpdateRect();
1438 Clear();
1439 }
1440
1441 wxLayoutList::~wxLayoutList()
1442 {
1443 InternalClear();
1444 m_FirstLine->DeleteLine(false, this);
1445 }
1446
1447 void
1448 wxLayoutList::Empty(void)
1449 {
1450 while(m_FirstLine)
1451 m_FirstLine = m_FirstLine->DeleteLine(false, this);
1452
1453 m_CursorPos = wxPoint(0,0);
1454 m_CursorScreenPos = wxPoint(0,0);
1455 m_CursorSize = wxPoint(0,0);
1456 m_FirstLine = new wxLayoutLine(NULL, this); // empty first line
1457 m_CursorLine = m_FirstLine;
1458 InvalidateUpdateRect();
1459 }
1460
1461
1462 void
1463 wxLayoutList::InternalClear(void)
1464 {
1465 Empty();
1466 m_Selection.m_selecting = false;
1467 m_Selection.m_valid = false;
1468
1469 m_DefaultSetting.family = wxSWISS;
1470 m_DefaultSetting.size = WXLO_DEFAULTFONTSIZE;
1471 m_DefaultSetting.style = wxNORMAL;
1472 m_DefaultSetting.weight = wxNORMAL;
1473 m_DefaultSetting.underline = 0;
1474 m_DefaultSetting.m_fg_valid = TRUE;
1475 m_DefaultSetting.m_fg = *wxBLACK;
1476 m_DefaultSetting.m_bg_valid = TRUE;
1477 m_DefaultSetting.m_bg = *wxWHITE;
1478
1479 m_CurrentSetting = m_DefaultSetting;
1480 }
1481
1482 void
1483 wxLayoutList::SetFont(int family, int size, int style, int weight,
1484 int underline, wxColour *fg,
1485 wxColour *bg)
1486 {
1487 if(family != -1) m_CurrentSetting.family = family;
1488 if(size != -1) m_CurrentSetting.size = size;
1489 if(style != -1) m_CurrentSetting.style = style;
1490 if(weight != -1) m_CurrentSetting.weight = weight;
1491 if(underline != -1) m_CurrentSetting.underline = underline != 0;
1492 if(fg) m_CurrentSetting.m_fg = *fg;
1493 if(bg) m_CurrentSetting.m_bg = *bg;
1494 Insert(
1495 new wxLayoutObjectCmd(
1496 m_CurrentSetting.family,
1497 m_CurrentSetting.size,
1498 m_CurrentSetting.style,
1499 m_CurrentSetting.weight,
1500 m_CurrentSetting.underline,
1501 fg, bg));
1502 }
1503
1504 void
1505 wxLayoutList::SetFont(int family, int size, int style, int weight,
1506 int underline, char const *fg, char const *bg)
1507
1508 {
1509 wxColour
1510 *cfg = NULL,
1511 *cbg = NULL;
1512
1513 if( fg )
1514 cfg = wxTheColourDatabase->FindColour(fg);
1515 if( bg )
1516 cbg = wxTheColourDatabase->FindColour(bg);
1517
1518 SetFont(family,size,style,weight,underline,cfg,cbg);
1519 }
1520
1521 void
1522 wxLayoutList::Clear(int family, int size, int style, int weight,
1523 int underline, wxColour *fg, wxColour *bg)
1524 {
1525 InternalClear();
1526 m_DefaultSetting = wxLayoutStyleInfo(family, size, style, weight,
1527 underline, fg, bg);
1528 m_CurrentSetting = m_DefaultSetting;
1529 }
1530
1531 wxPoint
1532 wxLayoutList::FindText(const wxString &needle, const wxPoint &cpos) const
1533 {
1534 int xpos;
1535
1536 wxLayoutLine *line;
1537 for(line = m_FirstLine;
1538 line;
1539 line = line->GetNextLine())
1540 {
1541 if(line->GetLineNumber() >= cpos.y)
1542 {
1543 xpos = line->FindText(needle,
1544 (line->GetLineNumber() == cpos.y) ?
1545 cpos.x : 0);
1546 if(xpos != -1)
1547 return wxPoint(xpos, line->GetLineNumber());
1548 }
1549 }
1550 return wxPoint(-1,-1);
1551 }
1552
1553
1554 bool
1555 wxLayoutList::MoveCursorTo(wxPoint const &p)
1556 {
1557 AddCursorPosToUpdateRect();
1558
1559 wxLayoutLine *line = m_FirstLine;
1560 while(line && line->GetLineNumber() != p.y)
1561 line = line->GetNextLine();
1562 if(line && line->GetLineNumber() == p.y) // found it
1563 {
1564 m_CursorPos.y = p.y;
1565 m_CursorLine = line;
1566 CoordType len = line->GetLength();
1567 if(len >= p.x)
1568 {
1569 m_CursorPos.x = p.x;
1570 return true;
1571 }
1572 else
1573 {
1574 m_CursorPos.x = len;
1575 return false;
1576 }
1577 }
1578 return false;
1579 }
1580
1581 bool
1582 wxLayoutList::MoveCursorVertically(int n)
1583 {
1584 AddCursorPosToUpdateRect();
1585
1586 bool rc;
1587 if(n < 0) // move up
1588 {
1589 if(m_CursorLine == m_FirstLine) return false;
1590 while(n < 0 && m_CursorLine)
1591 {
1592 m_CursorLine = m_CursorLine->GetPreviousLine();
1593 m_CursorPos.y--;
1594 n++;
1595 }
1596 if(! m_CursorLine)
1597 {
1598 m_CursorLine = m_FirstLine;
1599 m_CursorPos.y = 0;
1600 rc = false;
1601 }
1602 else
1603 {
1604 if(m_CursorPos.x > m_CursorLine->GetLength())
1605 m_CursorPos.x = m_CursorLine->GetLength();
1606 rc = true;
1607 }
1608 }
1609 else // move down
1610 {
1611 wxLayoutLine *last = m_CursorLine;
1612 if(! m_CursorLine->GetNextLine()) return false;
1613 while(n > 0 && m_CursorLine)
1614 {
1615 n--;
1616 m_CursorPos.y ++;
1617 m_CursorLine = m_CursorLine->GetNextLine();
1618 }
1619 if(! m_CursorLine)
1620 {
1621 m_CursorLine = last;
1622 m_CursorPos.y ++;
1623 rc = false;
1624 }
1625 else
1626 {
1627 if(m_CursorPos.x > m_CursorLine->GetLength())
1628 m_CursorPos.x = m_CursorLine->GetLength();
1629 rc = true;
1630 }
1631 }
1632 return rc;
1633 }
1634
1635 bool
1636 wxLayoutList::MoveCursorHorizontally(int n)
1637 {
1638 AddCursorPosToUpdateRect();
1639
1640 int move;
1641 while(n < 0)
1642 {
1643 if(m_CursorPos.x == 0) // at begin of line
1644 {
1645 if(! MoveCursorVertically(-1))
1646 break;
1647 MoveCursorToEndOfLine();
1648 n++;
1649 continue;
1650 }
1651 move = -n;
1652 if(move > m_CursorPos.x) move = m_CursorPos.x;
1653 m_CursorPos.x -= move; n += move;
1654 }
1655
1656 while(n > 0)
1657 {
1658 int len = m_CursorLine->GetLength();
1659 if(m_CursorPos.x == len) // at end of line
1660 {
1661 if(! MoveCursorVertically(1))
1662 break;
1663 MoveCursorToBeginOfLine();
1664 n--;
1665 continue;
1666 }
1667 move = n;
1668 if( move >= len-m_CursorPos.x) move = len-m_CursorPos.x;
1669 m_CursorPos.x += move;
1670 n -= move;
1671 }
1672 return n == 0;
1673 }
1674
1675 bool
1676 wxLayoutList::MoveCursorWord(int n)
1677 {
1678 wxCHECK_MSG( m_CursorLine, false, "no current line" );
1679 wxCHECK_MSG( n == -1 || n == +1, false, "not implemented yet" );
1680
1681 CoordType moveDistance = 0;
1682 CoordType offset;
1683 for ( wxLOiterator i = m_CursorLine->FindObject(m_CursorPos.x, &offset);
1684 n != 0;
1685 n > 0 ? i++ : i-- )
1686 {
1687 if ( i == NULLIT )
1688 return false;
1689
1690 wxLayoutObject *obj = *i;
1691 if( obj->GetType() != WXLO_TYPE_TEXT )
1692 {
1693 // any non text objects count as one word
1694 n > 0 ? n-- : n++;
1695
1696 moveDistance += obj->GetLength();
1697 }
1698 else
1699 {
1700 // text object
1701 wxLayoutObjectText *tobj = (wxLayoutObjectText *)obj;
1702
1703 if ( offset == tobj->GetLength() )
1704 {
1705 // at end of object
1706 n > 0 ? n-- : n++;
1707 }
1708 else
1709 {
1710 const char *start = tobj->GetText().c_str();
1711 const char *p = start + offset;
1712
1713 // to the beginning/end of the next/prev word
1714 while ( isspace(*p) )
1715 {
1716 n > 0 ? p++ : p--;
1717 }
1718
1719 // go to the end/beginning of the word (in a broad sense...)
1720 while ( p >= start && !isspace(*p) )
1721 {
1722 n > 0 ? p++ : p--;
1723 }
1724
1725 if ( n > 0 )
1726 {
1727 // now advance to the beginning of the next word
1728 while ( isspace(*p) )
1729 p++;
1730 }
1731
1732 n > 0 ? n-- : n++;
1733
1734 moveDistance = p - start - offset;
1735 }
1736 }
1737
1738 // except for the first iteration, offset is 0
1739 offset = 0;
1740 }
1741
1742 MoveCursorHorizontally(moveDistance);
1743
1744 return true;
1745 }
1746
1747 bool
1748 wxLayoutList::Insert(wxString const &text)
1749 {
1750 wxASSERT(m_CursorLine);
1751 wxASSERT_MSG( text.Find('\n') == wxNOT_FOUND, "use wxLayoutImportText!" );
1752
1753 if ( !text )
1754 return true;
1755
1756 AddCursorPosToUpdateRect();
1757
1758 if ( !m_CursorLine->Insert(m_CursorPos.x, text) )
1759 return false;
1760
1761 m_CursorPos.x += text.Length();
1762
1763 m_CursorLine->RecalculatePositions(0, this);
1764
1765 return true;
1766 }
1767
1768 bool
1769 wxLayoutList::Insert(wxLayoutObject *obj)
1770 {
1771 wxASSERT(m_CursorLine);
1772
1773 if(! m_CursorLine)
1774 m_CursorLine = GetFirstLine();
1775
1776 AddCursorPosToUpdateRect();
1777
1778 m_CursorLine->Insert(m_CursorPos.x, obj);
1779 m_CursorPos.x += obj->GetLength();
1780
1781 m_CursorLine->RecalculatePositions(0, this);
1782
1783 return true;
1784 }
1785
1786 bool
1787 wxLayoutList::Insert(wxLayoutList *llist)
1788 {
1789 wxASSERT(llist);
1790 bool rc = TRUE;
1791
1792 for(wxLayoutLine *line = llist->GetFirstLine();
1793 line;
1794 line = line->GetNextLine()
1795 )
1796 {
1797 for(wxLOiterator i = line->GetFirstObject();
1798 i != NULLIT;
1799 i++)
1800 rc |= Insert(*i);
1801 LineBreak();
1802 }
1803 return rc;
1804 }
1805
1806 bool
1807 wxLayoutList::LineBreak(void)
1808 {
1809 wxASSERT(m_CursorLine);
1810 bool setFirst = (m_CursorLine == m_FirstLine && m_CursorPos.x == 0);
1811
1812 AddCursorPosToUpdateRect();
1813
1814 wxPoint position(m_CursorLine->GetPosition());
1815
1816 wxCoord width = m_CursorLine->GetWidth(),
1817 height = m_CursorLine->GetHeight();
1818
1819 m_CursorLine = m_CursorLine->Break(m_CursorPos.x, this);
1820 if(setFirst) // we were at beginning of first line
1821 m_FirstLine = m_CursorLine->GetPreviousLine();
1822 if(m_CursorPos.x != 0)
1823 m_CursorPos.y++;
1824 m_CursorPos.x = 0;
1825 // doesn't help m_CursorLine.MarkDirty();
1826
1827 wxLayoutLine *prev = m_CursorLine->GetPreviousLine();
1828 wxCHECK_MSG(prev, false, "just broke the line, where is the previous one?");
1829
1830 height += prev->GetHeight();
1831
1832 SetUpdateRect(position);
1833 SetUpdateRect(position.x + width + MSW_CORRECTION,
1834 position.y + height + MSW_CORRECTION);
1835
1836 return true;
1837 }
1838
1839 bool
1840 wxLayoutList::WrapLine(CoordType column)
1841 {
1842 if(m_CursorPos.x <= column || column < 1)
1843 return false; // do nothing yet
1844 else
1845 {
1846 CoordType xpos = m_CursorLine->GetWrapPosition(column);
1847 if(xpos == -1)
1848 return false; // cannot break line
1849 //else:
1850 CoordType newpos = m_CursorPos.x - xpos - 1;
1851 m_CursorPos.x = xpos;
1852
1853 AddCursorPosToUpdateRect();
1854
1855 LineBreak();
1856 Delete(1); // delete the space
1857 m_CursorPos.x = newpos;
1858
1859 m_CursorLine->RecalculatePositions(1, this);
1860
1861 return true;
1862 }
1863 }
1864
1865 bool
1866 wxLayoutList::Delete(CoordType npos)
1867 {
1868 wxCHECK_MSG(m_CursorLine, false, "can't delete in non existing line");
1869 wxASSERT_MSG(npos > 0, "nothing to delete?");
1870
1871 AddCursorPosToUpdateRect();
1872
1873 // were other lines appended to this one (this is important to know because
1874 // this means that our width _increased_ as the result of deletion)
1875 bool wasMerged = false;
1876
1877 // the size of the region to update
1878 CoordType totalHeight = m_CursorLine->GetHeight(),
1879 totalWidth = m_CursorLine->GetWidth();
1880
1881 CoordType left;
1882 do
1883 {
1884 left = m_CursorLine->Delete(m_CursorPos.x, npos);
1885
1886 if( left > 0 )
1887 {
1888 // More to delete, continue on next line.
1889
1890 // First, check if line is empty:
1891 if(m_CursorLine->GetLength() == 0)
1892 {
1893 // in this case, updating could probably be optimised
1894 #ifdef WXLO_DEBUG
1895 wxASSERT(DeleteLines(1) == 0);
1896 #else
1897 DeleteLines(1);
1898 #endif
1899
1900 left--;
1901 }
1902 else
1903 {
1904 // Need to join next line
1905 if(! m_CursorLine->GetNextLine())
1906 break; // cannot
1907 else
1908 {
1909 wasMerged = true;
1910 wxLayoutLine *next = m_CursorLine->GetNextLine();
1911 if ( next )
1912 {
1913 totalHeight += next->GetHeight();
1914 totalWidth += next->GetWidth();
1915
1916 m_CursorLine->MergeNextLine(this);
1917 left--;
1918 }
1919 else
1920 {
1921 wxFAIL_MSG("can't delete all this");
1922
1923 return false;
1924 }
1925 }
1926 }
1927 }
1928 }
1929 while ( left> 0 );
1930
1931 // we need to update the whole tail of the line and the lines which
1932 // disappeared
1933 if ( wasMerged )
1934 {
1935 wxPoint position(m_CursorLine->GetPosition());
1936 SetUpdateRect(position);
1937 SetUpdateRect(position.x + totalWidth + MSW_CORRECTION,
1938 position.y + totalHeight + MSW_CORRECTION);
1939 }
1940
1941 return left == 0;
1942 }
1943
1944 int
1945 wxLayoutList::DeleteLines(int n)
1946 {
1947 wxASSERT(m_CursorLine);
1948 wxLayoutLine *line;
1949
1950 AddCursorPosToUpdateRect();
1951
1952 while(n > 0)
1953 {
1954 if(!m_CursorLine->GetNextLine())
1955 { // we cannot delete this line, but we can clear it
1956 MoveCursorToBeginOfLine();
1957 DeleteToEndOfLine();
1958 m_CursorLine->RecalculatePositions(2, this);
1959 return n-1;
1960 }
1961 //else:
1962 line = m_CursorLine;
1963 m_CursorLine = m_CursorLine->DeleteLine(true, this);
1964 n--;
1965 if(line == m_FirstLine) m_FirstLine = m_CursorLine;
1966 wxASSERT(m_FirstLine);
1967 wxASSERT(m_CursorLine);
1968 }
1969 m_CursorLine->RecalculatePositions(2, this);
1970 return n;
1971 }
1972
1973 void
1974 wxLayoutList::Recalculate(wxDC &dc, CoordType bottom)
1975 {
1976 wxLayoutLine *line = m_FirstLine;
1977
1978 // first, make sure everything is calculated - this might not be
1979 // needed, optimise it later
1980 ApplyStyle(&m_DefaultSetting, dc);
1981 while(line)
1982 {
1983 line->RecalculatePosition(this); // so we don't need to do it all the time
1984 // little condition to speed up redrawing:
1985 if(bottom != -1 && line->GetPosition().y > bottom) break;
1986 line = line->GetNextLine();
1987 }
1988 }
1989
1990 void
1991 wxLayoutList::UpdateCursorScreenPos(wxDC &dc)
1992 {
1993 wxASSERT(m_CursorLine);
1994 m_CursorLine->Layout(dc, this, (wxPoint *)&m_CursorScreenPos, (wxPoint *)&m_CursorSize, m_CursorPos.x);
1995 }
1996
1997 wxPoint
1998 wxLayoutList::GetCursorScreenPos(wxDC &dc)
1999 {
2000 UpdateCursorScreenPos(dc);
2001 return m_CursorScreenPos;
2002 }
2003
2004 /*
2005 Is called before each Draw(). Now, it will re-layout all lines which
2006 have changed.
2007 */
2008 void
2009 wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll)
2010 {
2011 // first, make sure everything is calculated - this might not be
2012 // needed, optimise it later
2013 ApplyStyle(&m_DefaultSetting, dc);
2014
2015 // FIXME this is completely wrong - we should start by first *visible* line
2016 // (and stop on the last one) instead of looping over all lines!!
2017 wxLayoutLine *line = m_FirstLine;
2018 while(line)
2019 {
2020 if(forceAll || line->IsDirty())
2021 {
2022 line->GetStyleInfo() = m_CurrentSetting;
2023 if(line == m_CursorLine)
2024 line->Layout(dc, this, (wxPoint *)&m_CursorScreenPos,
2025 (wxPoint *)&m_CursorSize, m_CursorPos.x);
2026 else
2027 line->Layout(dc, this);
2028
2029 // little condition to speed up redrawing:
2030 if(bottom != -1 && line->GetPosition().y > bottom)
2031 break;
2032 }
2033
2034 line->RecalculatePositions(1, this);
2035 line = line->GetNextLine();
2036 }
2037
2038 ///FIXME: disabled for now
2039 #if 0
2040 // can only be 0 if we are on the first line and have no next line
2041 wxASSERT(m_CursorSize.x != 0 || (m_CursorLine &&
2042 m_CursorLine->GetNextLine() == NULL &&
2043 m_CursorLine == m_FirstLine));
2044 #endif
2045 AddCursorPosToUpdateRect();
2046 }
2047
2048 void
2049 wxLayoutList::Draw(wxDC &dc,
2050 wxPoint const &offset,
2051 CoordType top,
2052 CoordType bottom)
2053 {
2054 wxLayoutLine *line = m_FirstLine;
2055
2056 Layout(dc);
2057 ApplyStyle(&m_DefaultSetting, dc);
2058 wxBrush brush(m_CurrentSetting.m_bg, wxSOLID);
2059 dc.SetBrush(brush);
2060 dc.SetBackgroundMode(wxTRANSPARENT);
2061
2062 while(line)
2063 {
2064 // only draw if between top and bottom:
2065 if((top == -1 || line->GetPosition().y + line->GetHeight() >= top))
2066 line->Draw(dc, this, offset);
2067 else
2068 line->Layout(dc, this);
2069 // little condition to speed up redrawing:
2070 if(bottom != -1 && line->GetPosition().y > bottom) break;
2071 line = line->GetNextLine();
2072 }
2073 InvalidateUpdateRect();
2074
2075 WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
2076 m_Selection.m_valid ? "valid" : "invalid",
2077 m_Selection.m_CursorA.x, m_Selection.m_CursorA.y,
2078 m_Selection.m_CursorB.x, m_Selection.m_CursorB.y));
2079 }
2080
2081 wxLayoutObject *
2082 wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos,
2083 wxPoint *cursorPos,
2084 bool *found)
2085 {
2086 // First, find the right line:
2087 wxLayoutLine *line = m_FirstLine;
2088 wxPoint p;
2089
2090 ApplyStyle(&m_DefaultSetting, dc);
2091 while(line)
2092 {
2093 p = line->GetPosition();
2094 if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y)
2095 break;
2096 #if 0
2097 // we need to run a layout here to get font sizes right :-(
2098
2099 // VZ: we can't call Layout() from here because it marks the line as
2100 // clean and it is not refreshed when it's called from wxLayoutList::
2101 // Layout() - if we really need to do this, we should introduce an
2102 // extra argument to Layout() to prevent the line from MarkClean()ing
2103 // itself here
2104 line->Layout(dc, this);
2105 #endif
2106 line = line->GetNextLine();
2107 }
2108 if(line == NULL)
2109 {
2110 if(found) *found = false;
2111 return NULL; // not found
2112 }
2113 if(cursorPos) cursorPos->y = line->GetLineNumber();
2114 // Now, find the object in the line:
2115 wxLOiterator i = line->FindObjectScreen(dc, pos.x,
2116 cursorPos ? & cursorPos->x : NULL ,
2117 found);
2118 return (i == NULLIT) ? NULL : *i;
2119
2120 }
2121
2122 wxPoint
2123 wxLayoutList::GetSize(void) const
2124 {
2125 wxLayoutLine
2126 *line = m_FirstLine,
2127 *last = line;
2128 if(! line)
2129 return wxPoint(0,0);
2130
2131 wxPoint maxPoint(0,0);
2132
2133 // find last line:
2134 while(line)
2135 {
2136 if(line->GetWidth() > maxPoint.x)
2137 maxPoint.x = line->GetWidth();
2138 last = line;
2139 line = line->GetNextLine();
2140 }
2141
2142 maxPoint.y = last->GetPosition().y + last->GetHeight();
2143 return maxPoint;
2144 }
2145
2146
2147 void
2148 wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate)
2149 {
2150 wxPoint coords(m_CursorScreenPos);
2151 coords += translate;
2152
2153 #ifdef WXLAYOUT_DEBUG
2154 WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld",
2155 (long)m_CursorPos.x, (long)m_CursorPos.y,
2156 (long)coords.x, (long)coords.y,
2157 (long)m_CursorSize.x, (long)m_CursorSize.y,
2158 (long)m_CursorLine->GetLineNumber(),
2159 (long)m_CursorLine->GetLength()));
2160
2161 wxLogStatus("Cursor is at (%d, %d)", m_CursorPos.x, m_CursorPos.y);
2162 #endif
2163
2164 #ifdef WXLAYOUT_USE_CARET
2165 m_caret->Move(coords);
2166 #else // !WXLAYOUT_USE_CARET
2167 dc.SetBrush(*wxBLACK_BRUSH);
2168 dc.SetLogicalFunction(wxXOR);
2169 dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
2170 if(active)
2171 {
2172 dc.DrawRectangle(coords.x, coords.y,
2173 m_CursorSize.x, m_CursorSize.y);
2174 SetUpdateRect(coords.x, coords.y);
2175 SetUpdateRect(coords.x+m_CursorSize.x, coords.y+m_CursorSize.y);
2176 }
2177 else
2178 {
2179 dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
2180 coords.x, coords.y);
2181 SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1);
2182 SetUpdateRect(coords.x, coords.y);
2183 }
2184 dc.SetLogicalFunction(wxCOPY);
2185 //dc.SetBrush(wxNullBrush);
2186 #endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
2187 }
2188
2189 void
2190 wxLayoutList::SetUpdateRect(CoordType x, CoordType y)
2191 {
2192 if(m_UpdateRectValid)
2193 GrowRect(m_UpdateRect, x, y);
2194 else
2195 {
2196 m_UpdateRect.x = x;
2197 m_UpdateRect.y = y;
2198 m_UpdateRect.width = 4; // large enough to avoid surprises from
2199 m_UpdateRect.height = 4;// wxGTK :-)
2200 m_UpdateRectValid = true;
2201 }
2202 }
2203
2204 void
2205 wxLayoutList::StartSelection(wxPoint cpos)
2206 {
2207 if(cpos.x == -1)
2208 cpos = m_CursorPos;
2209 WXLO_DEBUG(("Starting selection at %ld/%ld", cpos.x, cpos.y));
2210 m_Selection.m_CursorA = cpos;
2211 m_Selection.m_CursorB = cpos;
2212 m_Selection.m_selecting = true;
2213 m_Selection.m_valid = false;
2214 }
2215
2216 void
2217 wxLayoutList::ContinueSelection(wxPoint cpos)
2218 {
2219 if(cpos.x == -1)
2220 cpos = m_CursorPos;
2221 wxASSERT(m_Selection.m_selecting == true);
2222 wxASSERT(m_Selection.m_valid == false);
2223 WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos.x, cpos.y));
2224 if(m_Selection.m_CursorB <= cpos)
2225 m_Selection.m_CursorB = cpos;
2226 else
2227 m_Selection.m_CursorA = cpos;
2228 // We always want m_CursorA <= m_CursorB!
2229 if(! (m_Selection.m_CursorA <= m_Selection.m_CursorB))
2230 {
2231 wxPoint help = m_Selection.m_CursorB;
2232 m_Selection.m_CursorB = m_Selection.m_CursorA;
2233 m_Selection.m_CursorA = help;
2234 }
2235 }
2236
2237 void
2238 wxLayoutList::EndSelection(wxPoint cpos)
2239 {
2240 if(cpos.x == -1)
2241 cpos = m_CursorPos;
2242 ContinueSelection(cpos);
2243 WXLO_DEBUG(("Ending selection at %ld/%ld", cpos.x, cpos.y));
2244 m_Selection.m_selecting = false;
2245 m_Selection.m_valid = true;
2246 }
2247
2248
2249 bool
2250 wxLayoutList::IsSelecting(void)
2251 {
2252 return m_Selection.m_selecting;
2253 }
2254
2255 bool
2256 wxLayoutList::IsSelected(const wxPoint &cursor)
2257 {
2258 if(! m_Selection.m_valid && ! m_Selection.m_selecting)
2259 return false;
2260 return m_Selection.m_CursorA <= cursor
2261 && cursor <= m_Selection.m_CursorB;
2262 }
2263
2264
2265 /** Tests whether this layout line is selected and needs
2266 highlighting.
2267 @param line to test for
2268 @return 0 = not selected, 1 = fully selected, -1 = partially
2269 selected
2270 */
2271 int
2272 wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from,
2273 CoordType *to)
2274 {
2275 wxASSERT(line); wxASSERT(to); wxASSERT(from);
2276
2277 if(! m_Selection.m_valid && ! m_Selection.m_selecting)
2278 return 0;
2279
2280 CoordType y = line->GetLineNumber();
2281 if(m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y)
2282 return 1;
2283 else if(m_Selection.m_CursorA.y == y)
2284 {
2285 *from = m_Selection.m_CursorA.x;
2286 if(m_Selection.m_CursorB.y == y)
2287 *to = m_Selection.m_CursorB.x;
2288 else
2289 *to = line->GetLength();
2290 return -1;
2291 }
2292 else if(m_Selection.m_CursorB.y == y)
2293 {
2294 *to = m_Selection.m_CursorB.x;
2295 if(m_Selection.m_CursorA.y == y)
2296 *from = m_Selection.m_CursorA.x;
2297 else
2298 *from = 0;
2299 return -1;
2300 }
2301 else
2302 return 0;
2303 }
2304
2305 void
2306 wxLayoutList::DeleteSelection(void)
2307 {
2308 if(! m_Selection.m_valid)
2309 return;
2310
2311 m_Selection.m_valid = false;
2312
2313 // Only delete part of the current line?
2314 if(m_Selection.m_CursorA.y == m_Selection.m_CursorB.y)
2315 {
2316 MoveCursorTo(m_Selection.m_CursorA);
2317 Delete(m_Selection.m_CursorB.x - m_Selection.m_CursorA.x);
2318 return;
2319 }
2320
2321
2322 wxLayoutLine
2323 * firstLine = NULL,
2324 * lastLine = NULL;
2325
2326 for(firstLine = m_FirstLine;
2327 firstLine && firstLine->GetLineNumber() < m_Selection.m_CursorA.y;
2328 firstLine=firstLine->GetNextLine())
2329 ;
2330 if(!firstLine || firstLine->GetLineNumber() != m_Selection.m_CursorA.y)
2331 return;
2332
2333
2334 for(lastLine = m_FirstLine;
2335 lastLine && lastLine->GetLineNumber() < m_Selection.m_CursorB.y;
2336 lastLine=lastLine->GetNextLine())
2337 ;
2338 if(!lastLine || lastLine->GetLineNumber() != m_Selection.m_CursorB.y)
2339 return;
2340
2341
2342 // We now know that the two lines are different:
2343
2344 // First, delete what's left of this line:
2345 MoveCursorTo(m_Selection.m_CursorA);
2346 DeleteToEndOfLine();
2347
2348 wxLayoutLine *nextLine = firstLine->GetNextLine();
2349 while(nextLine && nextLine != lastLine)
2350 nextLine = nextLine->DeleteLine(false, this);
2351
2352 // Now nextLine = lastLine;
2353 Delete(1); // This joins firstLine and nextLine
2354 Delete(m_Selection.m_CursorB.x); // This deletes the first x
2355 // positions
2356
2357 /// Recalculate:
2358 firstLine->RecalculatePositions(1, this);
2359 }
2360
2361 /// Starts highlighting the selection
2362 void
2363 wxLayoutList::StartHighlighting(wxDC &dc)
2364 {
2365 #if SHOW_SELECTIONS
2366 dc.SetTextForeground(m_CurrentSetting.m_bg);
2367 dc.SetTextBackground(m_CurrentSetting.m_fg);
2368 dc.SetBackgroundMode(wxSOLID);
2369 #endif
2370 }
2371
2372 /// Ends highlighting the selection
2373 void
2374 wxLayoutList::EndHighlighting(wxDC &dc)
2375 {
2376 #if SHOW_SELECTIONS
2377 dc.SetTextForeground(m_CurrentSetting.m_fg);
2378 dc.SetTextBackground(m_CurrentSetting.m_bg);
2379 dc.SetBackgroundMode(wxTRANSPARENT);
2380 #endif
2381 }
2382
2383
2384 wxLayoutList *
2385 wxLayoutList::Copy(const wxPoint &from,
2386 const wxPoint &to)
2387 {
2388 wxLayoutLine
2389 * firstLine = NULL,
2390 * lastLine = NULL;
2391
2392 for(firstLine = m_FirstLine;
2393 firstLine && firstLine->GetLineNumber() < from.y;
2394 firstLine=firstLine->GetNextLine())
2395 ;
2396 if(!firstLine || firstLine->GetLineNumber() != from.y)
2397 return NULL;
2398
2399 for(lastLine = m_FirstLine;
2400 lastLine && lastLine->GetLineNumber() < to.y;
2401 lastLine=lastLine->GetNextLine())
2402 ;
2403 if(!lastLine || lastLine->GetLineNumber() != to.y)
2404 return NULL;
2405
2406 if(to <= from)
2407 {
2408 wxLayoutLine *tmp = firstLine;
2409 firstLine = lastLine;
2410 lastLine = tmp;
2411 }
2412
2413 wxLayoutList *llist = new wxLayoutList();
2414
2415 if(firstLine == lastLine)
2416 {
2417 firstLine->Copy(llist, from.x, to.x);
2418 }
2419 else
2420 {
2421 // Extract objects from first line
2422 firstLine->Copy(llist, from.x);
2423 llist->LineBreak();
2424 // Extract all lines between
2425 for(wxLayoutLine *line = firstLine->GetNextLine();
2426 line != lastLine;
2427 line = line->GetNextLine())
2428 {
2429 line->Copy(llist);
2430 llist->LineBreak();
2431 }
2432 // Extract objects from last line
2433 lastLine->Copy(llist, 0, to.x);
2434 }
2435 return llist;
2436 }
2437
2438 wxLayoutList *
2439 wxLayoutList::GetSelection(wxLayoutDataObject *wxlo, bool invalidate)
2440 {
2441 if(! m_Selection.m_valid)
2442 {
2443 if(m_Selection.m_selecting)
2444 EndSelection();
2445 else
2446 return NULL;
2447 }
2448
2449 if(invalidate) m_Selection.m_valid = false;
2450
2451 wxLayoutList *llist = Copy( m_Selection.m_CursorA,
2452 m_Selection.m_CursorB );
2453
2454 if(llist && wxlo) // export as data object, too
2455 {
2456 wxString string;
2457
2458 wxLayoutExportObject *export;
2459 wxLayoutExportStatus status(llist);
2460 while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL)
2461 {
2462 if(export->type == WXLO_EXPORT_EMPTYLINE)
2463 ; //FIXME missing support for linebreaks in string format
2464 else
2465 export->content.object->Write(string);
2466 delete export;
2467 }
2468
2469 wxlo->SetData(string.c_str(), string.Length()+1);
2470 }
2471 return llist;
2472 }
2473
2474
2475
2476 #define COPY_SI(what) if(si->what != -1) { m_CurrentSetting.what = si->what; fontChanged = TRUE; }
2477
2478 void
2479 wxLayoutList::ApplyStyle(wxLayoutStyleInfo *si, wxDC &dc)
2480 {
2481 bool fontChanged = FALSE;
2482 COPY_SI(family);
2483 COPY_SI(size);
2484 COPY_SI(style);
2485 COPY_SI(weight);
2486 COPY_SI(underline);
2487 if(fontChanged)
2488 dc.SetFont( m_FontCache.GetFont(m_CurrentSetting) );
2489
2490 if(si->m_fg_valid)
2491 {
2492 m_CurrentSetting.m_fg = si->m_fg;
2493 dc.SetTextForeground(m_CurrentSetting.m_fg);
2494 }
2495 if(si->m_bg_valid)
2496 {
2497 m_CurrentSetting.m_bg = si->m_bg;
2498 dc.SetTextBackground(m_CurrentSetting.m_bg);
2499 }
2500 }
2501
2502
2503 #ifdef WXLAYOUT_DEBUG
2504
2505 void
2506 wxLayoutList::Debug(void)
2507 {
2508 wxLayoutLine *line;
2509
2510
2511 for(line = m_FirstLine;
2512 line;
2513 line = line->GetNextLine())
2514 line->Debug();
2515 }
2516
2517 #endif
2518
2519
2520 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2521
2522 wxLayoutPrintout
2523
2524 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2525
2526 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist,
2527 wxString const & title)
2528 :wxPrintout(title)
2529 {
2530 m_llist = llist;
2531 m_title = title;
2532 // remove any highlighting which could interfere with printing:
2533 m_llist->StartSelection();
2534 m_llist->EndSelection();
2535 }
2536
2537 wxLayoutPrintout::~wxLayoutPrintout()
2538 {
2539 }
2540
2541 float
2542 wxLayoutPrintout::ScaleDC(wxDC *dc)
2543 {
2544 // The following bit is taken from the printing sample, let's see
2545 // whether it works for us.
2546
2547 /* You might use THIS code to set the printer DC to ROUGHLY reflect
2548 * the screen text size. This page also draws lines of actual length 5cm
2549 * on the page.
2550 */
2551 // Get the logical pixels per inch of screen and printer
2552 int ppiScreenX, ppiScreenY;
2553 GetPPIScreen(&ppiScreenX, &ppiScreenY);
2554 int ppiPrinterX, ppiPrinterY;
2555 GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
2556
2557 if(ppiScreenX == 0) // not yet set, need to guess
2558 {
2559 ppiScreenX = 100;
2560 ppiScreenY = 100;
2561 }
2562 if(ppiPrinterX == 0) // not yet set, need to guess
2563 {
2564 ppiPrinterX = 72;
2565 ppiPrinterY = 72;
2566 }
2567
2568 // This scales the DC so that the printout roughly represents the
2569 // the screen scaling. The text point size _should_ be the right size
2570 // but in fact is too small for some reason. This is a detail that will
2571 // need to be addressed at some point but can be fudged for the
2572 // moment.
2573 float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
2574
2575 // Now we have to check in case our real page size is reduced
2576 // (e.g. because we're drawing to a print preview memory DC)
2577 int pageWidth, pageHeight;
2578 int w, h;
2579 dc->GetSize(&w, &h);
2580 GetPageSizePixels(&pageWidth, &pageHeight);
2581 if(pageWidth != 0) // doesn't work always
2582 {
2583 // If printer pageWidth == current DC width, then this doesn't
2584 // change. But w might be the preview bitmap width, so scale down.
2585 scale = scale * (float)(w/(float)pageWidth);
2586 }
2587 dc->SetUserScale(scale, scale);
2588 return scale;
2589 }
2590
2591 bool wxLayoutPrintout::OnPrintPage(int page)
2592 {
2593 wxDC *dc = GetDC();
2594
2595 ScaleDC(dc);
2596
2597 if (dc)
2598 {
2599 int top, bottom;
2600 top = (page - 1)*m_PrintoutHeight;
2601 bottom = top + m_PrintoutHeight;
2602 // SetDeviceOrigin() doesn't work here, so we need to manually
2603 // translate all coordinates.
2604 wxPoint translate(m_Offset.x,m_Offset.y-top);
2605 m_llist->Draw(*dc, translate, top, bottom);
2606 return true;
2607 }
2608 else
2609 return false;
2610 }
2611
2612 void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
2613 {
2614 /* We allocate a temporary wxDC for printing, so that we can
2615 determine the correct paper size and scaling. We don't actually
2616 print anything on it. */
2617 #ifdef __WXMSW__
2618 wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false);
2619 #else
2620 wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
2621 #endif
2622
2623 float scale = ScaleDC(&psdc);
2624
2625 psdc.GetSize(&m_PageWidth, &m_PageHeight);
2626 // This sets a left/top origin of 15% and 20%:
2627 m_Offset = wxPoint((15*m_PageWidth)/100, m_PageHeight/20);
2628
2629 // This is the length of the printable area.
2630 m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
2631 m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height
2632
2633
2634 m_NumOfPages = 1 +
2635 (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight));
2636
2637 *minPage = 1;
2638 *maxPage = m_NumOfPages;
2639
2640 *selPageFrom = 1;
2641 *selPageTo = m_NumOfPages;
2642 wxRemoveFile(WXLLIST_TEMPFILE);
2643 }
2644
2645 bool wxLayoutPrintout::HasPage(int pageNum)
2646 {
2647 return pageNum <= m_NumOfPages;
2648 }
2649
2650 /*
2651 Stupid wxWindows doesn't draw proper ellipses, so we comment this
2652 out. It's a waste of paper anyway.
2653 */
2654 #if 0
2655 void
2656 wxLayoutPrintout::DrawHeader(wxDC &dc,
2657 wxPoint topleft, wxPoint bottomright,
2658 int pageno)
2659 {
2660 // make backups of all essential parameters
2661 const wxBrush& brush = dc.GetBrush();
2662 const wxPen& pen = dc.GetPen();
2663 const wxFont& font = dc.GetFont();
2664
2665 dc.SetBrush(*wxWHITE_BRUSH);
2666 dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
2667 dc.DrawRoundedRectangle(topleft.x,
2668 topleft.y,bottomright.x-topleft.x,
2669 bottomright.y-topleft.y);
2670 dc.SetBrush(*wxBLACK_BRUSH);
2671 wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10,
2672 wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
2673 dc.SetFont(myfont);
2674
2675 wxString page;
2676 page = "9999/9999 "; // many pages...
2677 long w,h;
2678 dc.GetTextExtent(page,&w,&h);
2679 page.Printf("%d/%d", pageno, (int) m_NumOfPages);
2680 dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
2681 dc.GetTextExtent("XXXX", &w,&h);
2682 dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
2683
2684 // restore settings
2685 dc.SetPen(pen);
2686 dc.SetBrush(brush);
2687 dc.SetFont(font);
2688 }
2689 #endif
2690
2691
2692 wxFont &
2693 wxFontCache::GetFont(int family, int size, int style, int weight,
2694 bool underline)
2695 {
2696 for(wxFCEList::iterator i = m_FontList.begin();
2697 i != m_FontList.end(); i++)
2698 if( (**i).Matches(family, size, style, weight, underline) )
2699 return (**i).GetFont();
2700 // not found:
2701 wxFontCacheEntry *fce = new wxFontCacheEntry(family, size, style,
2702 weight, underline);
2703 m_FontList.push_back(fce);
2704 return fce->GetFont();
2705 }
2706