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