Applied John's patch.
[wxWidgets.git] / src / gtk1 / combobox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: combobox.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "combobox.h"
12 #endif
13
14 #include "wx/combobox.h"
15
16 #if wxUSE_COMBOBOX
17
18 #include "wx/settings.h"
19 #include "wx/intl.h"
20
21 #include "wx/textctrl.h" // for wxEVT_COMMAND_TEXT_UPDATED
22
23 #include "wx/gtk/private.h"
24
25 //-----------------------------------------------------------------------------
26 // idle system
27 //-----------------------------------------------------------------------------
28
29 extern void wxapp_install_idle_handler();
30 extern bool g_isIdle;
31
32 //-----------------------------------------------------------------------------
33 // data
34 //-----------------------------------------------------------------------------
35
36 extern bool g_blockEventsOnDrag;
37
38 //-----------------------------------------------------------------------------
39 // "select"
40 //-----------------------------------------------------------------------------
41
42 static void
43 gtk_combo_clicked_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
44 {
45 if (g_isIdle) wxapp_install_idle_handler();
46
47 if (!combo->m_hasVMT) return;
48
49 if (g_blockEventsOnDrag) return;
50
51 if (combo->m_alreadySent)
52 {
53 combo->m_alreadySent = FALSE;
54 return;
55 }
56
57 combo->m_alreadySent = TRUE;
58
59 int curSelection = combo->GetSelection();
60
61 if (combo->m_prevSelection != curSelection)
62 {
63 GtkWidget *list = GTK_COMBO(combo->m_widget)->list;
64 gtk_list_unselect_item( GTK_LIST(list), combo->m_prevSelection );
65 }
66 else if ((curSelection >= 0) && (combo->GetString(curSelection) == combo->GetValue()))
67 return;
68
69 combo->m_prevSelection = curSelection;
70
71 wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, combo->GetId() );
72 event.SetInt( curSelection );
73 event.SetString( combo->GetStringSelection() );
74 event.SetEventObject( combo );
75
76 combo->GetEventHandler()->ProcessEvent( event );
77 }
78
79 //-----------------------------------------------------------------------------
80 // "changed"
81 //-----------------------------------------------------------------------------
82
83 static void
84 gtk_text_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
85 {
86 if (g_isIdle) wxapp_install_idle_handler();
87
88 if (!combo->m_hasVMT) return;
89
90 // avoids double events when the GetValue = one of the selections
91 if (combo->m_alreadySent)
92 {
93 combo->m_alreadySent = FALSE;
94 return;
95 }
96
97 wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, combo->GetId() );
98 event.SetString( combo->GetValue() );
99 event.SetEventObject( combo );
100 combo->GetEventHandler()->ProcessEvent( event );
101 }
102
103 //-----------------------------------------------------------------------------
104 // wxComboBox
105 //-----------------------------------------------------------------------------
106
107 IMPLEMENT_DYNAMIC_CLASS(wxComboBox,wxControl)
108
109 BEGIN_EVENT_TABLE(wxComboBox, wxControl)
110 EVT_SIZE(wxComboBox::OnSize)
111 EVT_CHAR(wxComboBox::OnChar)
112 END_EVENT_TABLE()
113
114 bool wxComboBox::Create( wxWindow *parent, wxWindowID id, const wxString& value,
115 const wxPoint& pos, const wxSize& size,
116 int n, const wxString choices[],
117 long style, const wxValidator& validator,
118 const wxString& name )
119 {
120 m_alreadySent = FALSE;
121 m_needParent = TRUE;
122 m_acceptsFocus = TRUE;
123 m_prevSelection = 0;
124
125 if (!PreCreation( parent, pos, size ) ||
126 !CreateBase( parent, id, pos, size, style, validator, name ))
127 {
128 wxFAIL_MSG( wxT("wxComboBox creation failed") );
129 return FALSE;
130 }
131
132 m_widget = gtk_combo_new();
133
134 // make it more useable
135 gtk_combo_set_use_arrows_always( GTK_COMBO(m_widget), TRUE );
136
137 // and case-sensitive
138 gtk_combo_set_case_sensitive( GTK_COMBO(m_widget), TRUE );
139
140 GtkWidget *list = GTK_COMBO(m_widget)->list;
141
142 #ifndef __WXGTK20__
143 // gtk_list_set_selection_mode( GTK_LIST(list), GTK_SELECTION_MULTIPLE );
144 #endif
145
146 for (int i = 0; i < n; i++)
147 {
148 /* don't send first event, which GTK sends aways when
149 inserting the first item */
150 m_alreadySent = TRUE;
151
152 GtkWidget *list_item = gtk_list_item_new_with_label( wxGTK_CONV( choices[i] ) );
153
154 m_clientDataList.Append( (wxObject*)NULL );
155 m_clientObjectList.Append( (wxObject*)NULL );
156
157 gtk_container_add( GTK_CONTAINER(list), list_item );
158
159 gtk_signal_connect( GTK_OBJECT(list_item), "select",
160 GTK_SIGNAL_FUNC(gtk_combo_clicked_callback), (gpointer)this );
161
162 gtk_widget_show( list_item );
163 }
164
165 m_parent->DoAddChild( this );
166
167 m_focusWidget = GTK_COMBO(m_widget)->entry;
168
169 PostCreation();
170
171 ConnectWidget( GTK_COMBO(m_widget)->button );
172
173 if (!value.IsNull()) SetValue( value );
174
175 if (style & wxCB_READONLY)
176 gtk_entry_set_editable( GTK_ENTRY( GTK_COMBO(m_widget)->entry ), FALSE );
177
178 gtk_signal_connect( GTK_OBJECT(GTK_COMBO(m_widget)->entry), "changed",
179 GTK_SIGNAL_FUNC(gtk_text_changed_callback), (gpointer)this);
180
181 wxSize size_best( DoGetBestSize() );
182 wxSize new_size( size );
183 if (new_size.x == -1)
184 new_size.x = size_best.x;
185 if (new_size.y == -1)
186 new_size.y = size_best.y;
187 if (new_size.y > size_best.y)
188 new_size.y = size_best.y;
189 if ((new_size.x != size.x) || (new_size.y != size.y))
190 {
191 SetSize( new_size.x, new_size.y );
192
193 // This is required for tool bar support
194 gtk_widget_set_usize( m_widget, new_size.x, new_size.y );
195 }
196
197
198 SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
199 SetForegroundColour( parent->GetForegroundColour() );
200
201 Show( TRUE );
202
203 return TRUE;
204 }
205
206 wxComboBox::~wxComboBox()
207 {
208 wxNode *node = m_clientObjectList.GetFirst();
209 while (node)
210 {
211 wxClientData *cd = (wxClientData*)node->GetData();
212 if (cd) delete cd;
213 node = node->GetNext();
214 }
215 m_clientObjectList.Clear();
216
217 m_clientDataList.Clear();
218 }
219
220 void wxComboBox::SetFocus()
221 {
222 if ( m_hasFocus )
223 {
224 // don't do anything if we already have focus
225 return;
226 }
227
228 gtk_widget_grab_focus( m_focusWidget );
229 }
230
231 void wxComboBox::AppendCommon( const wxString &item )
232 {
233 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
234
235 GtkWidget *list = GTK_COMBO(m_widget)->list;
236
237 GtkWidget *list_item = gtk_list_item_new_with_label( wxGTK_CONV( item ) );
238
239 gtk_container_add( GTK_CONTAINER(list), list_item );
240
241 gtk_signal_connect( GTK_OBJECT(list_item), "select",
242 GTK_SIGNAL_FUNC(gtk_combo_clicked_callback), (gpointer)this );
243
244 if (GTK_WIDGET_REALIZED(m_widget))
245 {
246 gtk_widget_realize( list_item );
247 gtk_widget_realize( GTK_BIN(list_item)->child );
248
249 if (m_widgetStyle) ApplyWidgetStyle();
250 }
251
252 gtk_widget_show( list_item );
253 }
254
255 void wxComboBox::Append( const wxString &item )
256 {
257 m_clientDataList.Append( (wxObject*) NULL );
258 m_clientObjectList.Append( (wxObject*) NULL );
259
260 AppendCommon( item );
261 }
262
263 void wxComboBox::Append( const wxString &item, void *clientData )
264 {
265 m_clientDataList.Append( (wxObject*) clientData );
266 m_clientObjectList.Append( (wxObject*)NULL );
267
268 AppendCommon( item );
269 }
270
271 void wxComboBox::Append( const wxString &item, wxClientData *clientData )
272 {
273 m_clientDataList.Append( (wxObject*) NULL );
274 m_clientObjectList.Append( (wxObject*) clientData );
275
276 AppendCommon( item );
277 }
278
279 void wxComboBox::SetClientData( int n, void* clientData )
280 {
281 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
282
283 wxNode *node = m_clientDataList.Item( n );
284 if (!node) return;
285
286 node->SetData( (wxObject*) clientData );
287 }
288
289 void* wxComboBox::GetClientData( int n )
290 {
291 wxCHECK_MSG( m_widget != NULL, NULL, wxT("invalid combobox") );
292
293 wxNode *node = m_clientDataList.Item( n );
294 if (!node) return NULL;
295
296 return node->GetData();
297 }
298
299 void wxComboBox::SetClientObject( int n, wxClientData* clientData )
300 {
301 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
302
303 wxNode *node = m_clientObjectList.Item( n );
304 if (!node) return;
305
306 wxClientData *cd = (wxClientData*) node->GetData();
307 if (cd) delete cd;
308
309 node->SetData( (wxObject*) clientData );
310 }
311
312 wxClientData* wxComboBox::GetClientObject( int n )
313 {
314 wxCHECK_MSG( m_widget != NULL, (wxClientData*)NULL, wxT("invalid combobox") );
315
316 wxNode *node = m_clientObjectList.Item( n );
317 if (!node) return (wxClientData*) NULL;
318
319 return (wxClientData*) node->GetData();
320 }
321
322 void wxComboBox::Clear()
323 {
324 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
325
326 GtkWidget *list = GTK_COMBO(m_widget)->list;
327 gtk_list_clear_items( GTK_LIST(list), 0, Number() );
328
329 wxNode *node = m_clientObjectList.GetFirst();
330 while (node)
331 {
332 wxClientData *cd = (wxClientData*)node->GetData();
333 if (cd) delete cd;
334 node = node->GetNext();
335 }
336 m_clientObjectList.Clear();
337
338 m_clientDataList.Clear();
339 }
340
341 void wxComboBox::Delete( int n )
342 {
343 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
344
345 GtkList *listbox = GTK_LIST( GTK_COMBO(m_widget)->list );
346
347 GList *child = g_list_nth( listbox->children, n );
348
349 if (!child)
350 {
351 wxFAIL_MSG(wxT("wrong index"));
352 return;
353 }
354
355 GList *list = g_list_append( (GList*) NULL, child->data );
356 gtk_list_remove_items( listbox, list );
357 g_list_free( list );
358
359 wxNode *node = m_clientObjectList.Item( n );
360 if (node)
361 {
362 wxClientData *cd = (wxClientData*)node->GetData();
363 if (cd) delete cd;
364 m_clientObjectList.DeleteNode( node );
365 }
366
367 node = m_clientDataList.Item( n );
368 if (node)
369 {
370 m_clientDataList.DeleteNode( node );
371 }
372 }
373
374 int wxComboBox::FindString( const wxString &item )
375 {
376 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
377
378 GtkWidget *list = GTK_COMBO(m_widget)->list;
379
380 GList *child = GTK_LIST(list)->children;
381 int count = 0;
382 while (child)
383 {
384 GtkBin *bin = GTK_BIN( child->data );
385 GtkLabel *label = GTK_LABEL( bin->child );
386 #ifdef __WXGTK20__
387 wxString str( wxGTK_CONV_BACK( gtk_label_get_text(label) ) );
388 #else
389 wxString str( label->label );
390 #endif
391 if (item == str)
392 return count;
393
394 count++;
395 child = child->next;
396 }
397
398 return wxNOT_FOUND;
399 }
400
401 int wxComboBox::GetSelection() const
402 {
403 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
404
405 GtkWidget *list = GTK_COMBO(m_widget)->list;
406
407 GList *selection = GTK_LIST(list)->selection;
408 if (selection)
409 {
410 GList *child = GTK_LIST(list)->children;
411 int count = 0;
412 while (child)
413 {
414 if (child->data == selection->data) return count;
415 count++;
416 child = child->next;
417 }
418 }
419
420 return -1;
421 }
422
423 wxString wxComboBox::GetString( int n ) const
424 {
425 wxCHECK_MSG( m_widget != NULL, wxT(""), wxT("invalid combobox") );
426
427 GtkWidget *list = GTK_COMBO(m_widget)->list;
428
429 wxString str;
430 GList *child = g_list_nth( GTK_LIST(list)->children, n );
431 if (child)
432 {
433 GtkBin *bin = GTK_BIN( child->data );
434 GtkLabel *label = GTK_LABEL( bin->child );
435 #ifdef __WXGTK20__
436 str = wxGTK_CONV_BACK( gtk_label_get_text(label) );
437 #else
438 str = wxString( label->label );
439 #endif
440 }
441 else
442 {
443 wxFAIL_MSG( wxT("wxComboBox: wrong index") );
444 }
445
446 return str;
447 }
448
449 wxString wxComboBox::GetStringSelection() const
450 {
451 wxCHECK_MSG( m_widget != NULL, wxT(""), wxT("invalid combobox") );
452
453 GtkWidget *list = GTK_COMBO(m_widget)->list;
454
455 GList *selection = GTK_LIST(list)->selection;
456 if (selection)
457 {
458 GtkBin *bin = GTK_BIN( selection->data );
459 GtkLabel *label = GTK_LABEL( bin->child );
460 #ifdef __WXGTK20__
461 wxString tmp( wxGTK_CONV_BACK( gtk_label_get_text(label) ) );
462 #else
463 wxString tmp( label->label );
464 #endif
465 return tmp;
466 }
467
468 wxFAIL_MSG( wxT("wxComboBox: no selection") );
469
470 return wxT("");
471 }
472
473 int wxComboBox::Number() const
474 {
475 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid combobox") );
476
477 GtkWidget *list = GTK_COMBO(m_widget)->list;
478
479 GList *child = GTK_LIST(list)->children;
480 int count = 0;
481 while (child) { count++; child = child->next; }
482 return count;
483 }
484
485 void wxComboBox::SetSelection( int n )
486 {
487 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
488
489 DisableEvents();
490
491 GtkWidget *list = GTK_COMBO(m_widget)->list;
492 gtk_list_unselect_item( GTK_LIST(list), m_prevSelection );
493 gtk_list_select_item( GTK_LIST(list), n );
494 m_prevSelection = n;
495
496 EnableEvents();
497 }
498
499 void wxComboBox::SetStringSelection( const wxString &string )
500 {
501 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
502
503 int res = FindString( string );
504 if (res == -1) return;
505 SetSelection( res );
506 }
507
508 wxString wxComboBox::GetValue() const
509 {
510 GtkEntry *entry = GTK_ENTRY( GTK_COMBO(m_widget)->entry );
511 wxString tmp( wxGTK_CONV_BACK( gtk_entry_get_text( entry ) ) );
512
513 #if 0
514 for (int i = 0; i < wxStrlen(tmp.c_str()) +1; i++)
515 {
516 wxChar c = tmp[i];
517 printf( "%d ", (int) (c) );
518 }
519 printf( "\n" );
520 #endif
521
522 return tmp;
523 }
524
525 void wxComboBox::SetValue( const wxString& value )
526 {
527 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
528
529 GtkWidget *entry = GTK_COMBO(m_widget)->entry;
530 wxString tmp = wxT("");
531 if (!value.IsNull()) tmp = value;
532 gtk_entry_set_text( GTK_ENTRY(entry), wxGTK_CONV( tmp ) );
533 }
534
535 void wxComboBox::Copy()
536 {
537 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
538
539 GtkWidget *entry = GTK_COMBO(m_widget)->entry;
540 gtk_editable_copy_clipboard( GTK_EDITABLE(entry) DUMMY_CLIPBOARD_ARG );
541 }
542
543 void wxComboBox::Cut()
544 {
545 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
546
547 GtkWidget *entry = GTK_COMBO(m_widget)->entry;
548 gtk_editable_cut_clipboard( GTK_EDITABLE(entry) DUMMY_CLIPBOARD_ARG );
549 }
550
551 void wxComboBox::Paste()
552 {
553 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
554
555 GtkWidget *entry = GTK_COMBO(m_widget)->entry;
556 gtk_editable_paste_clipboard( GTK_EDITABLE(entry) DUMMY_CLIPBOARD_ARG);
557 }
558
559 void wxComboBox::SetInsertionPoint( long pos )
560 {
561 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
562
563 GtkWidget *entry = GTK_COMBO(m_widget)->entry;
564 gtk_entry_set_position( GTK_ENTRY(entry), (int)pos );
565 }
566
567 void wxComboBox::SetInsertionPointEnd()
568 {
569 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
570
571 SetInsertionPoint( -1 );
572 }
573
574 long wxComboBox::GetInsertionPoint() const
575 {
576 return (long) GET_EDITABLE_POS( GTK_COMBO(m_widget)->entry );
577 }
578
579 long wxComboBox::GetLastPosition() const
580 {
581 GtkWidget *entry = GTK_COMBO(m_widget)->entry;
582 int pos = GTK_ENTRY(entry)->text_length;
583 return (long) pos-1;
584 }
585
586 void wxComboBox::Replace( long from, long to, const wxString& value )
587 {
588 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
589
590 GtkWidget *entry = GTK_COMBO(m_widget)->entry;
591 gtk_editable_delete_text( GTK_EDITABLE(entry), (gint)from, (gint)to );
592 if (value.IsNull()) return;
593 gint pos = (gint)to;
594
595 #if wxUSE_UNICODE
596 wxCharBuffer buffer = wxConvUTF8.cWX2MB( value );
597 gtk_editable_insert_text( GTK_EDITABLE(entry), (const char*) buffer, strlen( (const char*) buffer ), &pos );
598 #else
599 gtk_editable_insert_text( GTK_EDITABLE(entry), value.c_str(), value.Length(), &pos );
600 #endif
601 }
602
603 void wxComboBox::Remove(long from, long to)
604 {
605 wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
606
607 GtkWidget *entry = GTK_COMBO(m_widget)->entry;
608 gtk_editable_delete_text( GTK_EDITABLE(entry), (gint)from, (gint)to );
609 }
610
611 void wxComboBox::SetSelection( long from, long to )
612 {
613 GtkWidget *entry = GTK_COMBO(m_widget)->entry;
614 gtk_editable_select_region( GTK_EDITABLE(entry), (gint)from, (gint)to );
615 }
616
617 void wxComboBox::SetEditable( bool editable )
618 {
619 GtkWidget *entry = GTK_COMBO(m_widget)->entry;
620 gtk_entry_set_editable( GTK_ENTRY(entry), editable );
621 }
622
623 void wxComboBox::OnChar( wxKeyEvent &event )
624 {
625 if ( event.GetKeyCode() == WXK_RETURN )
626 {
627 wxString value = GetValue();
628 int selection = GetSelection();
629
630 // note that gtk automatically selects an item if its in the list
631 // so you don't have to call FindString
632 if ((selection >= 0) && (GetString(selection) == value))
633 {
634 // make Enter generate "selected" event if it equals an item
635 wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, GetId() );
636 event.SetInt( selection );
637 event.SetString( value );
638 event.SetEventObject( this );
639 GetEventHandler()->ProcessEvent( event );
640 }
641 else
642 {
643 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, GetId());
644 event.SetString(value);
645 event.SetInt(selection);
646 event.SetEventObject( this );
647 GetEventHandler()->ProcessEvent( event );
648 }
649 return;
650 }
651
652 event.Skip();
653 }
654
655 void wxComboBox::DisableEvents()
656 {
657 GtkList *list = GTK_LIST( GTK_COMBO(m_widget)->list );
658 GList *child = list->children;
659 while (child)
660 {
661 gtk_signal_disconnect_by_func( GTK_OBJECT(child->data),
662 GTK_SIGNAL_FUNC(gtk_combo_clicked_callback), (gpointer)this );
663
664 child = child->next;
665 }
666 }
667
668 void wxComboBox::EnableEvents()
669 {
670 GtkList *list = GTK_LIST( GTK_COMBO(m_widget)->list );
671 GList *child = list->children;
672 while (child)
673 {
674 gtk_signal_connect( GTK_OBJECT(child->data), "select",
675 GTK_SIGNAL_FUNC(gtk_combo_clicked_callback), (gpointer)this );
676
677 child = child->next;
678 }
679 }
680
681 void wxComboBox::OnSize( wxSizeEvent &event )
682 {
683 event.Skip();
684
685 #if 0
686 int w = 21;
687 gtk_widget_set_usize( GTK_COMBO(m_widget)->entry, m_width-w-1, m_height );
688
689 gtk_widget_set_uposition( GTK_COMBO(m_widget)->button, m_x+m_width-w, m_y );
690 gtk_widget_set_usize( GTK_COMBO(m_widget)->button, w, m_height );
691 #endif // 0
692 }
693
694 void wxComboBox::ApplyWidgetStyle()
695 {
696 SetWidgetStyle();
697
698 // gtk_widget_set_style( GTK_COMBO(m_widget)->button, m_widgetStyle );
699 gtk_widget_set_style( GTK_COMBO(m_widget)->entry, m_widgetStyle );
700 gtk_widget_set_style( GTK_COMBO(m_widget)->list, m_widgetStyle );
701
702 GtkList *list = GTK_LIST( GTK_COMBO(m_widget)->list );
703 GList *child = list->children;
704 while (child)
705 {
706 gtk_widget_set_style( GTK_WIDGET(child->data), m_widgetStyle );
707
708 GtkBin *bin = GTK_BIN(child->data);
709 gtk_widget_set_style( bin->child, m_widgetStyle );
710
711 child = child->next;
712 }
713 }
714
715 GtkWidget* wxComboBox::GetConnectWidget()
716 {
717 return GTK_COMBO(m_widget)->entry;
718 }
719
720 bool wxComboBox::IsOwnGtkWindow( GdkWindow *window )
721 {
722 return ( (window == GTK_ENTRY( GTK_COMBO(m_widget)->entry )->text_area) ||
723 (window == GTK_COMBO(m_widget)->button->window ) );
724 }
725
726 wxSize wxComboBox::DoGetBestSize() const
727 {
728 wxSize ret( wxControl::DoGetBestSize() );
729
730 // we know better our horizontal extent: it depends on the longest string
731 // in the combobox
732 ret.x = 0;
733 if ( m_widget )
734 {
735 int width;
736 size_t count = GetCount();
737 for ( size_t n = 0; n < count; n++ )
738 {
739 GetTextExtent( GetString(n), &width, NULL, NULL, NULL, &m_font );
740 if ( width > ret.x )
741 ret.x = width;
742 }
743 }
744
745 // empty combobox should have some reasonable default size too
746 if ( ret.x < 100 )
747 ret.x = 100;
748 return ret;
749 }
750
751 #endif