#endif // wxUSE_RICHEDIT
+// a small class used to set m_updatesCount to 0 (to filter duplicate events if
+// necessary) and to reset it back to -1 afterwards
+class UpdatesCountFilter
+{
+public:
+ UpdatesCountFilter(int& count)
+ : m_count(count)
+ {
+ wxASSERT_MSG( m_count == -1, _T("wrong initial m_updatesCount value") );
+
+ m_count = 0;
+ }
+
+ ~UpdatesCountFilter()
+ {
+ m_count = -1;
+ }
+
+ // return true if an event has been received
+ bool GotUpdate() const
+ {
+ return m_count == 1;
+ }
+
+private:
+ int& m_count;
+
+ DECLARE_NO_COPY_CLASS(UpdatesCountFilter)
+};
+
// ----------------------------------------------------------------------------
// event tables and other macros
// ----------------------------------------------------------------------------
#endif // wxUSE_RICHEDIT
m_privateContextMenu = NULL;
- m_suppressNextUpdate = false;
+ m_updatesCount = -1;
m_isNativeCaretShown = true;
+ m_isCaretAtEnd = true;
}
wxTextCtrl::~wxTextCtrl()
{
- if (m_privateContextMenu)
- {
- delete m_privateContextMenu;
- m_privateContextMenu = NULL;
- }
+ delete m_privateContextMenu;
}
bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id,
if ( !MSWCreateControl(windowClass, msStyle, pos, size, valueWin) )
return false;
- SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
-
#if wxUSE_RICHEDIT
if ( IsRich() )
{
// the cast below is needed for broken (very) old mingw32 headers
eds.pfnCallback = (EDITSTREAMCALLBACK)wxRichEditStreamIn;
- // we're going to receive 2 EN_CHANGE notifications if we got any selection
- // (same problem as in DoWriteText())
- if ( selectionOnly && HasSelection() )
- {
- // so suppress one of them
- m_suppressNextUpdate = true;
- }
+ // same problem as in DoWriteText(): we can get multiple events here
+ UpdatesCountFilter ucf(m_updatesCount);
::SendMessage(GetHwnd(), EM_STREAMIN,
SF_TEXT |
(selectionOnly ? SFF_SELECTION : 0),
(LPARAM)&eds);
+ wxASSERT_MSG( ucf.GotUpdate(), _T("EM_STREAMIN didn't send EN_UPDATE?") );
+
if ( eds.dwError )
{
wxLogLastError(_T("EM_STREAMIN"));
#endif // wxUSE_RICHEDIT
{
// in some cases we get 2 EN_CHANGE notifications after the SendMessage
- // call below which is confusing for the client code and so should be
- // avoided
- //
- // these cases are: (a) plain EDIT controls if EM_REPLACESEL is used
- // and there is a non empty selection currently and (b) rich text
- // controls in any case
- if (
-#if wxUSE_RICHEDIT
- IsRich() ||
-#endif // wxUSE_RICHEDIT
- (selectionOnly && HasSelection()) )
- {
- m_suppressNextUpdate = true;
- }
+ // call (this happens for plain EDITs with EM_REPLACESEL and under some
+ // -- undetermined -- conditions with rich edit) and sometimes we don't
+ // get any events at all (plain EDIT with WM_SETTEXT), so ensure that
+ // 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
+ UpdatesCountFilter ucf(m_updatesCount);
::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
0, (LPARAM)valueDos.c_str());
- // OTOH, non rich text controls don't generate any events at all when
- // we use WM_SETTEXT -- have to emulate them here
- if ( !selectionOnly
-#if wxUSE_RICHEDIT
- && !IsRich()
-#endif // wxUSE_RICHEDIT
- )
+ if ( !ucf.GotUpdate() )
{
- // Windows already sends an update event for single-line
- // controls.
- if ( m_windowStyle & wxTE_MULTILINE )
- SendUpdateEvent();
+ SendUpdateEvent();
}
}
-
- AdjustSpaceLimit();
}
void wxTextCtrl::AppendText(const wxString& text)
WriteText(text);
#if wxUSE_RICHEDIT
- if ( IsMultiLine() && GetRichVersion() > 1 )
+ // don't do this if we're frozen, saves some time
+ if ( !IsFrozen() && IsMultiLine() && GetRichVersion() > 1 )
{
// setting the caret to the end and showing it simply doesn't work for
// RichEdit 2.0 -- force it to still do what we want
size_t lenOld = GetValue().length();
wxUint32 code = event.GetRawKeyCode();
- ::keybd_event(code, 0, 0 /* key press */, 0);
- ::keybd_event(code, 0, KEYEVENTF_KEYUP, 0);
+ ::keybd_event((BYTE)code, 0, 0 /* key press */, 0);
+ ::keybd_event((BYTE)code, 0, KEYEVENTF_KEYUP, 0);
// assume that any alphanumeric key changes the total number of characters
// in the control - this should work in 99% of cases
void wxTextCtrl::SetInsertionPoint(long pos)
{
DoSetSelection(pos, pos);
+
+ m_isCaretAtEnd = pos == GetLastPosition();
}
void wxTextCtrl::SetInsertionPointEnd()
// 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() )
+ if ( m_isCaretAtEnd || GetInsertionPoint() == GetLastPosition() )
+ {
+ m_isCaretAtEnd = true;
return;
+ }
long pos;
::SendMessage(hWnd, EM_SETSEL, (WPARAM)from, (LPARAM)to);
}
- if ( scrollCaret )
+ if ( scrollCaret && !IsFrozen() )
{
#if wxUSE_RICHEDIT
// richedit 3.0 (i.e. the version living in riched20.dll distributed
if (linesToScroll != 0)
(void)::SendMessage(hWnd, EM_LINESCROLL, (WPARAM)0, (LPARAM)linesToScroll);
+
+ // be pessimistic
+ m_isCaretAtEnd = false;
}
long wxTextCtrl::GetLengthOfLineContainingPos(long pos) const
if (CanUndo())
{
::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
+
+ // it's not necessarily at the end any more
+ m_isCaretAtEnd = false;
}
}
#endif
// Same as Undo, since Undo undoes the undo, i.e. a redo.
::SendMessage(GetHwnd(), EM_UNDO, 0, 0);
+
+ // it's not necessarily at the end any more
+ m_isCaretAtEnd = false;
}
}
bool wxTextCtrl::SendUpdateEvent()
{
- // is event reporting suspended?
- if ( m_suppressNextUpdate )
+ switch ( m_updatesCount )
{
- // do process the next one
- m_suppressNextUpdate = false;
+ case 0:
+ // remember that we've got an update
+ m_updatesCount++;
+ break;
- return false;
+ case 1:
+ // we had already sent one event since the last control modification
+ return false;
+
+ default:
+ wxFAIL_MSG( _T("unexpected wxTextCtrl::m_updatesCount value") );
+ // fall through
+
+ case -1:
+ // we hadn't updated the control ourselves, this event comes from
+ // the user, don't need to ignore it nor update the count
+ break;
}
wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
return true;
}
-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)
-#endif
- )
+WXHBRUSH wxTextCtrl::MSWControlColor(WXHDC hDC)
{
-#if wxUSE_CTL3D
- if ( m_useCtl3D )
- {
- HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
- return (WXHBRUSH) hbrush;
- }
-#endif // wxUSE_CTL3D
+ if ( !IsEnabled() && !HasFlag(wxTE_MULTILINE) )
+ return MSWControlColorDisabled(hDC);
- HDC hdc = (HDC)pDC;
- wxColour colBack = GetBackgroundColour();
-
- if (!IsEnabled() && (GetWindowStyle() & wxTE_MULTILINE) == 0)
- colBack = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
-
- ::SetBkColor(hdc, wxColourToRGB(colBack));
- ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
-
- wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
-
- return (WXHBRUSH)brush->GetResourceHandle();
+ return wxTextCtrlBase::MSWControlColorSolid(hDC);
}
bool wxTextCtrl::AdjustSpaceLimit()
}
}
+// ----------------------------------------------------------------------------
+// Default colors for MSW text control
+//
+// Set default background color to the native white instead of
+// the default wxSYS_COLOUR_BTNFACE (is triggered with wxNullColour).
+// ----------------------------------------------------------------------------
+
+wxVisualAttributes wxTextCtrl::GetDefaultAttributes() const
+{
+ wxVisualAttributes attrs;
+ attrs.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
+ attrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
+ attrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); //white
+
+ return attrs;
+}
+
// the rest of the file only deals with the rich edit controls
#if wxUSE_RICHEDIT
case WM_SETCURSOR:
// ok, so it is hardcoded - do we really nee to
// customize it?
- ::SetCursor(GetHcursorOf(wxCursor(wxCURSOR_HAND)));
- *result = TRUE;
- break;
+ {
+ wxCursor cur(wxCURSOR_HAND);
+ ::SetCursor(GetHcursorOf(cur));
+ *result = TRUE;
+ break;
+ }
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
const wxArrayInt& tabs = style.GetTabs();
- pf.cTabCount = wxMin(tabs.GetCount(), MAX_TAB_STOPS);
+ pf.cTabCount = (SHORT)wxMin(tabs.GetCount(), MAX_TAB_STOPS);
size_t i;
for (i = 0; i < (size_t) pf.cTabCount; i++)
{
if ( !wxTextCtrlBase::SetDefaultStyle(style) )
return false;
- // we have to do this or the style wouldn't apply for the text typed by the
- // user
- long posLast = GetLastPosition();
- SetStyle(posLast, posLast, m_defaultStyle);
+ if ( IsEditable() )
+ {
+ // we have to do this or the style wouldn't apply for the text typed by
+ // the user
+ long posLast = GetLastPosition();
+ SetStyle(posLast, posLast, m_defaultStyle);
+ }
return true;
}
(void) ::SendMessage(GetHwnd(), EM_GETCHARFORMAT,
SCF_SELECTION, (LPARAM)&cf) ;
+
LOGFONT lf;
lf.lfHeight = cf.yHeight;
lf.lfWidth = 0;
lf.lfClipPrecision = 0;
lf.lfEscapement = 0;
wxStrcpy(lf.lfFaceName, cf.szFaceName);
+
+ //NOTE: we _MUST_ set each of these values to _something_ since we
+ //do not call wxZeroMemory on the LOGFONT lf
if (cf.dwEffects & CFE_ITALIC)
lf.lfItalic = TRUE;
+ else
+ lf.lfItalic = FALSE;
+
lf.lfOrientation = 0;
lf.lfPitchAndFamily = cf.bPitchAndFamily;
lf.lfQuality = 0;
+
if (cf.dwEffects & CFE_STRIKEOUT)
lf.lfStrikeOut = TRUE;
+ else
+ lf.lfStrikeOut = FALSE;
+
if (cf.dwEffects & CFE_UNDERLINE)
lf.lfUnderline = TRUE;
+ else
+ lf.lfUnderline = FALSE;
+
if (cf.dwEffects & CFE_BOLD)
lf.lfWeight = FW_BOLD;
+ else
+ lf.lfWeight = FW_NORMAL;
wxFont font = wxCreateFontFromLogFont(& lf);
if (font.Ok())