]> git.saurik.com Git - wxWidgets.git/blob - samples/richedit/wxlwindow.cpp
compile and cursor positioning fixes
[wxWidgets.git] / samples / richedit / wxlwindow.cpp
1 /*-*- c++ -*-********************************************************
2 * wxLwindow.h : a scrolled Window for displaying/entering rich text*
3 * *
4 * (C) 1998, 1999 by Karsten Ballüder (Ballueder@usa.net) *
5 * *
6 * $Id$
7 *******************************************************************/
8
9 // ===========================================================================
10 // declarations
11 // ===========================================================================
12
13 // ----------------------------------------------------------------------------
14 // headers
15 // ----------------------------------------------------------------------------
16
17 #ifdef __GNUG__
18 # pragma implementation "wxlwindow.h"
19 #endif
20
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 # pragma hdrstop
25 #endif
26
27 #include "Mpch.h"
28
29 #ifdef M_BASEDIR
30 # ifndef USE_PCH
31 # include "Mcommon.h"
32 # include "gui/wxMenuDefs.h"
33 # include "gui/wxMApp.h"
34 # endif // USE_PCH
35 # include "gui/wxlwindow.h"
36 # include "gui/wxlparser.h"
37 #else
38 # ifdef __WXMSW__
39 # include <wx/msw/private.h>
40 # endif
41
42 # include "wxlwindow.h"
43 # include "wxlparser.h"
44 #endif
45
46 #include <wx/clipbrd.h>
47 #include <wx/textctrl.h>
48 #include <wx/dataobj.h>
49
50 #ifdef WXLAYOUT_USE_CARET
51 # include <wx/caret.h>
52 #endif // WXLAYOUT_USE_CARET
53
54 #include <ctype.h>
55
56 // ----------------------------------------------------------------------------
57 // macros
58 // ----------------------------------------------------------------------------
59
60 #ifdef WXLAYOUT_DEBUG
61 # define WXLO_DEBUG(x) wxLogDebug x
62 #else
63 # define WXLO_DEBUG(x)
64 #endif
65
66 // ----------------------------------------------------------------------------
67 // constants
68 // ----------------------------------------------------------------------------
69
70 /// offsets to put a nice frame around text
71 #define WXLO_XOFFSET 4
72 #define WXLO_YOFFSET 4
73
74 /// offset to the right and bottom for when to redraw scrollbars
75 #define WXLO_ROFFSET 20
76 #define WXLO_BOFFSET 20
77
78 /// the size of one scrollbar page in pixels
79 static const int X_SCROLL_PAGE = 10;
80 static const int Y_SCROLL_PAGE = 20;
81
82 // ----------------------------------------------------------------------------
83 // event tables
84 // ----------------------------------------------------------------------------
85
86 BEGIN_EVENT_TABLE(wxLayoutWindow,wxScrolledWindow)
87 EVT_PAINT (wxLayoutWindow::OnPaint)
88 EVT_CHAR (wxLayoutWindow::OnChar)
89 EVT_KEY_UP (wxLayoutWindow::OnKeyUp)
90 EVT_LEFT_DOWN(wxLayoutWindow::OnLeftMouseClick)
91 EVT_RIGHT_DOWN(wxLayoutWindow::OnRightMouseClick)
92 EVT_LEFT_DCLICK(wxLayoutWindow::OnMouseDblClick)
93 EVT_MOTION (wxLayoutWindow::OnMouseMove)
94 EVT_MENU_RANGE(WXLOWIN_MENU_FIRST, WXLOWIN_MENU_LAST, wxLayoutWindow::OnMenu)
95 EVT_SET_FOCUS(wxLayoutWindow::OnSetFocus)
96 EVT_KILL_FOCUS(wxLayoutWindow::OnKillFocus)
97 END_EVENT_TABLE()
98
99 // ===========================================================================
100 // implementation
101 // ===========================================================================
102
103 /* LEAVE IT HERE UNTIL WXGTK WORKS AGAIN!!! */
104 #ifdef __WXGTK__
105 /// allows me to compare to wxPoints
106 static bool operator != (wxPoint const &p1, wxPoint const &p2)
107 {
108 return p1.x != p2.x || p1.y != p2.y;
109 }
110 #endif // __WXGTK__
111
112 #ifndef wxWANTS_CHARS
113 #define wxWANTS_CHARS 0
114 #endif
115
116 // ----------------------------------------------------------------------------
117 // wxLayoutWindow
118 // ----------------------------------------------------------------------------
119
120 wxLayoutWindow::wxLayoutWindow(wxWindow *parent)
121 : wxScrolledWindow(parent, -1,
122 wxDefaultPosition, wxDefaultSize,
123 wxHSCROLL | wxVSCROLL |
124 wxBORDER |
125 wxWANTS_CHARS)
126 {
127 SetStatusBar(NULL); // don't use statusbar
128 m_Editable = false;
129 m_doSendEvents = false;
130 m_ViewStartX = 0; m_ViewStartY = 0;
131 m_DoPopupMenu = true;
132 m_PopupMenu = MakeFormatMenu();
133 m_memDC = new wxMemoryDC;
134 m_bitmap = new wxBitmap(4,4);
135 m_bitmapSize = wxPoint(4,4);
136 m_llist = new wxLayoutList();
137
138 m_BGbitmap = NULL;
139 m_ScrollToCursor = false;
140 SetWrapMargin(0);
141 wxPoint max = m_llist->GetSize();
142 SetScrollbars(X_SCROLL_PAGE, Y_SCROLL_PAGE,
143 max.x / X_SCROLL_PAGE + 1, max.y / Y_SCROLL_PAGE + 1);
144 EnableScrolling(true, true);
145 m_maxx = max.x + X_SCROLL_PAGE;
146 m_maxy = max.y + Y_SCROLL_PAGE;
147 m_Selecting = false;
148
149 #ifdef WXLAYOUT_USE_CARET
150 // FIXME cursor size shouldn't be hardcoded
151 wxCaret *caret = new wxCaret(this, 2, 20);
152 SetCaret(caret);
153 m_llist->SetCaret(caret);
154 caret->Show();
155 #endif // WXLAYOUT_USE_CARET
156
157 SetCursorVisibility(-1);
158 SetCursor(wxCURSOR_IBEAM);
159 SetDirty();
160 }
161
162 wxLayoutWindow::~wxLayoutWindow()
163 {
164 delete m_memDC; // deletes bitmap automatically (?)
165 delete m_bitmap;
166 delete m_llist;
167 delete m_PopupMenu;
168 SetBackgroundBitmap(NULL);
169 }
170
171 void
172 wxLayoutWindow::Clear(int family,
173 int size,
174 int style,
175 int weight,
176 int underline,
177 wxColour *fg,
178 wxColour *bg)
179 {
180 GetLayoutList()->Clear(family,size,style,weight,underline,fg,bg);
181 SetBackgroundColour(GetLayoutList()->GetDefaultStyleInfo().GetBGColour());
182 ResizeScrollbars(true);
183 SetDirty();
184 SetModified(false);
185 wxScrolledWindow::Clear();
186 DoPaint((wxRect *)NULL);
187 }
188
189 void
190 wxLayoutWindow::OnMouse(int eventId, wxMouseEvent& event)
191 {
192 wxClientDC dc( this );
193 PrepareDC( dc );
194 if ( eventId != WXLOWIN_MENU_MOUSEMOVE )
195 {
196 // moving the mouse in a window shouldn't give it the focus!
197 SetFocus();
198 }
199
200 wxPoint findPos;
201 findPos.x = dc.DeviceToLogicalX(event.GetX());
202 findPos.y = dc.DeviceToLogicalY(event.GetY());
203
204 findPos.x -= WXLO_XOFFSET;
205 findPos.y -= WXLO_YOFFSET;
206
207 if(findPos.x < 0) findPos.x = 0;
208 if(findPos.y < 0) findPos.y = 0;
209
210 m_ClickPosition = wxPoint(event.GetX(), event.GetY());
211
212 wxPoint cursorPos;
213 bool found;
214 wxLayoutObject *obj = m_llist->FindObjectScreen(dc, findPos,
215 &cursorPos, &found);
216 wxLayoutObject::UserData *u = obj ? obj->GetUserData() : NULL;
217
218 //has the mouse only been moved?
219 if(eventId == WXLOWIN_MENU_MOUSEMOVE)
220 {
221 // found is only true if we are really over an object, not just
222 // behind it
223 if(found && u && ! m_Selecting)
224 {
225 if(!m_HandCursor)
226 SetCursor(wxCURSOR_HAND);
227 m_HandCursor = TRUE;
228 if(m_StatusBar && m_StatusFieldLabel != -1)
229 {
230 const wxString &label = u->GetLabel();
231 if(label.Length())
232 m_StatusBar->SetStatusText(label,
233 m_StatusFieldLabel);
234 }
235 }
236 else
237 {
238 if(m_HandCursor)
239 SetCursor(wxCURSOR_IBEAM);
240 m_HandCursor = FALSE;
241 if(m_StatusBar && m_StatusFieldLabel != -1)
242 m_StatusBar->SetStatusText("", m_StatusFieldLabel);
243 }
244 if(event.LeftIsDown())
245 {
246 if(! m_Selecting)
247 {
248 m_llist->StartSelection(wxPoint(-1, -1), m_ClickPosition);
249 m_Selecting = true;
250 DoPaint(); // TODO: we don't have to redraw everything!
251 }
252 else
253 {
254 m_llist->ContinueSelection(cursorPos, m_ClickPosition);
255 DoPaint(); // TODO: we don't have to redraw everything!
256 }
257 }
258 if(m_Selecting && ! event.LeftIsDown())
259 {
260 m_llist->EndSelection(cursorPos, m_ClickPosition);
261 m_Selecting = false;
262 DoPaint(); // TODO: we don't have to redraw everything!
263 }
264
265 if ( u )
266 {
267 u->DecRef();
268 u = NULL;
269 }
270 }
271 else if(eventId == WXLOWIN_MENU_LCLICK)
272 {
273 // always move cursor to mouse click:
274 m_llist->MoveCursorTo(cursorPos);
275
276 // clicking a mouse removes the selection
277 if ( m_llist->HasSelection() )
278 {
279 m_llist->DiscardSelection();
280 DoPaint(); // TODO: we don't have to redraw everything!
281 }
282
283 // Calculate where the top of the visible area is:
284 int x0, y0;
285 ViewStart(&x0,&y0);
286 int dx, dy;
287 GetScrollPixelsPerUnit(&dx, &dy);
288 x0 *= dx; y0 *= dy;
289
290 wxPoint offset(-x0+WXLO_XOFFSET, -y0+WXLO_YOFFSET);
291
292 if(m_CursorVisibility == -1)
293 m_CursorVisibility = 1;
294
295 if(m_CursorVisibility != 0)
296 {
297 // draw a thick cursor for editable windows with focus
298 m_llist->DrawCursor(dc, m_HaveFocus && IsEditable(), offset);
299 }
300
301 // VZ: this should be unnecessary because mouse can only click on a
302 // visible part of the canvas
303 #if 0
304 ScrollToCursor();
305 #endif // 0
306
307 #ifdef __WXGTK__
308 DoPaint(); // DoPaint suppresses flicker under GTK
309 #endif // wxGTK
310 }
311
312 // notify about mouse events?
313 if( m_doSendEvents )
314 {
315 // only do the menu if activated, editable and not on a clickable object
316 if(eventId == WXLOWIN_MENU_RCLICK
317 && IsEditable()
318 && (! obj || u == NULL))
319 {
320 PopupMenu(m_PopupMenu, m_ClickPosition.x, m_ClickPosition.y);
321 if(u) u->DecRef();
322 return;
323 }
324
325 // find the object at this position
326 if(obj)
327 {
328 wxCommandEvent commandEvent(wxEVT_COMMAND_MENU_SELECTED, eventId);
329 commandEvent.SetEventObject( this );
330 commandEvent.SetClientData((char *)obj);
331 GetEventHandler()->ProcessEvent(commandEvent);
332 }
333 }
334
335 if( u )
336 u->DecRef();
337 }
338
339 /*
340 * Some simple keyboard handling.
341 */
342 void
343 wxLayoutWindow::OnChar(wxKeyEvent& event)
344 {
345 int keyCode = event.KeyCode();
346
347 #ifdef WXLAYOUT_DEBUG
348 if(keyCode == WXK_F1)
349 {
350 m_llist->Debug();
351 return;
352 }
353 #endif
354
355 if(! m_Selecting && event.ShiftDown())
356 {
357 switch(keyCode)
358 {
359 case WXK_UP:
360 case WXK_DOWN:
361 case WXK_RIGHT:
362 case WXK_LEFT:
363 case WXK_PRIOR:
364 case WXK_NEXT:
365 case WXK_HOME:
366 case WXK_END:
367 m_Selecting = true;
368 m_llist->StartSelection();
369 break;
370 default:
371 ;
372 }
373 }
374
375 // If needed, make cursor visible:
376 if(m_CursorVisibility == -1)
377 m_CursorVisibility = 1;
378
379 /* These two nested switches work like this:
380 The first one processes all non-editing keycodes, to move the
381 cursor, etc. It's default will process all keycodes causing
382 modifications to the buffer, but only if editing is allowed.
383 */
384 bool ctrlDown = event.ControlDown();
385 switch(keyCode)
386 {
387 case WXK_RIGHT:
388 if ( ctrlDown )
389 m_llist->MoveCursorWord(1);
390 else
391 m_llist->MoveCursorHorizontally(1);
392 break;
393 case WXK_LEFT:
394 if ( ctrlDown )
395 m_llist->MoveCursorWord(-1);
396 else
397 m_llist->MoveCursorHorizontally(-1);
398 break;
399 case WXK_UP:
400 m_llist->MoveCursorVertically(-1);
401 break;
402 case WXK_DOWN:
403 m_llist->MoveCursorVertically(1);
404 break;
405 case WXK_PRIOR:
406 m_llist->MoveCursorVertically(-Y_SCROLL_PAGE);
407 break;
408 case WXK_NEXT:
409 m_llist->MoveCursorVertically(Y_SCROLL_PAGE);
410 break;
411 case WXK_HOME:
412 if ( ctrlDown )
413 m_llist->MoveCursorTo(wxPoint(0, 0));
414 else
415 m_llist->MoveCursorToBeginOfLine();
416 break;
417 case WXK_END:
418 if ( ctrlDown )
419 m_llist->MoveCursorTo(m_llist->GetSize());
420 else
421 m_llist->MoveCursorToEndOfLine();
422 break;
423
424 default:
425 if(keyCode == 'c' && ctrlDown)
426 {
427 // this should work even in read-only mode
428 Copy();
429 }
430 else if( IsEditable() )
431 {
432 /* First, handle control keys */
433 if(event.ControlDown() && ! event.AltDown())
434 {
435 switch(keyCode)
436 {
437 case WXK_INSERT:
438 Copy();
439 break;
440 case WXK_DELETE :
441 case 'd':
442 m_llist->Delete(1);
443 break;
444 case 'y':
445 m_llist->DeleteLines(1);
446 break;
447 case 'h': // like backspace
448 if(m_llist->MoveCursorHorizontally(-1)) m_llist->Delete(1);
449 break;
450 case 'u':
451 m_llist->DeleteToBeginOfLine();
452 break;
453 case 'k':
454 m_llist->DeleteToEndOfLine();
455 break;
456 case 'v':
457 Paste();
458 break;
459 case 'x':
460 Cut();
461 break;
462 #ifdef WXLAYOUT_DEBUG
463 case WXK_F1:
464 m_llist->SetFont(-1,-1,-1,-1,true); // underlined
465 break;
466 #endif
467 default:
468 ;
469 }
470 }
471 // ALT only:
472 else if( event.AltDown() && ! event.ControlDown() )
473 {
474 switch(keyCode)
475 {
476 case WXK_DELETE:
477 case 'd':
478 m_llist->DeleteWord();
479 break;
480 default:
481 ;
482 }
483 }
484 // no control keys:
485 else if ( ! event.AltDown() && ! event.ControlDown())
486 {
487 switch(keyCode)
488 {
489 case WXK_INSERT:
490 if(event.ShiftDown())
491 Paste();
492 break;
493 case WXK_DELETE :
494 if(event.ShiftDown())
495 Cut();
496 else
497 m_llist->Delete(1);
498 break;
499 case WXK_BACK: // backspace
500 if(m_llist->MoveCursorHorizontally(-1)) m_llist->Delete(1);
501 break;
502 case WXK_RETURN:
503 if(m_WrapMargin > 0)
504 m_llist->WrapLine(m_WrapMargin);
505 m_llist->LineBreak();
506 break;
507 default:
508 if((!(event.ControlDown() || event.AltDown() || event.MetaDown()))
509 && (keyCode < 256 && keyCode >= 32)
510 )
511 {
512 if(m_WrapMargin > 0 && isspace(keyCode))
513 m_llist->WrapLine(m_WrapMargin);
514 m_llist->Insert((char)keyCode);
515 }
516 break;
517 }
518 }
519 SetDirty();
520 SetModified();
521 }// if(IsEditable())
522 }// first switch()
523 if(m_Selecting)
524 {
525 if(event.ShiftDown())
526 m_llist->ContinueSelection();
527 else
528 {
529 m_llist->EndSelection();
530 m_Selecting = false;
531 }
532 }
533
534 // we must call ResizeScrollbars() before ScrollToCursor(), otherwise the
535 // ne cursor position might be outside the current scrolllbar range
536 ResizeScrollbars();
537 ScrollToCursor();
538
539 // refresh the screen
540 DoPaint(m_llist->GetUpdateRect());
541 }
542
543 void
544 wxLayoutWindow::OnKeyUp(wxKeyEvent& event)
545 {
546 if(event.KeyCode() == WXK_SHIFT && m_llist->IsSelecting())
547 {
548 m_llist->EndSelection();
549 m_Selecting = false;
550 }
551 event.Skip();
552 }
553
554
555 void
556 wxLayoutWindow::ScrollToCursor(void)
557 {
558 wxClientDC dc( this );
559 PrepareDC( dc );
560
561 int x0,y0,x1,y1, dx, dy;
562
563 // Calculate where the top of the visible area is:
564 ViewStart(&x0,&y0);
565 GetScrollPixelsPerUnit(&dx, &dy);
566 x0 *= dx; y0 *= dy;
567
568 WXLO_DEBUG(("ScrollToCursor: ViewStart is %d/%d", x0, y0));
569
570 // Get the size of the visible window:
571 GetClientSize(&x1,&y1);
572
573 // notice that the client size may be (0, 0)...
574 wxASSERT(x1 >= 0 && y1 >= 0);
575
576 // VZ: I think this is false - if you do it here, ResizeScrollbars() won't
577 // call SetScrollbars() later
578 #if 0
579 // As we have the values anyway, use them to avoid unnecessary scrollbar
580 // updates.
581 if(x1 > m_maxx) m_maxx = x1;
582 if(y1 > m_maxy) m_maxy = y1;
583 #endif // 0
584
585 // Make sure that the scrollbars are at a position so that the cursor is
586 // visible if we are editing
587 WXLO_DEBUG(("m_ScrollToCursor = %d", (int) m_ScrollToCursor));
588 wxPoint cc = m_llist->GetCursorScreenPos(dc);
589
590 // the cursor should be completely visible in both directions
591 wxPoint cs(m_llist->GetCursorSize());
592 int nx = -1,
593 ny = -1;
594 if ( cc.x < x0 || cc.x >= x0 + x1 - cs.x )
595 {
596 nx = cc.x - x1/2;
597 if ( nx < 0 )
598 nx = 0;
599 }
600
601 if ( cc.y < y0 || cc.y >= y0 + y1 - cs.y )
602 {
603 ny = cc.y - y1/2;
604 if ( ny < 0)
605 ny = 0;
606 }
607
608 if ( nx != -1 || ny != -1 )
609 {
610 // set new view start
611 Scroll(nx == -1 ? -1 : (nx+dx-1)/dx, ny == -1 ? -1 : (ny+dy-1)/dy);
612
613 // avoid recursion
614 m_ScrollToCursor = false;
615 }
616 }
617
618 void
619 wxLayoutWindow::OnPaint( wxPaintEvent &WXUNUSED(event))
620 {
621 wxRect region = GetUpdateRegion().GetBox();
622 InternalPaint(&region);
623 }
624
625 void
626 wxLayoutWindow::DoPaint(const wxRect *updateRect)
627 {
628 // Causes bad flicker under wxGTK!!!
629 #ifdef __WXGTK__
630 InternalPaint(updateRect);
631 #else
632 Refresh(FALSE); //, updateRect);
633
634 if ( !::UpdateWindow(GetHwnd()) )
635 wxLogLastError("UpdateWindow");
636 #endif
637 }
638
639 void
640 wxLayoutWindow::InternalPaint(const wxRect *updateRect)
641 {
642 wxPaintDC dc( this );
643 PrepareDC( dc );
644
645 #ifdef WXLAYOUT_USE_CARET
646 // hide the caret before drawing anything
647 GetCaret()->Hide();
648 #endif // WXLAYOUT_USE_CARET
649
650 int x0,y0,x1,y1, dx, dy;
651
652 // Calculate where the top of the visible area is:
653 ViewStart(&x0,&y0);
654 GetScrollPixelsPerUnit(&dx, &dy);
655 x0 *= dx; y0 *= dy;
656
657 // Get the size of the visible window:
658 GetClientSize(&x1,&y1);
659 wxASSERT(x1 >= 0);
660 wxASSERT(y1 >= 0);
661
662 if(updateRect)
663 {
664 WXLO_DEBUG(("Update rect: %ld,%ld / %ld,%ld",
665 updateRect->x, updateRect->y,
666 updateRect->x+updateRect->width,
667 updateRect->y+updateRect->height));
668 }
669 if(IsDirty())
670 {
671 m_llist->Layout(dc);
672 ResizeScrollbars();
673 }
674 /* Check whether the window has grown, if so, we need to reallocate
675 the bitmap to be larger. */
676 if(x1 > m_bitmapSize.x || y1 > m_bitmapSize.y)
677 {
678 wxASSERT(m_bitmapSize.x > 0);
679 wxASSERT(m_bitmapSize.y > 0);
680
681 m_memDC->SelectObject(wxNullBitmap);
682 delete m_bitmap;
683 m_bitmapSize = wxPoint(x1,y1);
684 m_bitmap = new wxBitmap(x1,y1);
685 m_memDC->SelectObject(*m_bitmap);
686 }
687
688 m_memDC->SetDeviceOrigin(0,0);
689 m_memDC->SetBrush(wxBrush(m_llist->GetDefaultStyleInfo().GetBGColour(),wxSOLID));
690 m_memDC->SetPen(wxPen(m_llist->GetDefaultStyleInfo().GetBGColour(),
691 0,wxTRANSPARENT));
692 m_memDC->SetLogicalFunction(wxCOPY);
693 m_memDC->Clear();
694
695 // fill the background with the background bitmap
696 if(m_BGbitmap)
697 {
698 CoordType
699 y, x,
700 w = m_BGbitmap->GetWidth(),
701 h = m_BGbitmap->GetHeight();
702 for(y = 0; y < y1; y+=h)
703 for(x = 0; x < x1; x+=w)
704 m_memDC->DrawBitmap(*m_BGbitmap, x, y);
705 m_memDC->SetBackgroundMode(wxTRANSPARENT);
706 }
707
708 // This is the important bit: we tell the list to draw itself
709 #if WXLO_DEBUG_URECT
710 if(updateRect)
711 {
712 WXLO_DEBUG(("Update rect: %ld,%ld / %ld,%ld",
713 updateRect->x, updateRect->y,
714 updateRect->x+updateRect->width,
715 updateRect->y+updateRect->height));
716 }
717 #endif
718
719 // Device origins on the memDC are suspect, we translate manually
720 // with the translate parameter of Draw().
721 wxPoint offset(-x0+WXLO_XOFFSET,-y0+WXLO_YOFFSET);
722 m_llist->Draw(*m_memDC,offset, y0, y0+y1);
723
724 // We start calculating a new update rect before drawing the
725 // cursor, so that the cursor coordinates get included in the next
726 // update rectangle (although they are drawn on the memDC, this is
727 // needed to erase it):
728 m_llist->InvalidateUpdateRect();
729 if(m_CursorVisibility != 0)
730 {
731 // draw a thick cursor for editable windows with focus
732 m_llist->DrawCursor(*m_memDC,
733 m_HaveFocus && IsEditable(),
734 offset);
735 }
736
737 // Now copy everything to the screen:
738 #if 0
739 // This somehow doesn't work, but even the following bit with the
740 // whole rect at once is still a bit broken I think.
741 wxRegionIterator ri ( GetUpdateRegion() );
742 if(ri)
743 while(ri)
744 {
745 WXLO_DEBUG(("UpdateRegion: %ld,%ld, %ld,%ld",
746 ri.GetX(),ri.GetY(),ri.GetW(),ri.GetH()));
747 dc.Blit(x0+ri.GetX(),y0+ri.GetY(),ri.GetW(),ri.GetH(),
748 m_memDC,ri.GetX(),ri.GetY(),wxCOPY,FALSE);
749 ri++;
750 }
751 else
752 #endif
753 {
754 // FIXME: Trying to copy only the changed parts, but it does not seem
755 // to work:
756 // x0 = updateRect->x; y0 = updateRect->y;
757 // if(updateRect->height < y1)
758 // y1 = updateRect->height;
759 // y1 += WXLO_YOFFSET; //FIXME might not be needed
760 dc.Blit(x0,y0,x1,y1,m_memDC,0,0,wxCOPY,FALSE);
761 }
762
763 #ifdef WXLAYOUT_USE_CARET
764 // show the caret back after everything is redrawn
765 m_caret->Show();
766 #endif // WXLAYOUT_USE_CARET
767
768 ResetDirty();
769 m_ScrollToCursor = false;
770
771 if ( m_StatusBar && m_StatusFieldCursor != -1 )
772 {
773 static wxPoint s_oldCursorPos(-1, -1);
774
775 wxPoint pos(m_llist->GetCursorPos());
776
777 // avoid unnecessary status bar refreshes
778 if ( pos != s_oldCursorPos )
779 {
780 s_oldCursorPos = pos;
781
782 wxString label;
783 label.Printf(_("Ln:%d Col:%d"), pos.y + 1, pos.x + 1);
784 m_StatusBar->SetStatusText(label, m_StatusFieldCursor);
785 }
786 }
787 }
788
789 // change the range and position of scrollbars
790 void
791 wxLayoutWindow::ResizeScrollbars(bool exact)
792 {
793 wxPoint max = m_llist->GetSize();
794
795 WXLO_DEBUG(("ResizeScrollbars: max size = (%ld, %ld)",
796 (long int)max.x, (long int) max.y));
797
798 if( max.x > m_maxx - WXLO_ROFFSET || max.y > m_maxy - WXLO_BOFFSET || exact )
799 {
800 if ( !exact )
801 {
802 // add an extra bit to the sizes to avoid future updates
803 max.x += WXLO_ROFFSET;
804 max.y += WXLO_BOFFSET;
805 }
806
807 ViewStart(&m_ViewStartX, &m_ViewStartY);
808 SetScrollbars(X_SCROLL_PAGE, Y_SCROLL_PAGE,
809 max.x / X_SCROLL_PAGE + 1, max.y / Y_SCROLL_PAGE + 1,
810 m_ViewStartX, m_ViewStartY,
811 true);
812
813 m_maxx = max.x + X_SCROLL_PAGE;
814 m_maxy = max.y + Y_SCROLL_PAGE;
815 }
816 }
817
818 void
819 wxLayoutWindow::Paste(void)
820 {
821 // Read some text
822 if (wxTheClipboard->Open())
823 {
824 #if wxUSE_PRIVATE_CLIPBOARD_FORMAT
825 wxLayoutDataObject wxldo;
826 if (wxTheClipboard->IsSupported( wxldo.GetFormat() ))
827 {
828 wxTheClipboard->GetData(&wxldo);
829 {
830 }
831 //FIXME: missing functionality m_llist->Insert(wxldo.GetList());
832 }
833 else
834 #endif
835 {
836 wxTextDataObject data;
837 if (wxTheClipboard->IsSupported( data.GetFormat() ))
838 {
839 wxTheClipboard->GetData(&data);
840 wxString text = data.GetText();
841 wxLayoutImportText( m_llist, text);
842 }
843 }
844 wxTheClipboard->Close();
845 }
846
847 #if 0
848 /* My attempt to get the primary selection, but it does not
849 work. :-( */
850 if(text.Length() == 0)
851 {
852 wxTextCtrl tmp_tctrl(this,-1);
853 tmp_tctrl.Paste();
854 text += tmp_tctrl.GetValue();
855 }
856 #endif
857 }
858
859 bool
860 wxLayoutWindow::Copy(bool invalidate)
861 {
862 // Calling GetSelection() will automatically do an EndSelection()
863 // on the list, but we need to take a note of it, too:
864 if(m_Selecting)
865 {
866 m_Selecting = false;
867 m_llist->EndSelection();
868 }
869
870 wxLayoutDataObject wldo;
871 wxLayoutList *llist = m_llist->GetSelection(&wldo, invalidate);
872 if(! llist)
873 return FALSE;
874 // Export selection as text:
875 wxString text;
876 wxLayoutExportObject *export;
877 wxLayoutExportStatus status(llist);
878 while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_TEXT)) != NULL)
879 {
880 if(export->type == WXLO_EXPORT_TEXT)
881 text << *(export->content.text);
882 delete export;
883 }
884 delete llist;
885
886 // The exporter always appends a newline, so we chop it off if it
887 // is there:
888 {
889 size_t len = text.Length();
890 if(len > 2 && text[len-2] == '\r') // Windows
891 text = text.Mid(0,len-2);
892 else if(len > 1 && text[len-1] == '\n')
893 text = text.Mid(0,len-1);
894 }
895
896
897 if (wxTheClipboard->Open())
898 {
899 wxTextDataObject *data = new wxTextDataObject( text );
900 bool rc = wxTheClipboard->SetData( data );
901 #if wxUSE_PRIVATE_CLIPBOARD_FORMAT
902 rc |= wxTheClipboard->AddData( &wldo );
903 #endif
904 wxTheClipboard->Close();
905 return rc;
906 }
907
908 return FALSE;
909 }
910
911 bool
912 wxLayoutWindow::Cut(void)
913 {
914 if(Copy(false)) // do not invalidate selection after copy
915 {
916 m_llist->DeleteSelection();
917 return TRUE;
918 }
919 else
920 return FALSE;
921 }
922 bool
923 wxLayoutWindow::Find(const wxString &needle,
924 wxPoint * fromWhere)
925 {
926 wxPoint found;
927
928 if(fromWhere == NULL)
929 found = m_llist->FindText(needle, m_llist->GetCursorPos());
930 else
931 found = m_llist->FindText(needle, *fromWhere);
932 if(found.x != -1)
933 {
934 if(fromWhere)
935 {
936 *fromWhere = found;
937 fromWhere->x ++;
938 }
939 m_llist->MoveCursorTo(found);
940 ScrollToCursor();
941 return true;
942 }
943 return false;
944 }
945
946 wxMenu *
947 wxLayoutWindow::MakeFormatMenu()
948 {
949 wxMenu *m = new wxMenu(_("Layout Menu"));
950
951 m->Append(WXLOWIN_MENU_LARGER ,_("&Larger"),_("Switch to larger font."), false);
952 m->Append(WXLOWIN_MENU_SMALLER ,_("&Smaller"),_("Switch to smaller font."), false);
953 m->AppendSeparator();
954 m->Append(WXLOWIN_MENU_UNDERLINE_ON, _("&Underline on"),_("Activate underline mode."), false);
955 m->Append(WXLOWIN_MENU_UNDERLINE_OFF,_("&Underline off"),_("Deactivate underline mode."), false);
956 m->Append(WXLOWIN_MENU_BOLD_ON ,_("&Bold on"),_("Activate bold mode."), false);
957 m->Append(WXLOWIN_MENU_BOLD_OFF ,_("&Bold off"),_("Deactivate bold mode."), false);
958 m->Append(WXLOWIN_MENU_ITALICS_ON ,_("&Italics on"),_("Activate italics mode."), false);
959 m->Append(WXLOWIN_MENU_ITALICS_OFF ,_("&Italics off"),_("Deactivate italics mode."), false);
960 m->AppendSeparator();
961 m->Append(WXLOWIN_MENU_ROMAN ,_("&Roman"),_("Switch to roman font."), false);
962 m->Append(WXLOWIN_MENU_TYPEWRITER,_("&Typewriter"),_("Switch to typewriter font."), false);
963 m->Append(WXLOWIN_MENU_SANSSERIF ,_("&Sans Serif"),_("Switch to sans serif font."), false);
964 return m;
965 }
966
967 void wxLayoutWindow::OnMenu(wxCommandEvent& event)
968 {
969 switch (event.GetId())
970 {
971 case WXLOWIN_MENU_LARGER:
972 m_llist->SetFontLarger(); break;
973 case WXLOWIN_MENU_SMALLER:
974 m_llist->SetFontSmaller(); break;
975 case WXLOWIN_MENU_UNDERLINE_ON:
976 m_llist->SetFontUnderline(true); break;
977 case WXLOWIN_MENU_UNDERLINE_OFF:
978 m_llist->SetFontUnderline(false); break;
979 case WXLOWIN_MENU_BOLD_ON:
980 m_llist->SetFontWeight(wxBOLD); break;
981 case WXLOWIN_MENU_BOLD_OFF:
982 m_llist->SetFontWeight(wxNORMAL); break;
983 case WXLOWIN_MENU_ITALICS_ON:
984 m_llist->SetFontStyle(wxITALIC); break;
985 case WXLOWIN_MENU_ITALICS_OFF:
986 m_llist->SetFontStyle(wxNORMAL); break;
987 case WXLOWIN_MENU_ROMAN:
988 m_llist->SetFontFamily(wxROMAN); break;
989 case WXLOWIN_MENU_TYPEWRITER:
990 m_llist->SetFontFamily(wxFIXED); break;
991 case WXLOWIN_MENU_SANSSERIF:
992 m_llist->SetFontFamily(wxSWISS); break;
993 }
994 }
995
996 void
997 wxLayoutWindow::OnSetFocus(wxFocusEvent &ev)
998 {
999 m_HaveFocus = true;
1000 ev.Skip();
1001 }
1002
1003 void
1004 wxLayoutWindow::OnKillFocus(wxFocusEvent &ev)
1005 {
1006 m_HaveFocus = false;
1007 ev.Skip();
1008 }