/////////////////////////////////////////////////////////////////////////////
-// Name: msw/textctrl.cpp
+// Name: src/msw/textctrl.cpp
// Purpose: wxTextCtrl
// Author: Julian Smart
// Modified by:
// declarations
// ============================================================================
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
- #pragma implementation "textctrl.h"
-#endif
-
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#endif
#include "wx/module.h"
+#include "wx/sysopt.h"
#if wxUSE_CLIPBOARD
#include "wx/clipbrd.h"
#if wxUSE_RICHEDIT
+#if wxUSE_INKEDIT
+#include "wx/dynlib.h"
+#endif
+
// old mingw32 has richedit stuff directly in windows.h and doesn't have
// richedit.h at all
#if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__)
class wxRichEditModule : public wxModule
{
public:
+ enum Version
+ {
+ Version_1, // riched32.dll
+ Version_2or3, // both use riched20.dll
+ Version_41, // msftedit.dll (XP SP1 and Windows 2003)
+ Version_Max
+ };
+
virtual bool OnInit();
virtual void OnExit();
- // load the richedit DLL of at least of required version
- static bool Load(int version = 1);
+ // load the richedit DLL for the specified version of rich edit
+ static bool Load(Version version);
+
+#if wxUSE_INKEDIT
+ // load the InkEdit library
+ static bool LoadInkEdit();
+#endif
private:
// the handles to richedit 1.0 and 2.0 (or 3.0) DLLs
- static HINSTANCE ms_hRichEdit[2];
+ static HINSTANCE ms_hRichEdit[Version_Max];
+
+#if wxUSE_INKEDIT
+ static wxDynamicLibrary ms_inkEditLib;
+ static bool ms_inkEditLibLoadAttemped;
+#endif
DECLARE_DYNAMIC_CLASS(wxRichEditModule)
};
-HINSTANCE wxRichEditModule::ms_hRichEdit[2] = { NULL, NULL };
+HINSTANCE wxRichEditModule::ms_hRichEdit[Version_Max] = { NULL, NULL, NULL };
+
+#if wxUSE_INKEDIT
+wxDynamicLibrary wxRichEditModule::ms_inkEditLib;
+bool wxRichEditModule::ms_inkEditLibLoadAttemped = false;
+#endif
IMPLEMENT_DYNAMIC_CLASS(wxRichEditModule, wxModule)
wxFLAGS_MEMBER(wxTE_CENTRE)
wxFLAGS_MEMBER(wxTE_RIGHT)
wxFLAGS_MEMBER(wxTE_DONTWRAP)
- wxFLAGS_MEMBER(wxTE_LINEWRAP)
+ wxFLAGS_MEMBER(wxTE_CHARWRAP)
wxFLAGS_MEMBER(wxTE_WORDWRAP)
wxEND_FLAGS( wxTextCtrlStyle )
wxCONSTRUCTOR_6( wxTextCtrl , wxWindow* , Parent , wxWindowID , Id , wxString , Value , wxPoint , Position , wxSize , Size , long , WindowStyle)
#else
-IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
+IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxTextCtrlBase)
#endif
-BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
+BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase)
EVT_CHAR(wxTextCtrl::OnChar)
EVT_DROP_FILES(wxTextCtrl::OnDropFiles)
EVT_SET_FOCUS(wxTextCtrl::OnSetFocus)
END_EVENT_TABLE()
+// ----------------------------------------------------------------------------
+// function prototypes
+// ----------------------------------------------------------------------------
+
+LRESULT APIENTRY _EXPORT wxTextCtrlWndProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+
+// ---------------------------------------------------------------------------
+// global vars
+// ---------------------------------------------------------------------------
+
+// the pointer to standard text control wnd proc
+static WNDPROC gs_wndprocEdit = (WNDPROC)NULL;
+
// ============================================================================
// implementation
// ============================================================================
+// ----------------------------------------------------------------------------
+// wnd proc for subclassed edit control
+// ----------------------------------------------------------------------------
+
+LRESULT APIENTRY _EXPORT wxTextCtrlWndProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ wxWindow *win = wxFindWinFromHandle((WXHWND)hWnd);
+
+ switch ( message )
+ {
+ case WM_CUT:
+ case WM_COPY:
+ case WM_PASTE:
+ if( win->HandleClipboardEvent( message ) )
+ return 0;
+ break;
+ }
+ return ::CallWindowProc(CASTWNDPROC gs_wndprocEdit, hWnd, message, wParam, lParam);
+}
+
// ----------------------------------------------------------------------------
// creation
// ----------------------------------------------------------------------------
m_verRichEdit = 0;
#endif // wxUSE_RICHEDIT
+#if wxUSE_INKEDIT && wxUSE_RICHEDIT
+ m_isInkEdit = 0;
+#endif
+
m_privateContextMenu = NULL;
m_updatesCount = -1;
m_isNativeCaretShown = true;
// do create the control - either an EDIT or RICHEDIT
wxString windowClass = wxT("EDIT");
-
+
#if defined(__POCKETPC__) || defined(__SMARTPHONE__)
// A control that capitalizes the first letter
if (style & wxTE_CAPITALIZE)
windowClass = wxT("CAPEDIT");
-#endif
+#endif
#if wxUSE_RICHEDIT
if ( m_windowStyle & wxTE_AUTO_URL )
// we need to load the richedit DLL before creating the rich edit control
if ( m_windowStyle & wxTE_RICH )
{
- static bool s_errorGiven = false;// MT-FIXME
-
- // Which version do we need? Use 1.0 by default because it is much more
- // like the the standard EDIT or 2.0 if explicitly requested, but use
- // only 2.0 in Unicode mode as 1.0 doesn't support Unicode at all
+ // versions 2.0, 3.0 and 4.1 of rich edit are mostly compatible with
+ // each other but not with version 1.0, so we have separate flags for
+ // the version 1.0 and the others (and so m_verRichEdit may be 0 (plain
+ // EDIT control), 1 for version 1.0 or 2 for any higher version)
//
- // TODO: RichEdit 3.0 is apparently capable of emulating RichEdit 1.0
- // (and thus EDIT) much better than RichEdit 2.0 so we probably
- // should use 3.0 if available as it is the best of both worlds -
- // but as I can't test it right now I don't do it (VZ)
+ // notice that 1.0 has no Unicode support at all so in Unicode build we
+ // must use another version
+
#if wxUSE_UNICODE
- const int verRichEdit = 2;
+ m_verRichEdit = 2;
#else // !wxUSE_UNICODE
- int verRichEdit = m_windowStyle & wxTE_RICH2 ? 2 : 1;
+ m_verRichEdit = m_windowStyle & wxTE_RICH2 ? 2 : 1;
#endif // wxUSE_UNICODE/!wxUSE_UNICODE
- // only give the error msg once if the DLL can't be loaded
- if ( !s_errorGiven )
+#if wxUSE_INKEDIT
+ // First test if we can load an ink edit control. Normally, all edit
+ // controls will be made ink edit controls if a tablet environment is
+ // found (or if msw.inkedit != 0 and the InkEd.dll is present).
+ // However, an application can veto ink edit controls by either specifying
+ // msw.inkedit = 0 or by removing wxTE_RICH[2] from the style.
+ //
+ if ((wxSystemSettings::HasFeature(wxSYS_TABLET_PRESENT) || wxSystemOptions::GetOptionInt(wxT("msw.inkedit")) != 0) &&
+ !(wxSystemOptions::HasOption(wxT("msw.inkedit")) && wxSystemOptions::GetOptionInt(wxT("msw.inkedit")) == 0))
{
- // try to load the RichEdit DLL (will do nothing if already done)
- if ( !wxRichEditModule::Load(verRichEdit) )
+ if (wxRichEditModule::LoadInkEdit())
{
-#if !wxUSE_UNICODE
- // try another version?
- verRichEdit = 3 - verRichEdit; // 1 <-> 2
+ windowClass = INKEDIT_CLASS;
- if ( !wxRichEditModule::Load(verRichEdit) )
-#endif // wxUSE_UNICODE
- {
- wxLogError(_("Impossible to create a rich edit control, using simple text control instead. Please reinstall riched32.dll"));
+#if wxUSE_INKEDIT && wxUSE_RICHEDIT
+ m_isInkEdit = 1;
+#endif
- s_errorGiven = true;
- }
+ // Fake rich text version for other calls
+ m_verRichEdit = 2;
}
}
+#endif
- // have we managed to load any richedit version?
- if ( !s_errorGiven )
+ if (!IsInkEdit())
{
- m_verRichEdit = verRichEdit;
- if ( m_verRichEdit == 1 )
- {
- windowClass = wxT("RICHEDIT");
- }
- else
+ if ( m_verRichEdit == 2 )
{
-#ifndef RICHEDIT_CLASS
- wxString RICHEDIT_CLASS;
- RICHEDIT_CLASS.Printf(_T("RichEdit%d0"), m_verRichEdit);
+ if ( wxRichEditModule::Load(wxRichEditModule::Version_41) )
+ {
+ // yes, class name for version 4.1 really is 5.0
+ windowClass = _T("RICHEDIT50W");
+ }
+ else if ( wxRichEditModule::Load(wxRichEditModule::Version_2or3) )
+ {
+ windowClass = _T("RichEdit20")
#if wxUSE_UNICODE
- RICHEDIT_CLASS += _T('W');
+ _T("W");
#else // ANSI
- RICHEDIT_CLASS += _T('A');
+ _T("A");
#endif // Unicode/ANSI
-#endif // !RICHEDIT_CLASS
+ }
+ else // failed to load msftedit.dll and riched20.dll
+ {
+ m_verRichEdit = 1;
+ }
+ }
- windowClass = RICHEDIT_CLASS;
+ if ( m_verRichEdit == 1 )
+ {
+ if ( wxRichEditModule::Load(wxRichEditModule::Version_1) )
+ {
+ windowClass = _T("RICHEDIT");
+ }
+ else // failed to load any richedit control DLL
+ {
+ // only give the error msg once if the DLL can't be loaded
+ static bool s_errorGiven = false; // MT ok as only used by GUI
+
+ if ( !s_errorGiven )
+ {
+ wxLogError(_("Impossible to create a rich edit control, using simple text control instead. Please reinstall riched32.dll"));
+
+ s_errorGiven = true;
+ }
+
+ m_verRichEdit = 0;
+ }
}
- }
+ } // !useInkEdit
}
#endif // wxUSE_RICHEDIT
return false;
#if wxUSE_RICHEDIT
- if ( IsRich() )
+ if (IsRich())
{
+#if wxUSE_INKEDIT
+ if (IsInkEdit())
+ {
+ // Pass IEM_InsertText (0) as wParam, in order to have the ink always
+ // converted to text.
+ ::SendMessage(GetHwnd(), EM_SETINKINSERTMODE, 0, 0);
+
+ // Make sure the mouse can be used for input
+ ::SendMessage(GetHwnd(), EM_SETUSEMOUSEFORINPUT, 1, 0);
+ }
+#endif
+
// enable the events we're interested in: we want to get EN_CHANGE as
// for the normal controls
LPARAM mask = ENM_CHANGE;
- if ( GetRichVersion() == 1 )
+ if (GetRichVersion() == 1 && !IsInkEdit())
{
// we also need EN_MSGFILTER for richedit 1.0 for the reasons
// explained in its handler
mask |= ENM_MOUSEEVENTS;
+
+ // we also need to force the appearance of the vertical scrollbar
+ // initially as otherwise the control doesn't refresh correctly
+ // after resize: but once the vertical scrollbar had been shown
+ // (even if it's subsequently hidden) it does
+ //
+ // this is clearly a bug and for now it has been only noticed under
+ // Windows XP, so if we're sure it works correctly under other
+ // systems we could do this only for XP
+ SetSize(-1, 1); // 1 is small enough to force vert scrollbar
+ SetSize(size);
}
else if ( m_windowStyle & wxTE_AUTO_URL )
{
}
#endif // wxUSE_RICHEDIT
+ gs_wndprocEdit = wxSetWindowProc((HWND)GetHwnd(),
+ wxTextCtrlWndProc);
+
return true;
}
{
DoWriteText(value, false /* not selection only */);
+ // mark the control as being not dirty - we changed its text, not the
+ // user
+ DiscardEdits();
+
// for compatibility, don't move the cursor when doing SetValue()
SetInsertionPoint(0);
}
else // same text
{
+ // still reset the modified flag even if the value didn't really change
+ // because now it comes from the program and not the user (and do it
+ // before generating the event so that the event handler could get the
+ // expected value from IsModified())
+ DiscardEdits();
+
// still send an event for consistency
SendUpdateEvent();
}
-
- // we should reset the modified flag even if the value didn't really change
-
- // mark the control as being not dirty - we changed its text, not the
- // user
- DiscardEdits();
}
#if wxUSE_RICHEDIT && (!wxUSE_UNICODE || wxUSE_UNICODE_MSLU)
(selectionOnly ? SFF_SELECTION : 0),
(LPARAM)&eds);
- wxASSERT_MSG( ucf.GotUpdate(), _T("EM_STREAMIN didn't send EN_UPDATE?") );
+ // It's okay for EN_UPDATE to not be sent if the selection is empty and
+ // the text is empty, otherwise warn the programmer about it.
+ wxASSERT_MSG( ucf.GotUpdate() || ( !HasSelection() && value.empty() ),
+ _T("EM_STREAMIN didn't send EN_UPDATE?") );
if ( eds.dwError )
{
UpdatesCountFilter ucf(m_updatesCount);
::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
- 0, (LPARAM)valueDos.c_str());
+ // EM_REPLACESEL takes 1 to indicate the operation should be redoable
+ selectionOnly ? 1 : 0, (LPARAM)valueDos.c_str());
if ( !ucf.GotUpdate() )
{
// ES_DISABLENOSCROLL
//
// this is very ugly but I don't see any other way to make this work
+ long style = 0;
if ( GetRichVersion() > 1 )
{
if ( !HasFlag(wxTE_NOHIDESEL) )
{
+ // setting ECO_NOHIDESEL also sets WS_VISIBLE and possibly
+ // others, remember the style so we can reset it later if needed
+ style = ::GetWindowLong(GetHwnd(), GWL_STYLE);
::SendMessage(GetHwnd(), EM_SETOPTIONS,
ECOOP_OR, ECO_NOHIDESEL);
}
{
::SendMessage(GetHwnd(), EM_SETOPTIONS,
ECOOP_AND, ~ECO_NOHIDESEL);
+ if ( style != ::GetWindowLong(GetHwnd(), GWL_STYLE) )
+ ::SetWindowLong(GetHwnd(), GWL_STYLE, style);
}
#endif // wxUSE_RICHEDIT
}
void wxTextCtrl::SetMaxLength(unsigned long len)
{
#if wxUSE_RICHEDIT
- if (IsRich())
- ::SendMessage(GetHwnd(), EM_EXLIMITTEXT, 0, (LPARAM) (DWORD) len);
+ if ( IsRich() )
+ {
+ ::SendMessage(GetHwnd(), EM_EXLIMITTEXT, 0, len ? len : 0x7fffffff);
+ }
else
-#endif
- ::SendMessage(GetHwnd(), EM_LIMITTEXT, len, 0);
+#endif // wxUSE_RICHEDIT
+ {
+ if ( len >= 0xffff )
+ {
+ // this will set it to a platform-dependent maximum (much more
+ // than 64Kb under NT)
+ len = 0;
+ }
+
+ ::SendMessage(GetHwnd(), EM_LIMITTEXT, len, 0);
+ }
}
// ----------------------------------------------------------------------------
switch ( event.GetKeyCode() )
{
case WXK_RETURN:
- if ( !HasFlag(wxTE_MULTILINE) )
{
wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
InitCommandEvent(event);
event.SetString(GetValue());
if ( GetEventHandler()->ProcessEvent(event) )
+ if ( !HasFlag(wxTE_MULTILINE) )
return;
+ //else: multiline controls need Enter for themselves
}
- //else: multiline controls need Enter for themselves
-
break;
case WXK_TAB:
// Insert tab since calling the default Windows handler
// doesn't seem to do it
WriteText(wxT("\t"));
+ return;
}
break;
}
return true;
}
-WXHBRUSH wxTextCtrl::MSWControlColor(WXHDC hDC)
+WXHBRUSH wxTextCtrl::MSWControlColor(WXHDC hDC, WXHWND hWnd)
{
if ( !IsEnabled() && !HasFlag(wxTE_MULTILINE) )
return MSWControlColorDisabled(hDC);
- return wxTextCtrlBase::MSWControlColorSolid(hDC);
+ return wxTextCtrlBase::MSWControlColor(hDC, hWnd);
}
-bool wxTextCtrl::AdjustSpaceLimit()
+bool wxTextCtrl::HasSpaceLimit(unsigned int *len) const
{
- 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()
+ // Unfortunately there is no EM_GETLIMITTEXTSETBYUSER and so we don't
+ // know the limit we set (if any). 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 small values while EN_MAXTEXT is only sent
+ // for large values (in practice the default limit seems to be 30000
+ // but make it smaller just to be on the safe side)
+ *len = ::SendMessage(GetHwnd(), EM_GETLIMITTEXT, 0, 0);
+ return *len < 10001;
+
+}
+
+bool wxTextCtrl::AdjustSpaceLimit()
+{
+ unsigned int limit;
+ if ( HasSpaceLimit(&limit) )
return false;
- }
unsigned int len = ::GetWindowTextLength(GetHwnd());
if ( len >= limit )
{
- limit = len + 0x8000; // 32Kb
-
-#if wxUSE_RICHEDIT
- if ( IsRich() )
- {
- // as a nice side effect, this also allows passing limit > 64Kb
- ::SendMessage(GetHwnd(), EM_EXLIMITTEXT, 0, limit);
- }
- else
-#endif // wxUSE_RICHEDIT
- {
- if ( limit > 0xffff )
- {
- // this will set it to a platform-dependent maximum (much more
- // than 64Kb under NT)
- limit = 0;
- }
-
- ::SendMessage(GetHwnd(), EM_LIMITTEXT, limit, 0);
- }
+ // increment in 32Kb chunks
+ SetMaxLength(len + 0x8000);
}
// we changed the limit
int hText = cy;
if ( m_windowStyle & wxTE_MULTILINE )
{
- hText *= wxMax(GetNumberOfLines(), 5);
+ hText *= wxMax(wxMin(GetNumberOfLines(), 10), 2);
}
//else: for single line control everything is ok
if ( changeSel )
{
- DoSetSelection(position, position, false /* don't scroll caret into view */);
+ DoSetSelection(position, position+1, false /* don't scroll caret into view */);
}
// get the selection formatting
// wxRichEditModule
// ----------------------------------------------------------------------------
+static const HINSTANCE INVALID_HINSTANCE = (HINSTANCE)-1;
+
bool wxRichEditModule::OnInit()
{
// don't do anything - we will load it when needed
{
for ( size_t i = 0; i < WXSIZEOF(ms_hRichEdit); i++ )
{
- if ( ms_hRichEdit[i] )
+ if ( ms_hRichEdit[i] && ms_hRichEdit[i] != INVALID_HINSTANCE )
{
::FreeLibrary(ms_hRichEdit[i]);
ms_hRichEdit[i] = NULL;
}
}
+#if wxUSE_INKEDIT
+ if (ms_inkEditLib.IsLoaded())
+ ms_inkEditLib.Unload();
+#endif
}
/* static */
-bool wxRichEditModule::Load(int version)
+bool wxRichEditModule::Load(Version version)
{
- // we don't support loading richedit 3.0 as I don't know how to distinguish
- // it from 2.0 anyhow
- wxCHECK_MSG( version == 1 || version == 2, false,
- _T("incorrect richedit control version requested") );
-
- // make it the index in the array
- version--;
-
- if ( ms_hRichEdit[version] == (HINSTANCE)-1 )
+ if ( ms_hRichEdit[version] == INVALID_HINSTANCE )
{
// we had already tried to load it and failed
return false;
return true;
}
- wxString dllname = version ? _T("riched20") : _T("riched32");
- dllname += _T(".dll");
+ static const wxChar *dllnames[] =
+ {
+ _T("riched32"),
+ _T("riched20"),
+ _T("msftedit"),
+ };
+
+ wxCOMPILE_TIME_ASSERT( WXSIZEOF(dllnames) == Version_Max,
+ RichEditDllNamesVersionsMismatch );
- ms_hRichEdit[version] = ::LoadLibrary(dllname);
+ ms_hRichEdit[version] = ::LoadLibrary(dllnames[version]);
if ( !ms_hRichEdit[version] )
{
- wxLogSysError(_("Could not load Rich Edit DLL '%s'"), dllname.c_str());
-
- ms_hRichEdit[version] = (HINSTANCE)-1;
+ ms_hRichEdit[version] = INVALID_HINSTANCE;
return false;
}
return true;
}
+#if wxUSE_INKEDIT
+// load the InkEdit library
+bool wxRichEditModule::LoadInkEdit()
+{
+ static wxDynamicLibrary ms_inkEditLib;
+ static bool ms_inkEditLibLoadAttemped;
+ if (ms_inkEditLibLoadAttemped)
+ ms_inkEditLib.IsLoaded();
+
+ ms_inkEditLibLoadAttemped = true;
+
+ wxLogNull logNull;
+ return ms_inkEditLib.Load(wxT("inked"));
+}
+#endif
+
+
#endif // wxUSE_RICHEDIT
#endif // wxUSE_TEXTCTRL && !(__SMARTPHONE__ && __WXWINCE__)