]> git.saurik.com Git - wxWidgets.git/blob - samples/richedit/wxlwindow.cpp
0745527116c6e1a093e9865d14652494cb8bfaf9
[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 else
891 {
892 // check if the window hasn't become too big, thus making the scrollbars
893 // unnecessary
894 if ( m_hasHScrollbar && (max.x < size.x) )
895 {
896 // remove the horizontal scrollbar
897 SetScrollbars(0, -1, 0, -1, 0, -1, true);
898 m_hasHScrollbar = false;
899 }
900
901 if ( m_hasVScrollbar && (max.y < size.y) )
902 {
903 // remove the vertical scrollbar
904 SetScrollbars(-1, 0, -1, 0, -1, 0, true);
905 m_hasVScrollbar = false;
906 }
907 }
908 }
909
910 // ----------------------------------------------------------------------------
911 // clipboard operations
912 // ----------------------------------------------------------------------------
913
914 void
915 wxLayoutWindow::Paste(void)
916 {
917 // Read some text
918 if (wxTheClipboard->Open())
919 {
920 #if wxUSE_PRIVATE_CLIPBOARD_FORMAT
921 wxLayoutDataObject wxldo;
922 if (wxTheClipboard->IsSupported( wxldo.GetFormat() ))
923 {
924 wxTheClipboard->GetData(&wxldo);
925 {
926 }
927 //FIXME: missing functionality m_llist->Insert(wxldo.GetList());
928 }
929 else
930 #endif
931 {
932 wxTextDataObject data;
933 if (wxTheClipboard->IsSupported( data.GetFormat() ))
934 {
935 wxTheClipboard->GetData(&data);
936 wxString text = data.GetText();
937 wxLayoutImportText( m_llist, text);
938 }
939 }
940 wxTheClipboard->Close();
941 }
942
943 #if 0
944 /* My attempt to get the primary selection, but it does not
945 work. :-( */
946 if(text.Length() == 0)
947 {
948 wxTextCtrl tmp_tctrl(this,-1);
949 tmp_tctrl.Paste();
950 text += tmp_tctrl.GetValue();
951 }
952 #endif
953 }
954
955 bool
956 wxLayoutWindow::Copy(bool invalidate)
957 {
958 // Calling GetSelection() will automatically do an EndSelection()
959 // on the list, but we need to take a note of it, too:
960 if(m_Selecting)
961 {
962 m_Selecting = false;
963 m_llist->EndSelection();
964 }
965
966 wxLayoutDataObject wldo;
967 wxLayoutList *llist = m_llist->GetSelection(&wldo, invalidate);
968 if(! llist)
969 return FALSE;
970 // Export selection as text:
971 wxString text;
972 wxLayoutExportObject *export;
973 wxLayoutExportStatus status(llist);
974 while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_TEXT)) != NULL)
975 {
976 if(export->type == WXLO_EXPORT_TEXT)
977 text << *(export->content.text);
978 delete export;
979 }
980 delete llist;
981
982 // The exporter always appends a newline, so we chop it off if it
983 // is there:
984 {
985 size_t len = text.Length();
986 if(len > 2 && text[len-2] == '\r') // Windows
987 text = text.Mid(0,len-2);
988 else if(len > 1 && text[len-1] == '\n')
989 text = text.Mid(0,len-1);
990 }
991
992
993 if (wxTheClipboard->Open())
994 {
995 wxTextDataObject *data = new wxTextDataObject( text );
996 bool rc = wxTheClipboard->SetData( data );
997 #if wxUSE_PRIVATE_CLIPBOARD_FORMAT
998 rc |= wxTheClipboard->AddData( &wldo );
999 #endif
1000 wxTheClipboard->Close();
1001 return rc;
1002 }
1003
1004 return FALSE;
1005 }
1006
1007 bool
1008 wxLayoutWindow::Cut(void)
1009 {
1010 if(Copy(false)) // do not invalidate selection after copy
1011 {
1012 m_llist->DeleteSelection();
1013 return TRUE;
1014 }
1015 else
1016 return FALSE;
1017 }
1018
1019 // ----------------------------------------------------------------------------
1020 // searching
1021 // ----------------------------------------------------------------------------
1022
1023 bool
1024 wxLayoutWindow::Find(const wxString &needle,
1025 wxPoint * fromWhere)
1026 {
1027 wxPoint found;
1028
1029 if(fromWhere == NULL)
1030 found = m_llist->FindText(needle, m_llist->GetCursorPos());
1031 else
1032 found = m_llist->FindText(needle, *fromWhere);
1033 if(found.x != -1)
1034 {
1035 if(fromWhere)
1036 {
1037 *fromWhere = found;
1038 fromWhere->x ++;
1039 }
1040 m_llist->MoveCursorTo(found);
1041 ScrollToCursor();
1042 return true;
1043 }
1044 return false;
1045 }
1046
1047 // ----------------------------------------------------------------------------
1048 // popup menu stuff
1049 // ----------------------------------------------------------------------------
1050
1051 wxMenu *
1052 wxLayoutWindow::MakeFormatMenu()
1053 {
1054 wxMenu *m = new wxMenu(_("Layout Menu"));
1055
1056 m->Append(WXLOWIN_MENU_LARGER ,_("&Larger"),_("Switch to larger font."), false);
1057 m->Append(WXLOWIN_MENU_SMALLER ,_("&Smaller"),_("Switch to smaller font."), false);
1058 m->AppendSeparator();
1059 m->Append(WXLOWIN_MENU_UNDERLINE, _("&Underline"),_("Underline mode."), true);
1060 m->Append(WXLOWIN_MENU_BOLD, _("&Bold"),_("Bold mode."), true);
1061 m->Append(WXLOWIN_MENU_ITALICS, _("&Italics"),_("Italics mode."), true);
1062 m->AppendSeparator();
1063 m->Append(WXLOWIN_MENU_ROMAN ,_("&Roman"),_("Switch to roman font."), false);
1064 m->Append(WXLOWIN_MENU_TYPEWRITER,_("&Typewriter"),_("Switch to typewriter font."), false);
1065 m->Append(WXLOWIN_MENU_SANSSERIF ,_("&Sans Serif"),_("Switch to sans serif font."), false);
1066
1067 return m;
1068 }
1069
1070 void wxLayoutWindow::OnUpdateMenuUnderline(wxUpdateUIEvent& event)
1071 {
1072 event.Check(m_llist->IsFontUnderlined());
1073 }
1074
1075 void wxLayoutWindow::OnUpdateMenuBold(wxUpdateUIEvent& event)
1076 {
1077 event.Check(m_llist->IsFontBold());
1078 }
1079
1080 void wxLayoutWindow::OnUpdateMenuItalic(wxUpdateUIEvent& event)
1081 {
1082 event.Check(m_llist->IsFontItalic());
1083 }
1084
1085 void wxLayoutWindow::OnMenu(wxCommandEvent& event)
1086 {
1087 switch (event.GetId())
1088 {
1089 case WXLOWIN_MENU_LARGER:
1090 m_llist->SetFontLarger();
1091 break;
1092 case WXLOWIN_MENU_SMALLER:
1093 m_llist->SetFontSmaller();
1094 break;
1095
1096 case WXLOWIN_MENU_UNDERLINE:
1097 m_llist->ToggleFontUnderline();
1098 break;
1099 case WXLOWIN_MENU_BOLD:
1100 m_llist->ToggleFontWeight();
1101 break;
1102 case WXLOWIN_MENU_ITALICS:
1103 m_llist->ToggleFontItalics();
1104 break;
1105
1106 case WXLOWIN_MENU_ROMAN:
1107 m_llist->SetFontFamily(wxROMAN); break;
1108 case WXLOWIN_MENU_TYPEWRITER:
1109 m_llist->SetFontFamily(wxFIXED); break;
1110 case WXLOWIN_MENU_SANSSERIF:
1111 m_llist->SetFontFamily(wxSWISS); break;
1112 }
1113 }
1114
1115 // ----------------------------------------------------------------------------
1116 // focus
1117 // ----------------------------------------------------------------------------
1118
1119 void
1120 wxLayoutWindow::OnSetFocus(wxFocusEvent &ev)
1121 {
1122 m_HaveFocus = true;
1123 ev.Skip();
1124 }
1125
1126 void
1127 wxLayoutWindow::OnKillFocus(wxFocusEvent &ev)
1128 {
1129 m_HaveFocus = false;
1130 ev.Skip();
1131 }
1132
1133 // ----------------------------------------------------------------------------
1134 // private functions
1135 // ----------------------------------------------------------------------------
1136
1137 static bool IsDirectionKey(long keyCode)
1138 {
1139 switch(keyCode)
1140 {
1141 case WXK_UP:
1142 case WXK_DOWN:
1143 case WXK_RIGHT:
1144 case WXK_LEFT:
1145 case WXK_PRIOR:
1146 case WXK_NEXT:
1147 case WXK_HOME:
1148 case WXK_END:
1149 return true;
1150
1151 default:
1152 return false;
1153 }
1154 }