]> git.saurik.com Git - wxWidgets.git/blob - user/wxLayout/wxllist.cpp
fixes
[wxWidgets.git] / user / wxLayout / wxllist.cpp
1 /*-*- c++ -*-********************************************************
2 * wxllist: wxLayoutList, a layout engine for text and graphics *
3 * *
4 * (C) 1998 by Karsten Ballüder (Ballueder@usa.net) *
5 * *
6 * $Id$
7 *******************************************************************/
8
9 /*
10 - each Object knows its size and how to draw itself
11 - the list is responsible for calculating positions
12 - the draw coordinates for each object are the top left corner
13 - coordinates only get calculated when things get redrawn
14 - The cursor position is the position before an object, i.e. if the
15 buffer starts with a text-object, cursor 0,0 is just before the
16 first character. For all non-text objects, the cursor positions
17 are 0==before or 1==behind. So that all non-text objects count as
18 one cursor position.
19 - Linebreaks are at the end of a line, that is a line like "abc\n"
20 is four cursor positions long. This makes sure that cursor
21 positions are "as expected", i.e. in "abc\ndef" the 'd' would be
22 at positions (x=0,y=1).
23 */
24
25 /*
26 TODO:
27
28 - cursor redraw problems
29 - blinking cursor
30 - mouse click positions cursor
31 - selection (SetMark(), GetSelection())
32 - DND acceptance of text / clipboard support
33 - wxlwindow: formatting menu: problem with checked/unchecked consistency gtk bug?
34 */
35
36 /*
37 Known wxGTK bugs:
38 - MaxX()/MaxY() don't get set
39 */
40
41
42 #ifdef __GNUG__
43 #pragma implementation "wxllist.h"
44 #endif
45
46 //#include "Mpch.h"
47 #ifdef M_BASEDIR
48 # include "gui/wxllist.h"
49 #else
50 # include "wxllist.h"
51 #endif
52
53 #ifndef USE_PCH
54 # include "iostream.h"
55 # include <wx/dc.h>
56 # include <wx/postscrp.h>
57 # include <wx/print.h>
58 # include <wx/log.h>
59 #endif
60
61 #define BASELINESTRETCH 12
62
63 // This should never really get created
64 #define WXLLIST_TEMPFILE "__wxllist.tmp"
65
66 #ifdef WXLAYOUT_DEBUG
67 static const char *g_aTypeStrings[] =
68 {
69 "invalid", "text", "cmd", "icon", "linebreak"
70 };
71
72 # define wxLayoutDebug wxLogDebug
73 # define WXL_VAR(x) cerr << #x " = " << x << endl;
74 # define WXL_DBG_POINT(p) wxLayoutDebug(#p ": (%d, %d)", p.x, p.y)
75 # define WXL_TRACE(f) wxLayoutDebug(#f ": ")
76 # define TypeString(t) g_aTypeStrings[t]
77
78 void
79 wxLayoutObjectBase::Debug(void)
80 {
81 CoordType bl = 0;
82 wxLayoutDebug("%s: size = %dx%d, pos=%d,%d, bl = %d",
83 TypeString(GetType()), GetSize(&bl).x,
84 GetSize(&bl).y,
85 GetPosition().x, GetPosition().y, bl);
86 }
87
88 #else
89 # define WXL_VAR(x)
90 # define WXL_DBG_POINT(p)
91 # define WXL_TRACE(f)
92 # define ShowCurrentObject()
93 # define TypeString(t) ""
94 inline void wxLayoutDebug(const char *, ...) { }
95 #endif
96
97
98 //-------------------------- wxLayoutObjectText
99
100 wxLayoutObjectText::wxLayoutObjectText(const String &txt)
101 {
102 m_Text = txt;
103 m_Width = 0;
104 m_Height = 0;
105 m_Position = wxPoint(-1,-1);
106 }
107
108
109 wxPoint
110 wxLayoutObjectText::GetSize(CoordType *baseLine) const
111 {
112 if(baseLine) *baseLine = m_BaseLine;
113 return wxPoint(m_Width, m_Height);
114 }
115
116 void
117 wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &translate)
118 {
119 dc.DrawText(Str(m_Text), m_Position.x + translate.x, m_Position.y+translate.y);
120 m_IsDirty = false;
121 }
122
123
124 void
125 wxLayoutObjectText::Layout(wxDC &dc, wxPoint position, CoordType baseLine)
126 {
127 long descent = 0l;
128
129 if(m_Position.x != position.x || m_Position.y != position.y)
130 m_IsDirty = true;
131
132 m_Position = position;
133 dc.GetTextExtent(Str(m_Text),&m_Width, &m_Height, &descent);
134 m_BaseLine = m_Height - descent;
135 if(m_Position.x != position.x || m_Position.y != position.y)
136 m_IsDirty = true;
137 }
138
139 #ifdef WXLAYOUT_DEBUG
140 void
141 wxLayoutObjectText::Debug(void)
142 {
143 wxLayoutObjectBase::Debug();
144 wxLayoutDebug(" `%s`", m_Text.c_str());
145 }
146 #endif
147
148 //-------------------------- wxLayoutObjectIcon
149
150 wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon const &icon)
151 {
152 m_Position = wxPoint(-1,-1);
153 m_Icon = new wxIcon(icon);
154 }
155
156 wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon *icon)
157 {
158 m_Icon = icon;
159 }
160
161 void
162 wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &translate)
163 {
164 dc.DrawIcon(m_Icon,m_Position.x+translate.x, m_Position.y+translate.y);
165 }
166
167 void
168 wxLayoutObjectIcon::Layout(wxDC &dc, wxPoint position, CoordType baseLine)
169 {
170 if(m_Position.x != position.x || m_Position.y != position.y)
171 m_IsDirty = true;
172 m_Position = position;
173 }
174
175 wxPoint
176 wxLayoutObjectIcon::GetSize(CoordType *baseLine) const
177 {
178 if(baseLine) *baseLine = m_Icon->GetHeight();
179 return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight());
180 }
181
182 //-------------------------- wxLayoutObjectCmd
183
184
185 wxLayoutObjectCmd::wxLayoutObjectCmd(int size, int family, int style, int
186 weight, bool underline,
187 wxColour const *fg, wxColour const *bg)
188
189 {
190 m_font = new wxFont(size,family,style,weight,underline);
191 m_ColourFG = fg;
192 m_ColourBG = bg;
193 }
194
195 wxLayoutObjectCmd::~wxLayoutObjectCmd()
196 {
197 delete m_font;
198 }
199
200 wxLayoutStyleInfo *
201 wxLayoutObjectCmd::GetStyle(void) const
202 {
203 wxLayoutStyleInfo *si = new wxLayoutStyleInfo();
204
205
206 si->size = m_font->GetPointSize();
207 si->family = m_font->GetFamily();
208 si->style = m_font->GetStyle();
209 si->underline = m_font->GetUnderlined();
210 si->weight = m_font->GetWeight();
211
212 si->fg_red = m_ColourFG->Red();
213 si->fg_green = m_ColourFG->Green();
214 si->fg_blue = m_ColourFG->Blue();
215 si->bg_red = m_ColourBG->Red();
216 si->bg_green = m_ColourBG->Green();
217 si->bg_blue = m_ColourBG->Blue();
218
219 return si;
220 }
221
222 void
223 wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const &translate)
224 {
225 wxASSERT(m_font);
226 dc.SetFont(m_font);
227 if(m_ColourFG)
228 dc.SetTextForeground(*m_ColourFG);
229 if(m_ColourBG)
230 dc.SetTextBackground(*m_ColourBG);
231 }
232 void
233 wxLayoutObjectCmd::Layout(wxDC &dc, wxPoint p, CoordType baseline)
234 {
235 m_Position = p; // required so we can find the right object for cursor
236 // this get called, so that recalculation uses right font sizes
237 Draw(dc,wxPoint(0,0));
238 }
239
240 //-------------------------- wxLayoutList
241
242 wxLayoutList::wxLayoutList()
243 {
244 m_DefaultSetting = NULL;
245 m_WrapMargin = -1;
246 Clear();
247 }
248
249 wxLayoutList::~wxLayoutList()
250 {
251 if(m_DefaultSetting)
252 delete m_DefaultSetting;
253 // no deletion of objects, they are owned by the list
254 }
255
256 void
257 wxLayoutList::LineBreak(void)
258 {
259 Insert(new wxLayoutObjectLineBreak);
260 // m_CursorPos.x = 0; m_CursorPos.y++;
261 }
262
263 void
264 wxLayoutList::SetFont(int family, int size, int style, int weight,
265 int underline, wxColour const *fg,
266 wxColour const *bg)
267 {
268 if(family != -1) m_FontFamily = family;
269 if(size != -1) m_FontPtSize = size;
270 if(style != -1) m_FontStyle = style;
271 if(weight != -1) m_FontWeight = weight;
272 if(underline != -1) m_FontUnderline = underline != 0;
273
274 if(fg != NULL) m_ColourFG = fg;
275 if(bg != NULL) m_ColourBG = bg;
276
277 Insert(
278 new wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,m_FontWeight,m_FontUnderline,
279 m_ColourFG, m_ColourBG));
280 }
281
282 void
283 wxLayoutList::SetFont(int family, int size, int style, int weight,
284 int underline, char const *fg, char const *bg)
285
286 {
287 wxColour const
288 * cfg = NULL,
289 * cbg = NULL;
290
291 if( fg )
292 cfg = wxTheColourDatabase->FindColour(fg);
293 if( bg )
294 cbg = wxTheColourDatabase->FindColour(bg);
295
296 SetFont(family,size,style,weight,underline,cfg,cbg);
297 }
298
299
300 /// for access by wxLayoutWindow:
301 void
302 wxLayoutList::GetSize(CoordType *max_x, CoordType *max_y,
303 CoordType *lineHeight)
304 {
305
306 if(max_x) *max_x = m_MaxX;
307 if(max_y) *max_y = m_MaxY;
308 if(lineHeight) *lineHeight = m_LineHeight;
309 }
310
311 void
312 wxLayoutList::ResetSettings(wxDC &dc)
313 {
314 // setting up the default:
315 dc.SetTextForeground( *wxBLACK );
316 dc.SetTextBackground( *wxWHITE );
317 dc.SetBackgroundMode( wxSOLID ); // to enable setting of text background
318 dc.SetFont( *wxNORMAL_FONT );
319 if(m_DefaultSetting)
320 m_DefaultSetting->Draw(dc,wxPoint(0,0));
321 }
322
323 void
324 wxLayoutList::Layout(wxDC &dc, wxLayoutMargins *margins)
325 {
326 iterator i;
327
328 // first object in current line
329 wxLayoutObjectList::iterator headOfLine;
330 // where we draw next
331 wxPoint position, position_HeadOfLine;
332 // size of last object
333 wxPoint size;
334 CoordType baseLine = m_FontPtSize;
335 CoordType baseLineSkip = (BASELINESTRETCH * baseLine)/10;
336 CoordType objBaseLine = baseLine;
337 wxLayoutObjectType type;
338
339 // we need to count cursor positions
340 wxPoint cursorPos = wxPoint(0,0);
341
342 if(margins)
343 {
344 position.y = margins->top;
345 position.x = margins->left;
346 }
347 else
348 {
349 position.y = 0;
350 position.x = 0;
351 }
352
353 ResetSettings(dc);
354
355 i = begin();
356 headOfLine = i;
357 position_HeadOfLine = position;
358
359 do
360 {
361 if(i == end())
362 return;
363
364 type = (*i)->GetType();
365 (*i)->Layout(dc, position, baseLine);
366 size = (*i)->GetSize(&objBaseLine);
367 // calculate next object's position:
368 position.x += size.x;
369
370 // do we need to increase the line's height?
371 if(size.y > baseLineSkip)
372 {
373 baseLineSkip = size.y;
374 i = headOfLine; position = position_HeadOfLine;
375 continue;
376 }
377 if(objBaseLine > baseLine)
378 {
379 baseLine = objBaseLine;
380 i = headOfLine; position = position_HeadOfLine;
381 continue;
382 }
383
384 // when we reach here, the coordinates are valid, this part of
385 // the loop gets run only once per object
386 if(position.x > m_MaxX)
387 m_MaxX = position.x;
388 if(type == WXLO_TYPE_LINEBREAK)
389 {
390 cursorPos.x = 0; cursorPos.y ++;
391 }
392 else
393 cursorPos.x += (**i).CountPositions();
394
395 // now check whether we have finished handling this line:
396 if(type == WXLO_TYPE_LINEBREAK && i != tail())
397 {
398 position.x = margins ? margins->left : 0;
399 position.y += baseLineSkip;
400 baseLine = m_FontPtSize;
401 objBaseLine = baseLine; // not all objects set it
402 baseLineSkip = (BASELINESTRETCH * baseLine)/10;
403 headOfLine = i;
404 headOfLine++;
405 position_HeadOfLine = position;
406 }
407 if(i == m_CursorObject)
408 CalculateCursor(dc);
409 i++;
410 }
411 while(i != end());
412 m_MaxY = position.y + baseLineSkip;
413 }
414
415 void
416 wxLayoutList::Draw(wxDC &dc,
417 CoordType fromLine, CoordType toLine,
418 iterator start,
419 wxPoint const &translate)
420 {
421 Layout(dc); // FIXME just for now
422
423 ResetSettings(dc);
424
425 wxLayoutObjectList::iterator i;
426
427 if(start == iterator(NULL))
428 start = begin();
429 else // we need to restore font settings
430 {
431 for( i = begin() ; i != start; i++)
432 if((**i).GetType() == WXLO_TYPE_CMD)
433 (**i).Draw(dc,translate); // apply font settings
434 }
435
436 while( start != end() && (**start).GetPosition().y < fromLine)
437 {
438 if((**start).GetType() == WXLO_TYPE_CMD)
439 (**start).Draw(dc,translate); // apply font settings
440 start++;
441 }
442 for( i = start ;
443 i != end() && (toLine == -1 || (**i).GetPosition().y < toLine) ;
444 i++ )
445 (*i)->Draw(dc,translate);
446 }
447
448 /** Erase at least to end of line */
449 void
450 wxLayoutList::EraseAndDraw(wxDC &dc, iterator start)
451 {
452 //look for begin of line
453 while(start != end() && start != begin() && (**start).GetType() !=
454 WXLO_TYPE_LINEBREAK)
455 start--;
456 if(start == iterator(NULL))
457 start = begin();
458 if(start == iterator(NULL))
459 return;
460
461 wxPoint p = (**start).GetPosition();
462
463 //FIXME: wxGTK: MaxX()/MaxY() broken
464 //WXL_VAR(dc.MaxX()); WXL_VAR(dc.MaxY());
465 dc.SetBrush(*wxWHITE_BRUSH);
466 dc.SetPen(wxPen(*wxWHITE,0,wxTRANSPARENT));
467 dc.DrawRectangle(p.x,p.y,2000,2000); //dc.MaxX(),dc.MaxY());
468 Draw(dc,-1,-1,start,wxPoint(0,0));
469 //dc.DrawRectangle(p.x,p.y,2000,2000); //dc.MaxX(),dc.MaxY());
470 }
471
472
473 void
474 wxLayoutList::CalculateCursor(wxDC &dc)
475 {
476 CoordType width, height, descent;
477 CoordType baseLineSkip = 20; //FIXME
478
479 if( m_CursorObject == iterator(NULL)) // empty list
480 {
481 m_CursorCoords = wxPoint(0,0);
482 m_CursorSize = wxPoint(2,baseLineSkip);
483 m_CursorMoved = false; // coords are valid
484 return;
485 }
486 wxLayoutObjectBase &obj = **m_CursorObject;
487
488 m_CursorCoords = obj.GetPosition();
489 if(obj.GetType() == WXLO_TYPE_TEXT)
490 {
491 wxLayoutObjectText *tobj = (wxLayoutObjectText *)&obj;
492 String & str = tobj->GetText();
493 String sstr = str.substr(0,m_CursorOffset);
494 dc.GetTextExtent(sstr,&width,&height,&descent);
495 m_CursorCoords = wxPoint(m_CursorCoords.x+width,
496 m_CursorCoords.y);
497 m_CursorSize = wxPoint(2,height);
498 }
499 else if(obj.GetType() == WXLO_TYPE_LINEBREAK)
500 {
501 if(m_CursorOffset == 1) // behind linebreak
502 m_CursorCoords = wxPoint(0, m_CursorCoords.y + baseLineSkip);
503 //m_CursorCoords = wxPoint(0, m_CursorCoords.y);
504 m_CursorSize = wxPoint(2,baseLineSkip);
505 }
506 else
507 {
508 // this is not necessarily the most "beautiful" solution:
509 //cursorPosition = wxPoint(position.x, position.y);
510 //cursorSize = wxPoint(size.x > 0 ? size.x : 1,size.y > 0 ? size.y : baseLineSkip);
511 m_CursorCoords = wxPoint(m_CursorCoords.x+obj.GetSize().x, m_CursorCoords.y);
512 m_CursorSize = wxPoint(2, obj.GetSize().y);
513 if(m_CursorSize.y < 1) m_CursorSize.y = baseLineSkip;
514 }
515 m_CursorMoved = false; // coords are valid
516 }
517
518 void
519 wxLayoutList::DrawCursor(wxDC &dc, bool erase)
520 {
521 if(! m_Editable)
522 return;
523
524 if(erase)
525 {
526 //dc.SetBrush(*wxWHITE_BRUSH);
527 //dc.SetPen(wxPen(*wxWHITE,1,wxSOLID));
528 //dc.DrawRectangle(m_CursorCoords.x, m_CursorCoords.y, m_CursorSize.x, m_CursorSize.y);
529 dc.Blit(m_CursorCoords.x, m_CursorCoords.y, m_CursorSize.x,
530 m_CursorSize.y, &m_CursorMemDC,
531 0, 0, 0, 0);
532 }
533 else
534 {
535 if(IsDirty() || CursorMoved())
536 {
537 DrawCursor(dc,true);
538 Layout(dc);
539 }
540 // Save background:
541 wxBitmap bm(m_CursorSize.x+1,m_CursorSize.y+1);
542 m_CursorMemDC.SelectObject(bm);
543 m_CursorMemDC.Blit(0, 0, m_CursorSize.x, m_CursorSize.y,
544 &dc, m_CursorCoords.x,
545 m_CursorCoords.y, 0, 0);
546 dc.SetBrush(*wxBLACK_BRUSH);
547 dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
548 dc.DrawRectangle(m_CursorCoords.x, m_CursorCoords.y,
549 m_CursorSize.x, m_CursorSize.y);
550 }
551 }
552
553
554
555
556
557
558
559 #ifdef WXLAYOUT_DEBUG
560 void
561 wxLayoutList::Debug(void)
562 {
563 wxLayoutObjectList::iterator i;
564
565 wxLayoutDebug("------------------------ debug start ------------------------");
566 for(i = begin(); i != end(); i++)
567 (*i)->Debug();
568 wxLayoutDebug("-------------------------- list end -------------------------");
569
570 // show current object:
571 ShowCurrentObject();
572 wxLayoutDebug("------------------------- debug end -------------------------");
573 }
574
575 void
576 wxLayoutList::ShowCurrentObject()
577 {
578 wxLayoutDebug("CursorPos (%d, %d)", (int) m_CursorPos.x, (int) m_CursorPos.y);
579 wxLayoutDebug("CursorOffset = %d", (int) m_CursorOffset);
580 wxLayoutDebug("CursorObject = %p", m_CursorObject);
581 if(m_CursorObject == iterator(NULL))
582 wxLayoutDebug("<<no object found>>");
583 else
584 {
585 if((*m_CursorObject)->GetType() == WXLO_TYPE_TEXT)
586 wxLayoutDebug(" \"%s\", offs: %d",
587 ((wxLayoutObjectText *)(*m_CursorObject))->GetText().c_str(),
588 m_CursorOffset);
589 else
590 wxLayoutDebug(" %s", TypeString((*m_CursorObject)->GetType()));
591 }
592 wxLayoutDebug("Line length: %d", GetLineLength(m_CursorObject));
593
594 }
595
596 #endif
597
598 /******************** editing stuff ********************/
599
600 // don't change this, I know how to optimise this and will do it real
601 // soon (KB)
602
603 /*
604 * FindObjectCursor:
605 * Finds the object belonging to a given cursor position cpos and
606 * returns an iterator to that object and stores the relative cursor
607 * position in offset.
608 *
609 * For linebreaks, the offset can be 0=before or 1=after.
610 *
611 * If the cpos coordinates don't exist, they are modified.
612 */
613
614 wxLayoutObjectList::iterator
615 wxLayoutList::FindObjectCursor(wxPoint *cpos, CoordType *offset)
616 {
617 wxPoint object = wxPoint(0,0); // runs along the objects
618 CoordType width = 0;
619 wxLayoutObjectList::iterator i, begin_it;
620 int go_up;
621
622 //#ifdef WXLAYOUT_DEBUG
623 // wxLayoutDebug("Looking for object at (%d, %d)", cpos->x, cpos->y);
624 //#endif
625
626 // optimisation: compare to last looked at object:
627 if(cpos->y > m_FoundCursor.y || (cpos->y == m_FoundCursor.y &&
628 cpos->x >= m_FoundCursor.x))
629 go_up = 1;
630 else
631 go_up = 0;
632
633 //broken at the moment
634 //begin_it = m_FoundIterator;
635 //m_FoundCursor = *cpos;
636 begin_it = begin();
637 go_up = 1;
638 for(i = begin_it; i != end() && object.y <= cpos->y; )
639 {
640 width = (**i).CountPositions();
641 if(cpos->y == object.y) // a possible candidate
642 {
643 if((**i).GetType() ==WXLO_TYPE_LINEBREAK)
644 {
645 if(cpos->x == object.x)
646 {
647 if(offset) *offset = 0;
648 return m_FoundIterator = i;
649 }
650 if(offset) *offset=1;
651 cpos->x = object.x;
652 return m_FoundIterator = i;
653 }
654 if(cpos->x >= object.x && cpos->x <= object.x+width) // overlap
655 {
656 if(offset) *offset = cpos->x-object.x;
657 //#ifdef WXLAYOUT_DEBUG
658 // wxLayoutDebug(" found object at (%d, %d), type: %s",
659 // object.x, object.y, TypeString((*i)->GetType()));
660 //#endif
661 return m_FoundIterator = i;
662 }
663 }
664 // no overlap, increment coordinates
665 object.x += width;
666 if((**i).GetType() == WXLO_TYPE_LINEBREAK)
667 {
668 object.x = 0;
669 object.y++;
670 }
671 if(go_up)
672 i++;
673 else
674 i--;
675 }//for
676 //#ifdef WXLAYOUT_DEBUG
677 // wxLayoutDebug(" not found");
678 //#endif
679 // return last object, coordinates of that one:
680 i = tail();
681 if(i == end())
682 return m_FoundIterator = i;
683 if((**i).GetType()==WXLO_TYPE_LINEBREAK)
684 {
685 if(offset)
686 *offset = 1;
687 return m_FoundIterator = i;
688 }
689 cpos->x = object.x; // would be the coordinate of next object
690 cpos->y = object.y;
691 cpos->x += width; // last object's width
692 if(*offset) *offset = cpos->x-object.x;
693 return m_FoundIterator = i; // not found
694 }
695
696 bool
697 wxLayoutList::MoveCursor(int dx, int dy)
698 {
699 CoordType diff;
700
701 m_CursorMoved = true;
702
703 enum { up, down} direction;
704
705 wxPoint newPos = wxPoint(m_CursorPos.x + dx,
706 m_CursorPos.y + dy);
707
708 // check for bounds
709 //if(newPos.x < 0) newPos.x = 0;
710 if(newPos.y < 0) newPos.y = 0;
711 else if(newPos.y > m_MaxLine) newPos.y = m_MaxLine;
712
713 if(newPos.y > m_CursorPos.y ||
714 newPos.y == m_CursorPos.y &&
715 newPos.x >= m_CursorPos.x)
716 direction = down;
717 else
718 direction = up;
719
720 wxASSERT(m_CursorObject);
721 // now move cursor forwards until at the new position:
722
723 // first, go to the right line:
724 while(newPos.y != m_CursorPos.y)
725 {
726 if(direction == down)
727 {
728 m_CursorPos.x +=
729 (**m_CursorObject).CountPositions() - m_CursorOffset;
730 if(m_CursorObject == tail())
731 break; // can't go any further
732 if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK
733 && m_CursorOffset == 0)
734 {
735 m_CursorPos.y++; m_CursorPos.x = 0;
736 }
737 m_CursorObject ++; m_CursorOffset = 0;
738 }
739 else // up
740 {
741 if(m_CursorObject == begin())
742 break; // can't go any further
743
744 if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK &&
745 m_CursorOffset == 1)
746 {
747 m_CursorPos.y--;
748 m_CursorPos.x = GetLineLength(m_CursorObject);
749 }
750 m_CursorPos.x -= m_CursorOffset;
751 m_CursorObject --; m_CursorOffset = (**m_CursorObject).CountPositions();
752 }
753 }
754 if(newPos.y != m_CursorPos.y) // reached begin/end of list,
755 newPos.y = m_CursorPos.y; // exited by break
756
757 // now line is right, go to right column:
758 direction = newPos.x >= m_CursorPos.x ? down : up;
759 while(newPos.x != m_CursorPos.x)
760 {
761 if(direction == down)
762 {
763 diff = newPos.x - m_CursorPos.x;
764 if(diff > (**m_CursorObject).CountPositions()-m_CursorOffset)
765 {
766 m_CursorPos.x +=
767 (**m_CursorObject).CountPositions()-m_CursorOffset;
768 if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK &&
769 m_CursorOffset == 0)
770 m_CursorPos = wxPoint(0, m_CursorPos.y+1);
771 if(m_CursorObject == tail())
772 break; // cannot go further
773 m_CursorObject++; m_CursorOffset = 0;
774 }
775 else
776 {
777 if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK)
778 {
779 newPos.y++; newPos.x -= m_CursorPos.x+1;
780 m_CursorPos = wxPoint(0,m_CursorPos.y+1);
781 }
782 else
783 m_CursorPos.x += diff;
784 m_CursorOffset += diff;
785 }
786 }
787 else // up
788 {
789 if(m_CursorPos.x == 0 && m_CursorOffset == 1 &&
790 (**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK) // can we go further up?
791 {
792 m_CursorPos.y--;
793 newPos.y--;
794 m_CursorOffset = 0;
795 m_CursorPos.x = GetLineLength(m_CursorObject)-1;
796 newPos.x += m_CursorPos.x+1;
797 continue;
798 }
799 diff = m_CursorPos.x - newPos.x;
800 if(diff >= m_CursorOffset)
801 {
802 if(m_CursorObject == begin())
803 {
804 m_CursorOffset = 0;
805 m_CursorPos.x = 0;
806 break; // cannot go further
807 }
808 m_CursorObject--;
809 m_CursorPos.x -= m_CursorOffset;
810 m_CursorOffset = (**m_CursorObject).CountPositions();
811 }
812 else
813 {
814 m_CursorPos.x -= diff;
815 m_CursorOffset -= diff;
816 }
817 }
818 }
819 return true; // FIXME: when return what?
820 }
821
822 void
823 wxLayoutList::Delete(CoordType count)
824 {
825 WXL_TRACE(Delete);
826
827 if(!m_Editable)
828 return;
829
830 m_bModified = true;
831
832 CoordType offs;
833 wxLayoutObjectList::iterator i;
834
835 do
836 {
837 i = m_CursorObject;
838 startover: // ugly, but easiest way to do it
839 if(i == end())
840 return; // we cannot delete anything more
841
842 /* Here we need to treat linebreaks differently.
843 If m_CursorOffset==0 we are before the linebreak, otherwise behind. */
844 if((*i)->GetType() == WXLO_TYPE_LINEBREAK)
845 {
846 if(m_CursorOffset == 0)
847 {
848 m_MaxLine--;
849 erase(i);
850 m_CursorObject = i; // new i!
851 m_CursorOffset = 0; // Pos unchanged
852 count--;
853 continue; // we're done
854 }
855 else // delete the object behind the linebreak
856 {
857 i++; // we increment and continue as normal
858 m_CursorOffset=0;
859 goto startover;
860 }
861 }
862 else if((*i)->GetType() == WXLO_TYPE_TEXT)
863 {
864 wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
865 CoordType len = tobj->CountPositions();
866 // If we find the end of a text object, this means that we
867 // have to delete from the object following it.
868 if(len == m_CursorOffset)
869 {
870 i++;
871 m_CursorOffset = 0;
872 goto startover;
873 }
874 else if(len <= count) // delete this object
875 {
876 count -= len;
877 erase(i);
878 m_CursorObject = i;
879 m_CursorOffset = 0;
880 continue;
881 }
882 else
883 {
884 len = count;
885 tobj->GetText().erase(m_CursorOffset,len);
886 // cursor unchanged
887 return; // we are done
888 }
889 }
890 else// all other objects: delete the object
891 // this only works as expected if the non-text object has 0/1
892 // as offset values. Not tested with "longer" objects.
893 {
894 CoordType len = (*i)->CountPositions();
895 if(offs == 0)
896 {
897 count = count > len ? count -= len : 0;
898 erase(i); // after this, i is the iterator for the
899 // following object
900 m_CursorObject = i;
901 m_CursorOffset = 0;
902 continue;
903 }
904 else // delete the following object
905 {
906 i++; // we increment and continue as normal
907 m_CursorOffset=0;
908 goto startover;
909 }
910 }
911 }
912 while(count && i != end());
913 }
914
915 void
916 wxLayoutList::Insert(wxLayoutObjectBase *obj)
917 {
918 wxCHECK_RET( obj, "no object to insert" );
919
920 m_bModified = true;
921
922 wxLayoutObjectList::iterator i = m_CursorObject;
923
924 if(i != iterator(NULL) && (*obj).GetType() == WXLO_TYPE_LINEBREAK)
925 {
926 m_CursorPos.x = 0; m_CursorPos.y ++;
927 }
928
929 if(i == end())
930 {
931 push_back(obj);
932 m_CursorObject = tail();
933 }
934 else if(m_CursorOffset == 0)
935 {
936 insert(i,obj);
937 m_CursorObject = i;
938 }
939 // do we have to split a text object?
940 else if((*i)->GetType() == WXLO_TYPE_TEXT && m_CursorOffset != (*i)->CountPositions())
941 {
942 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
943 String left = tobj->GetText().substr(0,m_CursorOffset); // get part before cursor
944 tobj->GetText() = tobj->GetText().substr(m_CursorOffset,(*i)->CountPositions()-m_CursorOffset); // keeps the right half
945 insert(i,obj);
946 m_CursorObject = i; // == obj
947 insert(i,new wxLayoutObjectText(left)); // inserts before
948 }
949 else
950 {
951 // all other cases, append after object:
952 wxLayoutObjectList::iterator j = i; // we want to apend after this object
953 j++;
954 if(j != end())
955 {
956 insert(j, obj);
957 m_CursorObject = j;
958 }
959 else
960 {
961 push_back(obj);
962 m_CursorObject = tail();
963 }
964 }
965
966 if(obj->GetType() != WXLO_TYPE_LINEBREAK) // handled separately above
967 m_CursorPos.x += obj->CountPositions();
968 // applies also for linebreak:
969 m_CursorOffset = obj->CountPositions();
970
971 if(obj->GetType() == WXLO_TYPE_LINEBREAK)
972 m_MaxLine++;
973 m_CursorMoved = true;
974 }
975
976 void
977 wxLayoutList::Insert(String const &text)
978 {
979 wxLayoutObjectText *tobj = NULL;
980 wxLayoutObjectList::iterator j;
981
982 // WXL_TRACE(Insert(text));
983
984 if(! m_Editable)
985 return;
986
987 m_bModified = true;
988
989 wxLayoutObjectList::iterator i = m_CursorObject;
990
991 if(i == end())
992 {
993 Insert(new wxLayoutObjectText(text));
994 return;
995 }
996
997 switch((**i).GetType())
998 {
999 case WXLO_TYPE_TEXT:
1000 // insert into an existing text object:
1001 tobj = (wxLayoutObjectText *)*i ;
1002 wxASSERT(tobj);
1003 tobj->GetText().insert(m_CursorOffset,text);
1004 m_CursorObject = i;
1005 m_CursorOffset = m_CursorOffset + text.length();
1006 m_CursorPos.x += text.length();
1007 break;
1008 case WXLO_TYPE_LINEBREAK:
1009 default:
1010 j = i;
1011 if(m_CursorOffset == 0) // try to append to previous object
1012 {
1013 j--;
1014 if(j != end() && (**j).GetType() == WXLO_TYPE_TEXT)
1015 {
1016 tobj = (wxLayoutObjectText *)*j;
1017 tobj->GetText()+=text;
1018 m_CursorObject = j;
1019 m_CursorOffset = (**j).CountPositions();
1020 m_CursorPos.x += text.length();
1021 }
1022 else
1023 {
1024 insert(i,new wxLayoutObjectText(text));
1025 m_CursorObject = i;
1026 m_CursorOffset = (**i).CountPositions();
1027 m_CursorPos.x += m_CursorOffset;
1028 }
1029 }
1030 else // offset == 1 : cursor after linebreak
1031 {
1032 j++;
1033 m_CursorObject = j;
1034 m_CursorOffset = 0;
1035 if(j != end() && (**j).GetType() == WXLO_TYPE_TEXT)
1036 {
1037 tobj = (wxLayoutObjectText *)*j;
1038 tobj->GetText()=text+tobj->GetText();
1039 m_CursorOffset = text.length();
1040 m_CursorPos.x += m_CursorOffset;
1041 }
1042 else
1043 {
1044 if(j == end())
1045 {
1046 push_back(new wxLayoutObjectText(text));
1047 m_CursorObject = tail();
1048 m_CursorOffset = (**m_CursorObject).CountPositions();
1049 m_CursorPos.x += text.length();
1050 }
1051 else
1052 {
1053 insert(j,new wxLayoutObjectText(text));
1054 m_CursorObject = j;
1055 m_CursorOffset = (**j).CountPositions();
1056 m_CursorPos.x += text.length();
1057 }
1058 }
1059 }
1060 break;
1061 }
1062 m_CursorMoved = true;
1063 }
1064
1065 CoordType
1066 wxLayoutList::GetLineLength(wxLayoutObjectList::iterator i, CoordType offs)
1067 {
1068 if(i == end())
1069 return 0;
1070
1071 CoordType len = 0;
1072
1073 if(offs == 0 && (**i).GetType() == WXLO_TYPE_LINEBREAK)
1074 i--;
1075
1076 // search backwards for beginning of line:
1077 while(i != begin() && (*i)->GetType() != WXLO_TYPE_LINEBREAK)
1078 i--;
1079 if((*i)->GetType() == WXLO_TYPE_LINEBREAK)
1080 i++;
1081 // now we can start counting:
1082 while(i != end() && (*i)->GetType() != WXLO_TYPE_LINEBREAK)
1083 {
1084 len += (*i)->CountPositions();
1085 i++;
1086 }
1087 len++; // one extra for the linebreak
1088 return len;
1089 }
1090
1091 void
1092 wxLayoutList::Clear(int family, int size, int style, int weight,
1093 int underline, char const *fg, char const *bg)
1094 {
1095 m_bModified = true;
1096 m_CursorMoved = true;
1097 m_dirty = true; // force redraw/recalc
1098 wxLayoutObjectList::iterator i = begin();
1099
1100 wxBitmap bm(1,1);
1101 m_CursorMemDC.SelectObject(bm);
1102
1103 while(i != end()) // == while valid
1104 erase(i);
1105
1106 // set defaults
1107 m_FontPtSize = size;
1108 m_FontUnderline = false;
1109 m_FontFamily = family;
1110 m_FontStyle = style;
1111 m_FontWeight = weight;
1112 m_ColourFG = wxTheColourDatabase->FindColour(fg);
1113 m_ColourBG = wxTheColourDatabase->FindColour(bg);
1114
1115 if(! m_ColourFG) m_ColourFG = wxBLACK;
1116 if(! m_ColourBG) m_ColourBG = wxWHITE;
1117
1118 m_Position = wxPoint(0,0);
1119 m_CursorPos = wxPoint(0,0);
1120 m_CursorObject = iterator(NULL);
1121 m_CursorOffset = 0;
1122 m_CursorSize = wxPoint(2,(BASELINESTRETCH*m_FontPtSize)/10);
1123
1124 m_MaxLine = 1;
1125 m_LineHeight = (BASELINESTRETCH*m_FontPtSize)/10;
1126 m_MaxX = 0; m_MaxY = 0;
1127
1128
1129 m_FoundCursor = wxPoint(0,0);
1130 m_FoundIterator = begin();
1131
1132 if(m_DefaultSetting)
1133 delete m_DefaultSetting;
1134
1135 m_DefaultSetting = new
1136 wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,
1137 m_FontWeight,m_FontUnderline,
1138 m_ColourFG, m_ColourBG);
1139 }
1140
1141
1142 wxLayoutObjectBase *
1143 wxLayoutList::Find(wxPoint coords) const
1144 {
1145 wxLayoutObjectList::iterator i = begin();
1146
1147 wxPoint topleft, bottomright;
1148
1149 while(i != end()) // == while valid
1150 {
1151 wxLayoutObjectBase *object = *i;
1152 topleft = object->GetPosition();
1153 if(coords.y >= topleft.y && coords.x >= topleft.x)
1154 {
1155 bottomright = topleft;
1156 bottomright.x += object->GetSize().x;
1157 bottomright.y += object->GetSize().y;
1158 if(coords.x <= bottomright.x && coords.y <= bottomright.y)
1159 return *i;
1160 }
1161 i++;
1162 }
1163 return NULL;
1164 }
1165
1166
1167 void
1168 wxLayoutList::SetWrapMargin(long n = -1)
1169 {
1170 m_WrapMargin = n;
1171 }
1172
1173 void
1174 wxLayoutList::WrapLine(void)
1175 {
1176 wxASSERT(m_CursorObject);
1177
1178 iterator i = m_CursorObject;
1179
1180 if(!DoWordWrap() || !i ) // empty list
1181 return;
1182 int cursorpos = m_CursorPos.x, cpos, offset;
1183
1184 if(cursorpos < m_WrapMargin)
1185 return;
1186
1187 // else: break line
1188
1189 // find the right object to break:
1190 // is it the current one?
1191
1192 i = m_CursorObject;
1193 cpos = cursorpos-m_CursorOffset;
1194 while(i != begin() && cpos >= m_WrapMargin)
1195 {
1196 i--;
1197 cpos -= (**i).CountPositions();
1198 }
1199 // now i is the object to break and cpos its position
1200
1201 offset = m_WrapMargin - cpos;
1202 wxASSERT(offset <= (**i).CountPositions());
1203
1204 // split it
1205 if((**i).GetType() == WXLO_TYPE_TEXT)
1206 {
1207 wxLayoutObjectText &t = *(wxLayoutObjectText *)*i;
1208 for(; offset > 0; offset--)
1209 if(t.GetText().c_str()[offset] == ' ' || t.GetText().c_str()[offset] == '\t')
1210 {
1211 String left = t.GetText().substr(0,offset-1); // get part before cursor
1212 t.GetText() = t.GetText().substr(offset,t.CountPositions()-offset); // keeps the right halve
1213 insert(i,new wxLayoutObjectLineBreak);
1214 insert(i,new wxLayoutObjectText(left)); // inserts before
1215 break;
1216 }
1217 if(offset == 0)
1218 {
1219 // only insert a line break if there isn't already one
1220 iterator j = i; j--;
1221 if(j && j != begin() && (**j).GetType() != WXLO_TYPE_LINEBREAK)
1222 insert(i,new wxLayoutObjectLineBreak);
1223 else
1224 return; // do nothing
1225 }
1226 }
1227 else
1228 insert(i,new wxLayoutObjectLineBreak);
1229 m_MaxLine++;
1230 m_CursorPos.y++;
1231 m_CursorPos.x -= offset;
1232 m_CursorOffset -= offset;
1233 }
1234 /******************** printing stuff ********************/
1235
1236 wxLayoutPrintout::wxLayoutPrintout(wxLayoutList &llist,
1237 wxString const & title)
1238 :wxPrintout(title)
1239 {
1240 m_llist = &llist;
1241 m_title = title;
1242 }
1243
1244 bool wxLayoutPrintout::OnPrintPage(int page)
1245 {
1246 wxDC *dc = GetDC();
1247 if (dc)
1248 {
1249 DrawHeader(*dc,wxPoint(m_Margins.left,m_Margins.top/2),wxPoint(m_Margins.right,m_Margins.top),page);
1250 int top, bottom;
1251 top = (page - 1)*m_PrintoutHeight;
1252 bottom = top + m_PrintoutHeight;
1253 // SetDeviceOrigin() doesn't work here, so we need to manually
1254 // translate all coordinates.
1255 wxPoint translate(m_Margins.left,-top+m_Margins.top);
1256 m_llist->Draw(*dc,top,bottom,wxLayoutObjectList::iterator(NULL),translate);
1257 return true;
1258 }
1259 else
1260 return false;
1261 }
1262
1263 bool wxLayoutPrintout::OnBeginDocument(int startPage, int endPage)
1264 {
1265 if (!wxPrintout::OnBeginDocument(startPage, endPage))
1266 return false;
1267
1268 return true;
1269 }
1270
1271 void
1272 wxLayoutPrintout::OnPreparePrinting(void)
1273 {
1274
1275 }
1276
1277
1278 void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
1279 {
1280 // ugly hack to get number of pages
1281 #ifdef __WXMSW__
1282 wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false);
1283 #else
1284 wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
1285 #endif
1286 psdc.GetSize(&m_PageWidth, &m_PageHeight); // that's all we need it for
1287
1288 // We do 5% margins on top and bottom, and a 5% high header line.
1289 m_Margins.top = m_PageHeight / 10 ; // 10%, half of it header
1290 m_Margins.bottom = m_PageHeight - m_PageHeight / 20; // 95%
1291 // On the sides we reserve 10% each for the margins.
1292 m_Margins.left = m_PageWidth / 10;
1293 m_Margins.right = m_PageWidth - m_PageWidth / 10;
1294
1295 // This is the length of the printable area.
1296 m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
1297
1298 //FIXME this is wrong but not used at the moment
1299 m_PageWidth = m_Margins.right - m_Margins.left;
1300
1301 m_NumOfPages = (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight) + 0.5);
1302 *minPage = 1;
1303 *maxPage = m_NumOfPages;
1304
1305 *selPageFrom = 1;
1306 *selPageTo = m_NumOfPages;
1307 wxRemoveFile(WXLLIST_TEMPFILE);
1308 }
1309
1310 bool wxLayoutPrintout::HasPage(int pageNum)
1311 {
1312 return pageNum <= m_NumOfPages;
1313 }
1314
1315
1316 void
1317 wxLayoutPrintout::DrawHeader(wxDC &dc,
1318 wxPoint topleft, wxPoint bottomright,
1319 int pageno)
1320 {
1321 // make backups of all essential parameters
1322 wxBrush *brush = dc.GetBrush();
1323 wxPen *pen = dc.GetPen();
1324 wxFont *font = dc.GetFont(),
1325 *myfont;;
1326
1327 dc.SetBrush(*wxWHITE_BRUSH);
1328 dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
1329 dc.DrawRoundedRectangle(topleft.x,
1330 topleft.y,bottomright.x-topleft.x,
1331 bottomright.y-topleft.y);
1332 dc.SetBrush(*wxBLACK_BRUSH);
1333 myfont = new wxFont((WXLO_DEFAULTFONTSIZE*12)/10,wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
1334 dc.SetFont(*myfont);
1335
1336 wxString page;
1337 page = "9999/9999 "; // many pages...
1338 long w,h;
1339 dc.GetTextExtent(page,&w,&h);
1340 page.Printf("%d/%d", pageno, (int) m_NumOfPages);
1341 dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
1342 dc.GetTextExtent("XXXX", &w,&h);
1343 dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
1344
1345 // restore settings
1346 dc.SetPen(*pen);
1347 dc.SetBrush(*brush);
1348 dc.SetFont(*font);
1349
1350 delete myfont;
1351 }
1352
1353
1354 wxLayoutPrintout *
1355 wxLayoutList::MakePrintout(wxString const &name)
1356 {
1357 return new wxLayoutPrintout(*this,name);
1358 }