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