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