X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/66a007fbcb7b73de4d433a2ba3201b1faa41363e..c989314692827562d3d2147ae880886b5eaa530e:/src/motif/textctrl.cpp diff --git a/src/motif/textctrl.cpp b/src/motif/textctrl.cpp index d5a0d021b1..3a66bd8ff8 100644 --- a/src/motif/textctrl.cpp +++ b/src/motif/textctrl.cpp @@ -21,6 +21,10 @@ #pragma implementation "textctrl.h" #endif +#ifdef __VMS +#define XtParent XTPARENT +#endif + #include #include #include @@ -31,7 +35,13 @@ #include "wx/filefn.h" #include "wx/utils.h" +#ifdef __VMS__ +#pragma message disable nosimpint +#endif #include +#ifdef __VMS__ +#pragma message enable nosimpint +#endif #include "wx/motif/private.h" @@ -39,6 +49,11 @@ // private functions // ---------------------------------------------------------------------------- +// helper: inserts the new text in the value of the text ctrl and returns the +// result in place +static void MergeChangesIntoString(wxString& value, + XmTextVerifyCallbackStruct *textStruct); + // callbacks static void wxTextWindowChangedProc(Widget w, XtPointer clientData, XtPointer ptr); static void wxTextWindowModifyProc(Widget w, XtPointer clientData, XmTextVerifyCallbackStruct *cbs); @@ -46,14 +61,25 @@ static void wxTextWindowGainFocusProc(Widget w, XtPointer clientData, XmAnyCallb static void wxTextWindowLoseFocusProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs); static void wxTextWindowActivateProc(Widget w, XtPointer clientData, XmAnyCallbackStruct *ptr); -#if !USE_SHARED_LIBRARY IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl) BEGIN_EVENT_TABLE(wxTextCtrl, wxControl) EVT_DROP_FILES(wxTextCtrl::OnDropFiles) EVT_CHAR(wxTextCtrl::OnChar) + + EVT_MENU(wxID_CUT, wxTextCtrl::OnCut) + EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy) + EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste) + EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo) + EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo) + + EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut) + EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy) + EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste) + EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo) + EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo) + END_EVENT_TABLE() -#endif // ============================================================================ // implementation @@ -65,9 +91,6 @@ static void wxTextWindowActivateProc(Widget w, XtPointer clientData, XmAnyCallba // Text item wxTextCtrl::wxTextCtrl() -#ifndef NO_TEXT_WINDOW_STREAM - : streambuf() -#endif { m_tempCallbackStruct = (void*) NULL; m_modified = FALSE; @@ -135,6 +158,10 @@ bool wxTextCtrl::Create(wxWindow *parent, NULL ); + XtVaSetValues ((Widget) m_mainWidget, + XmNeditable, ((style & wxTE_READONLY) ? False : True), + NULL); + // TODO: Is this relevant? What does it do? int noCols = 2; if (!value.IsNull() && (value.Length() > (unsigned int) noCols)) @@ -153,8 +180,21 @@ bool wxTextCtrl::Create(wxWindow *parent, } if ( !!value ) + { +#if 0 + // don't do this because it is just linking the text to a source + // string which is unsafe. MB + // XmTextSetString ((Widget) m_mainWidget, (char*)value.c_str()); - +#else + // do this instead... MB + // + XtVaSetValues( (Widget) m_mainWidget, + XmNvalue, (char *)value.c_str(), + NULL); +#endif + } + // install callbacks XtAddCallback((Widget) m_mainWidget, XmNvalueChangedCallback, (XtCallbackProc)wxTextWindowChangedProc, (XtPointer)this); @@ -167,7 +207,7 @@ bool wxTextCtrl::Create(wxWindow *parent, XtAddCallback((Widget) m_mainWidget, XmNlosingFocusCallback, (XtCallbackProc)wxTextWindowLoseFocusProc, (XtPointer)this); // font - m_windowFont = parent->GetFont(); + m_font = parent->GetFont(); ChangeFont(FALSE); SetCanAddEventHandler(TRUE); @@ -185,29 +225,52 @@ WXWidget wxTextCtrl::GetTopWidget() const wxString wxTextCtrl::GetValue() const { + wxString str; // result + if (m_windowStyle & wxTE_PASSWORD) - return m_value; + { + // the value is stored always in m_value because it can't be retrieved + // from the text control + str = m_value; + } else { + // just get the string from Motif char *s = XmTextGetString ((Widget) m_mainWidget); - if (s) + if ( s ) { - wxString str(s); + str = s; XtFree (s); - return str; } - else + //else: return empty string + + if ( m_tempCallbackStruct ) { - return wxEmptyString; + // the string in the control isn't yet updated, can't use it as is + MergeChangesIntoString(str, (XmTextVerifyCallbackStruct *) + m_tempCallbackStruct); } } + + return str; } void wxTextCtrl::SetValue(const wxString& value) { m_inSetValue = TRUE; +#if 0 + // don't do this because it is just linking the text to a source + // string which is unsafe. MB + // XmTextSetString ((Widget) m_mainWidget, (char*)value.c_str()); +#else + // do this instead... MB + // + XtVaSetValues( (Widget) m_mainWidget, + XmNvalue, (char *)value.c_str(), + NULL); +#endif m_inSetValue = FALSE; } @@ -228,6 +291,67 @@ void wxTextCtrl::Paste() XmTextPaste((Widget) m_mainWidget); } +bool wxTextCtrl::CanCopy() const +{ + // Can copy if there's a selection + long from, to; + GetSelection(& from, & to); + return (from != to) ; +} + +bool wxTextCtrl::CanCut() const +{ + // Can cut if there's a selection + long from, to; + GetSelection(& from, & to); + return (from != to) && (IsEditable()); +} + +bool wxTextCtrl::CanPaste() const +{ + return IsEditable() ; +} + +// Undo/redo +void wxTextCtrl::Undo() +{ + // Not possible in Motif +} + +void wxTextCtrl::Redo() +{ + // Not possible in Motif +} + +bool wxTextCtrl::CanUndo() const +{ + // No Undo in Motif + return FALSE; +} + +bool wxTextCtrl::CanRedo() const +{ + // No Redo in Motif + return FALSE; +} + +// If the return values from and to are the same, there is no +// selection. +void wxTextCtrl::GetSelection(long* from, long* to) const +{ + XmTextPosition left, right; + + XmTextGetSelectionPosition((Widget) m_mainWidget, & left, & right); + + *from = (long) left; + *to = (long) right; +} + +bool wxTextCtrl::IsEditable() const +{ + return (XmTextGetEditable((Widget) m_mainWidget) != 0); +} + void wxTextCtrl::SetEditable(bool editable) { XmTextSetEditable((Widget) m_mainWidget, (Boolean) editable); @@ -283,7 +407,7 @@ bool wxTextCtrl::LoadFile(const wxString& file) Clear(); Widget textWidget = (Widget) m_mainWidget; - FILE *fp; + FILE *fp = 0; struct stat statb; if ((stat ((char*) (const char*) file, &statb) == -1) || (statb.st_mode & S_IFMT) != S_IFREG || @@ -386,7 +510,6 @@ bool wxTextCtrl::IsModified() const // Makes 'unmodified' void wxTextCtrl::DiscardEdits() { - XmTextSetString ((Widget) m_mainWidget, ""); m_modified = FALSE; } @@ -435,11 +558,16 @@ long wxTextCtrl::XYToPosition(long x, long y) const return r+x; } -void wxTextCtrl::PositionToXY(long pos, long *x, long *y) const +bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const { Position xx, yy; XmTextPosToXY((Widget) m_mainWidget, pos, &xx, &yy); - *x = xx; *y = yy; + if ( x ) + *x = xx; + if ( y ) + *y = yy; + + return TRUE; } void wxTextCtrl::ShowPosition(long pos) @@ -497,161 +625,6 @@ void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event) } } -// The streambuf code was partly taken from chapter 3 by Jerry Schwarz of -// AT&T's "C++ Lanuage System Release 3.0 Library Manual" - Stein Somers - -//========================================================================= -// Called then the buffer is full (gcc 2.6.3) -// or when "endl" is output (Borland 4.5) -//========================================================================= -// Class declaration using multiple inheritance doesn't work properly for -// Borland. See note in wb_text.h. -#ifndef NO_TEXT_WINDOW_STREAM -int wxTextCtrl::overflow(int c) -{ - // Make sure there is a holding area - if ( allocate()==EOF ) - { - wxError("Streambuf allocation failed","Internal error"); - return EOF; - } - - // Verify that there are no characters in get area - if ( gptr() && gptr() < egptr() ) - { - wxError("wxTextCtrl::overflow: Who's trespassing my get area?","Internal error"); - return EOF; - } - - // Reset get area - setg(0,0,0); - - // Make sure there is a put area - if ( ! pptr() ) - { - /* This doesn't seem to be fatal so comment out error message */ - // wxError("Put area not opened","Internal error"); - setp( base(), base() ); - } - - // Determine how many characters have been inserted but no consumed - int plen = pptr() - pbase(); - - // Now Jerry relies on the fact that the buffer is at least 2 chars - // long, but the holding area "may be as small as 1" ??? - // And we need an additional \0, so let's keep this inefficient but - // safe copy. - - // If c!=EOF, it is a character that must also be comsumed - int xtra = c==EOF? 0 : 1; - - // Write temporary C-string to wxTextWindow - { - char *txt = new char[plen+xtra+1]; - memcpy(txt, pbase(), plen); - txt[plen] = (char)c; // append c - txt[plen+xtra] = '\0'; // append '\0' or overwrite c - // If the put area already contained \0, output will be truncated there - WriteText(txt); - delete[] txt; - } - - // Reset put area - setp(pbase(), epptr()); - -#if defined(__WATCOMC__) - return __NOT_EOF; -#elif defined(zapeof) // HP-UX (all cfront based?) - return zapeof(c); -#else - return c!=EOF ? c : 0; // this should make everybody happy -#endif -} - -//========================================================================= -// called then "endl" is output (gcc) or then explicit sync is done (Borland) -//========================================================================= -int wxTextCtrl::sync() -{ - // Verify that there are no characters in get area - if ( gptr() && gptr() < egptr() ) - { - wxError("Who's trespassing my get area?","Internal error"); - return EOF; - } - - if ( pptr() && pptr() > pbase() ) return overflow(EOF); - - return 0; - /* OLD CODE - int len = pptr() - pbase(); - char *txt = new char[len+1]; - strncpy(txt, pbase(), len); - txt[len] = '\0'; - (*this) << txt; - setp(pbase(), epptr()); - delete[] txt; - return 0; - */ -} - -//========================================================================= -// Should not be called by a "ostream". Used by a "istream" -//========================================================================= -int wxTextCtrl::underflow() -{ - return EOF; -} -#endif - -wxTextCtrl& wxTextCtrl::operator<<(const wxString& s) -{ - AppendText(s); - return *this; -} - -wxTextCtrl& wxTextCtrl::operator<<(float f) -{ - wxString str; - str.Printf("%.2f", f); - AppendText(str); - return *this; -} - -wxTextCtrl& wxTextCtrl::operator<<(double d) -{ - wxString str; - str.Printf("%.2f", d); - AppendText(str); - return *this; -} - -wxTextCtrl& wxTextCtrl::operator<<(int i) -{ - wxString str; - str.Printf("%d", i); - AppendText(str); - return *this; -} - -wxTextCtrl& wxTextCtrl::operator<<(long i) -{ - wxString str; - str.Printf("%ld", i); - AppendText(str); - return *this; -} - -wxTextCtrl& wxTextCtrl::operator<<(const char c) -{ - char buf[2]; - - buf[0] = c; - buf[1] = 0; - AppendText(buf); - return *this; -} - void wxTextCtrl::OnChar(wxKeyEvent& event) { // Indicates that we should generate a normal command, because @@ -727,7 +700,96 @@ void wxTextCtrl::ChangeForegroundColour() } } -static void wxTextWindowChangedProc (Widget w, XtPointer clientData, XtPointer ptr) +void wxTextCtrl::DoSendEvents(void *wxcbs, long keycode) +{ + // we're in process of updating the text control + m_tempCallbackStruct = wxcbs; + + XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *)wxcbs; + + wxKeyEvent event (wxEVT_CHAR); + event.SetId(GetId()); + event.m_keyCode = keycode; + event.SetEventObject(this); + + // Only if wxTextCtrl::OnChar is called will this be set to True (and + // the character passed through) + cbs->doit = False; + + GetEventHandler()->ProcessEvent(event); + + if ( !InSetValue() && m_processedDefault ) + { + // Can generate a command + wxCommandEvent commandEvent(wxEVT_COMMAND_TEXT_UPDATED, GetId()); + commandEvent.SetEventObject(this); + ProcessCommand(commandEvent); + } + + // do it after the (user) event handlers processed the events because + // otherwise GetValue() would return incorrect (not yet updated value) + m_tempCallbackStruct = NULL; +} + +// ---------------------------------------------------------------------------- +// helpers and Motif callbacks +// ---------------------------------------------------------------------------- + +static void MergeChangesIntoString(wxString& value, + XmTextVerifyCallbackStruct *cbs) +{ + /* _sm_ + * At least on my system (SunOS 4.1.3 + Motif 1.2), you need to think of + * every event as a replace event. cbs->text->ptr gives the replacement + * text, cbs->startPos gives the index of the first char affected by the + * replace, and cbs->endPos gives the index one more than the last char + * affected by the replace (startPos == endPos implies an empty range). + * Hence, a deletion is represented by replacing all input text with a + * blank string ("", *not* NULL!). A simple insertion that does not + * overwrite any text has startPos == endPos. + */ + + if ( !value ) + { + // easy case: the ol value was empty + value = cbs->text->ptr; + } + else + { + // merge the changes into the value + const char * const passwd = value; + int len = value.length(); + + len += strlen(cbs->text->ptr) + 1; // + new text (if any) + NUL + len -= cbs->endPos - cbs->startPos; // - text from affected region. + + char * newS = new char [len]; + char * dest = newS, + * insert = cbs->text->ptr; + + // Copy (old) text from passwd, up to the start posn of the change. + int i; + const char * p = passwd; + for (i = 0; i < cbs->startPos; ++i) + *dest++ = *p++; + + // Copy the text to be inserted). + while (*insert) + *dest++ = *insert++; + + // Finally, copy into newS any remaining text from passwd[endPos] on. + for (p = passwd + cbs->endPos; *p; ) + *dest++ = *p++; + *dest = 0; + + value = newS; + + delete[] newS; + } +} + +static void +wxTextWindowChangedProc (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr)) { if (!wxGetWindowFromTable(w)) // Widget has been deleted! @@ -738,110 +800,43 @@ static void wxTextWindowChangedProc (Widget w, XtPointer clientData, XtPointer p } static void -wxTextWindowModifyProc (Widget w, XtPointer clientData, XmTextVerifyCallbackStruct *cbs) +wxTextWindowModifyProc (Widget WXUNUSED(w), XtPointer clientData, XmTextVerifyCallbackStruct *cbs) { wxTextCtrl *tw = (wxTextCtrl *) clientData; tw->m_processedDefault = FALSE; - // First, do some stuff if it's a password control. - // (What does this do exactly?) + // First, do some stuff if it's a password control: in this case, we need + // to store the string inside the class because GetValue() can't retrieve + // it from the text ctrl. We do *not* do it in other circumstances because + // it would double the amount of memory needed. - if (tw->GetWindowStyleFlag() & wxTE_PASSWORD) + if ( tw->GetWindowStyleFlag() & wxTE_PASSWORD ) { - /* _sm_ - * At least on my system (SunOS 4.1.3 + Motif 1.2), you need to think of - * every event as a replace event. cbs->text->ptr gives the replacement - * text, cbs->startPos gives the index of the first char affected by the - * replace, and cbs->endPos gives the index one more than the last char - * affected by the replace (startPos == endPos implies an empty range). - * Hence, a deletion is represented by replacing all input text with a - * blank string ("", *not* NULL!). A simple insertion that does not - * overwrite any text has startPos == endPos. - */ + MergeChangesIntoString(tw->m_value, cbs); - if (tw->m_value.IsNull()) - { - tw->m_value = cbs->text->ptr; - } - else - { - char * passwd = (char*) (const char*) tw->m_value; // Set up a more convenient alias. - - int len = passwd ? strlen(passwd) : 0; // Enough room for old text - len += strlen(cbs->text->ptr) + 1; // + new text (if any) + NUL - len -= cbs->endPos - cbs->startPos; // - text from affected region. - - char * newS = new char [len]; - char * p = passwd, * dest = newS, * insert = cbs->text->ptr; - - // Copy (old) text from passwd, up to the start posn of the change. - int i; - for (i = 0; i < cbs->startPos; ++i) - *dest++ = *p++; - - // Copy the text to be inserted). - while (*insert) - *dest++ = *insert++; - - // Finally, copy into newS any remaining text from passwd[endPos] on. - for (p = passwd + cbs->endPos; *p; ) - *dest++ = *p++; - *dest = 0; - - tw->m_value = newS; - - delete[] newS; - } - - if (cbs->text->length>0) + if ( cbs->text->length > 0 ) { int i; for (i = 0; i < cbs->text->length; ++i) cbs->text->ptr[i] = '*'; - cbs->text->ptr[i] = 0; + cbs->text->ptr[i] = '\0'; } } - // If we're already within an OnChar, return: probably - // a programmatic insertion. + // If we're already within an OnChar, return: probably a programmatic + // insertion. if (tw->m_tempCallbackStruct) return; // Check for a backspace if (cbs->startPos == (cbs->currInsert - 1)) { - tw->m_tempCallbackStruct = (void*) cbs; - - wxKeyEvent event (wxEVT_CHAR); - event.SetId(tw->GetId()); - event.m_keyCode = WXK_DELETE; - event.SetEventObject(tw); - - // Only if wxTextCtrl::OnChar is called - // will this be set to True (and the character - // passed through) - cbs->doit = False; - - tw->GetEventHandler()->ProcessEvent(event); - - tw->m_tempCallbackStruct = NULL; - - if (tw->InSetValue()) - return; - - if (tw->m_processedDefault) - { - // Can generate a command - wxCommandEvent commandEvent(wxEVT_COMMAND_TEXT_UPDATED, tw->GetId()); - commandEvent.SetEventObject(tw); - tw->ProcessCommand(commandEvent); - } + tw->DoSendEvents((void *)cbs, WXK_DELETE); return; } - // Pasting operation: let it through without - // calling OnChar + // Pasting operation: let it through without calling OnChar if (cbs->text->length > 1) return; @@ -849,40 +844,13 @@ wxTextWindowModifyProc (Widget w, XtPointer clientData, XmTextVerifyCallbackStru if (cbs->text->ptr == NULL) return; - tw->m_tempCallbackStruct = (void*) cbs; - - wxKeyEvent event (wxEVT_CHAR); - event.SetId(tw->GetId()); - event.SetEventObject(tw); - event.m_keyCode = (cbs->text->ptr[0] == 10 ? 13 : cbs->text->ptr[0]); - - // Only if wxTextCtrl::OnChar is called - // will this be set to True (and the character - // passed through) - cbs->doit = False; - - tw->GetEventHandler()->ProcessEvent(event); - - tw->m_tempCallbackStruct = NULL; - - if (tw->InSetValue()) - return; - - if (tw->m_processedDefault) - { - // Can generate a command - wxCommandEvent commandEvent(wxEVT_COMMAND_TEXT_UPDATED, tw->GetId()); - commandEvent.SetEventObject(tw); - tw->ProcessCommand(commandEvent); - } + // normal key press + char ch = cbs->text->ptr[0]; + tw->DoSendEvents((void *)cbs, ch == '\n' ? '\r' : ch); } -// ---------------------------------------------------------------------------- -// callbacks -// ---------------------------------------------------------------------------- - static void -wxTextWindowGainFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs) +wxTextWindowGainFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *WXUNUSED(cbs)) { if (!wxGetWindowFromTable(w)) return; @@ -894,7 +862,7 @@ wxTextWindowGainFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct * } static void -wxTextWindowLoseFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs) +wxTextWindowLoseFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct *WXUNUSED(cbs)) { if (!wxGetWindowFromTable(w)) return; @@ -906,7 +874,7 @@ wxTextWindowLoseFocusProc (Widget w, XtPointer clientData, XmAnyCallbackStruct * } static void wxTextWindowActivateProc(Widget w, XtPointer clientData, - XmAnyCallbackStruct *ptr) + XmAnyCallbackStruct *WXUNUSED(ptr)) { if (!wxGetWindowFromTable(w)) return; @@ -921,3 +889,53 @@ static void wxTextWindowActivateProc(Widget w, XtPointer clientData, event.SetEventObject(tw); tw->ProcessCommand(event); } + +void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event)) +{ + Cut(); +} + +void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event)) +{ + Copy(); +} + +void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event)) +{ + Paste(); +} + +void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event)) +{ + Undo(); +} + +void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event)) +{ + Redo(); +} + +void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event) +{ + event.Enable( CanCut() ); +} + +void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event) +{ + event.Enable( CanCopy() ); +} + +void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event) +{ + event.Enable( CanPaste() ); +} + +void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event) +{ + event.Enable( CanUndo() ); +} + +void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event) +{ + event.Enable( CanRedo() ); +}