]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/motif/textctrl.cpp
Don't activate MSW dialogs when setting their initial size.
[wxWidgets.git] / src / motif / textctrl.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/motif/textctrl.cpp
3// Purpose: wxTextCtrl
4// Author: Julian Smart
5// Modified by:
6// Created: 17/09/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <ctype.h>
26
27#include "wx/textctrl.h"
28
29#ifndef WX_PRECOMP
30 #include "wx/utils.h"
31 #include "wx/settings.h"
32#endif
33
34#include "wx/filefn.h"
35
36#ifdef __VMS__
37#pragma message disable nosimpint
38#endif
39#include <Xm/Text.h>
40#ifdef __VMS__
41#pragma message enable nosimpint
42#endif
43
44#include "wx/motif/private.h"
45
46// ----------------------------------------------------------------------------
47// private functions
48// ----------------------------------------------------------------------------
49
50// helper: inserts the new text in the value of the text ctrl and returns the
51// result in place
52static void MergeChangesIntoString(wxString& value,
53 XmTextVerifyCallbackStruct *textStruct);
54
55// callbacks
56static void wxTextWindowChangedProc(Widget w, XtPointer clientData, XtPointer ptr);
57static void wxTextWindowModifyProc(Widget w, XtPointer clientData, XmTextVerifyCallbackStruct *cbs);
58static void wxTextWindowGainFocusProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs);
59static void wxTextWindowLoseFocusProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs);
60static void wxTextWindowActivateProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *ptr);
61
62 BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase)
63 EVT_DROP_FILES(wxTextCtrl::OnDropFiles)
64 EVT_CHAR(wxTextCtrl::OnChar)
65
66 EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
67 EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
68 EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
69 EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
70 EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
71
72 EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
73 EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
74 EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
75 EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
76 EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
77
78 END_EVENT_TABLE()
79
80// ============================================================================
81// implementation
82// ============================================================================
83
84// ----------------------------------------------------------------------------
85// wxTextCtrl
86// ----------------------------------------------------------------------------
87
88// Text item
89wxTextCtrl::wxTextCtrl()
90{
91 m_tempCallbackStruct = NULL;
92 m_modified = false;
93 m_processedDefault = false;
94}
95
96bool wxTextCtrl::Create(wxWindow *parent,
97 wxWindowID id,
98 const wxString& value,
99 const wxPoint& pos,
100 const wxSize& size,
101 long style,
102 const wxValidator& validator,
103 const wxString& name)
104{
105 if( !CreateControl( parent, id, pos, size, style, validator, name ) )
106 return false;
107 PreCreation();
108
109 m_tempCallbackStruct = NULL;
110 m_modified = false;
111 m_processedDefault = false;
112
113 Widget parentWidget = (Widget) parent->GetClientWidget();
114
115 Bool wantHorizScroll = (m_windowStyle & wxHSCROLL) != 0 ? True : False;
116 // If we don't have horizontal scrollbars, we want word wrap.
117 // OpenMotif 2.1 crashes if wantWordWrap is True in Japanese
118 // locale (and probably other multibyte locales). The check might be
119 // more precise
120#if wxCHECK_LESSTIF() || wxCHECK_MOTIF_VERSION( 2, 2 )
121 Bool wantWordWrap = wantHorizScroll == True ? False : True;
122#else
123 Bool wantWordWrap = False;
124#endif
125
126 if (m_windowStyle & wxTE_MULTILINE)
127 {
128 Arg args[8];
129 int count = 0;
130 XtSetArg (args[count], XmNscrollHorizontal, wantHorizScroll); ++count;
131 if( m_font.IsOk() )
132 XtSetArg (args[count], (String) wxFont::GetFontTag(),
133 m_font.GetFontType( XtDisplay(parentWidget) ) ); ++count;
134 XtSetArg (args[count], XmNwordWrap, wantWordWrap); ++count;
135 XtSetArg (args[count], XmNvalue, (const char*)value.mb_str()); ++count;
136 XtSetArg (args[count], XmNeditable,
137 style & wxTE_READONLY ? False : True); ++count;
138 XtSetArg (args[count], XmNeditMode, XmMULTI_LINE_EDIT ); ++count;
139
140 m_mainWidget =
141 (WXWidget) XmCreateScrolledText(parentWidget,
142 name.char_str(),
143 args, count);
144
145 XtManageChild ((Widget) m_mainWidget);
146 }
147 else
148 {
149 m_mainWidget = (WXWidget)XtVaCreateManagedWidget
150 (
151 name.mb_str(),
152 xmTextWidgetClass,
153 parentWidget,
154 wxFont::GetFontTag(), m_font.GetFontType( XtDisplay(parentWidget) ),
155 XmNvalue, (const char*)value.mb_str(),
156 XmNeditable, (style & wxTE_READONLY) ?
157 False : True,
158 NULL
159 );
160
161#if 0
162 // TODO: Is this relevant? What does it do?
163 int noCols = 2;
164 if (!value.empty() && (value.length() > (unsigned int) noCols))
165 noCols = value.length();
166 XtVaSetValues((Widget) m_mainWidget,
167 XmNcolumns, noCols,
168 NULL);
169#endif
170 }
171
172 // remove border if asked for
173 if ( style & wxNO_BORDER )
174 {
175 XtVaSetValues((Widget)m_mainWidget,
176 XmNshadowThickness, 0,
177 NULL);
178 }
179
180 // install callbacks
181 XtAddCallback((Widget) m_mainWidget, XmNvalueChangedCallback, (XtCallbackProc)wxTextWindowChangedProc, (XtPointer)this);
182
183 XtAddCallback((Widget) m_mainWidget, XmNmodifyVerifyCallback, (XtCallbackProc)wxTextWindowModifyProc, (XtPointer)this);
184
185 XtAddCallback((Widget) m_mainWidget, XmNactivateCallback, (XtCallbackProc)wxTextWindowActivateProc, (XtPointer)this);
186
187 XtAddCallback((Widget) m_mainWidget, XmNfocusCallback, (XtCallbackProc)wxTextWindowGainFocusProc, (XtPointer)this);
188
189 XtAddCallback((Widget) m_mainWidget, XmNlosingFocusCallback, (XtCallbackProc)wxTextWindowLoseFocusProc, (XtPointer)this);
190
191 PostCreation();
192 AttachWidget (parent, m_mainWidget, (WXWidget) NULL,
193 pos.x, pos.y, size.x, size.y);
194
195 return true;
196}
197
198WXWidget wxTextCtrl::GetTopWidget() const
199{
200 return IsMultiLine() ? (WXWidget)XtParent((Widget)m_mainWidget)
201 : m_mainWidget;
202}
203
204wxString wxTextCtrl::GetValue() const
205{
206 wxString str; // result
207
208 if (m_windowStyle & wxTE_PASSWORD)
209 {
210 // the value is stored always in m_value because it can't be retrieved
211 // from the text control
212 str = m_value;
213 }
214 else
215 {
216 str = wxTextEntry::GetValue();
217
218 if ( m_tempCallbackStruct )
219 {
220 // the string in the control isn't yet updated, can't use it as is
221 MergeChangesIntoString(str, (XmTextVerifyCallbackStruct *)
222 m_tempCallbackStruct);
223 }
224 }
225
226 return str;
227}
228
229void wxTextCtrl::DoSetValue(const wxString& text, int flags)
230{
231 m_inSetValue = true;
232
233 XmTextSetString ((Widget) m_mainWidget, text.char_str());
234 XtVaSetValues ((Widget) m_mainWidget,
235 XmNcursorPosition, text.length(),
236 NULL);
237
238 SetInsertionPoint(text.length());
239 XmTextShowPosition ((Widget) m_mainWidget, text.length());
240 m_modified = true;
241
242 m_inSetValue = false;
243
244 if ( flags & SetValue_SendEvent )
245 SendTextUpdatedEvent();
246}
247
248bool wxTextCtrl::IsModified() const
249{
250 return m_modified;
251}
252
253// Makes modified or unmodified
254void wxTextCtrl::MarkDirty()
255{
256 m_modified = true;
257}
258
259void wxTextCtrl::DiscardEdits()
260{
261 m_modified = false;
262}
263
264int wxTextCtrl::GetNumberOfLines() const
265{
266 // HIDEOUSLY inefficient, but we have no choice.
267 char *s = XmTextGetString ((Widget) m_mainWidget);
268 if (s)
269 {
270 long i = 0;
271 int currentLine = 0;
272 bool finished = false;
273 while (!finished)
274 {
275 int ch = s[i];
276 if (ch == '\n')
277 {
278 currentLine++;
279 i++;
280 }
281 else if (ch == 0)
282 {
283 finished = true;
284 }
285 else
286 i++;
287 }
288
289 XtFree (s);
290 return currentLine;
291 }
292 return 0;
293}
294
295long wxTextCtrl::XYToPosition(long x, long y) const
296{
297/* It seems, that there is a bug in some versions of the Motif library,
298 so the original wxWin-Code doesn't work. */
299 /*
300 Widget textWidget = (Widget) handle;
301 return (long) XmTextXYToPos (textWidget, (Position) x, (Position) y);
302 */
303 /* Now a little workaround: */
304 long r=0;
305 for (int i=0; i<y; i++) r+=(GetLineLength(i)+1);
306 return r+x;
307}
308
309bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
310{
311 Position xx, yy;
312 XmTextPosToXY((Widget) m_mainWidget, pos, &xx, &yy);
313 if ( x )
314 *x = xx;
315 if ( y )
316 *y = yy;
317
318 return true;
319}
320
321void wxTextCtrl::ShowPosition(long pos)
322{
323 XmTextShowPosition ((Widget) m_mainWidget, (XmTextPosition) pos);
324}
325
326int wxTextCtrl::GetLineLength(long lineNo) const
327{
328 wxString str = GetLineText (lineNo);
329 return (int) str.length();
330}
331
332wxString wxTextCtrl::GetLineText(long lineNo) const
333{
334 // HIDEOUSLY inefficient, but we have no choice.
335 char *s = XmTextGetString ((Widget) m_mainWidget);
336
337 if (s)
338 {
339 wxString buf;
340 long i;
341 int currentLine = 0;
342 for (i = 0; currentLine != lineNo && s[i]; i++ )
343 if (s[i] == '\n')
344 currentLine++;
345 // Now get the text
346 int j;
347 for (j = 0; s[i] && s[i] != '\n'; i++, j++ )
348 buf += s[i];
349
350 XtFree(s);
351 return buf;
352 }
353 else
354 return wxEmptyString;
355}
356
357/*
358* Text item
359*/
360
361void wxTextCtrl::Command(wxCommandEvent & event)
362{
363 SetValue (event.GetString());
364 ProcessCommand (event);
365}
366
367void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
368{
369 // By default, load the first file into the text window.
370 if (event.GetNumberOfFiles() > 0)
371 {
372 LoadFile(event.GetFiles()[0]);
373 }
374}
375
376void wxTextCtrl::OnChar(wxKeyEvent& event)
377{
378 // Indicates that we should generate a normal command, because
379 // we're letting default behaviour happen (otherwise it's vetoed
380 // by virtue of overriding OnChar)
381 m_processedDefault = true;
382
383 if (m_tempCallbackStruct)
384 {
385 XmTextVerifyCallbackStruct *textStruct =
386 (XmTextVerifyCallbackStruct *) m_tempCallbackStruct;
387 textStruct->doit = True;
388 if (wxIsascii(event.m_keyCode) && (textStruct->text->length == 1))
389 {
390 textStruct->text->ptr[0] = (char)((event.m_keyCode == WXK_RETURN) ? 10 : event.m_keyCode);
391 }
392 }
393}
394
395void wxTextCtrl::ChangeFont(bool keepOriginalSize)
396{
397 wxWindow::ChangeFont(keepOriginalSize);
398}
399
400void wxTextCtrl::ChangeBackgroundColour()
401{
402 wxWindow::ChangeBackgroundColour();
403
404 /* TODO: should scrollbars be affected? Should probably have separate
405 * function to change them (by default, taken from wxSystemSettings)
406 */
407 if (m_windowStyle & wxTE_MULTILINE)
408 {
409 Widget parent = XtParent ((Widget) m_mainWidget);
410 Widget hsb, vsb;
411
412 XtVaGetValues (parent,
413 XmNhorizontalScrollBar, &hsb,
414 XmNverticalScrollBar, &vsb,
415 NULL);
416 wxColour backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
417 if (hsb)
418 wxDoChangeBackgroundColour((WXWidget) hsb, backgroundColour, true);
419 if (vsb)
420 wxDoChangeBackgroundColour((WXWidget) vsb, backgroundColour, true);
421
422 // MBN: why change parent background?
423 // DoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, true);
424 }
425}
426
427void wxTextCtrl::ChangeForegroundColour()
428{
429 wxWindow::ChangeForegroundColour();
430
431 if (m_windowStyle & wxTE_MULTILINE)
432 {
433 Widget parent = XtParent ((Widget) m_mainWidget);
434 Widget hsb, vsb;
435
436 XtVaGetValues (parent,
437 XmNhorizontalScrollBar, &hsb,
438 XmNverticalScrollBar, &vsb,
439 NULL);
440
441 /* TODO: should scrollbars be affected? Should probably have separate
442 * function to change them (by default, taken from wxSystemSettings)
443 if (hsb)
444 DoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
445 if (vsb)
446 DoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
447 */
448 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
449 }
450}
451
452void wxTextCtrl::DoSendEvents(void *wxcbs, long keycode)
453{
454 // we're in process of updating the text control
455 m_tempCallbackStruct = wxcbs;
456
457 XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *)wxcbs;
458
459 wxKeyEvent event (wxEVT_CHAR);
460 event.SetId(GetId());
461 event.m_keyCode = keycode;
462 event.SetEventObject(this);
463
464 // Only if wxTextCtrl::OnChar is called will this be set to True (and
465 // the character passed through)
466 cbs->doit = False;
467
468 HandleWindowEvent(event);
469
470 if ( !InSetValue() && m_processedDefault )
471 {
472 // Can generate a command
473 wxCommandEvent commandEvent(wxEVT_COMMAND_TEXT_UPDATED, GetId());
474 commandEvent.SetEventObject(this);
475 ProcessCommand(commandEvent);
476 }
477
478 // do it after the (user) event handlers processed the events because
479 // otherwise GetValue() would return incorrect (not yet updated value)
480 m_tempCallbackStruct = NULL;
481}
482
483wxSize wxDoGetSingleTextCtrlBestSize( Widget textWidget,
484 const wxWindow* window )
485{
486 Dimension xmargin, ymargin, highlight, shadow;
487 char* value;
488
489 XtVaGetValues( textWidget,
490 XmNmarginWidth, &xmargin,
491 XmNmarginHeight, &ymargin,
492 XmNvalue, &value,
493 XmNhighlightThickness, &highlight,
494 XmNshadowThickness, &shadow,
495 NULL );
496
497 if( !value )
498 value = wxMOTIF_STR("|");
499
500 int x, y;
501 window->GetTextExtent( value, &x, &y );
502
503 if( x < 90 )
504 x = 90;
505
506 return wxSize( x + 2 * xmargin + 2 * highlight + 2 * shadow,
507 // MBN: +2 necessary: Lesstif bug or mine?
508 y + 2 * ymargin + 2 * highlight + 2 * shadow + 2 );
509}
510
511wxSize wxTextCtrl::DoGetBestSize() const
512{
513 if( IsSingleLine() )
514 {
515 wxSize best = wxControl::DoGetBestSize();
516#if wxCHECK_MOTIF_VERSION( 2, 3 )
517 // OpenMotif 2.3 gives way too big X sizes
518 wxSize other_best = wxDoGetSingleTextCtrlBestSize
519 ( (Widget) GetTopWidget(), this );
520 return wxSize( other_best.x, best.y );
521#else
522 if( best.x < 90 ) best.x = 90;
523
524 return best;
525#endif
526 }
527 else
528 return wxWindow::DoGetBestSize();
529}
530
531// ----------------------------------------------------------------------------
532// helpers and Motif callbacks
533// ----------------------------------------------------------------------------
534
535static void MergeChangesIntoString(wxString& value,
536 XmTextVerifyCallbackStruct *cbs)
537{
538 /* _sm_
539 * At least on my system (SunOS 4.1.3 + Motif 1.2), you need to think of
540 * every event as a replace event. cbs->text->ptr gives the replacement
541 * text, cbs->startPos gives the index of the first char affected by the
542 * replace, and cbs->endPos gives the index one more than the last char
543 * affected by the replace (startPos == endPos implies an empty range).
544 * Hence, a deletion is represented by replacing all input text with a
545 * blank string ("", *not* NULL!). A simple insertion that does not
546 * overwrite any text has startPos == endPos.
547 */
548
549 if ( !value )
550 {
551 // easy case: the ol value was empty
552 value = cbs->text->ptr;
553 }
554 else
555 {
556 // merge the changes into the value
557 const char * const passwd = value;
558 int len = value.length();
559
560 len += ( cbs->text->ptr ?
561 strlen(cbs->text->ptr) :
562 0 ) + 1; // + new text (if any) + NUL
563 len -= cbs->endPos - cbs->startPos; // - text from affected region.
564
565 char * newS = new char [len];
566 char * dest = newS,
567 * insert = cbs->text->ptr;
568
569 // Copy (old) text from passwd, up to the start posn of the change.
570 int i;
571 const char * p = passwd;
572 for (i = 0; i < cbs->startPos; ++i)
573 *dest++ = *p++;
574
575 // Copy the text to be inserted).
576 if (insert)
577 while (*insert)
578 *dest++ = *insert++;
579
580 // Finally, copy into newS any remaining text from passwd[endPos] on.
581 for (p = passwd + cbs->endPos; *p; )
582 *dest++ = *p++;
583 *dest = 0;
584
585 value = newS;
586
587 delete[] newS;
588 }
589}
590
591static void
592wxTextWindowChangedProc (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr))
593{
594 if (!wxGetWindowFromTable(w))
595 // Widget has been deleted!
596 return;
597
598 wxTextCtrl *tw = (wxTextCtrl *) clientData;
599 tw->SetModified(true);
600}
601
602static void
603wxTextWindowModifyProc (Widget WXUNUSED(w), XtPointer clientData, XmTextVerifyCallbackStruct *cbs)
604{
605 wxTextCtrl *tw = (wxTextCtrl *) clientData;
606 tw->m_processedDefault = false;
607
608 // First, do some stuff if it's a password control: in this case, we need
609 // to store the string inside the class because GetValue() can't retrieve
610 // it from the text ctrl. We do *not* do it in other circumstances because
611 // it would double the amount of memory needed.
612
613 if ( tw->GetWindowStyleFlag() & wxTE_PASSWORD )
614 {
615 MergeChangesIntoString(tw->m_value, cbs);
616
617 if ( cbs->text->length > 0 )
618 {
619 int i;
620 for (i = 0; i < cbs->text->length; ++i)
621 cbs->text->ptr[i] = '*';
622 cbs->text->ptr[i] = '\0';
623 }
624 }
625
626 if(tw->InSetValue())
627 return;
628
629 // If we're already within an OnChar, return: probably a programmatic
630 // insertion.
631 if (tw->m_tempCallbackStruct)
632 return;
633
634 // Check for a backspace
635 if (cbs->startPos == (cbs->currInsert - 1))
636 {
637 tw->DoSendEvents((void *)cbs, WXK_DELETE);
638
639 return;
640 }
641
642 // Pasting operation: let it through without calling OnChar
643 if (cbs->text->length > 1)
644 return;
645
646 // Something other than text
647 if (cbs->text->ptr == NULL)
648 return;
649
650 // normal key press
651 char ch = cbs->text->ptr[0];
652 tw->DoSendEvents((void *)cbs, ch == '\n' ? '\r' : ch);
653}
654
655static void
656wxTextWindowGainFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *WXUNUSED(cbs))
657{
658 if (!wxGetWindowFromTable(w))
659 return;
660
661 wxTextCtrl *tw = (wxTextCtrl *) clientData;
662 wxFocusEvent event(wxEVT_SET_FOCUS, tw->GetId());
663 event.SetEventObject(tw);
664 tw->HandleWindowEvent(event);
665}
666
667static void
668wxTextWindowLoseFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *WXUNUSED(cbs))
669{
670 if (!wxGetWindowFromTable(w))
671 return;
672
673 wxTextCtrl *tw = (wxTextCtrl *) clientData;
674 wxFocusEvent event(wxEVT_KILL_FOCUS, tw->GetId());
675 event.SetEventObject(tw);
676 tw->HandleWindowEvent(event);
677}
678
679static void wxTextWindowActivateProc(Widget w, XtPointer clientData,
680 XmAnyCallbackStruct *WXUNUSED(ptr))
681{
682 if (!wxGetWindowFromTable(w))
683 return;
684
685 wxTextCtrl *tw = (wxTextCtrl *) clientData;
686
687 if (tw->InSetValue())
688 return;
689
690 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER);
691 event.SetId(tw->GetId());
692 event.SetEventObject(tw);
693 tw->ProcessCommand(event);
694}
695
696void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
697{
698 Cut();
699}
700
701void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
702{
703 Copy();
704}
705
706void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
707{
708 Paste();
709}
710
711void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
712{
713 Undo();
714}
715
716void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
717{
718 Redo();
719}
720
721void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
722{
723 event.Enable( CanCut() );
724}
725
726void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
727{
728 event.Enable( CanCopy() );
729}
730
731void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
732{
733 event.Enable( CanPaste() );
734}
735
736void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
737{
738 event.Enable( CanUndo() );
739}
740
741void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
742{
743 event.Enable( CanRedo() );
744}