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