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