#pragma hdrstop
#endif
+#if wxUSE_TEXTCTRL
+
#ifndef WX_PRECOMP
#include "wx/textctrl.h"
#include "wx/settings.h"
#include <stdlib.h>
#include <sys/types.h>
-#if wxUSE_IOSTREAMH
-# include <fstream.h>
-#else
-# include <fstream>
+#if wxUSE_RICHEDIT && (!defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__))
+ #include <richedit.h>
#endif
-#if wxUSE_RICHEDIT && !defined(__GNUWIN32_OLD__)
- #include <richedit.h>
+// old mingw32 doesn't define this
+#ifndef CFM_CHARSET
+ #define CFM_CHARSET 0x08000000
+#endif // CFM_CHARSET
+
+#ifndef CFM_BACKCOLOR
+ #define CFM_BACKCOLOR 0x04000000
+#endif
+
+// cygwin does not have these defined for richedit
+#ifndef ENM_LINK
+ #define ENM_LINK 0x04000000
+#endif
+
+#ifndef EM_AUTOURLDETECT
+ #define EM_AUTOURLDETECT (WM_USER + 91)
#endif
+#ifndef EN_LINK
+ #define EN_LINK 0x070b
+
+ typedef struct _enlink
+ {
+ NMHDR nmhdr;
+ UINT msg;
+ WPARAM wParam;
+ LPARAM lParam;
+ CHARRANGE chrg;
+ } ENLINK;
+#endif // ENLINK
+
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
#endif
END_EVENT_TABLE()
-
// ============================================================================
// implementation
// ============================================================================
// translate wxWin style flags to MSW ones, checking for consistency while
// doing it
- long msStyle = ES_LEFT | WS_VISIBLE | WS_CHILD | WS_TABSTOP /* | WS_CLIPSIBLINGS */ ;
+ long msStyle = ES_LEFT | WS_TABSTOP;
+
+ if ( m_windowStyle & wxCLIP_SIBLINGS )
+ msStyle |= WS_CLIPSIBLINGS;
+
if ( m_windowStyle & wxTE_MULTILINE )
{
wxASSERT_MSG( !(m_windowStyle & wxTE_PROCESS_ENTER),
msStyle |= WS_VSCROLL;
m_windowStyle |= wxTE_PROCESS_ENTER;
}
- else
+ else // !multiline
+ {
+ // there is really no reason to not have this style for single line
+ // text controls
msStyle |= ES_AUTOHSCROLL;
+ }
- if (m_windowStyle & wxHSCROLL)
- msStyle |= (WS_HSCROLL | ES_AUTOHSCROLL);
+ if ( m_windowStyle & wxHSCROLL )
+ msStyle |= WS_HSCROLL | ES_AUTOHSCROLL;
- if (m_windowStyle & wxTE_READONLY)
+ if ( m_windowStyle & wxTE_READONLY )
msStyle |= ES_READONLY;
- if (m_windowStyle & wxTE_PASSWORD) // hidden input
+ if ( m_windowStyle & wxTE_PASSWORD )
msStyle |= ES_PASSWORD;
- if (m_windowStyle & wxTE_AUTO_SCROLL)
- msStyle |= ES_AUTOHSCROLL;
+ if ( m_windowStyle & wxTE_AUTO_SCROLL )
+ msStyle |= ES_AUTOHSCROLL;
+ if ( m_windowStyle & wxTE_NOHIDESEL )
+ msStyle |= ES_NOHIDESEL;
// we always want the characters and the arrows
m_lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;
else
{
msStyle |= ES_AUTOVSCROLL;
+ // Experimental: this seems to help with the scroll problem. See messages from Jekabs Andrushaitis <j.andrusaitis@konts.lv>
+ // wx-dev list, entitled "[wx-dev] wxMSW-EVT_KEY_DOWN and wxMSW-wxTextCtrl" and "[wx-dev] TextCtrl (RichEdit)"
+ // Unfortunately, showing the selection in blue when the control doesn't have
+ // the focus is non-standard behaviour, and we need to find another workaround.
+ //msStyle |= ES_NOHIDESEL ;
m_isRich = TRUE;
int ver = wxRichEditModule::GetLoadedVersion();
m_isRich = FALSE;
#endif // wxUSE_RICHEDIT
- bool want3D;
- WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D);
-
- // Even with extended styles, need to combine with WS_BORDER for them to
- // look right.
- if ( want3D || wxStyleHasBorder(m_windowStyle) )
- msStyle |= WS_BORDER;
-
- // NB: don't use pos and size as CreateWindowEx arguments because they
- // might be -1 in which case we should use the default values (and
- // SetSize called below takes care of it)
- m_hWnd = (WXHWND)::CreateWindowEx(exStyle,
- windowClass.c_str(),
- NULL,
- msStyle,
- 0, 0, 0, 0,
- GetHwndOf(parent),
- (HMENU)m_windowId,
- wxGetInstance(),
- NULL);
-
- wxCHECK_MSG( m_hWnd, FALSE, wxT("Failed to create text ctrl") );
-
-#if wxUSE_CTL3D
- if ( want3D )
+ // we need to turn '\n's into "\r\n"s for the multiline controls
+ wxString valueWin;
+ if ( m_windowStyle & wxTE_MULTILINE )
{
- Ctl3dSubclassCtl(GetHwnd());
- m_useCtl3D = TRUE;
+ valueWin = wxTextFile::Translate(value, wxTextFileType_Dos);
}
-#endif
-
-#if wxUSE_RICHEDIT
- if (m_isRich)
+ else // single line
{
- // Have to enable events
- ::SendMessage(GetHwnd(), EM_SETEVENTMASK, 0,
- ENM_CHANGE | ENM_DROPFILES | ENM_SELCHANGE | ENM_UPDATE);
+ valueWin = value;
}
-#endif
- SubclassWin(GetHWND());
+ if ( !MSWCreateControl(windowClass, msStyle, pos, size, valueWin) )
+ return FALSE;
- // set font, position, size and initial value
- wxFont& fontParent = parent->GetFont();
- if ( fontParent.Ok() )
- {
- SetFont(fontParent);
- }
- else
- {
- SetFont(wxSystemSettings::GetSystemFont(wxSYS_SYSTEM_FONT));
- }
+ SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
- // Causes a crash for Symantec C++ and WIN32 for some reason
-#if !(defined(__SC__) && defined(__WIN32__))
- if ( !value.IsEmpty() )
+#if wxUSE_RICHEDIT
+ if (m_isRich)
{
- SetValue(value);
- }
-#endif
+ // have to enable events manually
+ LPARAM mask = ENM_CHANGE | ENM_DROPFILES | ENM_SELCHANGE | ENM_UPDATE;
- // set colours
- SetupColours();
+ if ( m_windowStyle & wxTE_AUTO_URL )
+ {
+ mask |= ENM_LINK;
- SetSize(pos.x, pos.y, size.x, size.y);
+ ::SendMessage(GetHwnd(), EM_AUTOURLDETECT, TRUE, 0);
+ }
+
+ ::SendMessage(GetHwnd(), EM_SETEVENTMASK, 0, mask);
+ }
+#endif // wxUSE_RICHEDIT
return TRUE;
}
m_windowStyle |= wxTE_PROCESS_ENTER;
}
-void wxTextCtrl::SetupColours()
-{
- wxColour bkgndColour;
-// if (IsEditable() || (m_windowStyle & wxTE_MULTILINE))
- bkgndColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW);
-// else
-// bkgndColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);
-
- SetBackgroundColour(bkgndColour);
- SetForegroundColour(GetParent()->GetForegroundColour());
-}
-
// ----------------------------------------------------------------------------
// set/get the controls text
// ----------------------------------------------------------------------------
{
wxString valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
+#if wxUSE_RICHEDIT
+ // ensure that the new text will be in the default style
+ if ( IsRich() &&
+ (m_defaultStyle.HasFont() || m_defaultStyle.HasTextColour()) )
+ {
+ long start, end;
+ GetSelection(&start, &end);
+ SetStyle(start, end, m_defaultStyle );
+ }
+#endif // wxUSE_RICHEDIT
+
SendMessage(GetHwnd(), EM_REPLACESEL, 0, (LPARAM)valueDos.c_str());
AdjustSpaceLimit();
SendMessage(hWnd, EM_SETSEL, 0, MAKELPARAM(pos, pos));
#endif // Win32/16
- static const wxChar *nothing = _T("");
- SendMessage(hWnd, EM_REPLACESEL, 0, (LPARAM)nothing);
+#if wxUSE_RICHEDIT
+ if ( !m_isRich)
+#endif
+ {
+ static const wxChar *nothing = _T("");
+ SendMessage(hWnd, EM_REPLACESEL, 0, (LPARAM)nothing);
+ }
}
void wxTextCtrl::SetInsertionPointEnd()
*from = charRange.cpMin;
*to = charRange.cpMax;
+ }
+ else
+#endif // rich/!rich
+ {
+ DWORD dwStart, dwEnd;
+ WPARAM wParam = (WPARAM) &dwStart; // receives starting position
+ LPARAM lParam = (LPARAM) &dwEnd; // receives ending position
+
+ ::SendMessage(GetHwnd(), EM_GETSEL, wParam, lParam);
- return;
+ *from = dwStart;
+ *to = dwEnd;
}
-#endif
- DWORD dwStart, dwEnd;
- WPARAM wParam = (WPARAM) (DWORD*) & dwStart; // receives starting position
- LPARAM lParam = (LPARAM) (DWORD*) & dwEnd; // receives ending position
+}
- ::SendMessage(GetHwnd(), EM_GETSEL, wParam, lParam);
+wxString wxTextCtrl::GetStringSelection() const
+{
+ // the base class version works correctly for the rich text controls
+ // because there the lines are terminated with just '\r' which means that
+ // the string length is not changed in the result of the translations doen
+ // in GetValue() but for the normal ones when we replace "\r\n" with '\n'
+ // we break the indices
+#if wxUSE_RICHEDIT
+ if ( m_isRich )
+ return wxTextCtrlBase::GetStringSelection();
+#endif // wxUSE_RICHEDIT
- *from = dwStart;
- *to = dwEnd;
+ long from, to;
+ GetSelection(&from, &to);
+
+ wxString str;
+ if ( from < to )
+ {
+ str = wxGetWindowText(GetHWND()).Mid(from, to - from);
+
+ // and now that we have the correct selection, convert it to the
+ // correct format
+ str = wxTextFile::Translate(str, wxTextFileType_Unix);
+ }
+
+ return str;
}
bool wxTextCtrl::IsEditable() const
void wxTextCtrl::Replace(long from, long to, const wxString& value)
{
-#if wxUSE_CLIPBOARD
HWND hWnd = GetHwnd();
long fromChar = from;
long toChar = to;
// Set selection and remove it
#ifdef __WIN32__
SendMessage(hWnd, EM_SETSEL, fromChar, toChar);
+ SendMessage(hWnd, EM_REPLACESEL, (WPARAM)TRUE, (LPARAM)value.c_str());
#else
SendMessage(hWnd, EM_SETSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
-#endif
- SendMessage(hWnd, WM_CUT, (WPARAM)0, (LPARAM)0);
-
- // Now replace with 'value', by pasting.
- wxSetClipboardData(wxDF_TEXT, (wxObject *) (const wxChar *)value, 0, 0);
-
- // Paste into edit control
- SendMessage(hWnd, WM_PASTE, (WPARAM)0, (LPARAM)0L);
-#else
- wxFAIL_MSG("wxTextCtrl::Replace not implemented if wxUSE_CLIPBOARD is 0.");
+ SendMessage(hWnd, EM_REPLACESEL, (WPARAM)0, (LPARAM)value.c_str());
#endif
}
// Cut all selected text
#ifdef __WIN32__
SendMessage(hWnd, EM_SETSEL, fromChar, toChar);
+ SendMessage(hWnd, EM_REPLACESEL, (WPARAM)TRUE, (LPARAM)"");
#else
SendMessage(hWnd, EM_SETSEL, (WPARAM)0, (LPARAM)MAKELONG(fromChar, toChar));
+ SendMessage(hWnd, EM_REPLACESEL, (WPARAM)0, (LPARAM)"");
#endif
- SendMessage(hWnd, WM_CUT, (WPARAM)0, (LPARAM)0);
}
void wxTextCtrl::SetSelection(long from, long to)
return str;
}
+void wxTextCtrl::SetMaxLength(unsigned long len)
+{
+ ::SendMessage(GetHwnd(), EM_LIMITTEXT, len, 0);
+}
+
// ----------------------------------------------------------------------------
// Undo/redo
// ----------------------------------------------------------------------------
}
}
+// ----------------------------------------------------------------------------
+// kbd input processing
+// ----------------------------------------------------------------------------
+
+bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* pMsg)
+{
+ MSG *msg = (MSG *)pMsg;
+
+ // check for our special keys here: if we don't do it and the parent frame
+ // uses them as accelerators, they wouldn't work at all, so we disable
+ // usual preprocessing for them
+ if ( msg->message == WM_KEYDOWN )
+ {
+ WORD vkey = msg->wParam;
+ if ( (HIWORD(msg->lParam) & KF_ALTDOWN) == KF_ALTDOWN )
+ {
+ if ( vkey == VK_BACK )
+ return FALSE;
+ }
+ else // no Alt
+ {
+ if ( wxIsCtrlDown() )
+ {
+ switch ( vkey )
+ {
+ case 'C':
+ case 'V':
+ case 'X':
+ case VK_INSERT:
+ case VK_DELETE:
+ case VK_HOME:
+ case VK_END:
+ return FALSE;
+ }
+ }
+ else if ( wxIsShiftDown() )
+ {
+ if ( vkey == VK_INSERT || vkey == VK_DELETE )
+ return FALSE;
+ }
+ }
+ }
+
+ return wxControl::MSWShouldPreProcessMessage(pMsg);
+}
+
void wxTextCtrl::OnChar(wxKeyEvent& event)
{
switch ( event.KeyCode() )
case EN_KILLFOCUS:
{
wxFocusEvent event(param == EN_KILLFOCUS ? wxEVT_KILL_FOCUS
- : wxEVT_SET_FOCUS,
- m_windowId);
+ : wxEVT_SET_FOCUS,
+ m_windowId);
event.SetEventObject( this );
GetEventHandler()->ProcessEvent(event);
}
case EN_MAXTEXT:
// the text size limit has been hit - increase it
- AdjustSpaceLimit();
+ if ( !AdjustSpaceLimit() )
+ {
+ wxCommandEvent event(wxEVT_COMMAND_TEXT_MAXLEN, m_windowId);
+ InitCommandEvent(event);
+ event.SetString(GetValue());
+ ProcessCommand(event);
+ }
break;
// the other notification messages are not processed
}
WXHBRUSH wxTextCtrl::OnCtlColor(WXHDC pDC, WXHWND WXUNUSED(pWnd), WXUINT WXUNUSED(nCtlColor),
+#if wxUSE_CTL3D
+ WXUINT message,
+ WXWPARAM wParam,
+ WXLPARAM lParam
+#else
WXUINT WXUNUSED(message),
WXWPARAM WXUNUSED(wParam),
- WXLPARAM WXUNUSED(lParam))
+ WXLPARAM WXUNUSED(lParam)
+#endif
+ )
{
#if wxUSE_CTL3D
if ( m_useCtl3D )
}
#endif
-void wxTextCtrl::AdjustSpaceLimit()
+bool wxTextCtrl::AdjustSpaceLimit()
{
#ifndef __WIN16__
- unsigned int len = ::GetWindowTextLength(GetHwnd()),
- limit = ::SendMessage(GetHwnd(), EM_GETLIMITTEXT, 0, 0);
+ unsigned int limit = ::SendMessage(GetHwnd(), EM_GETLIMITTEXT, 0, 0);
+
+ // HACK: we try to automatically extend the limit for the amount of text
+ // to allow (interactively) entering more than 64Kb of text under
+ // Win9x but we shouldn't reset the text limit which was previously
+ // set explicitly with SetMaxLength()
+ //
+ // we could solve this by storing the limit we set in wxTextCtrl but
+ // to save space we prefer to simply test here the actual limit
+ // value: we consider that SetMaxLength() can only be called for
+ // values < 32Kb
+ if ( limit < 0x8000 )
+ {
+ // we've got more text than limit set by SetMaxLength()
+ return FALSE;
+ }
+
+ unsigned int len = ::GetWindowTextLength(GetHwnd());
if ( len >= limit )
{
limit = len + 0x8000; // 32Kb
}
}
#endif // !Win16
+
+ // we changed the limit
+ return TRUE;
}
bool wxTextCtrl::AcceptsFocus() const
event.Enable( CanRedo() );
}
+// the rest of the file only deals with the rich edit controls
+#if wxUSE_RICHEDIT
+
// ----------------------------------------------------------------------------
-// colour setting for the rich edit controls
+// EN_LINK processing
// ----------------------------------------------------------------------------
-#if wxUSE_RICHEDIT
+bool wxTextCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
+{
+ NMHDR *hdr = (NMHDR* )lParam;
+ if ( hdr->code == EN_LINK )
+ {
+ ENLINK *enlink = (ENLINK *)hdr;
+
+ switch ( enlink->msg )
+ {
+ case WM_SETCURSOR:
+ // ok, so it is hardcoded - do we really nee to customize it?
+ ::SetCursor(GetHcursorOf(wxCursor(wxCURSOR_HAND)));
+ *result = TRUE;
+ break;
+
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_RBUTTONDBLCLK:
+ // send a mouse event
+ {
+ static const wxEventType eventsMouse[] =
+ {
+ wxEVT_MOTION,
+ wxEVT_LEFT_DOWN,
+ wxEVT_LEFT_UP,
+ wxEVT_LEFT_DCLICK,
+ wxEVT_RIGHT_DOWN,
+ wxEVT_RIGHT_UP,
+ wxEVT_RIGHT_DCLICK,
+ };
+
+ // the event ids are consecutive
+ wxMouseEvent
+ evtMouse(eventsMouse[enlink->msg - WM_MOUSEMOVE]);
+
+ InitMouseEvent(evtMouse,
+ GET_X_LPARAM(enlink->lParam),
+ GET_Y_LPARAM(enlink->lParam),
+ enlink->wParam);
+
+ wxTextUrlEvent event(m_windowId, evtMouse,
+ enlink->chrg.cpMin,
+ enlink->chrg.cpMax);
+
+ InitCommandEvent(event);
+
+ *result = ProcessCommand(event);
+ }
+ break;
+ }
+
+ return TRUE;
+ }
+
+ // not processed
+ return FALSE;
+}
+
+// ----------------------------------------------------------------------------
+// colour setting for the rich edit controls
+// ----------------------------------------------------------------------------
// Watcom C++ doesn't define this
#ifndef SCF_ALL
return TRUE;
}
-#endif // wxUSE_RICHEDIT
+// ----------------------------------------------------------------------------
+// styling support for rich edit controls
+// ----------------------------------------------------------------------------
+
+bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
+{
+ if ( !IsRich() )
+ {
+ // can't do it with normal text control
+ return FALSE;
+ }
+
+ // the rich text control doesn't handle setting background colour, so don't
+ // even try if it's the only thing we want to change
+ if ( wxRichEditModule::GetLoadedVersion() < 2 &&
+ !style.HasFont() && !style.HasTextColour() )
+ {
+ // nothing to do: return TRUE if there was really nothing to do and
+ // FALSE if we failed to set bg colour
+ return !style.HasBackgroundColour();
+ }
+
+ // order the range if needed
+ if ( start > end )
+ {
+ long tmp = start;
+ start = end;
+ end = tmp;
+ }
+
+ // we can only change the format of the selection, so select the range we
+ // want and restore the old selection later
+ long startOld, endOld;
+ GetSelection(&startOld, &endOld);
+
+ // but do we really have to change the selection?
+ bool changeSel = start != startOld || end != endOld;
+
+ if ( changeSel )
+ SendMessage(GetHwnd(), EM_SETSEL, (WPARAM) start, (LPARAM) end);
+
+ // initialize CHARFORMAT struct
+#if wxUSE_RICHEDIT2
+ CHARFORMAT2 cf;
+#else
+ CHARFORMAT cf;
+#endif
+ wxZeroMemory(cf);
+ cf.cbSize = sizeof(cf);
+
+ if ( style.HasFont() )
+ {
+ cf.dwMask |= CFM_FACE | CFM_SIZE | CFM_CHARSET |
+ CFM_ITALIC | CFM_BOLD | CFM_UNDERLINE;
+
+ // fill in data from LOGFONT but recalculate lfHeight because we need
+ // the real height in twips and not the negative number which
+ // wxFillLogFont() returns (this is correct in general and works with
+ // the Windows font mapper, but not here)
+ LOGFONT lf;
+ wxFillLogFont(&lf, &style.GetFont());
+ cf.yHeight = 20*style.GetFont().GetPointSize(); // 1 pt = 20 twips
+ cf.bCharSet = lf.lfCharSet;
+ cf.bPitchAndFamily = lf.lfPitchAndFamily;
+ wxStrncpy( cf.szFaceName, lf.lfFaceName, WXSIZEOF(cf.szFaceName) );
+
+ // also deal with underline/italic/bold attributes: note that we must
+ // always set CFM_ITALIC &c bits in dwMask, even if we don't set the
+ // style to allow clearing it
+ if ( lf.lfItalic )
+ {
+ cf.dwEffects |= CFE_ITALIC;
+ }
+
+ if ( lf.lfWeight == FW_BOLD )
+ {
+ cf.dwEffects |= CFE_BOLD;
+ }
+
+ if ( lf.lfUnderline )
+ {
+ cf.dwEffects |= CFE_UNDERLINE;
+ }
+
+ // strikeout fonts are not supported by wxWindows
+ }
+
+ if ( style.HasTextColour() )
+ {
+ cf.dwMask |= CFM_COLOR;
+ cf.crTextColor = wxColourToRGB(style.GetTextColour());
+ }
+
+#if wxUSE_RICHEDIT2
+ if ( wxRichEditModule::GetLoadedVersion() > 1 && style.HasBackgroundColour() )
+ {
+ cf.dwMask |= CFM_BACKCOLOR;
+ cf.crBackColor = wxColourToRGB(style.GetBackgroundColour());
+ }
+#endif // wxUSE_RICHEDIT2
+
+ // do format the selection
+ bool ok = ::SendMessage(GetHwnd(), EM_SETCHARFORMAT,
+ SCF_SELECTION, (LPARAM)&cf) != 0;
+ if ( !ok )
+ {
+ wxLogDebug(_T("SendMessage(EM_SETCHARFORMAT, SCF_SELECTION) failed"));
+ }
+
+ if ( changeSel )
+ {
+ // restore the original selection
+ SendMessage(GetHwnd(), EM_SETSEL, (WPARAM)startOld, (LPARAM)endOld);
+ }
+
+ return ok;
+}
// ----------------------------------------------------------------------------
// wxRichEditModule
// ----------------------------------------------------------------------------
-#if wxUSE_RICHEDIT
-
bool wxRichEditModule::OnInit()
{
// don't do anything - we will load it when needed
#endif // wxUSE_RICHEDIT
+#endif // wxUSE_TEXTCTRL