]> git.saurik.com Git - wxWidgets.git/blob - samples/richedit/wxlwindow.cpp
Formerly known as wxLayout.
[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 m_llist->UpdateCursorScreenPos(dc, true, offset);
292
293 if(m_CursorVisibility == -1)
294 m_CursorVisibility = 1;
295
296 // VZ: this should be unnecessary because mouse can only click on a
297 // visible part of the canvas
298 #if 0
299 ScrollToCursor();
300 #endif // 0
301
302 #ifdef __WXGTK__
303 DoPaint(); // DoPaint suppresses flicker under GTK
304 #endif // wxGTK
305 }
306
307 // notify about mouse events?
308 if( m_doSendEvents )
309 {
310 // only do the menu if activated, editable and not on a clickable object
311 if(eventId == WXLOWIN_MENU_RCLICK
312 && IsEditable()
313 && (! obj || u == NULL))
314 {
315 PopupMenu(m_PopupMenu, m_ClickPosition.x, m_ClickPosition.y);
316 if(u) u->DecRef();
317 return;
318 }
319
320 // find the object at this position
321 if(obj)
322 {
323 wxCommandEvent commandEvent(wxEVT_COMMAND_MENU_SELECTED, eventId);
324 commandEvent.SetEventObject( this );
325 commandEvent.SetClientData((char *)obj);
326 GetEventHandler()->ProcessEvent(commandEvent);
327 }
328 }
329
330 if( u )
331 u->DecRef();
332 }
333
334 /*
335 * Some simple keyboard handling.
336 */
337 void
338 wxLayoutWindow::OnChar(wxKeyEvent& event)
339 {
340 int keyCode = event.KeyCode();
341
342 #ifdef WXLAYOUT_DEBUG
343 if(keyCode == WXK_F1)
344 {
345 m_llist->Debug();
346 return;
347 }
348 #endif
349
350 if(! m_Selecting && event.ShiftDown())
351 {
352 switch(keyCode)
353 {
354 case WXK_UP:
355 case WXK_DOWN:
356 case WXK_RIGHT:
357 case WXK_LEFT:
358 case WXK_PRIOR:
359 case WXK_NEXT:
360 case WXK_HOME:
361 case WXK_END:
362 m_Selecting = true;
363 m_llist->StartSelection();
364 break;
365 default:
366 ;
367 }
368 }
369
370 // If needed, make cursor visible:
371 if(m_CursorVisibility == -1)
372 m_CursorVisibility = 1;
373
374 /* These two nested switches work like this:
375 The first one processes all non-editing keycodes, to move the
376 cursor, etc. It's default will process all keycodes causing
377 modifications to the buffer, but only if editing is allowed.
378 */
379 bool ctrlDown = event.ControlDown();
380 switch(keyCode)
381 {
382 case WXK_RIGHT:
383 if ( ctrlDown )
384 m_llist->MoveCursorWord(1);
385 else
386 m_llist->MoveCursorHorizontally(1);
387 break;
388 case WXK_LEFT:
389 if ( ctrlDown )
390 m_llist->MoveCursorWord(-1);
391 else
392 m_llist->MoveCursorHorizontally(-1);
393 break;
394 case WXK_UP:
395 m_llist->MoveCursorVertically(-1);
396 break;
397 case WXK_DOWN:
398 m_llist->MoveCursorVertically(1);
399 break;
400 case WXK_PRIOR:
401 m_llist->MoveCursorVertically(-Y_SCROLL_PAGE);
402 break;
403 case WXK_NEXT:
404 m_llist->MoveCursorVertically(Y_SCROLL_PAGE);
405 break;
406 case WXK_HOME:
407 if ( ctrlDown )
408 m_llist->MoveCursorTo(wxPoint(0, 0));
409 else
410 m_llist->MoveCursorToBeginOfLine();
411 break;
412 case WXK_END:
413 if ( ctrlDown )
414 m_llist->MoveCursorTo(m_llist->GetSize());
415 else
416 m_llist->MoveCursorToEndOfLine();
417 break;
418
419 default:
420 if(keyCode == 'c' && ctrlDown)
421 {
422 // this should work even in read-only mode
423 Copy();
424 }
425 else if( IsEditable() )
426 {
427 /* First, handle control keys */
428 if(event.ControlDown() && ! event.AltDown())
429 {
430 switch(keyCode)
431 {
432 case WXK_INSERT:
433 Copy();
434 break;
435 case WXK_DELETE :
436 case 'd':
437 m_llist->Delete(1);
438 break;
439 case 'y':
440 m_llist->DeleteLines(1);
441 break;
442 case 'h': // like backspace
443 if(m_llist->MoveCursorHorizontally(-1)) m_llist->Delete(1);
444 break;
445 case 'u':
446 m_llist->DeleteToBeginOfLine();
447 break;
448 case 'k':
449 m_llist->DeleteToEndOfLine();
450 break;
451 case 'v':
452 Paste();
453 break;
454 case 'x':
455 Cut();
456 break;
457 #ifdef WXLAYOUT_DEBUG
458 case WXK_F1:
459 m_llist->SetFont(-1,-1,-1,-1,true); // underlined
460 break;
461 #endif
462 default:
463 ;
464 }
465 }
466 // ALT only:
467 else if( event.AltDown() && ! event.ControlDown() )
468 {
469 switch(keyCode)
470 {
471 case WXK_DELETE:
472 case 'd':
473 m_llist->DeleteWord();
474 break;
475 default:
476 ;
477 }
478 }
479 // no control keys:
480 else if ( ! event.AltDown() && ! event.ControlDown())
481 {
482 switch(keyCode)
483 {
484 case WXK_INSERT:
485 if(event.ShiftDown())
486 Paste();
487 break;
488 case WXK_DELETE :
489 if(event.ShiftDown())
490 Cut();
491 else
492 m_llist->Delete(1);
493 break;
494 case WXK_BACK: // backspace
495 if(m_llist->MoveCursorHorizontally(-1)) m_llist->Delete(1);
496 break;
497 case WXK_RETURN:
498 if(m_WrapMargin > 0)
499 m_llist->WrapLine(m_WrapMargin);
500 m_llist->LineBreak();
501 break;
502 default:
503 if((!(event.ControlDown() || event.AltDown() || event.MetaDown()))
504 && (keyCode < 256 && keyCode >= 32)
505 )
506 {
507 if(m_WrapMargin > 0 && isspace(keyCode))
508 m_llist->WrapLine(m_WrapMargin);
509 m_llist->Insert((char)keyCode);
510 }
511 break;
512 }
513 }
514 SetDirty();
515 SetModified();
516 }// if(IsEditable())
517 }// first switch()
518 if(m_Selecting)
519 {
520 if(event.ShiftDown())
521 m_llist->ContinueSelection();
522 else
523 {
524 m_llist->EndSelection();
525 m_Selecting = false;
526 }
527 }
528
529 // we must call ResizeScrollbars() before ScrollToCursor(), otherwise the
530 // ne cursor position might be outside the current scrolllbar range
531 ResizeScrollbars();
532 ScrollToCursor();
533
534 // refresh the screen
535 DoPaint(m_llist->GetUpdateRect());
536 }
537
538 void
539 wxLayoutWindow::OnKeyUp(wxKeyEvent& event)
540 {
541 if(event.KeyCode() == WXK_SHIFT && m_llist->IsSelecting())
542 {
543 m_llist->EndSelection();
544 m_Selecting = false;
545 }
546 event.Skip();
547 }
548
549
550 void
551 wxLayoutWindow::ScrollToCursor(void)
552 {
553 wxClientDC dc( this );
554 PrepareDC( dc );
555
556 int x0,y0,x1,y1, dx, dy;
557
558 // Calculate where the top of the visible area is:
559 ViewStart(&x0,&y0);
560 GetScrollPixelsPerUnit(&dx, &dy);
561 x0 *= dx; y0 *= dy;
562
563 WXLO_DEBUG(("ScrollToCursor: ViewStart is %d/%d", x0, y0));
564
565 // Get the size of the visible window:
566 GetClientSize(&x1,&y1);
567
568 // notice that the client size may be (0, 0)...
569 wxASSERT(x1 >= 0 && y1 >= 0);
570
571 // VZ: I think this is false - if you do it here, ResizeScrollbars() won't
572 // call SetScrollbars() later
573 #if 0
574 // As we have the values anyway, use them to avoid unnecessary scrollbar
575 // updates.
576 if(x1 > m_maxx) m_maxx = x1;
577 if(y1 > m_maxy) m_maxy = y1;
578 #endif // 0
579
580 // Make sure that the scrollbars are at a position so that the cursor is
581 // visible if we are editing
582 WXLO_DEBUG(("m_ScrollToCursor = %d", (int) m_ScrollToCursor));
583 wxPoint cc = m_llist->GetCursorScreenPos(dc);
584
585 // the cursor should be completely visible in both directions
586 wxPoint cs(m_llist->GetCursorSize());
587 int nx = -1,
588 ny = -1;
589 if ( cc.x < x0 || cc.x >= x0 + x1 - cs.x )
590 {
591 nx = cc.x - x1/2;
592 if ( nx < 0 )
593 nx = 0;
594 }
595
596 if ( cc.y < y0 || cc.y >= y0 + y1 - cs.y )
597 {
598 ny = cc.y - y1/2;
599 if ( ny < 0)
600 ny = 0;
601 }
602
603 if ( nx != -1 || ny != -1 )
604 {
605 // set new view start
606 Scroll(nx == -1 ? -1 : (nx+dx-1)/dx, ny == -1 ? -1 : (ny+dy-1)/dy);
607
608 // avoid recursion
609 m_ScrollToCursor = false;
610 }
611 }
612
613 void
614 wxLayoutWindow::OnPaint( wxPaintEvent &WXUNUSED(event))
615 {
616 wxRect region = GetUpdateRegion().GetBox();
617 InternalPaint(&region);
618 }
619
620 void
621 wxLayoutWindow::DoPaint(const wxRect *updateRect)
622 {
623 #ifndef __WXMSW__
624 InternalPaint(updateRect);
625 #else
626 Refresh(FALSE, updateRect); // Causes bad flicker under wxGTK!!!
627
628 if ( !::UpdateWindow(GetHwnd()) )
629 wxLogLastError("UpdateWindow");
630 #endif
631 }
632
633 void
634 wxLayoutWindow::InternalPaint(const wxRect *updateRect)
635 {
636 wxPaintDC dc( this );
637 PrepareDC( dc );
638
639 #ifdef WXLAYOUT_USE_CARET
640 // hide the caret before drawing anything
641 GetCaret()->Hide();
642 #endif // WXLAYOUT_USE_CARET
643
644 int x0,y0,x1,y1, dx, dy;
645
646 // Calculate where the top of the visible area is:
647 ViewStart(&x0,&y0);
648 GetScrollPixelsPerUnit(&dx, &dy);
649 x0 *= dx; y0 *= dy;
650
651 // Get the size of the visible window:
652 GetClientSize(&x1,&y1);
653 wxASSERT(x1 >= 0);
654 wxASSERT(y1 >= 0);
655
656 if(updateRect)
657 {
658 WXLO_DEBUG(("Update rect: %ld,%ld / %ld,%ld",
659 updateRect->x, updateRect->y,
660 updateRect->x+updateRect->width,
661 updateRect->y+updateRect->height));
662 }
663 if(IsDirty())
664 {
665 m_llist->Layout(dc);
666 ResizeScrollbars();
667 }
668 /* Check whether the window has grown, if so, we need to reallocate
669 the bitmap to be larger. */
670 if(x1 > m_bitmapSize.x || y1 > m_bitmapSize.y)
671 {
672 wxASSERT(m_bitmapSize.x > 0);
673 wxASSERT(m_bitmapSize.y > 0);
674
675 m_memDC->SelectObject(wxNullBitmap);
676 delete m_bitmap;
677 m_bitmapSize = wxPoint(x1,y1);
678 m_bitmap = new wxBitmap(x1,y1);
679 m_memDC->SelectObject(*m_bitmap);
680 }
681
682 m_memDC->SetDeviceOrigin(0,0);
683 m_memDC->SetBrush(wxBrush(m_llist->GetDefaultStyleInfo().GetBGColour(),wxSOLID));
684 m_memDC->SetPen(wxPen(m_llist->GetDefaultStyleInfo().GetBGColour(),
685 0,wxTRANSPARENT));
686 m_memDC->SetLogicalFunction(wxCOPY);
687 m_memDC->Clear();
688
689 // fill the background with the background bitmap
690 if(m_BGbitmap)
691 {
692 CoordType
693 y, x,
694 w = m_BGbitmap->GetWidth(),
695 h = m_BGbitmap->GetHeight();
696 for(y = 0; y < y1; y+=h)
697 for(x = 0; x < x1; x+=w)
698 m_memDC->DrawBitmap(*m_BGbitmap, x, y);
699 m_memDC->SetBackgroundMode(wxTRANSPARENT);
700 }
701
702 // This is the important bit: we tell the list to draw itself
703 #if WXLO_DEBUG_URECT
704 if(updateRect)
705 {
706 WXLO_DEBUG(("Update rect: %ld,%ld / %ld,%ld",
707 updateRect->x, updateRect->y,
708 updateRect->x+updateRect->width,
709 updateRect->y+updateRect->height));
710 }
711 #endif
712
713 // Device origins on the memDC are suspect, we translate manually
714 // with the translate parameter of Draw().
715 wxPoint offset(-x0+WXLO_XOFFSET,-y0+WXLO_YOFFSET);
716 m_llist->Draw(*m_memDC,offset, y0, y0+y1);
717
718 // We start calculating a new update rect before drawing the
719 // cursor, so that the cursor coordinates get included in the next
720 // update rectangle (although they are drawn on the memDC, this is
721 // needed to erase it):
722 m_llist->InvalidateUpdateRect();
723 if(m_CursorVisibility != 0)
724 {
725 m_llist->UpdateCursorScreenPos(dc, true, offset);
726 m_llist->DrawCursor(*m_memDC,
727 m_HaveFocus && IsEditable(), // draw a thick
728 // cursor for editable windows with focus
729 offset);
730 }
731
732 // Now copy everything to the screen:
733 #if 0
734 // This somehow doesn't work, but even the following bit with the
735 // whole rect at once is still a bit broken I think.
736 wxRegionIterator ri ( GetUpdateRegion() );
737 if(ri)
738 while(ri)
739 {
740 WXLO_DEBUG(("UpdateRegion: %ld,%ld, %ld,%ld",
741 ri.GetX(),ri.GetY(),ri.GetW(),ri.GetH()));
742 dc.Blit(x0+ri.GetX(),y0+ri.GetY(),ri.GetW(),ri.GetH(),
743 m_memDC,ri.GetX(),ri.GetY(),wxCOPY,FALSE);
744 ri++;
745 }
746 else
747 #endif
748 {
749 // FIXME: Trying to copy only the changed parts, but it does not seem
750 // to work:
751 // x0 = updateRect->x; y0 = updateRect->y;
752 // if(updateRect->height < y1)
753 // y1 = updateRect->height;
754 // y1 += WXLO_YOFFSET; //FIXME might not be needed
755 dc.Blit(x0,y0,x1,y1,m_memDC,0,0,wxCOPY,FALSE);
756 }
757
758 #ifdef WXLAYOUT_USE_CARET
759 // show the caret back after everything is redrawn
760 m_caret->Show();
761 #endif // WXLAYOUT_USE_CARET
762
763 ResetDirty();
764 m_ScrollToCursor = false;
765
766 if ( m_StatusBar && m_StatusFieldCursor != -1 )
767 {
768 static wxPoint s_oldCursorPos(-1, -1);
769
770 wxPoint pos(m_llist->GetCursorPos());
771
772 // avoid unnecessary status bar refreshes
773 if ( pos != s_oldCursorPos )
774 {
775 s_oldCursorPos = pos;
776
777 wxString label;
778 label.Printf(_("Ln:%d Col:%d"), pos.y + 1, pos.x + 1);
779 m_StatusBar->SetStatusText(label, m_StatusFieldCursor);
780 }
781 }
782 }
783
784 // change the range and position of scrollbars
785 void
786 wxLayoutWindow::ResizeScrollbars(bool exact)
787 {
788 wxPoint max = m_llist->GetSize();
789
790 WXLO_DEBUG(("ResizeScrollbars: max size = (%ld, %ld)",
791 (long int)max.x, (long int) max.y));
792
793 if( max.x > m_maxx - WXLO_ROFFSET || max.y > m_maxy - WXLO_BOFFSET || exact )
794 {
795 if ( !exact )
796 {
797 // add an extra bit to the sizes to avoid future updates
798 max.x += WXLO_ROFFSET;
799 max.y += WXLO_BOFFSET;
800 }
801
802 ViewStart(&m_ViewStartX, &m_ViewStartY);
803 SetScrollbars(X_SCROLL_PAGE, Y_SCROLL_PAGE,
804 max.x / X_SCROLL_PAGE + 1, max.y / Y_SCROLL_PAGE + 1,
805 m_ViewStartX, m_ViewStartY,
806 true);
807
808 m_maxx = max.x + X_SCROLL_PAGE;
809 m_maxy = max.y + Y_SCROLL_PAGE;
810 }
811 }
812
813 void
814 wxLayoutWindow::Paste(void)
815 {
816 // Read some text
817 if (wxTheClipboard->Open())
818 {
819 #if wxUSE_PRIVATE_CLIPBOARD_FORMAT
820 wxLayoutDataObject wxldo;
821 if (wxTheClipboard->IsSupported( wxldo.GetFormat() ))
822 {
823 wxTheClipboard->GetData(&wxldo);
824 {
825 }
826 //FIXME: missing functionality m_llist->Insert(wxldo.GetList());
827 }
828 else
829 #endif
830 {
831 wxTextDataObject data;
832 if (wxTheClipboard->IsSupported( data.GetFormat() ))
833 {
834 wxTheClipboard->GetData(&data);
835 wxString text = data.GetText();
836 wxLayoutImportText( m_llist, text);
837 }
838 }
839 wxTheClipboard->Close();
840 }
841
842 #if 0
843 /* My attempt to get the primary selection, but it does not
844 work. :-( */
845 if(text.Length() == 0)
846 {
847 wxTextCtrl tmp_tctrl(this,-1);
848 tmp_tctrl.Paste();
849 text += tmp_tctrl.GetValue();
850 }
851 #endif
852 }
853
854 bool
855 wxLayoutWindow::Copy(bool invalidate)
856 {
857 // Calling GetSelection() will automatically do an EndSelection()
858 // on the list, but we need to take a note of it, too:
859 if(m_Selecting)
860 {
861 m_Selecting = false;
862 m_llist->EndSelection();
863 }
864
865 wxLayoutDataObject wldo;
866 wxLayoutList *llist = m_llist->GetSelection(&wldo, invalidate);
867 if(! llist)
868 return FALSE;
869 // Export selection as text:
870 wxString text;
871 wxLayoutExportObject *export;
872 wxLayoutExportStatus status(llist);
873 while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_TEXT)) != NULL)
874 {
875 if(export->type == WXLO_EXPORT_TEXT)
876 text << *(export->content.text);
877 delete export;
878 }
879 delete llist;
880
881 // The exporter always appends a newline, so we chop it off if it
882 // is there:
883 {
884 size_t len = text.Length();
885 if(len > 2 && text[len-2] == '\r') // Windows
886 text = text.Mid(0,len-2);
887 else if(len > 1 && text[len-1] == '\n')
888 text = text.Mid(0,len-1);
889 }
890
891
892 if (wxTheClipboard->Open())
893 {
894 wxTextDataObject *data = new wxTextDataObject( text );
895 bool rc = wxTheClipboard->SetData( data );
896 #if wxUSE_PRIVATE_CLIPBOARD_FORMAT
897 rc |= wxTheClipboard->AddData( &wldo );
898 #endif
899 wxTheClipboard->Close();
900 return rc;
901 }
902
903 return FALSE;
904 }
905
906 bool
907 wxLayoutWindow::Cut(void)
908 {
909 if(Copy(false)) // do not invalidate selection after copy
910 {
911 m_llist->DeleteSelection();
912 return TRUE;
913 }
914 else
915 return FALSE;
916 }
917 bool
918 wxLayoutWindow::Find(const wxString &needle,
919 wxPoint * fromWhere)
920 {
921 wxPoint found;
922
923 if(fromWhere == NULL)
924 found = m_llist->FindText(needle, m_llist->GetCursorPos());
925 else
926 found = m_llist->FindText(needle, *fromWhere);
927 if(found.x != -1)
928 {
929 if(fromWhere)
930 {
931 *fromWhere = found;
932 fromWhere->x ++;
933 }
934 m_llist->MoveCursorTo(found);
935 ScrollToCursor();
936 return true;
937 }
938 return false;
939 }
940
941 wxMenu *
942 wxLayoutWindow::MakeFormatMenu()
943 {
944 wxMenu *m = new wxMenu(_("Layout Menu"));
945
946 m->Append(WXLOWIN_MENU_LARGER ,_("&Larger"),_("Switch to larger font."), false);
947 m->Append(WXLOWIN_MENU_SMALLER ,_("&Smaller"),_("Switch to smaller font."), false);
948 m->AppendSeparator();
949 m->Append(WXLOWIN_MENU_UNDERLINE_ON, _("&Underline on"),_("Activate underline mode."), false);
950 m->Append(WXLOWIN_MENU_UNDERLINE_OFF,_("&Underline off"),_("Deactivate underline mode."), false);
951 m->Append(WXLOWIN_MENU_BOLD_ON ,_("&Bold on"),_("Activate bold mode."), false);
952 m->Append(WXLOWIN_MENU_BOLD_OFF ,_("&Bold off"),_("Deactivate bold mode."), false);
953 m->Append(WXLOWIN_MENU_ITALICS_ON ,_("&Italics on"),_("Activate italics mode."), false);
954 m->Append(WXLOWIN_MENU_ITALICS_OFF ,_("&Italics off"),_("Deactivate italics mode."), false);
955 m->AppendSeparator();
956 m->Append(WXLOWIN_MENU_ROMAN ,_("&Roman"),_("Switch to roman font."), false);
957 m->Append(WXLOWIN_MENU_TYPEWRITER,_("&Typewriter"),_("Switch to typewriter font."), false);
958 m->Append(WXLOWIN_MENU_SANSSERIF ,_("&Sans Serif"),_("Switch to sans serif font."), false);
959 return m;
960 }
961
962 void wxLayoutWindow::OnMenu(wxCommandEvent& event)
963 {
964 switch (event.GetId())
965 {
966 case WXLOWIN_MENU_LARGER:
967 m_llist->SetFontLarger(); break;
968 case WXLOWIN_MENU_SMALLER:
969 m_llist->SetFontSmaller(); break;
970 case WXLOWIN_MENU_UNDERLINE_ON:
971 m_llist->SetFontUnderline(true); break;
972 case WXLOWIN_MENU_UNDERLINE_OFF:
973 m_llist->SetFontUnderline(false); break;
974 case WXLOWIN_MENU_BOLD_ON:
975 m_llist->SetFontWeight(wxBOLD); break;
976 case WXLOWIN_MENU_BOLD_OFF:
977 m_llist->SetFontWeight(wxNORMAL); break;
978 case WXLOWIN_MENU_ITALICS_ON:
979 m_llist->SetFontStyle(wxITALIC); break;
980 case WXLOWIN_MENU_ITALICS_OFF:
981 m_llist->SetFontStyle(wxNORMAL); break;
982 case WXLOWIN_MENU_ROMAN:
983 m_llist->SetFontFamily(wxROMAN); break;
984 case WXLOWIN_MENU_TYPEWRITER:
985 m_llist->SetFontFamily(wxFIXED); break;
986 case WXLOWIN_MENU_SANSSERIF:
987 m_llist->SetFontFamily(wxSWISS); break;
988 }
989 }
990
991 void
992 wxLayoutWindow::OnSetFocus(wxFocusEvent &ev)
993 {
994 m_HaveFocus = true;
995 ev.Skip();
996 }
997
998 void
999 wxLayoutWindow::OnKillFocus(wxFocusEvent &ev)
1000 {
1001 m_HaveFocus = false;
1002 ev.Skip();
1003 }