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