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