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