1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/combobox.cpp
3 // Purpose:     wxComboBox class
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     01/02/97
7 // RCS-ID:      $Id: combobox.cpp 49562 2007-10-31 18:55:58Z VZ $
8 // Copyright:   (c) Julian Smart
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // ============================================================================
13 // declarations
14 // ============================================================================
15 
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19 
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22 
23 #ifdef __BORLANDC__
24     #pragma hdrstop
25 #endif
26 
27 #if wxUSE_COMBOBOX
28 
29 #include "wx/combobox.h"
30 
31 #ifndef WX_PRECOMP
32     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
33     #include "wx/settings.h"
34     #include "wx/log.h"
35     // for wxEVT_COMMAND_TEXT_ENTER
36     #include "wx/textctrl.h"
37     #include "wx/app.h"
38     #include "wx/brush.h"
39 #endif
40 
41 #include "wx/clipbrd.h"
42 #include "wx/msw/private.h"
43 
44 #if wxUSE_TOOLTIPS
45     #include "wx/tooltip.h"
46 #endif // wxUSE_TOOLTIPS
47 
48 // ----------------------------------------------------------------------------
49 // wxWin macros
50 // ----------------------------------------------------------------------------
51 
52 #if wxUSE_EXTENDED_RTTI
53 WX_DEFINE_FLAGS( wxComboBoxStyle )
54 
55 wxBEGIN_FLAGS( wxComboBoxStyle )
56     // new style border flags, we put them first to
57     // use them for streaming out
58     wxFLAGS_MEMBER(wxBORDER_SIMPLE)
59     wxFLAGS_MEMBER(wxBORDER_SUNKEN)
60     wxFLAGS_MEMBER(wxBORDER_DOUBLE)
61     wxFLAGS_MEMBER(wxBORDER_RAISED)
62     wxFLAGS_MEMBER(wxBORDER_STATIC)
63     wxFLAGS_MEMBER(wxBORDER_NONE)
64 
65     // old style border flags
66     wxFLAGS_MEMBER(wxSIMPLE_BORDER)
67     wxFLAGS_MEMBER(wxSUNKEN_BORDER)
68     wxFLAGS_MEMBER(wxDOUBLE_BORDER)
69     wxFLAGS_MEMBER(wxRAISED_BORDER)
70     wxFLAGS_MEMBER(wxSTATIC_BORDER)
71     wxFLAGS_MEMBER(wxBORDER)
72 
73     // standard window styles
74     wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
75     wxFLAGS_MEMBER(wxCLIP_CHILDREN)
76     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
77     wxFLAGS_MEMBER(wxWANTS_CHARS)
78     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
79     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
80     wxFLAGS_MEMBER(wxVSCROLL)
81     wxFLAGS_MEMBER(wxHSCROLL)
82 
83     wxFLAGS_MEMBER(wxCB_SIMPLE)
84     wxFLAGS_MEMBER(wxCB_SORT)
85     wxFLAGS_MEMBER(wxCB_READONLY)
86     wxFLAGS_MEMBER(wxCB_DROPDOWN)
87 
88 wxEND_FLAGS( wxComboBoxStyle )
89 
90 IMPLEMENT_DYNAMIC_CLASS_XTI(wxComboBox, wxControl,"wx/combobox.h")
91 
92 wxBEGIN_PROPERTIES_TABLE(wxComboBox)
93     wxEVENT_PROPERTY( Select , wxEVT_COMMAND_COMBOBOX_SELECTED , wxCommandEvent )
94     wxEVENT_PROPERTY( TextEnter , wxEVT_COMMAND_TEXT_ENTER , wxCommandEvent )
95 
96     // TODO DELEGATES
97     wxPROPERTY( Font , wxFont , SetFont , GetFont  , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
98     wxPROPERTY_COLLECTION( Choices , wxArrayString , wxString , AppendString , GetStrings , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
99     wxPROPERTY( Value ,wxString, SetValue, GetValue, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
100     wxPROPERTY( Selection ,int, SetSelection, GetSelection, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
101     wxPROPERTY_FLAGS( WindowStyle , wxComboBoxStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
102 wxEND_PROPERTIES_TABLE()
103 
104 wxBEGIN_HANDLERS_TABLE(wxComboBox)
105 wxEND_HANDLERS_TABLE()
106 
107 wxCONSTRUCTOR_5( wxComboBox , wxWindow* , Parent , wxWindowID , Id , wxString , Value , wxPoint , Position , wxSize , Size )
108 
109 #else
110 
111 IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
112 
113 #endif
114 
115 BEGIN_EVENT_TABLE(wxComboBox, wxControl)
116     EVT_MENU(wxID_CUT, wxComboBox::OnCut)
117     EVT_MENU(wxID_COPY, wxComboBox::OnCopy)
118     EVT_MENU(wxID_PASTE, wxComboBox::OnPaste)
119     EVT_MENU(wxID_UNDO, wxComboBox::OnUndo)
120     EVT_MENU(wxID_REDO, wxComboBox::OnRedo)
121     EVT_MENU(wxID_CLEAR, wxComboBox::OnDelete)
122     EVT_MENU(wxID_SELECTALL, wxComboBox::OnSelectAll)
123 
124     EVT_UPDATE_UI(wxID_CUT, wxComboBox::OnUpdateCut)
125     EVT_UPDATE_UI(wxID_COPY, wxComboBox::OnUpdateCopy)
126     EVT_UPDATE_UI(wxID_PASTE, wxComboBox::OnUpdatePaste)
127     EVT_UPDATE_UI(wxID_UNDO, wxComboBox::OnUpdateUndo)
128     EVT_UPDATE_UI(wxID_REDO, wxComboBox::OnUpdateRedo)
129     EVT_UPDATE_UI(wxID_CLEAR, wxComboBox::OnUpdateDelete)
130     EVT_UPDATE_UI(wxID_SELECTALL, wxComboBox::OnUpdateSelectAll)
131 END_EVENT_TABLE()
132 
133 // ----------------------------------------------------------------------------
134 // function prototypes
135 // ----------------------------------------------------------------------------
136 
137 LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
138                                             UINT message,
139                                             WPARAM wParam,
140                                             LPARAM lParam);
141 
142 // ---------------------------------------------------------------------------
143 // global vars
144 // ---------------------------------------------------------------------------
145 
146 // the pointer to standard radio button wnd proc
147 static WNDPROC gs_wndprocEdit = (WNDPROC)NULL;
148 
149 // ============================================================================
150 // implementation
151 // ============================================================================
152 
153 // ----------------------------------------------------------------------------
154 // wnd proc for subclassed edit control
155 // ----------------------------------------------------------------------------
156 
wxComboEditWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)157 LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
158                                             UINT message,
159                                             WPARAM wParam,
160                                             LPARAM lParam)
161 {
162     HWND hwndCombo = ::GetParent(hWnd);
163     wxWindow *win = wxFindWinFromHandle((WXHWND)hwndCombo);
164 
165     switch ( message )
166     {
167         // forward some messages to the combobox to generate the appropriate
168         // wxEvents from them
169         case WM_KEYUP:
170         case WM_KEYDOWN:
171         case WM_CHAR:
172         case WM_SYSCHAR:
173         case WM_SYSKEYDOWN:
174         case WM_SYSKEYUP:
175         case WM_SETFOCUS:
176         case WM_KILLFOCUS:
177             {
178                 wxComboBox *combo = wxDynamicCast(win, wxComboBox);
179                 if ( !combo )
180                 {
181                     // we can get WM_KILLFOCUS while our parent is already half
182                     // destroyed and hence doesn't look like a combobx any
183                     // longer, check for it to avoid bogus assert failures
184                     if ( !win->IsBeingDeleted() )
185                     {
186                         wxFAIL_MSG( _T("should have combo as parent") );
187                     }
188                 }
189                 else if ( combo->MSWProcessEditMsg(message, wParam, lParam) )
190                 {
191                     // handled by parent
192                     return 0;
193                 }
194             }
195             break;
196 
197         case WM_GETDLGCODE:
198             {
199                 wxCHECK_MSG( win, 0, _T("should have a parent") );
200 
201                 if ( win->GetWindowStyle() & wxTE_PROCESS_ENTER )
202                 {
203                     // need to return a custom dlg code or we'll never get it
204                     return DLGC_WANTMESSAGE;
205                 }
206             }
207             break;
208 
209         case WM_CUT:
210         case WM_COPY:
211         case WM_PASTE:
212             if( win->HandleClipboardEvent( message ) )
213                 return 0;
214             break;
215     }
216 
217     return ::CallWindowProc(CASTWNDPROC gs_wndprocEdit, hWnd, message, wParam, lParam);
218 }
219 
220 // ----------------------------------------------------------------------------
221 // wxComboBox callbacks
222 // ----------------------------------------------------------------------------
223 
MSWWindowProc(WXUINT nMsg,WXWPARAM wParam,WXLPARAM lParam)224 WXLRESULT wxComboBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
225 {
226     // TODO: handle WM_CTLCOLOR messages from our EDIT control to be able to
227     //       set its colour correctly (to be the same as our own one)
228 
229     switch ( nMsg )
230     {
231         case WM_SIZE:
232         // wxStaticBox can generate this message, when modifying the control's style.
233         // This causes the content of the combobox to be selected, for some reason.
234         case WM_STYLECHANGED:
235             {
236                 // combobox selection sometimes spontaneously changes when its
237                 // size changes, restore it to the old value if necessary
238                 long fromOld, toOld;
239                 GetSelection(&fromOld, &toOld);
240                 WXLRESULT result = wxChoice::MSWWindowProc(nMsg, wParam, lParam);
241 
242                 long fromNew, toNew;
243                 GetSelection(&fromNew, &toNew);
244 
245                 if ( fromOld != fromNew || toOld != toNew )
246                 {
247                     SetSelection(fromOld, toOld);
248                 }
249 
250                 return result;
251             }
252     }
253 
254     return wxChoice::MSWWindowProc(nMsg, wParam, lParam);
255 }
256 
MSWProcessEditMsg(WXUINT msg,WXWPARAM wParam,WXLPARAM lParam)257 bool wxComboBox::MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam)
258 {
259     switch ( msg )
260     {
261         case WM_CHAR:
262             // for compatibility with wxTextCtrl, generate a special message
263             // when Enter is pressed
264             if ( wParam == VK_RETURN )
265             {
266                 if (SendMessage(GetHwnd(), CB_GETDROPPEDSTATE, 0, 0))
267                     return false;
268 
269                 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
270 
271                 const int sel = GetSelection();
272                 event.SetInt(sel);
273                 event.SetString(GetValue());
274                 InitCommandEventWithItems(event, sel);
275 
276                 if ( ProcessCommand(event) )
277                 {
278                     // don't let the event through to the native control
279                     // because it doesn't need it and may generate an annoying
280                     // beep if it gets it
281                     return true;
282                 }
283             }
284             // fall through
285 
286         case WM_SYSCHAR:
287             return HandleChar(wParam, lParam, true /* isASCII */);
288 
289         case WM_SYSKEYDOWN:
290         case WM_KEYDOWN:
291             return HandleKeyDown(wParam, lParam);
292 
293         case WM_SYSKEYUP:
294         case WM_KEYUP:
295             return HandleKeyUp(wParam, lParam);
296 
297         case WM_SETFOCUS:
298             return HandleSetFocus((WXHWND)wParam);
299 
300         case WM_KILLFOCUS:
301             return HandleKillFocus((WXHWND)wParam);
302     }
303 
304     return false;
305 }
306 
MSWCommand(WXUINT param,WXWORD id)307 bool wxComboBox::MSWCommand(WXUINT param, WXWORD id)
308 {
309     int sel = -1;
310     wxString value;
311 
312     switch ( param )
313     {
314         case CBN_SELENDOK:
315 #ifndef __SMARTPHONE__
316             // we need to reset this to prevent the selection from being undone
317             // by wxChoice, see wxChoice::MSWCommand() and comments there
318             m_lastAcceptedSelection = wxID_NONE;
319 #endif
320 
321             // set these variables so that they could be also fixed in
322             // CBN_EDITCHANGE below
323             sel = GetSelection();
324             value = GetStringSelection();
325 
326             // this string is going to become the new combobox value soon but
327             // we need it to be done right now, otherwise the event handler
328             // could get a wrong value when it calls our GetValue()
329             ::SetWindowText(GetHwnd(), value);
330 
331             {
332                 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, GetId());
333                 event.SetInt(sel);
334                 event.SetString(value);
335                 InitCommandEventWithItems(event, sel);
336 
337                 ProcessCommand(event);
338             }
339 
340             // fall through: for compability with wxGTK, also send the text
341             // update event when the selection changes (this also seems more
342             // logical as the text does change)
343 
344         case CBN_EDITCHANGE:
345             {
346                 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
347 
348                 // if sel != -1, value was already initialized above
349                 if ( sel == -1 )
350                 {
351                     value = wxGetWindowText(GetHwnd());
352                 }
353 
354                 event.SetString(value);
355                 InitCommandEventWithItems(event, sel);
356 
357                 ProcessCommand(event);
358             }
359             break;
360 
361         default:
362             return wxChoice::MSWCommand(param, id);
363     }
364 
365     // skip wxChoice version as it would generate its own events for
366     // CBN_SELENDOK
367     return true;
368 }
369 
MSWShouldPreProcessMessage(WXMSG * pMsg)370 bool wxComboBox::MSWShouldPreProcessMessage(WXMSG *pMsg)
371 {
372     // prevent command accelerators from stealing editing
373     // hotkeys when we have the focus
374     if (wxIsCtrlDown())
375     {
376         WPARAM vkey = pMsg->wParam;
377 
378         switch (vkey)
379         {
380             case 'C':
381             case 'V':
382             case 'X':
383             case VK_INSERT:
384             case VK_DELETE:
385             case VK_HOME:
386             case VK_END:
387                 return false;
388         }
389     }
390 
391     return wxChoice::MSWShouldPreProcessMessage(pMsg);
392 }
393 
GetEditHWND() const394 WXHWND wxComboBox::GetEditHWND() const
395 {
396     // this function should not be called for wxCB_READONLY controls, it is
397     // the callers responsability to check this
398     wxASSERT_MSG( !(GetWindowStyle() & wxCB_READONLY),
399                   _T("read-only combobox doesn't have any edit control") );
400 
401     POINT pt;
402     pt.x = pt.y = 4;
403     HWND hwndEdit = ::ChildWindowFromPoint(GetHwnd(), pt);
404     if ( !hwndEdit || hwndEdit == GetHwnd() )
405     {
406         wxFAIL_MSG(_T("not read only combobox without edit control?"));
407     }
408 
409     return (WXHWND)hwndEdit;
410 }
411 
412 // ----------------------------------------------------------------------------
413 // wxComboBox creation
414 // ----------------------------------------------------------------------------
415 
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,int n,const wxString choices[],long style,const wxValidator & validator,const wxString & name)416 bool wxComboBox::Create(wxWindow *parent, wxWindowID id,
417                         const wxString& value,
418                         const wxPoint& pos,
419                         const wxSize& size,
420                         int n, const wxString choices[],
421                         long style,
422                         const wxValidator& validator,
423                         const wxString& name)
424 {
425     // pretend that wxComboBox is hidden while it is positioned and resized and
426     // show it only right before leaving this method because otherwise there is
427     // some noticeable flicker while the control rearranges itself
428     m_isShown = false;
429 
430     if ( !CreateAndInit(parent, id, pos, size, n, choices, style,
431                         validator, name) )
432         return false;
433 
434     // we shouldn't call SetValue() for an empty string because this would
435     // (correctly) result in an assert with a read only combobox and is useless
436     // for the other ones anyhow
437     if ( !value.empty() )
438         SetValue(value);
439 
440     // a (not read only) combobox is, in fact, 2 controls: the combobox itself
441     // and an edit control inside it and if we want to catch events from this
442     // edit control, we must subclass it as well
443     if ( !(style & wxCB_READONLY) )
444     {
445         gs_wndprocEdit = wxSetWindowProc((HWND)GetEditHWND(), wxComboEditWndProc);
446     }
447 
448     // and finally, show the control
449     Show(true);
450 
451     return true;
452 }
453 
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,long style,const wxValidator & validator,const wxString & name)454 bool wxComboBox::Create(wxWindow *parent, wxWindowID id,
455                         const wxString& value,
456                         const wxPoint& pos,
457                         const wxSize& size,
458                         const wxArrayString& choices,
459                         long style,
460                         const wxValidator& validator,
461                         const wxString& name)
462 {
463     wxCArrayString chs(choices);
464     return Create(parent, id, value, pos, size, chs.GetCount(),
465                   chs.GetStrings(), style, validator, name);
466 }
467 
MSWGetStyle(long style,WXDWORD * exstyle) const468 WXDWORD wxComboBox::MSWGetStyle(long style, WXDWORD *exstyle) const
469 {
470     // we never have an external border
471     WXDWORD msStyle = wxChoice::MSWGetStyle
472                       (
473                         (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
474                       );
475 
476     // usually WS_TABSTOP is added by wxControl::MSWGetStyle() but as we're
477     // created hidden (see Create() above), it is not done for us but we still
478     // want to have this style
479     msStyle |= WS_TABSTOP;
480 
481     // remove the style always added by wxChoice
482     msStyle &= ~CBS_DROPDOWNLIST;
483 
484     if ( style & wxCB_READONLY )
485         msStyle |= CBS_DROPDOWNLIST;
486 #ifndef __WXWINCE__
487     else if ( style & wxCB_SIMPLE )
488         msStyle |= CBS_SIMPLE; // A list (shown always) and edit control
489 #endif
490     else
491         msStyle |= CBS_DROPDOWN;
492 
493     // there is no reason to not always use CBS_AUTOHSCROLL, so do use it
494     msStyle |= CBS_AUTOHSCROLL;
495 
496     // NB: we used to also add CBS_NOINTEGRALHEIGHT here but why?
497 
498     return msStyle;
499 }
500 
501 // ----------------------------------------------------------------------------
502 // wxComboBox text control-like methods
503 // ----------------------------------------------------------------------------
504 
GetValue() const505 wxString wxComboBox::GetValue() const
506 {
507     return wxGetWindowText(m_hWnd);
508 }
509 
SetValue(const wxString & value)510 void wxComboBox::SetValue(const wxString& value)
511 {
512     if ( HasFlag(wxCB_READONLY) )
513         SetStringSelection(value);
514     else
515         SetWindowText(GetHwnd(), value.c_str());
516 }
517 
518 // Clipboard operations
Copy()519 void wxComboBox::Copy()
520 {
521     SendMessage(GetHwnd(), WM_COPY, 0, 0L);
522 }
523 
Cut()524 void wxComboBox::Cut()
525 {
526     SendMessage(GetHwnd(), WM_CUT, 0, 0L);
527 }
528 
Paste()529 void wxComboBox::Paste()
530 {
531     SendMessage(GetHwnd(), WM_PASTE, 0, 0L);
532 }
533 
Undo()534 void wxComboBox::Undo()
535 {
536     if (CanUndo())
537     {
538         HWND hEditWnd = (HWND) GetEditHWND();
539         if ( hEditWnd )
540             ::SendMessage(hEditWnd, EM_UNDO, 0, 0);
541     }
542 }
543 
Redo()544 void wxComboBox::Redo()
545 {
546     if (CanUndo())
547     {
548         // Same as Undo, since Undo undoes the undo, i.e. a redo.
549         HWND hEditWnd = (HWND) GetEditHWND();
550         if ( hEditWnd )
551             ::SendMessage(hEditWnd, EM_UNDO, 0, 0);
552     }
553 }
554 
SelectAll()555 void wxComboBox::SelectAll()
556 {
557     SetSelection(0, GetLastPosition());
558 }
559 
CanUndo() const560 bool wxComboBox::CanUndo() const
561 {
562     if (!IsEditable())
563         return false;
564 
565     HWND hEditWnd = (HWND) GetEditHWND();
566     if ( hEditWnd )
567         return ::SendMessage(hEditWnd, EM_CANUNDO, 0, 0) != 0;
568     else
569         return false;
570 }
571 
CanRedo() const572 bool wxComboBox::CanRedo() const
573 {
574     if (!IsEditable())
575         return false;
576 
577     HWND hEditWnd = (HWND) GetEditHWND();
578     if ( hEditWnd )
579         return ::SendMessage(hEditWnd, EM_CANUNDO, 0, 0) != 0;
580     else
581         return false;
582 }
583 
HasSelection() const584 bool wxComboBox::HasSelection() const
585 {
586     long from, to;
587     GetSelection(&from, &to);
588     return from != to;
589 }
590 
CanCopy() const591 bool wxComboBox::CanCopy() const
592 {
593     // Can copy if there's a selection
594     return HasSelection();
595 }
596 
CanCut() const597 bool wxComboBox::CanCut() const
598 {
599     return IsEditable() && CanCopy();
600 }
601 
CanPaste() const602 bool wxComboBox::CanPaste() const
603 {
604     if ( !IsEditable() )
605         return false;
606 
607     // Standard edit control: check for straight text on clipboard
608     if ( !::OpenClipboard(GetHwndOf(wxTheApp->GetTopWindow())) )
609         return false;
610 
611     bool isTextAvailable = ::IsClipboardFormatAvailable(CF_TEXT) != 0;
612     ::CloseClipboard();
613 
614     return isTextAvailable;
615 }
616 
IsEditable() const617 bool wxComboBox::IsEditable() const
618 {
619     return !HasFlag(wxCB_READONLY);
620 }
621 
SetEditable(bool editable)622 void wxComboBox::SetEditable(bool editable)
623 {
624     HWND hWnd = (HWND)GetEditHWND();
625     if ( !::SendMessage(hWnd, EM_SETREADONLY, !editable, 0) )
626     {
627         wxLogLastError(_T("SendMessage(EM_SETREADONLY)"));
628     }
629 }
630 
SetInsertionPoint(long pos)631 void wxComboBox::SetInsertionPoint(long pos)
632 {
633     if ( GetWindowStyle() & wxCB_READONLY )
634         return;
635 
636     HWND hWnd = GetHwnd();
637     ::SendMessage(hWnd, CB_SETEDITSEL, 0, MAKELPARAM(pos, pos));
638     HWND hEditWnd = (HWND) GetEditHWND();
639     if ( hEditWnd )
640     {
641         // Scroll insertion point into view
642         SendMessage(hEditWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
643     }
644 }
645 
SetInsertionPointEnd()646 void wxComboBox::SetInsertionPointEnd()
647 {
648     // setting insertion point doesn't make sense for read only comboboxes
649     if ( !(GetWindowStyle() & wxCB_READONLY) )
650     {
651         wxTextPos pos = GetLastPosition();
652         SetInsertionPoint(pos);
653     }
654 }
655 
GetInsertionPoint() const656 long wxComboBox::GetInsertionPoint() const
657 {
658     // CB_GETEDITSEL returns the index of the first character of the selection in
659     // its low-order word
660     DWORD pos= (DWORD)::SendMessage(GetHwnd(), CB_GETEDITSEL, 0, 0L);
661     return LOWORD(pos);
662 }
663 
GetLastPosition() const664 wxTextPos wxComboBox::GetLastPosition() const
665 {
666     HWND hEditWnd = (HWND) GetEditHWND();
667 
668     // Get number of characters in the last (only) line. We'll add this to the character
669     // index for the last line, 1st position.
670     wxTextPos lineLength = (wxTextPos)SendMessage(hEditWnd, EM_LINELENGTH, (WPARAM) 0, (LPARAM)0L);
671 
672     return lineLength;
673 }
674 
Replace(long from,long to,const wxString & value)675 void wxComboBox::Replace(long from, long to, const wxString& value)
676 {
677 #if wxUSE_CLIPBOARD
678     Remove(from, to);
679 
680     // Now replace with 'value', by pasting.
681     wxSetClipboardData(wxDF_TEXT, (wxObject *)(const wxChar *)value, 0, 0);
682 
683     // Paste into edit control
684     SendMessage(GetHwnd(), WM_PASTE, (WPARAM)0, (LPARAM)0L);
685 #else
686     wxUnusedVar(from);
687     wxUnusedVar(to);
688     wxUnusedVar(value);
689 #endif
690 }
691 
Remove(long from,long to)692 void wxComboBox::Remove(long from, long to)
693 {
694     // Set selection and remove it
695     SetSelection(from, to);
696     SendMessage(GetHwnd(), WM_CUT, (WPARAM)0, (LPARAM)0);
697 }
698 
SetSelection(long from,long to)699 void wxComboBox::SetSelection(long from, long to)
700 {
701     // if from and to are both -1, it means (in wxWidgets) that all text should
702     // be selected, translate this into Windows convention
703     if ( (from == -1) && (to == -1) )
704     {
705         from = 0;
706     }
707 
708     if ( SendMessage(GetHwnd(), CB_SETEDITSEL,
709                      0, (LPARAM)MAKELONG(from, to)) == CB_ERR )
710     {
711         wxLogDebug(_T("CB_SETEDITSEL failed"));
712     }
713 }
714 
GetSelection(long * from,long * to) const715 void wxComboBox::GetSelection(long* from, long* to) const
716 {
717     DWORD dwStart, dwEnd;
718     if ( ::SendMessage(GetHwnd(), CB_GETEDITSEL,
719                        (WPARAM)&dwStart, (LPARAM)&dwEnd) == CB_ERR )
720     {
721         *from =
722         *to = 0;
723     }
724     else
725     {
726         *from = dwStart;
727         *to = dwEnd;
728     }
729 }
730 
731 // ----------------------------------------------------------------------------
732 // standard event handling
733 // ----------------------------------------------------------------------------
734 
OnCut(wxCommandEvent & WXUNUSED (event))735 void wxComboBox::OnCut(wxCommandEvent& WXUNUSED(event))
736 {
737     Cut();
738 }
739 
OnCopy(wxCommandEvent & WXUNUSED (event))740 void wxComboBox::OnCopy(wxCommandEvent& WXUNUSED(event))
741 {
742     Copy();
743 }
744 
OnPaste(wxCommandEvent & WXUNUSED (event))745 void wxComboBox::OnPaste(wxCommandEvent& WXUNUSED(event))
746 {
747     Paste();
748 }
749 
OnUndo(wxCommandEvent & WXUNUSED (event))750 void wxComboBox::OnUndo(wxCommandEvent& WXUNUSED(event))
751 {
752     Undo();
753 }
754 
OnRedo(wxCommandEvent & WXUNUSED (event))755 void wxComboBox::OnRedo(wxCommandEvent& WXUNUSED(event))
756 {
757     Redo();
758 }
759 
OnDelete(wxCommandEvent & WXUNUSED (event))760 void wxComboBox::OnDelete(wxCommandEvent& WXUNUSED(event))
761 {
762     long from, to;
763     GetSelection(& from, & to);
764     if (from != -1 && to != -1)
765         Remove(from, to);
766 }
767 
OnSelectAll(wxCommandEvent & WXUNUSED (event))768 void wxComboBox::OnSelectAll(wxCommandEvent& WXUNUSED(event))
769 {
770     SetSelection(-1, -1);
771 }
772 
OnUpdateCut(wxUpdateUIEvent & event)773 void wxComboBox::OnUpdateCut(wxUpdateUIEvent& event)
774 {
775     event.Enable( CanCut() );
776 }
777 
OnUpdateCopy(wxUpdateUIEvent & event)778 void wxComboBox::OnUpdateCopy(wxUpdateUIEvent& event)
779 {
780     event.Enable( CanCopy() );
781 }
782 
OnUpdatePaste(wxUpdateUIEvent & event)783 void wxComboBox::OnUpdatePaste(wxUpdateUIEvent& event)
784 {
785     event.Enable( CanPaste() );
786 }
787 
OnUpdateUndo(wxUpdateUIEvent & event)788 void wxComboBox::OnUpdateUndo(wxUpdateUIEvent& event)
789 {
790     event.Enable( CanUndo() );
791 }
792 
OnUpdateRedo(wxUpdateUIEvent & event)793 void wxComboBox::OnUpdateRedo(wxUpdateUIEvent& event)
794 {
795     event.Enable( CanRedo() );
796 }
797 
OnUpdateDelete(wxUpdateUIEvent & event)798 void wxComboBox::OnUpdateDelete(wxUpdateUIEvent& event)
799 {
800     event.Enable(IsEditable() && HasSelection());
801 }
802 
OnUpdateSelectAll(wxUpdateUIEvent & event)803 void wxComboBox::OnUpdateSelectAll(wxUpdateUIEvent& event)
804 {
805     event.Enable(IsEditable() && GetLastPosition() > 0);
806 }
807 
808 #endif // wxUSE_COMBOBOX
809