]> git.saurik.com Git - wxWidgets.git/blob - user/wxLayout/wxllist.cpp
Added my wxWindows based layout engine to the repository.
[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
21 #ifdef __GNUG__
22 #pragma implementation "wxllist.h"
23 #endif
24
25 #include "wxllist.h"
26 #include "iostream"
27
28 #define VAR(x) cerr << #x"=" << x << endl;
29 #define DBG_POINT(p) cerr << #p << ": " << p.x << ',' << p.y << endl
30 #define TRACE(f) cerr << #f":" << endl;
31
32 #ifdef DEBUG
33 static const char *_t[] = { "invalid", "text", "cmd", "icon",
34 "linebreak"};
35
36 void
37 wxLayoutObjectBase::Debug(void)
38 {
39 CoordType bl = 0;
40 cerr << _t[GetType()] << ": size=" << GetSize(&bl).x << ","
41 << GetSize(&bl).y << " bl=" << bl;
42 }
43 #endif
44
45 //-------------------------- wxLayoutObjectText
46
47 wxLayoutObjectText::wxLayoutObjectText(const wxString &txt)
48 {
49 m_Text = txt;
50 m_Width = 0;
51 m_Height = 0;
52 }
53
54
55 wxPoint
56 wxLayoutObjectText::GetSize(CoordType *baseLine) const
57 {
58 if(baseLine) *baseLine = m_BaseLine;
59 return wxPoint(m_Width, m_Height);
60 }
61
62 void
63 wxLayoutObjectText::Draw(wxDC &dc, wxPoint position, CoordType baseLine,
64 bool draw)
65 {
66 long descent = 0l;
67 dc.GetTextExtent(m_Text,&m_Width, &m_Height, &descent);
68 //FIXME: wxGTK does not set descent to a descent value yet.
69 if(descent == 0)
70 descent = (2*m_Height)/10; // crude fix
71 m_BaseLine = m_Height - descent;
72 position.y += baseLine-m_BaseLine;
73 if(draw)
74 dc.DrawText(m_Text,position.x,position.y);
75 # ifdef DEBUG
76 // dc.DrawRectangle(position.x, position.y, m_Width, m_Height);
77 # endif
78 }
79
80 #ifdef DEBUG
81 void
82 wxLayoutObjectText::Debug(void)
83 {
84 wxLayoutObjectBase::Debug();
85 cerr << " `" << m_Text << '\'';
86 }
87 #endif
88
89 //-------------------------- wxLayoutObjectIcon
90
91 wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon *icon)
92 {
93 m_Icon = icon;
94 }
95
96 void
97 wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint position, CoordType baseLine,
98 bool draw)
99 {
100 position.y += baseLine - m_Icon->GetHeight();
101 if(draw)
102 dc.DrawIcon(m_Icon,position.x,position.y);
103 }
104
105 wxPoint
106 wxLayoutObjectIcon::GetSize(CoordType *baseLine) const
107 {
108 wxASSERT(baseLine);
109 *baseLine = m_Icon->GetHeight();
110 return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight());
111 }
112
113 //-------------------------- wxLayoutObjectCmd
114
115
116 wxLayoutObjectCmd::wxLayoutObjectCmd(int size, int family, int style, int
117 weight, bool underline,
118 wxColour const *fg, wxColour const *bg)
119
120 {
121 m_font = new wxFont(size,family,style,weight,underline);
122 m_ColourFG = fg;
123 m_ColourBG = bg;
124 }
125
126 wxLayoutObjectCmd::~wxLayoutObjectCmd()
127 {
128 delete m_font;
129 }
130
131 wxLayoutStyleInfo *
132 wxLayoutObjectCmd::GetStyle(void) const
133 {
134 wxLayoutStyleInfo *si = new wxLayoutStyleInfo();
135
136
137 si->size = m_font->GetPointSize();
138 si->family = m_font->GetFamily();
139 si->style = m_font->GetStyle();
140 si->underline = m_font->GetUnderlined();
141 si->weight = m_font->GetWeight();
142
143 si->fg_red = m_ColourFG->Red();
144 si->fg_green = m_ColourFG->Green();
145 si->fg_blue = m_ColourFG->Blue();
146 si->bg_red = m_ColourBG->Red();
147 si->bg_green = m_ColourBG->Green();
148 si->bg_blue = m_ColourBG->Blue();
149
150 return si;
151 }
152
153 void
154 wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint position, CoordType lineHeight,
155 bool draw)
156 {
157 wxASSERT(m_font);
158 // this get called even when draw==false, so that recalculation
159 // uses right font sizes
160 dc.SetFont(m_font);
161 if(m_ColourFG)
162 dc.SetTextForeground(*m_ColourFG);
163 if(m_ColourBG)
164 dc.SetTextBackground(*m_ColourBG);
165 }
166
167 //-------------------------- wxwxLayoutList
168
169 wxLayoutList::wxLayoutList()
170 {
171 Clear();
172 }
173
174 wxLayoutList::~wxLayoutList()
175 {
176 }
177
178
179 void
180 wxLayoutList::LineBreak(void)
181 {
182 Insert(new wxLayoutObjectLineBreak);
183 m_CursorPosition.x = 0; m_CursorPosition.y++;
184 }
185
186 void
187 wxLayoutList::SetFont(int family, int size, int style, int weight,
188 int underline, wxColour const *fg,
189 wxColour const *bg)
190 {
191 if(family != -1) m_FontFamily = family;
192 if(size != -1) m_FontPtSize = size;
193 if(style != -1) m_FontStyle = style;
194 if(weight != -1) m_FontWeight = weight;
195 if(underline != -1) m_FontUnderline = underline;
196
197 if(fg != NULL) m_ColourFG = fg;
198 if(bg != NULL) m_ColourBG = bg;
199
200 Insert(
201 new wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,m_FontWeight,m_FontUnderline,
202 m_ColourFG, m_ColourBG));
203 }
204
205 void
206 wxLayoutList::SetFont(int family, int size, int style, int weight,
207 int underline, char const *fg, char const *bg)
208 {
209 wxColour const
210 * cfg = NULL,
211 * cbg = NULL;
212
213 if( fg )
214 cfg = wxTheColourDatabase->FindColour(fg);
215 if( bg )
216 cbg = wxTheColourDatabase->FindColour(bg);
217
218 SetFont(family,size,style,weight,underline,cfg,cbg);
219 }
220
221
222 /// for access by wxLayoutWindow:
223 void
224 wxLayoutList::GetSize(CoordType *max_x, CoordType *max_y,
225 CoordType *lineHeight)
226 {
227 wxASSERT(max_x); wxASSERT(max_y); wxASSERT(lineHeight);
228 *max_x = m_MaxX;
229 *max_y = m_MaxY;
230 *lineHeight = m_LineHeight;
231 }
232
233 wxLayoutObjectBase *
234 wxLayoutList::Draw(wxDC &dc, bool findObject, wxPoint const &findCoords)
235 {
236 wxLayoutObjectList::iterator i;
237
238 // in case we need to look for an object
239 wxLayoutObjectBase *foundObject = NULL;
240
241 // first object in current line
242 wxLayoutObjectList::iterator headOfLine;
243
244 // do we need to recalculate current line?
245 bool recalculate = false;
246
247 // do we calculate or draw? Either true or false.
248 bool draw = false;
249 // drawing parameters:
250 wxPoint position = wxPoint(0,0);
251 wxPoint position_HeadOfLine;
252 CoordType baseLine = m_FontPtSize;
253 CoordType baseLineSkip = (12 * baseLine)/10;
254
255 // we trace the objects' cursor positions so we can draw the cursor
256 wxPoint cursor = wxPoint(0,0);
257 // the cursor position inside the object
258 CoordType cursorOffset = 0;
259 // object under cursor
260 wxLayoutObjectList::iterator cursorObject = FindCurrentObject(&cursorOffset);
261
262 // queried from each object:
263 wxPoint size = wxPoint(0,0);
264 CoordType objBaseLine = baseLine;
265 wxLayoutObjectType type;
266
267 VAR(findObject); VAR(findCoords.x); VAR(findCoords.y);
268 // if the cursorobject is a cmd, we need to find the first
269 // printable object:
270 while(cursorObject != end()
271 && (*cursorObject)->GetType() == WXLO_TYPE_CMD)
272 cursorObject++;
273
274 headOfLine = begin();
275 position_HeadOfLine = position;
276
277 // setting up the default:
278 dc.SetTextForeground( *wxBLACK );
279 dc.SetFont( *wxNORMAL_FONT );
280
281 // we calculate everything for drawing a line, then rewind to the
282 // begin of line and actually draw it
283 i = begin();
284 for(;;)
285 {
286 recalculate = false;
287
288 if(i == end())
289 break;
290 type = (*i)->GetType();
291
292 // to initialise sizes of objects, we need to call Draw
293 (*i)->Draw(dc, position, baseLine, draw);
294
295 // update coordinates for next object:
296 size = (*i)->GetSize(&objBaseLine);
297 if(findObject && draw) // we need to look for an object
298 {
299 if(findCoords.y >= position.y
300 && findCoords.y <= position.y+size.y
301 && findCoords.x >= position.x
302 && findCoords.x <= position.x+size.x)
303 {
304 foundObject = *i;
305 findObject = false; // speeds things up
306 }
307 }
308 // draw the cursor
309 if(m_Editable && draw && i == cursorObject)
310 {
311 if(type == WXLO_TYPE_TEXT) // special treatment
312 {
313 long descent = 0l; long width, height;
314 wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
315 wxString str = tobj->GetText();
316 VAR(m_CursorPosition.x); VAR(cursor.x);
317 str = str.substr(0, cursorOffset);
318 VAR(str);
319 dc.GetTextExtent(str, &width,&height, &descent);
320 VAR(height);
321 VAR(width); VAR(descent);
322 dc.DrawLine(position.x+width,
323 position.y+(baseLineSkip-height),
324 position.x+width, position.y+baseLineSkip);
325 }
326 else
327 {
328 if(type == WXLO_TYPE_LINEBREAK)
329 dc.DrawLine(0, position.y+baseLineSkip, 0, position.y+2*baseLineSkip);
330 else
331 {
332 if(size.x == 0)
333 {
334 if(size.y == 0)
335 dc.DrawLine(position.x, position.y, position.x, position.y+baseLineSkip);
336 else
337 dc.DrawLine(position.x, position.y, position.x, position.y+size.y);
338 }
339 else
340 dc.DrawRectangle(position.x, position.y, size.x, size.y);
341 }
342 }
343 }
344
345 // calculate next object's position:
346 position.x += size.x;
347
348 // do we need to increase the line's height?
349 if(size.y > baseLineSkip)
350 {
351 baseLineSkip = size.y;
352 recalculate = true;
353 }
354 if(objBaseLine > baseLine)
355 {
356 baseLine = objBaseLine;
357 recalculate = true;
358 }
359
360 // now check whether we have finished handling this line:
361 if(type == WXLO_TYPE_LINEBREAK || i == tail())
362 {
363 if(recalculate) // do this line again
364 {
365 position.x = position_HeadOfLine.x;
366 i = headOfLine;
367 continue;
368 }
369
370 if(! draw) // finished calculating sizes
371 { // do this line again, this time drawing it
372 position = position_HeadOfLine;
373 draw = true;
374 i = headOfLine;
375 continue;
376 }
377 else // we have drawn a line, so continue calculating next one
378 draw = false;
379 }
380
381 if(position.x+size.x > m_MaxX)
382 m_MaxX = position.x+size.x;
383 // is it a linebreak?
384 if(type == WXLO_TYPE_LINEBREAK || i == tail())
385 {
386 position.x = 0;
387 position.y += baseLineSkip;
388 baseLine = m_FontPtSize;
389 baseLineSkip = (12 * baseLine)/10;
390 headOfLine = i;
391 headOfLine++;
392 position_HeadOfLine = position;
393 }
394 i++;
395 }
396 m_MaxY = position.y;
397 return foundObject;
398 }
399
400 #ifdef DEBUG
401 void
402 wxLayoutList::Debug(void)
403 {
404 CoordType offs;
405 wxLayoutObjectList::iterator i;
406
407 cerr <<
408 "------------------------debug start-------------------------" << endl;
409 for(i = begin(); i != end(); i++)
410 {
411 (*i)->Debug();
412 cerr << endl;
413 }
414 cerr <<
415 "-----------------------debug end----------------------------"
416 << endl;
417 // show current object:
418 cerr << "Cursor: "
419 << m_CursorPosition.x << ','
420 << m_CursorPosition.y;
421
422 i = FindCurrentObject(&offs);
423 cerr << " line length: " << GetLineLength(i) << " ";
424 if(i == end())
425 {
426 cerr << "<<no object found>>" << endl;
427 return; // FIXME we should set cursor position to maximum allowed
428 // value then
429 }
430 if((*i)->GetType() == WXLO_TYPE_TEXT)
431 {
432 cerr << " \"" << ((wxLayoutObjectText *)(*i))->GetText() << "\", offs: "
433 << offs << endl;
434 }
435 else
436 cerr << ' ' << _t[(*i)->GetType()] << endl;
437
438 }
439 #endif
440
441 /******************** editing stuff ********************/
442
443 wxLayoutObjectList::iterator
444 wxLayoutList::FindObjectCursor(wxPoint const &cpos, CoordType *offset)
445 {
446 wxPoint cursor = wxPoint(0,0); // runs along the objects
447 CoordType width;
448 wxLayoutObjectList::iterator i;
449
450 #ifdef DEBUG
451 cerr << "Looking for object at " << cpos.x << ',' << cpos.y <<
452 endl;
453 #endif
454 for(i = begin(); i != end() && cursor.y <= cpos.y; i++)
455 {
456 width = 0;
457 if((*i)->GetType() == WXLO_TYPE_LINEBREAK)
458 {
459 if(cpos.y == cursor.y)
460 {
461 --i;
462 if(offset)
463 *offset = (*i)->CountPositions();
464 return i;
465 }
466 cursor.x = 0; cursor.y ++;
467 }
468 else
469 cursor.x += (width = (*i)->CountPositions());
470 if(cursor.y == cpos.y && (cursor.x > cpos.x ||
471 ((*i)->GetType() != WXLO_TYPE_CMD && cursor.x == cpos.x))
472 ) // found it ?
473 {
474 if(offset)
475 *offset = cpos.x-(cursor.x-width); // 0==cursor before
476 // the object
477 #ifdef DEBUG
478 cerr << " found object at " << cursor.x-width << ',' <<
479 cursor.y << ", type:" << _t[(*i)->GetType()] <<endl;
480 #endif
481 return i;
482 }
483 }
484 #ifdef DEBUG
485 cerr << " not found" << endl;
486 #endif
487 return end(); // not found
488 }
489
490 wxLayoutObjectList::iterator
491 wxLayoutList::FindCurrentObject(CoordType *offset)
492 {
493 wxLayoutObjectList::iterator obj = end();
494
495 obj = FindObjectCursor(m_CursorPosition, offset);
496 if(obj == end()) // not ideal yet
497 {
498 obj = tail();
499 if(obj != end()) // tail really exists
500 *offset = (*obj)->CountPositions(); // at the end of it
501 }
502 return obj;
503 }
504
505 void
506 wxLayoutList::MoveCursor(int dx, int dy)
507 {
508 CoordType offs, lineLength;
509 wxLayoutObjectList::iterator i;
510
511
512 if(dy > 0 && m_CursorPosition.y < m_MaxLine)
513 m_CursorPosition.y += dy;
514 else if(dy < 0 && m_CursorPosition.y > 0)
515 m_CursorPosition.y += dy; // dy is negative
516 if(m_CursorPosition.y < 0)
517 m_CursorPosition.y = 0;
518 else if (m_CursorPosition.y > m_MaxLine)
519 m_CursorPosition.y = m_MaxLine;
520
521 while(dx > 0)
522 {
523 i = FindCurrentObject(&offs);
524 lineLength = GetLineLength(i);
525 if(m_CursorPosition.x < lineLength)
526 {
527 m_CursorPosition.x ++;
528 dx--;
529 continue;
530 }
531 else
532 {
533 if(m_CursorPosition.y < m_MaxLine)
534 {
535 m_CursorPosition.y++;
536 m_CursorPosition.x = 0;
537 dx--;
538 }
539 else
540 break; // cannot move there
541 }
542 }
543 while(dx < 0)
544 {
545 if(m_CursorPosition.x > 0)
546 {
547 m_CursorPosition.x --;
548 dx++;
549 }
550 else
551 {
552 if(m_CursorPosition.y > 0)
553 {
554 m_CursorPosition.y --;
555 m_CursorPosition.x = 0;
556 i = FindCurrentObject(&offs);
557 lineLength = GetLineLength(i);
558 m_CursorPosition.x = lineLength;
559 dx++;
560 continue;
561 }
562 else
563 break; // cannot move left any more
564 }
565 }
566 // final adjustment:
567 i = FindCurrentObject(&offs);
568 lineLength = GetLineLength(i);
569 if(m_CursorPosition.x > lineLength)
570 m_CursorPosition.x = lineLength;
571
572 #ifdef DEBUG
573 i = FindCurrentObject(&offs);
574 cerr << "Cursor: "
575 << m_CursorPosition.x << ','
576 << m_CursorPosition.y;
577
578 if(i == end())
579 {
580 cerr << "<<no object found>>" << endl;
581 return; // FIXME we should set cursor position to maximum allowed
582 // value then
583 }
584 if((*i)->GetType() == WXLO_TYPE_TEXT)
585 {
586 cerr << " \"" << ((wxLayoutObjectText *)(*i))->GetText() << "\", offs: "
587 << offs << endl;
588 }
589 else
590 cerr << ' ' << _t[(*i)->GetType()] << endl;
591 #endif
592 }
593
594 void
595 wxLayoutList::Delete(CoordType count)
596 {
597 TRACE(Delete);
598
599 if(!m_Editable)
600 return;
601
602 VAR(count);
603
604 CoordType offs, len;
605 wxLayoutObjectList::iterator i;
606
607 do
608 {
609 i = FindCurrentObject(&offs);
610 if(i == end())
611 return;
612 #ifdef DEBUG
613 cerr << "trying to delete: " << _t[(*i)->GetType()] << endl;
614 #endif
615 if((*i)->GetType() == WXLO_TYPE_LINEBREAK)
616 m_MaxLine--;
617 if((*i)->GetType() == WXLO_TYPE_TEXT)
618 {
619 wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
620 len = tobj->CountPositions();
621 // If we find the end of a text object, this means that we
622 // have to delete from the object following it.
623 if(offs == len)
624 {
625 i++;
626 if((*i)->GetType() == WXLO_TYPE_TEXT)
627 {
628 offs = 0; // delete from begin of next string
629 tobj = (wxLayoutObjectText *)*i;
630 len = tobj->CountPositions();
631 }
632 else
633 {
634 erase(i);
635 return;
636 }
637 }
638 if(len <= count) // delete this object
639 {
640 count -= len;
641 erase(i);
642 }
643 else
644 {
645 len = count;
646 VAR(offs); VAR(len);
647 tobj->GetText().erase(offs,len);
648 return; // we are done
649 }
650 }
651 else // delete the object
652 {
653 len = (*i)->CountPositions();
654 erase(i); // after this, i is the iterator for the following object
655 if(count > len)
656 count -= len;
657 else
658 count = 0;
659 }
660 }
661 while(count && i != end());
662 }
663
664 void
665 wxLayoutList::Insert(wxLayoutObjectBase *obj)
666 {
667 wxASSERT(obj);
668 CoordType offs;
669 wxLayoutObjectList::iterator i = FindCurrentObject(&offs);
670
671 TRACE(Insert(obj));
672
673 if(i == end())
674 push_back(obj);
675 else
676 {
677 // do we have to split a text object?
678 if((*i)->GetType() == WXLO_TYPE_TEXT && offs != 0 && offs != (*i)->CountPositions())
679 {
680 wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
681 #ifdef DEBUG
682 cerr << "text: '" << tobj->GetText() << "'" << endl;
683 VAR(offs);
684 #endif
685 wxString left = tobj->GetText().substr(0,offs); // get part before cursor
686 VAR(left);
687 tobj->GetText() = tobj->GetText().substr(offs,(*i)->CountPositions()-offs); // keeps the right half
688 VAR(tobj->GetText());
689 insert(i,obj);
690 insert(i,new wxLayoutObjectText(left)); // inserts before
691 }
692 else
693 {
694 wxLayoutObjectList::iterator j = i; // we want to apend after this object
695 j++;
696 if(j != end())
697 insert(j, obj);
698 else
699 push_back(obj);
700 }
701 }
702 m_CursorPosition.x += obj->CountPositions();
703 if(obj->GetType() == WXLO_TYPE_LINEBREAK)
704 m_MaxLine++;
705 }
706
707 void
708 wxLayoutList::Insert(wxString const &text)
709 {
710 TRACE(Insert(text));
711
712 if(! m_Editable)
713 return;
714
715 CoordType offs;
716 wxLayoutObjectList::iterator i = FindCurrentObject(&offs);
717
718 if(i != end() && (*i)->GetType() == WXLO_TYPE_TEXT)
719 { // insert into an existing text object:
720 TRACE(inserting into existing object);
721 wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
722 tobj->GetText().insert(offs,text);
723 }
724 else
725 {
726 // check whether the previous object is text:
727 wxLayoutObjectList::iterator j = i;
728 j--;
729 TRACE(checking previous object);
730 if(0 && j != end() && (*j)->GetType() == WXLO_TYPE_TEXT)
731 {
732 wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
733 tobj->GetText()+=text;
734 }
735 else // insert a new text object
736 {
737 TRACE(creating new object);
738 Insert(new wxLayoutObjectText(text)); //FIXME not too optimal, slow
739 return; // position gets incremented in Insert(obj)
740 }
741 }
742 m_CursorPosition.x += strlen(text.c_str());
743 }
744
745 CoordType
746 wxLayoutList::GetLineLength(wxLayoutObjectList::iterator i)
747 {
748 if(i == end())
749 return 0;
750
751 CoordType len = 0;
752
753 // search backwards for beginning of line:
754 while(i != begin() && (*i)->GetType() != WXLO_TYPE_LINEBREAK)
755 i--;
756 if((*i)->GetType() == WXLO_TYPE_LINEBREAK)
757 i++;
758 // now we can start counting:
759 while(i != end() && (*i)->GetType() != WXLO_TYPE_LINEBREAK)
760 {
761 len += (*i)->CountPositions();
762 i++;
763 }
764 return len;
765 }
766
767 void
768 wxLayoutList::Clear(void)
769 {
770 wxLayoutObjectList::iterator i = begin();
771
772 while(i != end()) // == while valid
773 erase(i);
774
775 // set defaults
776 m_FontPtSize = 12;
777 m_FontUnderline = false;
778 m_FontFamily = wxDEFAULT;
779 m_FontStyle = wxNORMAL;
780 m_FontWeight = wxNORMAL;
781 m_ColourFG = wxTheColourDatabase->FindColour("BLACK");
782 m_ColourBG = wxTheColourDatabase->FindColour("WHITE");
783
784 m_Position = wxPoint(0,0);
785 m_CursorPosition = wxPoint(0,0);
786 m_MaxLine = 0;
787 m_LineHeight = (12*m_FontPtSize)/10;
788 m_MaxX = 0; m_MaxY = 0;
789 }