source: trunk/third/gtk/gtk/gtkcombo.c @ 15781

Revision 15781, 27.4 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15780, which included commits to RCS files with non-trunk default branches.
Line 
1/* gtkcombo - combo widget for gtk+
2 * Copyright 1997 Paolo Molaro
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20/*
21 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22 * file for a list of people on the GTK+ Team.  See the ChangeLog
23 * files for a list of changes.  These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25 */
26
27#include <string.h>
28
29#include "gtkarrow.h"
30#include "gtklabel.h"
31#include "gtklist.h"
32#include "gtkentry.h"
33#include "gtkeventbox.h"
34#include "gtkbutton.h"
35#include "gtklistitem.h"
36#include "gtkscrolledwindow.h"
37#include "gtkmain.h"
38#include "gtksignal.h"
39#include "gtkwindow.h"
40#include "gdk/gdkkeysyms.h"
41#include "gtkcombo.h"
42#include "gtkframe.h"
43
44const gchar *gtk_combo_string_key = "gtk-combo-string-value";
45
46#define COMBO_LIST_MAX_HEIGHT   (400)
47#define EMPTY_LIST_HEIGHT       (15)
48
49static void         gtk_combo_class_init         (GtkComboClass    *klass);
50static void         gtk_combo_init               (GtkCombo         *combo);
51static void         gtk_combo_destroy            (GtkObject        *combo);
52static GtkListItem *gtk_combo_find               (GtkCombo         *combo);
53static gchar *      gtk_combo_func               (GtkListItem      *li);
54static gint         gtk_combo_focus_idle         (GtkCombo         *combo);
55static gint         gtk_combo_entry_focus_out    (GtkEntry         *entry,
56                                                  GdkEventFocus    *event,
57                                                  GtkCombo         *combo);
58static void         gtk_combo_get_pos            (GtkCombo         *combo,
59                                                  gint             *x,
60                                                  gint             *y,
61                                                  gint             *height,
62                                                  gint             *width);
63static void         gtk_combo_popup_list         (GtkCombo         *combo);
64static void         gtk_combo_activate           (GtkWidget        *widget,
65                                                  GtkCombo         *combo);
66static void         gtk_combo_popup_button_press (GtkWidget        *button,
67                                                  GdkEventButton   *event,
68                                                  GtkCombo         *combo);
69static void         gtk_combo_popup_button_leave (GtkWidget        *button,
70                                                  GdkEventCrossing *event,
71                                                  GtkCombo         *combo);
72static void         gtk_combo_update_entry       (GtkList          *list,
73                                                  GtkCombo         *combo);
74static void         gtk_combo_update_list        (GtkEntry         *entry,
75                                                  GtkCombo         *combo);
76static gint         gtk_combo_button_press       (GtkWidget        *widget,
77                                                  GdkEvent         *event,
78                                                  GtkCombo         *combo);
79static gint         gtk_combo_button_release     (GtkWidget        *widget,
80                                                  GdkEvent         *event,
81                                                  GtkCombo         *combo);
82static gint         gtk_combo_list_enter         (GtkWidget        *widget,
83                                                  GdkEventCrossing *event,
84                                                  GtkCombo         *combo);
85static gint         gtk_combo_list_key_press     (GtkWidget        *widget,
86                                                  GdkEventKey      *event,
87                                                  GtkCombo         *combo);
88static gint         gtk_combo_entry_key_press    (GtkEntry         *widget,
89                                                  GdkEventKey      *event,
90                                                  GtkCombo         *combo);
91static gint         gtk_combo_window_key_press   (GtkWidget        *window,
92                                                  GdkEventKey      *event,
93                                                  GtkCombo         *combo);
94static void         gtk_combo_item_destroy       (GtkObject        *object);
95static void         gtk_combo_size_allocate      (GtkWidget        *widget,
96                                                  GtkAllocation    *allocation);
97
98static GtkHBoxClass *parent_class = NULL;
99
100static void
101gtk_combo_class_init (GtkComboClass * klass)
102{
103  GtkObjectClass *oclass;
104  GtkWidgetClass *widget_class;
105
106  parent_class = gtk_type_class (gtk_hbox_get_type ());
107  oclass = (GtkObjectClass *) klass;
108  widget_class = (GtkWidgetClass *) klass;
109
110  oclass->destroy = gtk_combo_destroy;
111 
112  widget_class->size_allocate = gtk_combo_size_allocate;
113}
114
115static void
116gtk_combo_destroy (GtkObject * combo)
117{
118  gtk_widget_destroy (GTK_COMBO (combo)->popwin);
119  gtk_widget_unref (GTK_COMBO (combo)->popwin);
120
121  if (GTK_OBJECT_CLASS (parent_class)->destroy)
122    (*GTK_OBJECT_CLASS (parent_class)->destroy) (combo);
123}
124
125static int
126gtk_combo_entry_key_press (GtkEntry * entry, GdkEventKey * event, GtkCombo * combo)
127{
128  GList *li;
129
130  /* completion */
131  if ((event->keyval == GDK_Tab) && (event->state & GDK_MOD1_MASK))
132    {
133    GCompletion * cmpl;
134    gchar* prefix;
135    gchar* nprefix = NULL;
136    gint pos;
137
138    if ( !GTK_LIST (combo->list)->children )
139      return FALSE;
140   
141    gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event");
142
143    cmpl = g_completion_new ((GCompletionFunc)gtk_combo_func);
144    g_completion_add_items (cmpl, GTK_LIST (combo->list)->children);
145
146    pos = GTK_EDITABLE (entry)->current_pos;
147    prefix = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, pos);
148
149    g_completion_complete(cmpl, prefix, &nprefix);
150
151    if (nprefix && strlen (nprefix) > strlen (prefix))
152      {
153        gtk_editable_insert_text (GTK_EDITABLE (entry), nprefix + pos,
154                                 strlen (nprefix) - strlen (prefix), &pos);
155        GTK_EDITABLE (entry)->current_pos = pos;
156    }
157
158    if (nprefix)
159      g_free (nprefix);
160    g_free (prefix);
161    g_completion_free (cmpl);
162
163    return TRUE;
164  }
165
166  if (!combo->use_arrows || !GTK_LIST (combo->list)->children)
167    return FALSE;
168
169  li = g_list_find (GTK_LIST (combo->list)->children, gtk_combo_find (combo));
170
171  if ((event->keyval == GDK_Up)
172      || (event->keyval == GDK_KP_Up)
173      || ((event->state & GDK_MOD1_MASK) && ((event->keyval == 'p') || (event->keyval == 'P'))))
174    {
175      if (li)
176        li = li->prev;
177      if (!li && combo->use_arrows_always)
178        {
179          li = g_list_last (GTK_LIST (combo->list)->children);
180        }
181      if (li)
182        {
183          gtk_list_select_child (GTK_LIST (combo->list), GTK_WIDGET (li->data));
184          gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event");
185          return TRUE;
186        }
187    }
188  else if ((event->keyval == GDK_Down)
189           || (event->keyval == GDK_KP_Down)
190           || ((event->state & GDK_MOD1_MASK) && ((event->keyval == 'n') || (event->keyval == 'N'))))
191    {
192      if (li)
193        li = li->next;
194      if (!li && combo->use_arrows_always)
195        {
196          li = GTK_LIST (combo->list)->children;
197        }
198      if (li)
199        {
200          gtk_list_select_child (GTK_LIST (combo->list), GTK_WIDGET (li->data));
201          gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event");
202          return TRUE;
203        }
204    }
205  return FALSE;
206}
207
208static int
209gtk_combo_window_key_press (GtkWidget   *window,
210                            GdkEventKey *event,
211                            GtkCombo    *combo)
212{
213  GList *li;
214
215  if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
216    {
217      if (GTK_WIDGET_VISIBLE (combo->popwin))
218        {
219          gtk_widget_hide (combo->popwin);
220         
221          if (GTK_WIDGET_HAS_GRAB (combo->popwin))
222            {
223              gtk_grab_remove (combo->popwin);
224              gdk_pointer_ungrab (event->time);
225            }
226        }
227
228      gtk_signal_emit_stop_by_name (GTK_OBJECT (window), "key_press_event");
229
230      return TRUE;
231    }
232
233  return FALSE;
234}
235
236static GtkListItem *
237gtk_combo_find (GtkCombo * combo)
238{
239  gchar *text;
240  gchar *ltext;
241  GList *clist;
242  int (*string_compare) (const char *, const char *);
243
244  if (combo->case_sensitive)
245    string_compare = strcmp;
246  else
247    string_compare = g_strcasecmp;
248
249  text = gtk_entry_get_text (GTK_ENTRY (combo->entry));
250  clist = GTK_LIST (combo->list)->children;
251
252  while (clist && clist->data)
253    {
254      ltext = gtk_combo_func (GTK_LIST_ITEM (clist->data));
255      if (!ltext)
256        continue;
257      if (!(*string_compare) (ltext, text))
258        return (GtkListItem *) clist->data;
259      clist = clist->next;
260    }
261
262  return NULL;
263}
264
265static gchar *
266gtk_combo_func (GtkListItem * li)
267{
268  GtkWidget *label;
269  gchar *ltext = NULL;
270
271  ltext = (gchar *) gtk_object_get_data (GTK_OBJECT (li), gtk_combo_string_key);
272  if (!ltext)
273    {
274      label = GTK_BIN (li)->child;
275      if (!label || !GTK_IS_LABEL (label))
276        return NULL;
277      gtk_label_get (GTK_LABEL (label), &ltext);
278    }
279  return ltext;
280}
281
282static gint
283gtk_combo_focus_idle (GtkCombo * combo)
284{
285  if (combo)
286    {
287      GDK_THREADS_ENTER ();
288      gtk_widget_grab_focus (combo->entry);
289      GDK_THREADS_LEAVE ();
290    }
291  return FALSE;
292}
293
294static gint
295gtk_combo_entry_focus_out (GtkEntry * entry, GdkEventFocus * event, GtkCombo * combo)
296{
297
298  if (combo->value_in_list && !gtk_combo_find (combo))
299    {
300      /* gdk_beep(); *//* this can be annoying */
301      if (combo->ok_if_empty && !strcmp (gtk_entry_get_text (entry), ""))
302        return FALSE;
303#ifdef TEST
304      printf ("INVALID ENTRY: `%s'\n", gtk_entry_get_text (entry));
305#endif
306      gtk_grab_add (GTK_WIDGET (combo));
307      /* this is needed because if we call gtk_widget_grab_focus()
308         it isn't guaranteed it's the *last* call before the main-loop,
309         so the focus can be lost anyway...
310         the signal_emit_stop doesn't seem to work either...
311       */
312      gtk_idle_add ((GtkFunction) gtk_combo_focus_idle, combo);
313      /*gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "focus_out_event"); */
314      return TRUE;
315    }
316  return FALSE;
317}
318
319static void
320gtk_combo_get_pos (GtkCombo * combo, gint * x, gint * y, gint * height, gint * width)
321{
322  GtkBin *popwin;
323  GtkWidget *widget;
324  GtkScrolledWindow *popup;
325 
326  gint real_height;
327  GtkRequisition list_requisition;
328  gboolean show_hscroll = FALSE;
329  gboolean show_vscroll = FALSE;
330  gint avail_height;
331  gint min_height;
332  gint alloc_width;
333  gint work_height;
334  gint old_height;
335  gint old_width;
336 
337  widget = GTK_WIDGET(combo);
338  popup  = GTK_SCROLLED_WINDOW (combo->popup);
339  popwin = GTK_BIN (combo->popwin);
340 
341  gdk_window_get_origin (combo->entry->window, x, y);
342  real_height = MIN (combo->entry->requisition.height,
343                     combo->entry->allocation.height);
344  *y += real_height;
345  avail_height = gdk_screen_height () - *y;
346 
347  gtk_widget_size_request (combo->list, &list_requisition);
348  min_height = MIN (list_requisition.height,
349                    popup->vscrollbar->requisition.height);
350  if (!GTK_LIST (combo->list)->children)
351    list_requisition.height += EMPTY_LIST_HEIGHT;
352 
353  alloc_width = (widget->allocation.width -
354                 2 * popwin->child->style->klass->xthickness -
355                 2 * GTK_CONTAINER (popwin->child)->border_width -
356                 2 * GTK_CONTAINER (combo->popup)->border_width -
357                 2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width -
358                 2 * GTK_BIN (popup)->child->style->klass->xthickness);
359 
360  work_height = (2 * popwin->child->style->klass->ythickness +
361                 2 * GTK_CONTAINER (popwin->child)->border_width +
362                 2 * GTK_CONTAINER (combo->popup)->border_width +
363                 2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width +
364                 2 * GTK_BIN (popup)->child->style->klass->xthickness);
365 
366  do
367    {
368      old_width = alloc_width;
369      old_height = work_height;
370     
371      if (!show_hscroll &&
372          alloc_width < list_requisition.width)
373        {
374          work_height += popup->hscrollbar->requisition.height +
375            GTK_SCROLLED_WINDOW_CLASS
376            (GTK_OBJECT (combo->popup)->klass)->scrollbar_spacing;
377          show_hscroll = TRUE;
378        }
379      if (!show_vscroll &&
380          work_height + list_requisition.height > avail_height)
381        {
382          if (work_height + min_height > avail_height &&
383              *y - real_height > avail_height)
384            {
385              *y -= (work_height + list_requisition.height + real_height);
386              break;
387            }
388          alloc_width -=
389            popup->vscrollbar->requisition.width +
390            GTK_SCROLLED_WINDOW_CLASS
391            (GTK_OBJECT (combo->popup)->klass)->scrollbar_spacing;
392          show_vscroll = TRUE;
393        }
394    } while (old_width != alloc_width || old_height != work_height);
395 
396  *width = widget->allocation.width;
397  if (show_vscroll)
398    *height = avail_height;
399  else
400    *height = work_height + list_requisition.height;
401 
402  if (*x < 0)
403    *x = 0;
404}
405
406static void
407gtk_combo_popup_list (GtkCombo * combo)
408{
409  gint height, width, x, y;
410  gint old_width, old_height;
411
412  old_width = combo->popwin->allocation.width;
413  old_height  = combo->popwin->allocation.height;
414
415  gtk_combo_get_pos (combo, &x, &y, &height, &width);
416
417  /* workaround for gtk_scrolled_window_size_allocate bug */
418  if (old_width != width || old_height != height)
419    {
420      gtk_widget_hide (GTK_SCROLLED_WINDOW (combo->popup)->hscrollbar);
421      gtk_widget_hide (GTK_SCROLLED_WINDOW (combo->popup)->vscrollbar);
422    }
423
424  gtk_widget_set_uposition (combo->popwin, x, y);
425  gtk_widget_set_usize (combo->popwin, width, height);
426  gtk_widget_realize (combo->popwin);
427  gdk_window_resize (combo->popwin->window, width, height);
428  gtk_widget_show (combo->popwin);
429
430  gtk_widget_grab_focus (combo->popwin);
431}
432
433static void       
434gtk_combo_activate (GtkWidget        *widget,
435                    GtkCombo         *combo)
436{
437  gtk_combo_popup_list (combo);
438
439  if (!GTK_WIDGET_HAS_FOCUS (combo->entry))
440    gtk_widget_grab_focus (combo->entry);
441
442  gtk_grab_add (combo->popwin);
443  gdk_pointer_grab (combo->popwin->window, TRUE,
444                    GDK_BUTTON_PRESS_MASK |
445                    GDK_BUTTON_RELEASE_MASK |
446                    GDK_POINTER_MOTION_MASK,
447                    NULL, NULL, GDK_CURRENT_TIME);
448}
449
450static void       
451gtk_combo_popup_button_press (GtkWidget        *button,
452                              GdkEventButton   *event,
453                              GtkCombo         *combo)
454{
455  if (!GTK_WIDGET_HAS_FOCUS (combo->entry))
456    gtk_widget_grab_focus (combo->entry);
457  if (!combo->current_button && (event->button == 1))
458    gtk_combo_popup_list (combo);
459
460  combo->current_button = event->button;
461 
462  GTK_LIST (combo->list)->drag_selection = TRUE;
463  gdk_pointer_grab (combo->list->window, TRUE,
464                    GDK_POINTER_MOTION_HINT_MASK |
465                    GDK_BUTTON1_MOTION_MASK |
466                    GDK_BUTTON_RELEASE_MASK,
467                    NULL, NULL, event->time);
468  gtk_grab_add (combo->list);
469}
470
471static void         
472gtk_combo_popup_button_leave (GtkWidget        *button,
473                              GdkEventCrossing *event,
474                              GtkCombo         *combo)
475{
476  if (combo->current_button)
477    gtk_signal_emit_stop_by_name (GTK_OBJECT (button), "leave_notify_event");
478}
479
480
481static void
482gtk_combo_update_entry (GtkList * list, GtkCombo * combo)
483{
484  char *text;
485
486  gtk_grab_remove (GTK_WIDGET (combo));
487  gtk_signal_handler_block (GTK_OBJECT (list), combo->list_change_id);
488  if (list->selection)
489    {
490      text = gtk_combo_func (GTK_LIST_ITEM (list->selection->data));
491      if (!text)
492        text = "";
493      gtk_entry_set_text (GTK_ENTRY (combo->entry), text);
494    }
495  gtk_signal_handler_unblock (GTK_OBJECT (list), combo->list_change_id);
496}
497
498static void
499gtk_combo_update_list (GtkEntry * entry, GtkCombo * combo)
500{
501  GtkList *list = GTK_LIST (combo->list);
502  GList *slist = list->selection;
503  GtkListItem *li;
504
505  gtk_grab_remove (GTK_WIDGET (combo));
506
507  gtk_signal_handler_block (GTK_OBJECT (entry), combo->entry_change_id);
508  if (slist && slist->data)
509    gtk_list_unselect_child (list, GTK_WIDGET (slist->data));
510  li = gtk_combo_find (combo);
511  if (li)
512    gtk_list_select_child (list, GTK_WIDGET (li));
513  gtk_signal_handler_unblock (GTK_OBJECT (entry), combo->entry_change_id);
514}
515
516static gint
517gtk_combo_button_press (GtkWidget * widget, GdkEvent * event, GtkCombo * combo)
518{
519  GtkWidget *child;
520
521  child = gtk_get_event_widget (event);
522
523  /* We don't ask for button press events on the grab widget, so
524   *  if an event is reported directly to the grab widget, it must
525   *  be on a window outside the application (and thus we remove
526   *  the popup window). Otherwise, we check if the widget is a child
527   *  of the grab widget, and only remove the popup window if it
528   *  is not.
529   */
530  if (child != widget)
531    {
532      while (child)
533        {
534          if (child == widget)
535            return FALSE;
536          child = child->parent;
537        }
538    }
539
540  gtk_widget_hide (combo->popwin);
541  gtk_grab_remove (combo->popwin);
542  gdk_pointer_ungrab (event->button.time);
543
544  return TRUE;
545}
546
547static gint
548gtk_combo_button_release (GtkWidget * widget, GdkEvent * event, GtkCombo * combo)
549{
550  GtkWidget *child;
551
552  if ((combo->current_button != 0) && (event->button.button == 1))
553    {
554      /* This was the initial button press */
555
556      GdkEventCrossing tmp_event;
557
558      combo->current_button = 0;
559
560      if (widget != combo->button)
561        gtk_widget_event (combo->button, event);
562
563      /* Un-pre-hightlight */
564     
565      tmp_event.type = GDK_LEAVE_NOTIFY;
566      tmp_event.window = combo->button->window;
567      tmp_event.send_event = TRUE;
568      tmp_event.subwindow = NULL;
569      tmp_event.detail = GDK_NOTIFY_ANCESTOR;
570     
571      gtk_widget_event (combo->button, (GdkEvent *)&tmp_event);
572
573      /* Check to see if we released inside the button */
574      child = gtk_get_event_widget ((GdkEvent*) event);
575
576      while (child && child != (combo->button))
577        child = child->parent;
578
579      if (child == combo->button)
580        {
581          gtk_grab_add (combo->popwin);
582          gdk_pointer_grab (combo->popwin->window, TRUE,
583                            GDK_BUTTON_PRESS_MASK |
584                            GDK_BUTTON_RELEASE_MASK |
585                            GDK_POINTER_MOTION_MASK,
586                            NULL, NULL, GDK_CURRENT_TIME);
587          return FALSE;
588        }
589    }
590  else
591    {
592      /* The user has clicked inside the popwin and released */
593
594      if (GTK_WIDGET_HAS_GRAB (combo->popwin))
595        {
596          gtk_grab_remove (combo->popwin);
597          gdk_pointer_ungrab (event->button.time);
598        }
599    }
600 
601  gtk_widget_hide (combo->popwin);
602
603  return TRUE;
604}
605
606static gint         
607gtk_combo_list_enter (GtkWidget        *widget,
608                      GdkEventCrossing *event,
609                      GtkCombo         *combo)
610{
611  GtkWidget *event_widget;
612
613  event_widget = gtk_get_event_widget ((GdkEvent*) event);
614 
615  if ((event_widget == combo->list) &&
616      (combo->current_button != 0) &&
617      (!GTK_WIDGET_HAS_GRAB (combo->list)))
618    {
619      GdkEvent tmp_event;
620      gint x, y;
621      GdkModifierType mask;
622
623      gtk_grab_remove (combo->popwin);
624
625      /* Transfer the grab over to the list by synthesizing
626       * a button press event
627       */
628      gdk_window_get_pointer (combo->list->window, &x, &y, &mask);
629
630      tmp_event.button.type = GDK_BUTTON_PRESS;
631      tmp_event.button.window = combo->list->window;
632      tmp_event.button.send_event = TRUE;
633      tmp_event.button.time = GDK_CURRENT_TIME; /* bad */
634      tmp_event.button.x = x;
635      tmp_event.button.y = y;
636      /* We leave all the XInput fields unfilled here, in the expectation
637       * that GtkList doesn't care.
638       */
639      tmp_event.button.button = combo->current_button;
640      tmp_event.button.state = mask;
641
642      gtk_widget_event (combo->list, &tmp_event);
643    }
644
645  return FALSE;
646}
647
648static int
649gtk_combo_list_key_press (GtkWidget * widget, GdkEventKey * event, GtkCombo * combo)
650{
651  if (event->keyval == GDK_Escape)
652    {
653      if (GTK_WIDGET_HAS_GRAB (combo->popwin))
654        {
655          gtk_grab_remove (combo->popwin);
656          gdk_pointer_ungrab (GDK_CURRENT_TIME);
657        }
658      else if (GTK_WIDGET_HAS_GRAB (combo->list))
659        gtk_list_end_drag_selection (GTK_LIST (combo->list));
660      gtk_widget_hide (combo->popwin);
661      if (GTK_WIDGET_HAS_GRAB (combo->button))
662        {
663          combo->current_button = 0;
664          GTK_BUTTON (combo->button)->in_button = FALSE;
665          gtk_button_released (GTK_BUTTON (combo->button));
666          gtk_grab_remove (combo->button);
667        }
668      return TRUE;
669    }
670  return FALSE;
671}
672
673static void
674gtk_combo_init (GtkCombo * combo)
675{
676  GtkWidget *arrow;
677  GtkWidget *frame;
678  GtkWidget *event_box;
679  GdkCursor *cursor;
680
681  combo->case_sensitive = 0;
682  combo->value_in_list = 0;
683  combo->ok_if_empty = 1;
684  combo->use_arrows = 1;
685  combo->use_arrows_always = 0;
686  combo->entry = gtk_entry_new ();
687  combo->button = gtk_button_new ();
688  combo->current_button = 0;
689  arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
690  gtk_widget_show (arrow);
691  gtk_container_add (GTK_CONTAINER (combo->button), arrow);
692  gtk_box_pack_start (GTK_BOX (combo), combo->entry, TRUE, TRUE, 0);
693  gtk_box_pack_end (GTK_BOX (combo), combo->button, FALSE, FALSE, 0);
694  GTK_WIDGET_UNSET_FLAGS (combo->button, GTK_CAN_FOCUS);
695  gtk_widget_show (combo->entry);
696  gtk_widget_show (combo->button);
697  combo->entry_change_id = gtk_signal_connect (GTK_OBJECT (combo->entry), "changed",
698                              (GtkSignalFunc) gtk_combo_update_list, combo);
699  gtk_signal_connect (GTK_OBJECT (combo->entry), "key_press_event",
700                      (GtkSignalFunc) gtk_combo_entry_key_press, combo);
701  gtk_signal_connect_after (GTK_OBJECT (combo->entry), "focus_out_event",
702                            (GtkSignalFunc) gtk_combo_entry_focus_out, combo);
703  combo->activate_id = gtk_signal_connect (GTK_OBJECT (combo->entry), "activate",
704                      (GtkSignalFunc) gtk_combo_activate, combo);
705  gtk_signal_connect_after (GTK_OBJECT (combo->button), "button_press_event",
706                            (GtkSignalFunc) gtk_combo_popup_button_press, combo);
707  /*gtk_signal_connect_after (GTK_OBJECT (combo->button), "button_release_event",
708    (GtkSignalFunc) gtk_combo_button_release, combo);*/
709  gtk_signal_connect (GTK_OBJECT (combo->button), "leave_notify_event",
710                      (GtkSignalFunc) gtk_combo_popup_button_leave, combo);
711  /*gtk_signal_connect(GTK_OBJECT(combo->button), "clicked",
712     (GtkSignalFunc)prelight_bug, combo); */
713
714  combo->popwin = gtk_window_new (GTK_WINDOW_POPUP);
715  gtk_widget_ref (combo->popwin);
716  gtk_window_set_policy (GTK_WINDOW (combo->popwin), 1, 1, 0);
717
718  gtk_signal_connect (GTK_OBJECT (combo->popwin), "key_press_event",
719                      GTK_SIGNAL_FUNC (gtk_combo_window_key_press), combo);
720 
721  gtk_widget_set_events (combo->popwin, GDK_KEY_PRESS_MASK);
722
723  event_box = gtk_event_box_new ();
724  gtk_container_add (GTK_CONTAINER (combo->popwin), event_box);
725  gtk_widget_show (event_box);
726
727  gtk_widget_realize (event_box);
728  cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
729  gdk_window_set_cursor (event_box->window, cursor);
730  gdk_cursor_destroy (cursor);
731
732  frame = gtk_frame_new (NULL);
733  gtk_container_add (GTK_CONTAINER (event_box), frame);
734  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
735  gtk_widget_show (frame);
736
737  combo->popup = gtk_scrolled_window_new (NULL, NULL);
738  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo->popup),
739                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
740  GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (combo->popup)->hscrollbar, GTK_CAN_FOCUS);
741  GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (combo->popup)->vscrollbar, GTK_CAN_FOCUS);
742  gtk_container_add (GTK_CONTAINER (frame), combo->popup);
743  gtk_widget_show (combo->popup);
744
745  combo->list = gtk_list_new ();
746  /* We'll use enter notify events to figure out when to transfer
747   * the grab to the list
748   */
749  gtk_widget_set_events (combo->list, GDK_ENTER_NOTIFY_MASK);
750
751  gtk_list_set_selection_mode(GTK_LIST(combo->list), GTK_SELECTION_BROWSE);
752  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (combo->popup), combo->list);
753  gtk_container_set_focus_vadjustment (GTK_CONTAINER (combo->list),
754                                       gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo->popup)));
755  gtk_container_set_focus_hadjustment (GTK_CONTAINER (combo->list),
756                                       gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo->popup)));
757  gtk_widget_show (combo->list);
758
759  combo->list_change_id = gtk_signal_connect (GTK_OBJECT (combo->list), "selection_changed",
760                             (GtkSignalFunc) gtk_combo_update_entry, combo);
761  gtk_signal_connect (GTK_OBJECT (combo->popwin), "key_press_event",
762                      (GtkSignalFunc) gtk_combo_list_key_press, combo);
763  gtk_signal_connect (GTK_OBJECT (combo->popwin), "button_press_event",
764                      GTK_SIGNAL_FUNC (gtk_combo_button_press), combo);
765
766  gtk_signal_connect_after (GTK_OBJECT (combo->list), "button_release_event",
767                            GTK_SIGNAL_FUNC (gtk_combo_button_release), combo);
768  /* We connect here on the button, because we'll have a grab on it
769   * when the event occurs. But we are actually interested in enters
770   * for the combo->list.
771   */
772  gtk_signal_connect (GTK_OBJECT (combo->button), "enter_notify_event",
773                      GTK_SIGNAL_FUNC (gtk_combo_list_enter), combo);
774}
775
776guint
777gtk_combo_get_type (void)
778{
779  static guint combo_type = 0;
780
781  if (!combo_type)
782    {
783      static const GtkTypeInfo combo_info =
784      {
785        "GtkCombo",
786        sizeof (GtkCombo),
787        sizeof (GtkComboClass),
788        (GtkClassInitFunc) gtk_combo_class_init,
789        (GtkObjectInitFunc) gtk_combo_init,
790        /* reserved_1 */ NULL,
791        /* reserved_2 */ NULL,
792        (GtkClassInitFunc) NULL,
793      };
794      combo_type = gtk_type_unique (gtk_hbox_get_type (), &combo_info);
795    }
796  return combo_type;
797}
798
799GtkWidget *
800gtk_combo_new (void)
801{
802  return GTK_WIDGET (gtk_type_new (gtk_combo_get_type ()));
803}
804
805void
806gtk_combo_set_value_in_list (GtkCombo * combo, gint val, gint ok_if_empty)
807{
808  g_return_if_fail (combo != NULL);
809  g_return_if_fail (GTK_IS_COMBO (combo));
810
811  combo->value_in_list = val;
812  combo->ok_if_empty = ok_if_empty;
813}
814
815void
816gtk_combo_set_case_sensitive (GtkCombo * combo, gint val)
817{
818  g_return_if_fail (combo != NULL);
819  g_return_if_fail (GTK_IS_COMBO (combo));
820
821  combo->case_sensitive = val;
822}
823
824void
825gtk_combo_set_use_arrows (GtkCombo * combo, gint val)
826{
827  g_return_if_fail (combo != NULL);
828  g_return_if_fail (GTK_IS_COMBO (combo));
829
830  combo->use_arrows = val;
831}
832
833void
834gtk_combo_set_use_arrows_always (GtkCombo * combo, gint val)
835{
836  g_return_if_fail (combo != NULL);
837  g_return_if_fail (GTK_IS_COMBO (combo));
838
839  combo->use_arrows_always = val;
840  combo->use_arrows = 1;
841}
842
843void
844gtk_combo_set_popdown_strings (GtkCombo * combo, GList * strings)
845{
846  GList *list;
847  GtkWidget *li;
848
849  g_return_if_fail (combo != NULL);
850  g_return_if_fail (GTK_IS_COMBO (combo));
851  g_return_if_fail (strings != NULL);
852
853  gtk_list_clear_items (GTK_LIST (combo->list), 0, -1);
854  list = strings;
855  while (list)
856    {
857      li = gtk_list_item_new_with_label ((gchar *) list->data);
858      gtk_widget_show (li);
859      gtk_container_add (GTK_CONTAINER (combo->list), li);
860      list = list->next;
861    }
862}
863
864static void
865gtk_combo_item_destroy (GtkObject * object)
866{
867  gchar *key;
868
869  key = gtk_object_get_data (object, gtk_combo_string_key);
870  if (key)
871    g_free (key);
872}
873
874void
875gtk_combo_set_item_string (GtkCombo * combo, GtkItem * item, const gchar * item_value)
876{
877  gchar *val;
878  gint connected = 0;
879
880  g_return_if_fail (combo != NULL);
881  g_return_if_fail (GTK_IS_COMBO (combo));
882  g_return_if_fail (item != NULL);
883
884  val = gtk_object_get_data (GTK_OBJECT (item), gtk_combo_string_key);
885  if (val)
886    {
887      g_free (val);
888      connected = 1;
889    }
890  if (item_value)
891    {
892      val = g_strdup(item_value);
893      gtk_object_set_data (GTK_OBJECT (item), gtk_combo_string_key, val);
894      if (!connected)
895        gtk_signal_connect (GTK_OBJECT (item), "destroy",
896                          (GtkSignalFunc) gtk_combo_item_destroy, val);
897    }
898  else
899    {
900      gtk_object_set_data (GTK_OBJECT (item), gtk_combo_string_key, NULL);
901      if (connected)
902        gtk_signal_disconnect_by_data(GTK_OBJECT (item), val);
903    }
904}
905
906static void
907gtk_combo_size_allocate (GtkWidget     *widget,
908                         GtkAllocation *allocation)
909{
910  GtkCombo *combo;
911
912  g_return_if_fail (widget != NULL);
913  g_return_if_fail (GTK_IS_COMBO (widget));
914  g_return_if_fail (allocation != NULL);
915
916  GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
917 
918  combo = GTK_COMBO (widget);
919
920  if (combo->entry->allocation.height > combo->entry->requisition.height)
921    {
922      GtkAllocation button_allocation;
923
924      button_allocation = combo->button->allocation;
925      button_allocation.height = combo->entry->requisition.height;
926      button_allocation.y = combo->entry->allocation.y +
927        (combo->entry->allocation.height - combo->entry->requisition.height)
928        / 2;
929      gtk_widget_size_allocate (combo->button, &button_allocation);
930    }
931}
932
933void
934gtk_combo_disable_activate (GtkCombo* combo)
935{
936  g_return_if_fail (combo != NULL);
937  g_return_if_fail (GTK_IS_COMBO (combo));
938
939  if ( combo->activate_id ) {
940    gtk_signal_disconnect(GTK_OBJECT(combo->entry), combo->activate_id);
941    combo->activate_id = 0;
942  }
943}
Note: See TracBrowser for help on using the repository browser.