#include "wx/app.h"
#include "wx/menu.h"
#include "wx/math.h"
+ #include "wx/module.h"
+ #include "wx/wxcrtvararg.h"
#endif
-#include "wx/module.h"
#include "wx/sysopt.h"
#if wxUSE_CLIPBOARD
#include "wx/msw/private.h"
#include "wx/msw/winundef.h"
+#include "wx/msw/mslu.h"
#include <string.h>
#include <stdlib.h>
#include <richedit.h>
#endif
-#include "wx/msw/missing.h"
-
#endif // wxUSE_RICHEDIT
+#include "wx/msw/missing.h"
+
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
UpdatesCountFilter(int& count)
: m_count(count)
{
- wxASSERT_MSG( m_count == -1, _T("wrong initial m_updatesCount value") );
+ wxASSERT_MSG( m_count == -1 || m_count == -2,
+ _T("wrong initial m_updatesCount value") );
- m_count = 0;
+ if (m_count != -2)
+ m_count = 0;
+ //else: we don't want to count how many update events we get as we're going
+ // to ignore all of them
}
~UpdatesCountFilter()
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
// ----------------------------------------------------------------------------
delete m_privateContextMenu;
}
-bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
+bool wxTextCtrl::Create(wxWindow *parent,
+ wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
const wxValidator& validator,
const wxString& name)
{
-#ifdef __WXWINCE__
- if ((style & wxBORDER_MASK) == 0)
- style |= wxBORDER_SIMPLE;
-#endif
-
// base initialization
if ( !CreateControl(parent, id, pos, size, style, validator, name) )
return false;
+ if ( !MSWCreateText(value, pos, size) )
+ return false;
+
+ return true;
+}
+
+// returns true if the platform should explicitly apply a theme border
+bool wxTextCtrl::CanApplyThemeBorder() const
+{
+#ifdef __WXWINCE__
+ return false;
+#else
+ // Standard text control already handles theming
+ return ((GetWindowStyle() & (wxTE_RICH|wxTE_RICH2)) != 0);
+#endif
+}
+
+bool wxTextCtrl::MSWCreateText(const wxString& value,
+ const wxPoint& pos,
+ const wxSize& size)
+{
// translate wxWin style flags to MSW ones
WXDWORD msStyle = MSWGetCreateWindowFlags();
#if defined(__POCKETPC__) || defined(__SMARTPHONE__)
// A control that capitalizes the first letter
- if (style & wxTE_CAPITALIZE)
+ if ( HasFlag(wxTE_CAPITALIZE) )
windowClass = wxT("CAPEDIT");
#endif
valueWin = value;
}
- if ( !MSWCreateControl(windowClass, msStyle, pos, size, valueWin) )
+ if ( !MSWCreateControl(windowClass.wx_str(), msStyle, pos, size, valueWin) )
return false;
#if wxUSE_RICHEDIT
// 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);
+ SetInitialSize(size);
}
else if ( m_windowStyle & wxTE_AUTO_URL )
{
}
#endif // wxUSE_RICHEDIT
- gs_wndprocEdit = wxSetWindowProc((HWND)GetHwnd(),
- wxTextCtrlWndProc);
+#ifndef __WXWINCE__
+ // Without this, if we pass the size in the constructor and then don't change it,
+ // the themed borders will be drawn incorrectly.
+ SetWindowPos(GetHwnd(), NULL, 0, 0, 0, 0,
+ SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|
+ SWP_FRAMECHANGED);
+#endif
return true;
}
// styles which we alaways add by default
if ( style & wxTE_MULTILINE )
{
- wxASSERT_MSG( !(style & wxTE_PROCESS_ENTER),
- wxT("wxTE_PROCESS_ENTER style is ignored for multiline text controls (they always process it)") );
-
msStyle |= ES_MULTILINE | ES_WANTRETURN;
if ( !(style & wxTE_NO_VSCROLL) )
{
void wxTextCtrl::SetWindowStyleFlag(long style)
{
+ // changing the alignment of the control dynamically works under Win2003
+ // (but not older Windows version: it seems to work under some versions of
+ // XP but not other ones, and we have no way to determine it so be
+ // conservative here) and only for plain EDIT controls (not RICH ones) and
+ // we have to recreate the control to make it always work
+ if ( IsRich() || wxGetWinVersion() < wxWinVersion_2003 )
+ {
+ const long alignMask = wxTE_LEFT | wxTE_CENTRE | wxTE_RIGHT;
+ if ( (style & alignMask) != (GetWindowStyle() & alignMask) )
+ {
+ const wxString value = GetValue();
+ const wxPoint pos = GetPosition();
+ const wxSize size = GetSize();
+
+ // delete the old window
+ HWND hwnd = GetHwnd();
+ DissociateHandle();
+ ::DestroyWindow(hwnd);
+
+ // create the new one with the updated flags
+ m_windowStyle = style;
+ MSWCreateText(value, pos, size);
+
+ // and make sure it has the same attributes as before
+ if ( m_hasFont )
+ {
+ // calling SetFont(m_font) would do nothing as the code would
+ // notice that the font didn't change, so force it to believe
+ // that it did
+ wxFont font = m_font;
+ m_font = wxNullFont;
+ SetFont(font);
+ }
+
+ if ( m_hasFgCol )
+ {
+ wxColour colFg = m_foregroundColour;
+ m_foregroundColour = wxNullColour;
+ SetForegroundColour(colFg);
+ }
+
+ if ( m_hasBgCol )
+ {
+ wxColour colBg = m_backgroundColour;
+ m_backgroundColour = wxNullColour;
+ SetBackgroundColour(colBg);
+ }
+
+ // note that text styles are lost but this is probably not a big
+ // problem: if you use styles, you probably don't use nor change
+ // alignment flags anyhow
+
+ return;
+ }
+ }
+
#if wxUSE_RICHEDIT
// we have to deal with some styles separately because they can't be
// changed by simply calling SetWindowLong(GWL_STYLE) but can be changed
// set/get the controls text
// ----------------------------------------------------------------------------
+bool wxTextCtrl::IsEmpty() const
+{
+ // this is an optimization for multiline controls containing a lot of text
+ if ( IsMultiLine() && GetNumberOfLines() != 1 )
+ return false;
+
+ return wxTextCtrlBase::IsEmpty();
+}
+
wxString wxTextCtrl::GetValue() const
{
// range 0..-1 is special for GetRange() and means to retrieve all text
return str;
}
-void wxTextCtrl::SetValue(const wxString& value)
+void wxTextCtrl::DoSetValue(const wxString& value, int flags)
{
// if the text is long enough, it's faster to just set it instead of first
// comparing it with the old one (chances are that it will be different
// edit controls mostly)
if ( (value.length() > 0x400) || (value != GetValue()) )
{
- DoWriteText(value, false /* not selection only */);
+ DoWriteText(value, flags /* doesn't include SelectionOnly here */);
// mark the control as being not dirty - we changed its text, not the
// user
DiscardEdits();
// still send an event for consistency
- SendUpdateEvent();
+ if (flags & SetValue_SendEvent)
+ SendUpdateEvent();
}
}
#else // !wxUSE_UNICODE_MSLU
wxCSConv conv(encoding);
- const size_t len = conv.MB2WC(NULL, value, value.length());
+ const size_t len = conv.MB2WC(NULL, value.mb_str(), value.length());
#if wxUSE_WCHAR_T
wxWCharBuffer wchBuf(len);
wchar_t *wpc = wchBuf;
#endif
- conv.MB2WC(wpc, value, value.length());
+ conv.MB2WC(wpc, value.mb_str(), value.length());
#endif // wxUSE_UNICODE_MSLU
// finally, stream it in the control
DoWriteText(value);
}
-void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly)
+void wxTextCtrl::DoWriteText(const wxString& value, int flags)
{
+ bool selectionOnly = (flags & SetValue_SelectionOnly) != 0;
wxString valueDos;
if ( m_windowStyle & wxTE_MULTILINE )
valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
// we generate exactly one of them by ignoring all but the first one in
// SendUpdateEvent() and generating one ourselves if we hadn't got any
// notifications from Windows
+ if ( !(flags & SetValue_SendEvent) )
+ m_updatesCount = -2; // suppress any update event
+
UpdatesCountFilter ucf(m_updatesCount);
::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
// EM_REPLACESEL takes 1 to indicate the operation should be redoable
- selectionOnly ? 1 : 0, (LPARAM)valueDos.c_str());
+ selectionOnly ? 1 : 0, (LPARAM)valueDos.wx_str());
- if ( !ucf.GotUpdate() )
+ if ( !ucf.GotUpdate() && (flags & SetValue_SendEvent) )
{
SendUpdateEvent();
}
// if it doesn't actually move the caret anywhere and so the simple fact of
// doing it results in horrible flicker when appending big amounts of text
// to the control in a few chunks (see DoAddText() test in the text sample)
- if ( GetInsertionPoint() == GetLastPosition() )
+ const wxTextPos lastPosition = GetLastPosition();
+ if ( GetInsertionPoint() == lastPosition )
{
return;
}
else // !RichEdit 1.0
#endif // wxUSE_RICHEDIT
{
- pos = GetLastPosition();
+ pos = lastPosition;
}
SetInsertionPoint(pos);
// Working with files
// ----------------------------------------------------------------------------
-bool wxTextCtrl::LoadFile(const wxString& file)
+bool wxTextCtrl::DoLoadFile(const wxString& file, int fileType)
{
- if ( wxTextCtrlBase::LoadFile(file) )
+ if ( wxTextCtrlBase::DoLoadFile(file, fileType) )
{
// update the size limit if needed
AdjustSpaceLimit();
// Set selection and remove it
DoSetSelection(from, to, false /* don't scroll caret into view */);
- DoWriteText(value, true /* selection only */);
+ DoWriteText(value);
}
void wxTextCtrl::Remove(long from, long to)
// kbd input processing
// ----------------------------------------------------------------------------
-bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* pMsg)
+bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* msg)
{
- 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 = (WORD) msg->wParam;
- if ( (HIWORD(msg->lParam) & KF_ALTDOWN) == KF_ALTDOWN )
+ const WPARAM vkey = msg->wParam;
+ if ( HIWORD(msg->lParam) & KF_ALTDOWN )
{
+ // Alt-Backspace is accelerator for "Undo"
if ( vkey == VK_BACK )
return false;
}
// fall through
case 0:
+ if ( IsMultiLine() && vkey == VK_RETURN )
+ return false;
+ // fall through
case 2:
break;
}
}
- return wxControl::MSWShouldPreProcessMessage(pMsg);
+ return wxControl::MSWShouldPreProcessMessage(msg);
}
void wxTextCtrl::OnChar(wxKeyEvent& event)
{
WXLRESULT lRc = wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
- if ( nMsg == WM_GETDLGCODE )
+ switch ( nMsg )
{
- // we always want the chars and the arrows: the arrows for navigation
- // and the chars because we want Ctrl-C to work even in a read only
- // control
- long lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;
+ case WM_GETDLGCODE:
+ {
+ // we always want the chars and the arrows: the arrows for
+ // navigation and the chars because we want Ctrl-C to work even
+ // in a read only control
+ long lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;
- if ( IsEditable() )
- {
- // we may have several different cases:
- // 1. normal case: both TAB and ENTER are used for dlg navigation
- // 2. ctrl which wants TAB for itself: ENTER is used to pass to the
- // next control in the dialog
- // 3. ctrl which wants ENTER for itself: TAB is used for dialog
- // navigation
- // 4. ctrl which wants both TAB and ENTER: Ctrl-ENTER is used to go
- // to the next control
-
- // the multiline edit control should always get <Return> for itself
- if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) )
- lDlgCode |= DLGC_WANTMESSAGE;
-
- if ( HasFlag(wxTE_PROCESS_TAB) )
- lDlgCode |= DLGC_WANTTAB;
-
- lRc |= lDlgCode;
- }
- else // !editable
- {
- // NB: use "=", not "|=" as the base class version returns the
- // same flags is this state as usual (i.e. including
- // DLGC_WANTMESSAGE). This is strange (how does it work in the
- // native Win32 apps?) but for now live with it.
- lRc = lDlgCode;
- }
+ if ( IsEditable() )
+ {
+ // we may have several different cases:
+ // 1. normal: both TAB and ENTER are used for navigation
+ // 2. ctrl wants TAB for itself: ENTER is used to pass to
+ // the next control in the dialog
+ // 3. ctrl wants ENTER for itself: TAB is used for dialog
+ // navigation
+ // 4. ctrl wants both TAB and ENTER: Ctrl-ENTER is used to
+ // go to the next control (we need some way to do it)
+
+ // multiline controls should always get ENTER for themselves
+ if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) )
+ lDlgCode |= DLGC_WANTMESSAGE;
+
+ if ( HasFlag(wxTE_PROCESS_TAB) )
+ lDlgCode |= DLGC_WANTTAB;
+
+ lRc |= lDlgCode;
+ }
+ else // !editable
+ {
+ // NB: use "=", not "|=" as the base class version returns
+ // the same flags is this state as usual (i.e.
+ // including DLGC_WANTMESSAGE). This is strange (how
+ // does it work in the native Win32 apps?) but for now
+ // live with it.
+ lRc = lDlgCode;
+ }
+ }
+ break;
+
+ case WM_CUT:
+ case WM_COPY:
+ case WM_PASTE:
+ if ( HandleClipboardEvent(nMsg) )
+ lRc = 0;
+ break;
}
return lRc;
// we hadn't updated the control ourselves, this event comes from
// the user, don't need to ignore it nor update the count
break;
+
+ case -2:
+ // the control was updated programmatically and we do NOT want to
+ // send events
+ return false;
}
wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
}
}
- if (pf.dwMask != 0)
+#if wxUSE_RICHEDIT2
+ if ( m_verRichEdit > 1 )
+ {
+ if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft )
+ {
+ // Use RTL paragraphs in RTL mode to get proper layout
+ pf.dwMask |= PFM_RTLPARA;
+ pf.wEffects |= PFE_RTLPARA;
+ }
+ }
+#endif // wxUSE_RICHEDIT2
+
+ if ( pf.dwMask )
{
// do format the selection
bool ok = ::SendMessage(GetHwnd(), EM_SETPARAFORMAT,
- 0, (LPARAM) &pf) != 0;
+ 0, (LPARAM) &pf) != 0;
if ( !ok )
{
wxLogDebug(_T("SendMessage(EM_SETPARAFORMAT, 0) failed"));