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

Revision 17071, 27.4 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17070, which included commits to RCS files with non-trunk default branches.
RevLine 
[14481]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
[15780]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);
[14481]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);
[15780]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);
[14481]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
[15780]208static int
209gtk_combo_window_key_press (GtkWidget   *window,
210                            GdkEventKey *event,
211                            GtkCombo    *combo)
212{
213  if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
214    {
215      if (GTK_WIDGET_VISIBLE (combo->popwin))
216        {
217          gtk_widget_hide (combo->popwin);
218         
219          if (GTK_WIDGET_HAS_GRAB (combo->popwin))
220            {
221              gtk_grab_remove (combo->popwin);
222              gdk_pointer_ungrab (event->time);
223            }
224        }
225
226      gtk_signal_emit_stop_by_name (GTK_OBJECT (window), "key_press_event");
227
228      return TRUE;
229    }
230
231  return FALSE;
232}
233
[14481]234static GtkListItem *
235gtk_combo_find (GtkCombo * combo)
236{
237  gchar *text;
238  gchar *ltext;
239  GList *clist;
240  int (*string_compare) (const char *, const char *);
241
242  if (combo->case_sensitive)
243    string_compare = strcmp;
244  else
245    string_compare = g_strcasecmp;
246
247  text = gtk_entry_get_text (GTK_ENTRY (combo->entry));
248  clist = GTK_LIST (combo->list)->children;
249
250  while (clist && clist->data)
251    {
252      ltext = gtk_combo_func (GTK_LIST_ITEM (clist->data));
253      if (!ltext)
254        continue;
255      if (!(*string_compare) (ltext, text))
256        return (GtkListItem *) clist->data;
257      clist = clist->next;
258    }
259
260  return NULL;
261}
262
263static gchar *
264gtk_combo_func (GtkListItem * li)
265{
266  GtkWidget *label;
267  gchar *ltext = NULL;
268
269  ltext = (gchar *) gtk_object_get_data (GTK_OBJECT (li), gtk_combo_string_key);
270  if (!ltext)
271    {
272      label = GTK_BIN (li)->child;
273      if (!label || !GTK_IS_LABEL (label))
274        return NULL;
275      gtk_label_get (GTK_LABEL (label), &ltext);
276    }
277  return ltext;
278}
279
280static gint
281gtk_combo_focus_idle (GtkCombo * combo)
282{
283  if (combo)
284    {
285      GDK_THREADS_ENTER ();
286      gtk_widget_grab_focus (combo->entry);
287      GDK_THREADS_LEAVE ();
288    }
289  return FALSE;
290}
291
292static gint
293gtk_combo_entry_focus_out (GtkEntry * entry, GdkEventFocus * event, GtkCombo * combo)
294{
295
296  if (combo->value_in_list && !gtk_combo_find (combo))
297    {
298      /* gdk_beep(); *//* this can be annoying */
299      if (combo->ok_if_empty && !strcmp (gtk_entry_get_text (entry), ""))
300        return FALSE;
301#ifdef TEST
302      printf ("INVALID ENTRY: `%s'\n", gtk_entry_get_text (entry));
303#endif
304      gtk_grab_add (GTK_WIDGET (combo));
305      /* this is needed because if we call gtk_widget_grab_focus()
306         it isn't guaranteed it's the *last* call before the main-loop,
307         so the focus can be lost anyway...
308         the signal_emit_stop doesn't seem to work either...
309       */
310      gtk_idle_add ((GtkFunction) gtk_combo_focus_idle, combo);
311      /*gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "focus_out_event"); */
312      return TRUE;
313    }
314  return FALSE;
315}
316
317static void
318gtk_combo_get_pos (GtkCombo * combo, gint * x, gint * y, gint * height, gint * width)
319{
320  GtkBin *popwin;
321  GtkWidget *widget;
322  GtkScrolledWindow *popup;
323 
324  gint real_height;
325  GtkRequisition list_requisition;
326  gboolean show_hscroll = FALSE;
327  gboolean show_vscroll = FALSE;
328  gint avail_height;
329  gint min_height;
330  gint alloc_width;
331  gint work_height;
332  gint old_height;
333  gint old_width;
334 
335  widget = GTK_WIDGET(combo);
336  popup  = GTK_SCROLLED_WINDOW (combo->popup);
337  popwin = GTK_BIN (combo->popwin);
338 
339  gdk_window_get_origin (combo->entry->window, x, y);
340  real_height = MIN (combo->entry->requisition.height,
341                     combo->entry->allocation.height);
342  *y += real_height;
343  avail_height = gdk_screen_height () - *y;
344 
345  gtk_widget_size_request (combo->list, &list_requisition);
346  min_height = MIN (list_requisition.height,
347                    popup->vscrollbar->requisition.height);
348  if (!GTK_LIST (combo->list)->children)
349    list_requisition.height += EMPTY_LIST_HEIGHT;
350 
351  alloc_width = (widget->allocation.width -
352                 2 * popwin->child->style->klass->xthickness -
353                 2 * GTK_CONTAINER (popwin->child)->border_width -
354                 2 * GTK_CONTAINER (combo->popup)->border_width -
355                 2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width -
356                 2 * GTK_BIN (popup)->child->style->klass->xthickness);
357 
358  work_height = (2 * popwin->child->style->klass->ythickness +
359                 2 * GTK_CONTAINER (popwin->child)->border_width +
360                 2 * GTK_CONTAINER (combo->popup)->border_width +
361                 2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width +
362                 2 * GTK_BIN (popup)->child->style->klass->xthickness);
363 
364  do
365    {
366      old_width = alloc_width;
367      old_height = work_height;
368     
369      if (!show_hscroll &&
370          alloc_width < list_requisition.width)
371        {
372          work_height += popup->hscrollbar->requisition.height +
373            GTK_SCROLLED_WINDOW_CLASS
374            (GTK_OBJECT (combo->popup)->klass)->scrollbar_spacing;
375          show_hscroll = TRUE;
376        }
377      if (!show_vscroll &&
378          work_height + list_requisition.height > avail_height)
379        {
380          if (work_height + min_height > avail_height &&
381              *y - real_height > avail_height)
382            {
383              *y -= (work_height + list_requisition.height + real_height);
384              break;
385            }
386          alloc_width -=
387            popup->vscrollbar->requisition.width +
388            GTK_SCROLLED_WINDOW_CLASS
389            (GTK_OBJECT (combo->popup)->klass)->scrollbar_spacing;
390          show_vscroll = TRUE;
391        }
392    } while (old_width != alloc_width || old_height != work_height);
393 
394  *width = widget->allocation.width;
395  if (show_vscroll)
396    *height = avail_height;
397  else
398    *height = work_height + list_requisition.height;
399 
400  if (*x < 0)
401    *x = 0;
402}
403
404static void
405gtk_combo_popup_list (GtkCombo * combo)
406{
407  gint height, width, x, y;
408  gint old_width, old_height;
409
410  old_width = combo->popwin->allocation.width;
411  old_height  = combo->popwin->allocation.height;
412
413  gtk_combo_get_pos (combo, &x, &y, &height, &width);
414
415  /* workaround for gtk_scrolled_window_size_allocate bug */
416  if (old_width != width || old_height != height)
417    {
418      gtk_widget_hide (GTK_SCROLLED_WINDOW (combo->popup)->hscrollbar);
419      gtk_widget_hide (GTK_SCROLLED_WINDOW (combo->popup)->vscrollbar);
420    }
421
422  gtk_widget_set_uposition (combo->popwin, x, y);
423  gtk_widget_set_usize (combo->popwin, width, height);
424  gtk_widget_realize (combo->popwin);
425  gdk_window_resize (combo->popwin->window, width, height);
426  gtk_widget_show (combo->popwin);
427
428  gtk_widget_grab_focus (combo->popwin);
429}
430
431static void       
432gtk_combo_activate (GtkWidget        *widget,
433                    GtkCombo         *combo)
434{
435  gtk_combo_popup_list (combo);
436
437  if (!GTK_WIDGET_HAS_FOCUS (combo->entry))
438    gtk_widget_grab_focus (combo->entry);
439
440  gtk_grab_add (combo->popwin);
441  gdk_pointer_grab (combo->popwin->window, TRUE,
442                    GDK_BUTTON_PRESS_MASK |
443                    GDK_BUTTON_RELEASE_MASK |
444                    GDK_POINTER_MOTION_MASK,
445                    NULL, NULL, GDK_CURRENT_TIME);
446}
447
448static void       
449gtk_combo_popup_button_press (GtkWidget        *button,
450                              GdkEventButton   *event,
451                              GtkCombo         *combo)
452{
453  if (!GTK_WIDGET_HAS_FOCUS (combo->entry))
454    gtk_widget_grab_focus (combo->entry);
455  if (!combo->current_button && (event->button == 1))
456    gtk_combo_popup_list (combo);
457
458  combo->current_button = event->button;
459 
460  GTK_LIST (combo->list)->drag_selection = TRUE;
461  gdk_pointer_grab (combo->list->window, TRUE,
462                    GDK_POINTER_MOTION_HINT_MASK |
463                    GDK_BUTTON1_MOTION_MASK |
464                    GDK_BUTTON_RELEASE_MASK,
465                    NULL, NULL, event->time);
466  gtk_grab_add (combo->list);
467}
468
469static void         
470gtk_combo_popup_button_leave (GtkWidget        *button,
471                              GdkEventCrossing *event,
472                              GtkCombo         *combo)
473{
474  if (combo->current_button)
475    gtk_signal_emit_stop_by_name (GTK_OBJECT (button), "leave_notify_event");
476}
477
478
479static void
480gtk_combo_update_entry (GtkList * list, GtkCombo * combo)
481{
482  char *text;
483
484  gtk_grab_remove (GTK_WIDGET (combo));
485  gtk_signal_handler_block (GTK_OBJECT (list), combo->list_change_id);
486  if (list->selection)
487    {
488      text = gtk_combo_func (GTK_LIST_ITEM (list->selection->data));
489      if (!text)
490        text = "";
491      gtk_entry_set_text (GTK_ENTRY (combo->entry), text);
492    }
493  gtk_signal_handler_unblock (GTK_OBJECT (list), combo->list_change_id);
494}
495
496static void
497gtk_combo_update_list (GtkEntry * entry, GtkCombo * combo)
498{
499  GtkList *list = GTK_LIST (combo->list);
500  GList *slist = list->selection;
501  GtkListItem *li;
502
503  gtk_grab_remove (GTK_WIDGET (combo));
504
505  gtk_signal_handler_block (GTK_OBJECT (entry), combo->entry_change_id);
506  if (slist && slist->data)
507    gtk_list_unselect_child (list, GTK_WIDGET (slist->data));
508  li = gtk_combo_find (combo);
509  if (li)
510    gtk_list_select_child (list, GTK_WIDGET (li));
511  gtk_signal_handler_unblock (GTK_OBJECT (entry), combo->entry_change_id);
512}
513
514static gint
515gtk_combo_button_press (GtkWidget * widget, GdkEvent * event, GtkCombo * combo)
516{
517  GtkWidget *child;
518
519  child = gtk_get_event_widget (event);
520
521  /* We don't ask for button press events on the grab widget, so
522   *  if an event is reported directly to the grab widget, it must
523   *  be on a window outside the application (and thus we remove
524   *  the popup window). Otherwise, we check if the widget is a child
525   *  of the grab widget, and only remove the popup window if it
526   *  is not.
527   */
528  if (child != widget)
529    {
530      while (child)
531        {
532          if (child == widget)
533            return FALSE;
534          child = child->parent;
535        }
536    }
537
538  gtk_widget_hide (combo->popwin);
539  gtk_grab_remove (combo->popwin);
540  gdk_pointer_ungrab (event->button.time);
541
542  return TRUE;
543}
544
545static gint
546gtk_combo_button_release (GtkWidget * widget, GdkEvent * event, GtkCombo * combo)
547{
548  GtkWidget *child;
549
550  if ((combo->current_button != 0) && (event->button.button == 1))
551    {
552      /* This was the initial button press */
553
554      GdkEventCrossing tmp_event;
555
556      combo->current_button = 0;
557
558      if (widget != combo->button)
559        gtk_widget_event (combo->button, event);
560
561      /* Un-pre-hightlight */
562     
563      tmp_event.type = GDK_LEAVE_NOTIFY;
564      tmp_event.window = combo->button->window;
565      tmp_event.send_event = TRUE;
566      tmp_event.subwindow = NULL;
567      tmp_event.detail = GDK_NOTIFY_ANCESTOR;
568     
569      gtk_widget_event (combo->button, (GdkEvent *)&tmp_event);
570
571      /* Check to see if we released inside the button */
572      child = gtk_get_event_widget ((GdkEvent*) event);
573
574      while (child && child != (combo->button))
575        child = child->parent;
576
577      if (child == combo->button)
578        {
579          gtk_grab_add (combo->popwin);
580          gdk_pointer_grab (combo->popwin->window, TRUE,
581                            GDK_BUTTON_PRESS_MASK |
582                            GDK_BUTTON_RELEASE_MASK |
583                            GDK_POINTER_MOTION_MASK,
584                            NULL, NULL, GDK_CURRENT_TIME);
585          return FALSE;
586        }
587    }
588  else
589    {
590      /* The user has clicked inside the popwin and released */
591
592      if (GTK_WIDGET_HAS_GRAB (combo->popwin))
593        {
594          gtk_grab_remove (combo->popwin);
595          gdk_pointer_ungrab (event->button.time);
596        }
597    }
598 
599  gtk_widget_hide (combo->popwin);
600
601  return TRUE;
602}
603
604static gint         
605gtk_combo_list_enter (GtkWidget        *widget,
606                      GdkEventCrossing *event,
607                      GtkCombo         *combo)
608{
609  GtkWidget *event_widget;
610
611  event_widget = gtk_get_event_widget ((GdkEvent*) event);
612 
613  if ((event_widget == combo->list) &&
614      (combo->current_button != 0) &&
615      (!GTK_WIDGET_HAS_GRAB (combo->list)))
616    {
617      GdkEvent tmp_event;
618      gint x, y;
619      GdkModifierType mask;
620
621      gtk_grab_remove (combo->popwin);
622
623      /* Transfer the grab over to the list by synthesizing
624       * a button press event
625       */
626      gdk_window_get_pointer (combo->list->window, &x, &y, &mask);
627
628      tmp_event.button.type = GDK_BUTTON_PRESS;
629      tmp_event.button.window = combo->list->window;
630      tmp_event.button.send_event = TRUE;
631      tmp_event.button.time = GDK_CURRENT_TIME; /* bad */
632      tmp_event.button.x = x;
633      tmp_event.button.y = y;
634      /* We leave all the XInput fields unfilled here, in the expectation
635       * that GtkList doesn't care.
636       */
637      tmp_event.button.button = combo->current_button;
638      tmp_event.button.state = mask;
639
640      gtk_widget_event (combo->list, &tmp_event);
641    }
642
643  return FALSE;
644}
645
646static int
647gtk_combo_list_key_press (GtkWidget * widget, GdkEventKey * event, GtkCombo * combo)
648{
649  if (event->keyval == GDK_Escape)
650    {
651      if (GTK_WIDGET_HAS_GRAB (combo->popwin))
652        {
653          gtk_grab_remove (combo->popwin);
654          gdk_pointer_ungrab (GDK_CURRENT_TIME);
655        }
656      else if (GTK_WIDGET_HAS_GRAB (combo->list))
657        gtk_list_end_drag_selection (GTK_LIST (combo->list));
658      gtk_widget_hide (combo->popwin);
659      if (GTK_WIDGET_HAS_GRAB (combo->button))
660        {
661          combo->current_button = 0;
662          GTK_BUTTON (combo->button)->in_button = FALSE;
663          gtk_button_released (GTK_BUTTON (combo->button));
664          gtk_grab_remove (combo->button);
665        }
666      return TRUE;
667    }
668  return FALSE;
669}
670
671static void
672gtk_combo_init (GtkCombo * combo)
673{
674  GtkWidget *arrow;
675  GtkWidget *frame;
676  GtkWidget *event_box;
677  GdkCursor *cursor;
678
679  combo->case_sensitive = 0;
680  combo->value_in_list = 0;
681  combo->ok_if_empty = 1;
682  combo->use_arrows = 1;
683  combo->use_arrows_always = 0;
684  combo->entry = gtk_entry_new ();
685  combo->button = gtk_button_new ();
686  combo->current_button = 0;
687  arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
688  gtk_widget_show (arrow);
689  gtk_container_add (GTK_CONTAINER (combo->button), arrow);
690  gtk_box_pack_start (GTK_BOX (combo), combo->entry, TRUE, TRUE, 0);
691  gtk_box_pack_end (GTK_BOX (combo), combo->button, FALSE, FALSE, 0);
692  GTK_WIDGET_UNSET_FLAGS (combo->button, GTK_CAN_FOCUS);
693  gtk_widget_show (combo->entry);
694  gtk_widget_show (combo->button);
695  combo->entry_change_id = gtk_signal_connect (GTK_OBJECT (combo->entry), "changed",
696                              (GtkSignalFunc) gtk_combo_update_list, combo);
697  gtk_signal_connect (GTK_OBJECT (combo->entry), "key_press_event",
698                      (GtkSignalFunc) gtk_combo_entry_key_press, combo);
699  gtk_signal_connect_after (GTK_OBJECT (combo->entry), "focus_out_event",
700                            (GtkSignalFunc) gtk_combo_entry_focus_out, combo);
701  combo->activate_id = gtk_signal_connect (GTK_OBJECT (combo->entry), "activate",
702                      (GtkSignalFunc) gtk_combo_activate, combo);
703  gtk_signal_connect_after (GTK_OBJECT (combo->button), "button_press_event",
704                            (GtkSignalFunc) gtk_combo_popup_button_press, combo);
705  /*gtk_signal_connect_after (GTK_OBJECT (combo->button), "button_release_event",
706    (GtkSignalFunc) gtk_combo_button_release, combo);*/
707  gtk_signal_connect (GTK_OBJECT (combo->button), "leave_notify_event",
708                      (GtkSignalFunc) gtk_combo_popup_button_leave, combo);
709  /*gtk_signal_connect(GTK_OBJECT(combo->button), "clicked",
710     (GtkSignalFunc)prelight_bug, combo); */
711
712  combo->popwin = gtk_window_new (GTK_WINDOW_POPUP);
713  gtk_widget_ref (combo->popwin);
714  gtk_window_set_policy (GTK_WINDOW (combo->popwin), 1, 1, 0);
[15780]715
716  gtk_signal_connect (GTK_OBJECT (combo->popwin), "key_press_event",
717                      GTK_SIGNAL_FUNC (gtk_combo_window_key_press), combo);
[14481]718 
719  gtk_widget_set_events (combo->popwin, GDK_KEY_PRESS_MASK);
720
721  event_box = gtk_event_box_new ();
722  gtk_container_add (GTK_CONTAINER (combo->popwin), event_box);
723  gtk_widget_show (event_box);
724
725  gtk_widget_realize (event_box);
726  cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
727  gdk_window_set_cursor (event_box->window, cursor);
728  gdk_cursor_destroy (cursor);
729
730  frame = gtk_frame_new (NULL);
731  gtk_container_add (GTK_CONTAINER (event_box), frame);
732  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
733  gtk_widget_show (frame);
734
735  combo->popup = gtk_scrolled_window_new (NULL, NULL);
736  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo->popup),
737                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
738  GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (combo->popup)->hscrollbar, GTK_CAN_FOCUS);
739  GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (combo->popup)->vscrollbar, GTK_CAN_FOCUS);
740  gtk_container_add (GTK_CONTAINER (frame), combo->popup);
741  gtk_widget_show (combo->popup);
742
743  combo->list = gtk_list_new ();
744  /* We'll use enter notify events to figure out when to transfer
745   * the grab to the list
746   */
747  gtk_widget_set_events (combo->list, GDK_ENTER_NOTIFY_MASK);
748
749  gtk_list_set_selection_mode(GTK_LIST(combo->list), GTK_SELECTION_BROWSE);
750  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (combo->popup), combo->list);
751  gtk_container_set_focus_vadjustment (GTK_CONTAINER (combo->list),
752                                       gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo->popup)));
753  gtk_container_set_focus_hadjustment (GTK_CONTAINER (combo->list),
754                                       gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo->popup)));
755  gtk_widget_show (combo->list);
756
757  combo->list_change_id = gtk_signal_connect (GTK_OBJECT (combo->list), "selection_changed",
758                             (GtkSignalFunc) gtk_combo_update_entry, combo);
759  gtk_signal_connect (GTK_OBJECT (combo->popwin), "key_press_event",
760                      (GtkSignalFunc) gtk_combo_list_key_press, combo);
761  gtk_signal_connect (GTK_OBJECT (combo->popwin), "button_press_event",
762                      GTK_SIGNAL_FUNC (gtk_combo_button_press), combo);
763
764  gtk_signal_connect_after (GTK_OBJECT (combo->list), "button_release_event",
765                            GTK_SIGNAL_FUNC (gtk_combo_button_release), combo);
766  /* We connect here on the button, because we'll have a grab on it
767   * when the event occurs. But we are actually interested in enters
768   * for the combo->list.
769   */
770  gtk_signal_connect (GTK_OBJECT (combo->button), "enter_notify_event",
771                      GTK_SIGNAL_FUNC (gtk_combo_list_enter), combo);
772}
773
774guint
775gtk_combo_get_type (void)
776{
777  static guint combo_type = 0;
778
779  if (!combo_type)
780    {
781      static const GtkTypeInfo combo_info =
782      {
783        "GtkCombo",
784        sizeof (GtkCombo),
785        sizeof (GtkComboClass),
786        (GtkClassInitFunc) gtk_combo_class_init,
787        (GtkObjectInitFunc) gtk_combo_init,
788        /* reserved_1 */ NULL,
789        /* reserved_2 */ NULL,
790        (GtkClassInitFunc) NULL,
791      };
792      combo_type = gtk_type_unique (gtk_hbox_get_type (), &combo_info);
793    }
794  return combo_type;
795}
796
797GtkWidget *
798gtk_combo_new (void)
799{
800  return GTK_WIDGET (gtk_type_new (gtk_combo_get_type ()));
801}
802
803void
804gtk_combo_set_value_in_list (GtkCombo * combo, gint val, gint ok_if_empty)
805{
806  g_return_if_fail (combo != NULL);
807  g_return_if_fail (GTK_IS_COMBO (combo));
808
809  combo->value_in_list = val;
810  combo->ok_if_empty = ok_if_empty;
811}
812
813void
814gtk_combo_set_case_sensitive (GtkCombo * combo, gint val)
815{
816  g_return_if_fail (combo != NULL);
817  g_return_if_fail (GTK_IS_COMBO (combo));
818
819  combo->case_sensitive = val;
820}
821
822void
823gtk_combo_set_use_arrows (GtkCombo * combo, gint val)
824{
825  g_return_if_fail (combo != NULL);
826  g_return_if_fail (GTK_IS_COMBO (combo));
827
828  combo->use_arrows = val;
829}
830
831void
832gtk_combo_set_use_arrows_always (GtkCombo * combo, gint val)
833{
834  g_return_if_fail (combo != NULL);
835  g_return_if_fail (GTK_IS_COMBO (combo));
836
837  combo->use_arrows_always = val;
838  combo->use_arrows = 1;
839}
840
841void
842gtk_combo_set_popdown_strings (GtkCombo * combo, GList * strings)
843{
844  GList *list;
845  GtkWidget *li;
846
847  g_return_if_fail (combo != NULL);
848  g_return_if_fail (GTK_IS_COMBO (combo));
849  g_return_if_fail (strings != NULL);
850
851  gtk_list_clear_items (GTK_LIST (combo->list), 0, -1);
852  list = strings;
853  while (list)
854    {
855      li = gtk_list_item_new_with_label ((gchar *) list->data);
856      gtk_widget_show (li);
857      gtk_container_add (GTK_CONTAINER (combo->list), li);
858      list = list->next;
859    }
860}
861
862static void
863gtk_combo_item_destroy (GtkObject * object)
864{
865  gchar *key;
866
867  key = gtk_object_get_data (object, gtk_combo_string_key);
868  if (key)
869    g_free (key);
870}
871
872void
873gtk_combo_set_item_string (GtkCombo * combo, GtkItem * item, const gchar * item_value)
874{
875  gchar *val;
876  gint connected = 0;
877
878  g_return_if_fail (combo != NULL);
879  g_return_if_fail (GTK_IS_COMBO (combo));
880  g_return_if_fail (item != NULL);
881
882  val = gtk_object_get_data (GTK_OBJECT (item), gtk_combo_string_key);
883  if (val)
884    {
885      g_free (val);
886      connected = 1;
887    }
888  if (item_value)
889    {
890      val = g_strdup(item_value);
891      gtk_object_set_data (GTK_OBJECT (item), gtk_combo_string_key, val);
892      if (!connected)
893        gtk_signal_connect (GTK_OBJECT (item), "destroy",
894                          (GtkSignalFunc) gtk_combo_item_destroy, val);
895    }
896  else
897    {
898      gtk_object_set_data (GTK_OBJECT (item), gtk_combo_string_key, NULL);
899      if (connected)
900        gtk_signal_disconnect_by_data(GTK_OBJECT (item), val);
901    }
902}
903
904static void
905gtk_combo_size_allocate (GtkWidget     *widget,
906                         GtkAllocation *allocation)
907{
908  GtkCombo *combo;
909
910  g_return_if_fail (widget != NULL);
911  g_return_if_fail (GTK_IS_COMBO (widget));
912  g_return_if_fail (allocation != NULL);
913
914  GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
915 
916  combo = GTK_COMBO (widget);
917
918  if (combo->entry->allocation.height > combo->entry->requisition.height)
919    {
920      GtkAllocation button_allocation;
921
922      button_allocation = combo->button->allocation;
923      button_allocation.height = combo->entry->requisition.height;
924      button_allocation.y = combo->entry->allocation.y +
925        (combo->entry->allocation.height - combo->entry->requisition.height)
926        / 2;
927      gtk_widget_size_allocate (combo->button, &button_allocation);
928    }
929}
930
931void
932gtk_combo_disable_activate (GtkCombo* combo)
933{
934  g_return_if_fail (combo != NULL);
935  g_return_if_fail (GTK_IS_COMBO (combo));
936
937  if ( combo->activate_id ) {
938    gtk_signal_disconnect(GTK_OBJECT(combo->entry), combo->activate_id);
939    combo->activate_id = 0;
940  }
941}
Note: See TracBrowser for help on using the repository browser.