1 /* /////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk/win_gtk.c 
   3 // Purpose:     Native GTK+ widget for wxWidgets, based on GtkLayout and 
   4 //              GtkFixed. It makes use of the gravity window property and 
   5 //              therefore does not work with GTK 1.0. 
   6 // Author:      Robert Roebling 
   8 // Copyright:   (c) 1998 Robert Roebling 
   9 // Licence:     wxWidgets licence 
  10 /////////////////////////////////////////////////////////////////////////// */ 
  13 #define XCheckIfEvent XCHECKIFEVENT 
  16 #include "wx/platform.h" 
  17 #include "wx/gtk/win_gtk.h" 
  21 #endif /* __cplusplus */ 
  23 typedef struct _GtkPizzaAdjData  GtkPizzaAdjData
; 
  25 struct _GtkPizzaAdjData
 
  31 static void gtk_pizza_class_init    (GtkPizzaClass    
*klass
); 
  32 static void gtk_pizza_init          (GtkPizza         
*pizza
); 
  34 static void gtk_pizza_realize       (GtkWidget        
*widget
); 
  35 static void gtk_pizza_unrealize     (GtkWidget        
*widget
); 
  37 static void gtk_pizza_map           (GtkWidget        
*widget
); 
  39 static void gtk_pizza_size_request  (GtkWidget        
*widget
, 
  40                                      GtkRequisition   
*requisition
); 
  41 static void gtk_pizza_size_allocate (GtkWidget        
*widget
, 
  42                                      GtkAllocation    
*allocation
); 
  43 static gint 
gtk_pizza_expose        (GtkWidget        
*widget
, 
  44                                      GdkEventExpose   
*event
); 
  45 static void gtk_pizza_style_set     (GtkWidget 
*widget
, 
  46                                      GtkStyle  
*previous_style
); 
  47 static void gtk_pizza_add           (GtkContainer     
*container
, 
  49 static void gtk_pizza_remove        (GtkContainer     
*container
, 
  51 static void gtk_pizza_forall        (GtkContainer     
*container
, 
  52                                      gboolean          include_internals
, 
  54                                      gpointer          callback_data
); 
  56 static void     gtk_pizza_allocate_child     (GtkPizza      
*pizza
, 
  57                                               GtkPizzaChild 
*child
); 
  58 static void     gtk_pizza_adjust_allocations_recurse (GtkWidget 
*widget
, 
  61 static GtkType 
gtk_pizza_child_type (GtkContainer     
*container
); 
  63 static void  gtk_pizza_scroll_set_adjustments (GtkPizza      
*pizza
, 
  68 GtkContainerClass 
*pizza_parent_class 
= NULL
; 
  73     static GtkType pizza_type 
= 0; 
  77         static const GTypeInfo pizza_info 
= 
  79             sizeof (GtkPizzaClass
), 
  81             NULL
,           /* base_finalize */ 
  82             (GClassInitFunc
) gtk_pizza_class_init
, 
  83             NULL
,           /* class_finalize */ 
  84             NULL
,           /* class_data */ 
  87             (GInstanceInitFunc
) gtk_pizza_init
, 
  89         pizza_type 
= g_type_register_static (GTK_TYPE_CONTAINER
, "GtkPizza", &pizza_info
, (GTypeFlags
)0); 
  95 /* Marshaller needed for set_scroll_adjustments signal, 
  96    generated with GLib-2.4.6 glib-genmarshal */ 
  97 #define g_marshal_value_peek_object(v)   g_value_get_object (v) 
  99 g_cclosure_user_marshal_VOID__OBJECT_OBJECT (GClosure     
*closure
, 
 100                                              GValue       
*return_value
, 
 101                                              guint         n_param_values
, 
 102                                              const GValue 
*param_values
, 
 103                                              gpointer      invocation_hint
, 
 104                                              gpointer      marshal_data
) 
 106   typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT
) (gpointer     data1
, 
 110   register GMarshalFunc_VOID__OBJECT_OBJECT callback
; 
 111   register GCClosure 
*cc 
= (GCClosure
*) closure
; 
 112   register gpointer data1
, data2
; 
 114   g_return_if_fail (n_param_values 
== 3); 
 116   if (G_CCLOSURE_SWAP_DATA (closure
)) 
 118       data1 
= closure
->data
; 
 119       data2 
= g_value_peek_pointer (param_values 
+ 0); 
 123       data1 
= g_value_peek_pointer (param_values 
+ 0); 
 124       data2 
= closure
->data
; 
 126   callback 
= (GMarshalFunc_VOID__OBJECT_OBJECT
) (marshal_data 
? marshal_data 
: cc
->callback
); 
 129             g_marshal_value_peek_object (param_values 
+ 1), 
 130             g_marshal_value_peek_object (param_values 
+ 2), 
 135 gtk_pizza_class_init (GtkPizzaClass 
*klass
) 
 137     GtkObjectClass 
*object_class
; 
 138     GtkWidgetClass 
*widget_class
; 
 139     GtkContainerClass 
*container_class
; 
 141     object_class 
= (GtkObjectClass
*) klass
; 
 142     widget_class 
= (GtkWidgetClass
*) klass
; 
 143     container_class 
= (GtkContainerClass
*) klass
; 
 144     pizza_parent_class 
= gtk_type_class (GTK_TYPE_CONTAINER
); 
 146     widget_class
->map 
= gtk_pizza_map
; 
 147     widget_class
->realize 
= gtk_pizza_realize
; 
 148     widget_class
->unrealize 
= gtk_pizza_unrealize
; 
 149     widget_class
->size_request 
= gtk_pizza_size_request
; 
 150     widget_class
->size_allocate 
= gtk_pizza_size_allocate
; 
 151     widget_class
->expose_event 
= gtk_pizza_expose
; 
 152     widget_class
->style_set 
= gtk_pizza_style_set
; 
 154     container_class
->add 
= gtk_pizza_add
; 
 155     container_class
->remove 
= gtk_pizza_remove
; 
 156     container_class
->forall 
= gtk_pizza_forall
; 
 158     container_class
->child_type 
= gtk_pizza_child_type
; 
 160     klass
->set_scroll_adjustments 
= gtk_pizza_scroll_set_adjustments
; 
 162     widget_class
->set_scroll_adjustments_signal 
= 
 164             "set_scroll_adjustments", 
 165             G_TYPE_FROM_CLASS(object_class
), 
 167             G_STRUCT_OFFSET(GtkPizzaClass
, set_scroll_adjustments
), 
 170             g_cclosure_user_marshal_VOID__OBJECT_OBJECT
, 
 174             GTK_TYPE_ADJUSTMENT
); 
 178 gtk_pizza_child_type (GtkContainer     
*container
) 
 180     return GTK_TYPE_WIDGET
; 
 184 gtk_pizza_init (GtkPizza 
*pizza
) 
 186     GTK_WIDGET_UNSET_FLAGS (pizza
, GTK_NO_WINDOW
); 
 188     pizza
->shadow_type 
= GTK_MYSHADOW_NONE
; 
 190     pizza
->children 
= NULL
; 
 195     pizza
->bin_window 
= NULL
; 
 203     pizza
->external_expose 
= FALSE
; 
 211     pizza 
= g_object_new (gtk_pizza_get_type (), NULL
); 
 213     return GTK_WIDGET (pizza
); 
 217 gtk_pizza_scroll_set_adjustments (GtkPizza     
*pizza
, 
 221    /* We handle scrolling in the wxScrolledWindow, not here. */ 
 225 gtk_pizza_set_shadow_type (GtkPizza        
*pizza
, 
 226                            GtkMyShadowType  type
) 
 228     g_return_if_fail (pizza 
!= NULL
); 
 229     g_return_if_fail (GTK_IS_PIZZA (pizza
)); 
 231     if (pizza
->shadow_type 
!= type
) 
 233         pizza
->shadow_type 
= type
; 
 235         if (GTK_WIDGET_VISIBLE (pizza
)) 
 237             gtk_widget_size_allocate (GTK_WIDGET (pizza
), &(GTK_WIDGET (pizza
)->allocation
)); 
 238             gtk_widget_queue_draw (GTK_WIDGET (pizza
)); 
 244 gtk_pizza_set_external (GtkPizza  
*pizza
, 
 247     g_return_if_fail (pizza 
!= NULL
); 
 248     g_return_if_fail (GTK_IS_PIZZA (pizza
)); 
 250     pizza
->external_expose 
= expose
; 
 254 gtk_pizza_put (GtkPizza   
*pizza
, 
 261     GtkPizzaChild 
*child_info
; 
 263     g_return_if_fail (pizza 
!= NULL
); 
 264     g_return_if_fail (GTK_IS_PIZZA (pizza
)); 
 265     g_return_if_fail (widget 
!= NULL
); 
 267     child_info 
= g_new (GtkPizzaChild
, 1); 
 269     child_info
->widget 
= widget
; 
 272     child_info
->width 
= width
; 
 273     child_info
->height 
= height
; 
 275     pizza
->children 
= g_list_append (pizza
->children
, child_info
); 
 277     if (GTK_WIDGET_REALIZED (pizza
)) 
 278       gtk_widget_set_parent_window (widget
, pizza
->bin_window
); 
 280     gtk_widget_set_parent (widget
, GTK_WIDGET (pizza
)); 
 282     gtk_widget_set_size_request (widget
, width
, height
); 
 286 gtk_pizza_set_size (GtkPizza   
*pizza
, 
 293     GtkPizzaChild 
*child
; 
 296     g_return_if_fail (pizza 
!= NULL
); 
 297     g_return_if_fail (GTK_IS_PIZZA (pizza
)); 
 298     g_return_if_fail (widget 
!= NULL
); 
 300 #ifndef WX_WARN_ILLEGAL_SETSIZE 
 301     /* this really shouldn't happen -- but it does, a lot, right now and we 
 302        can't pass negative values to gtk_widget_set_size_request() without getting 
 303        a warning printed out, so filter them out here */ 
 310     children 
= pizza
->children
; 
 313         child 
= children
->data
; 
 314         children 
= children
->next
; 
 316         if (child
->widget 
== widget
) 
 318             if ((child
->x 
== x
) && 
 320                 (child
->width 
== width
) && 
 321                 (child
->height 
== height
)) return; 
 325             child
->width 
= width
; 
 326             child
->height 
= height
; 
 328             gtk_widget_set_size_request (widget
, width
, height
); 
 330             if (GTK_WIDGET_VISIBLE (widget
) && GTK_WIDGET_VISIBLE (pizza
)) 
 331                 gtk_widget_queue_resize (widget
); 
 339 gtk_pizza_map (GtkWidget 
*widget
) 
 342     GtkPizzaChild 
*child
; 
 345     g_return_if_fail (widget 
!= NULL
); 
 346     g_return_if_fail (GTK_IS_PIZZA (widget
)); 
 348     GTK_WIDGET_SET_FLAGS (widget
, GTK_MAPPED
); 
 349     pizza 
= GTK_PIZZA (widget
); 
 351     children 
= pizza
->children
; 
 354         child 
= children
->data
; 
 355         children 
= children
->next
; 
 357         if ( GTK_WIDGET_VISIBLE (child
->widget
) && 
 358             !GTK_WIDGET_MAPPED (child
->widget
) ) 
 360             gtk_widget_map (child
->widget
); 
 364     gdk_window_show (widget
->window
); 
 365     gdk_window_show (pizza
->bin_window
); 
 369 gtk_pizza_realize (GtkWidget 
*widget
) 
 372     GdkWindowAttr attributes
; 
 373     gint attributes_mask
; 
 374     GtkPizzaChild 
*child
; 
 377     g_return_if_fail (widget 
!= NULL
); 
 378     g_return_if_fail (GTK_IS_PIZZA (widget
)); 
 380     pizza 
= GTK_PIZZA (widget
); 
 381     GTK_WIDGET_SET_FLAGS (widget
, GTK_REALIZED
); 
 383     attributes
.window_type 
= GDK_WINDOW_CHILD
; 
 385     attributes
.x 
= widget
->allocation
.x
; 
 386     attributes
.y 
= widget
->allocation
.y
; 
 387     attributes
.width 
= widget
->allocation
.width
; 
 388     attributes
.height 
= widget
->allocation
.height
; 
 390 #ifndef __WXUNIVERSAL__ 
 391     if (pizza
->shadow_type 
== GTK_MYSHADOW_NONE
) 
 393         /* no border, no changes to sizes */ 
 395     else if (pizza
->shadow_type 
== GTK_MYSHADOW_THIN
) 
 397         /* GTK_MYSHADOW_THIN == wxSIMPLE_BORDER */ 
 400         attributes
.width 
-= 2; 
 401         attributes
.height 
-= 2; 
 405         /* GTK_MYSHADOW_IN == wxSUNKEN_BORDER */ 
 406         /* GTK_MYSHADOW_OUT == wxRAISED_BORDER */ 
 409         attributes
.width 
-= 4; 
 410         attributes
.height 
-= 4; 
 412 #endif /* __WXUNIVERSAL__ */ 
 415     if (attributes
.width 
< 2) attributes
.width 
= 2; 
 416     if (attributes
.height 
< 2) attributes
.height 
= 2; 
 418     attributes
.wclass 
= GDK_INPUT_OUTPUT
; 
 419     attributes
.visual 
= gtk_widget_get_visual (widget
); 
 420     attributes
.colormap 
= gtk_widget_get_colormap (widget
); 
 421     attributes
.event_mask 
= GDK_VISIBILITY_NOTIFY_MASK
; 
 422     attributes_mask 
= GDK_WA_X 
| GDK_WA_Y 
| GDK_WA_VISUAL 
| GDK_WA_COLORMAP
; 
 424     widget
->window 
= gdk_window_new(gtk_widget_get_parent_window (widget
), 
 425                                      &attributes
, attributes_mask
); 
 426     gdk_window_set_user_data (widget
->window
, widget
); 
 431     attributes
.event_mask 
= gtk_widget_get_events (widget
); 
 432     attributes
.event_mask 
|= GDK_EXPOSURE_MASK              
| 
 434                              GDK_POINTER_MOTION_MASK        
| 
 435                              GDK_POINTER_MOTION_HINT_MASK   
| 
 436                              GDK_BUTTON_MOTION_MASK         
| 
 437                              GDK_BUTTON1_MOTION_MASK        
| 
 438                              GDK_BUTTON2_MOTION_MASK        
| 
 439                              GDK_BUTTON3_MOTION_MASK        
| 
 440                              GDK_BUTTON_PRESS_MASK          
| 
 441                              GDK_BUTTON_RELEASE_MASK        
| 
 443                              GDK_KEY_RELEASE_MASK           
| 
 444                              GDK_ENTER_NOTIFY_MASK          
| 
 445                              GDK_LEAVE_NOTIFY_MASK          
| 
 446                              GDK_FOCUS_CHANGE_MASK
; 
 448     pizza
->bin_window 
= gdk_window_new(widget
->window
, 
 449                                           &attributes
, attributes_mask
); 
 450     gdk_window_set_user_data (pizza
->bin_window
, widget
); 
 452     widget
->style 
= gtk_style_attach (widget
->style
, widget
->window
); 
 453     gtk_style_set_background (widget
->style
, widget
->window
, GTK_STATE_NORMAL
); 
 454     gtk_style_set_background (widget
->style
, pizza
->bin_window
, GTK_STATE_NORMAL 
); 
 457     gdk_window_set_back_pixmap( widget->window, NULL, FALSE ); 
 458     gdk_window_set_back_pixmap( pizza->bin_window, NULL, FALSE ); 
 461     /* cannot be done before realisation */ 
 462     children 
= pizza
->children
; 
 465         child 
= children
->data
; 
 466         children 
= children
->next
; 
 468         gtk_widget_set_parent_window (child
->widget
, pizza
->bin_window
); 
 473 gtk_pizza_unrealize (GtkWidget 
*widget
) 
 477     g_return_if_fail (widget 
!= NULL
); 
 478     g_return_if_fail (GTK_IS_PIZZA (widget
)); 
 480     pizza 
= GTK_PIZZA (widget
); 
 482     gdk_window_set_user_data (pizza
->bin_window
, NULL
); 
 483     gdk_window_destroy (pizza
->bin_window
); 
 484     pizza
->bin_window 
= NULL
; 
 486     if (GTK_WIDGET_CLASS (pizza_parent_class
)->unrealize
) 
 487        (* GTK_WIDGET_CLASS (pizza_parent_class
)->unrealize
) (widget
); 
 491 gtk_pizza_size_request (GtkWidget      
*widget
, 
 492                         GtkRequisition 
*requisition
) 
 495     GtkPizzaChild 
*child
; 
 497     GtkRequisition child_requisition
; 
 499     g_return_if_fail (widget 
!= NULL
); 
 500     g_return_if_fail (GTK_IS_PIZZA (widget
)); 
 501     g_return_if_fail (requisition 
!= NULL
); 
 503     pizza 
= GTK_PIZZA (widget
); 
 505     children 
= pizza
->children
; 
 508         child 
= children
->data
; 
 509         children 
= children
->next
; 
 511         if (GTK_WIDGET_VISIBLE (child
->widget
)) 
 513             gtk_widget_size_request (child
->widget
, &child_requisition
); 
 517     /* request very little, I'm not sure if requesting nothing 
 518        will always have positive effects on stability... */ 
 519     requisition
->width 
= 2; 
 520     requisition
->height 
= 2; 
 524 gtk_pizza_size_allocate (GtkWidget     
*widget
, 
 525                          GtkAllocation 
*allocation
) 
 530     GtkPizzaChild 
*child
; 
 533     g_return_if_fail (widget 
!= NULL
); 
 534     g_return_if_fail (GTK_IS_PIZZA(widget
)); 
 535     g_return_if_fail (allocation 
!= NULL
); 
 537     pizza 
= GTK_PIZZA (widget
); 
 539     widget
->allocation 
= *allocation
; 
 541     if (pizza
->shadow_type 
== GTK_MYSHADOW_NONE
) 
 544     if (pizza
->shadow_type 
== GTK_MYSHADOW_THIN
) 
 549     x 
= allocation
->x 
+ border
; 
 550     y 
= allocation
->y 
+ border
; 
 551     w 
= allocation
->width 
- border
*2; 
 552     h 
= allocation
->height 
- border
*2; 
 554     if (GTK_WIDGET_REALIZED (widget
)) 
 556         gdk_window_move_resize( widget
->window
, x
, y
, w
, h 
); 
 557         gdk_window_move_resize( pizza
->bin_window
, 0, 0, w
, h 
); 
 560     children 
= pizza
->children
; 
 563         child 
= children
->data
; 
 564         children 
= children
->next
; 
 566         gtk_pizza_allocate_child (pizza
, child
); 
 571 gtk_pizza_expose (GtkWidget      
*widget
, 
 572                   GdkEventExpose 
*event
) 
 576     g_return_val_if_fail (widget 
!= NULL
, FALSE
); 
 577     g_return_val_if_fail (GTK_IS_PIZZA (widget
), FALSE
); 
 578     g_return_val_if_fail (event 
!= NULL
, FALSE
); 
 580     pizza 
= GTK_PIZZA (widget
); 
 582     if (event
->window 
!= pizza
->bin_window
) 
 585     /* We handle all expose events in window.cpp now. */ 
 586     if (pizza
->external_expose
) 
 589     (* GTK_WIDGET_CLASS (pizza_parent_class
)->expose_event
) (widget
, event
); 
 595 gtk_pizza_style_set(GtkWidget 
*widget
, GtkStyle  
*previous_style
) 
 597     if (GTK_WIDGET_REALIZED(widget
)) 
 599         gtk_style_set_background(widget
->style
, widget
->window
, GTK_STATE_NORMAL
); 
 600         gtk_style_set_background(widget
->style
, GTK_PIZZA(widget
)->bin_window
, GTK_STATE_NORMAL 
); 
 603     (* GTK_WIDGET_CLASS (pizza_parent_class
)->style_set
) (widget
, previous_style
); 
 607 gtk_pizza_add (GtkContainer 
*container
, 
 610     g_return_if_fail (container 
!= NULL
); 
 611     g_return_if_fail (GTK_IS_PIZZA (container
)); 
 612     g_return_if_fail (widget 
!= NULL
); 
 614     gtk_pizza_put (GTK_PIZZA (container
), widget
, 0, 0, 20, 20 ); 
 618 gtk_pizza_remove (GtkContainer 
*container
, 
 622     GtkPizzaChild 
*child
; 
 625     g_return_if_fail (container 
!= NULL
); 
 626     g_return_if_fail (GTK_IS_PIZZA (container
)); 
 627     g_return_if_fail (widget 
!= NULL
); 
 629     pizza 
= GTK_PIZZA (container
); 
 631     children 
= pizza
->children
; 
 634         child 
= children
->data
; 
 636         if (child
->widget 
== widget
) 
 638             gtk_widget_unparent (widget
); 
 640             /* security checks */ 
 641             g_return_if_fail (GTK_IS_WIDGET (widget
)); 
 643             pizza
->children 
= g_list_remove_link (pizza
->children
, children
); 
 644             g_list_free (children
); 
 647             /* security checks */ 
 648             g_return_if_fail (GTK_IS_WIDGET (widget
)); 
 653         children 
= children
->next
; 
 658 gtk_pizza_forall (GtkContainer 
*container
, 
 659                   gboolean      include_internals
, 
 660                   GtkCallback   callback
, 
 661                   gpointer      callback_data
) 
 664     GtkPizzaChild 
*child
; 
 667     g_return_if_fail (container 
!= NULL
); 
 668     g_return_if_fail (GTK_IS_PIZZA (container
)); 
 669     g_return_if_fail (callback 
!= (GtkCallback
)NULL
); 
 671     pizza 
= GTK_PIZZA (container
); 
 673     children 
= pizza
->children
; 
 676         child 
= children
->data
; 
 677         children 
= children
->next
; 
 679         (* callback
) (child
->widget
, callback_data
); 
 684 gtk_pizza_allocate_child (GtkPizza      
*pizza
, 
 685                           GtkPizzaChild 
*child
) 
 687     GtkAllocation allocation
; 
 688     GtkRequisition requisition
; 
 690     allocation
.x 
= child
->x 
- pizza
->xoffset
; 
 691     allocation
.y 
= child
->y 
- pizza
->yoffset
; 
 692     gtk_widget_get_child_requisition (child
->widget
, &requisition
); 
 693     allocation
.width 
= requisition
.width
; 
 694     allocation
.height 
= requisition
.height
; 
 696     gtk_widget_size_allocate (child
->widget
, &allocation
); 
 700 gtk_pizza_adjust_allocations_recurse (GtkWidget 
*widget
, 
 703     GtkPizzaAdjData 
*data 
= cb_data
; 
 705     widget
->allocation
.x 
+= data
->dx
; 
 706     widget
->allocation
.y 
+= data
->dy
; 
 708     if (GTK_WIDGET_NO_WINDOW (widget
) && GTK_IS_CONTAINER (widget
)) 
 710         gtk_container_forall (GTK_CONTAINER (widget
), 
 711                           gtk_pizza_adjust_allocations_recurse
, 
 717 gtk_pizza_adjust_allocations (GtkPizza 
*pizza
, 
 722     GtkPizzaAdjData data
; 
 727     tmp_list 
= pizza
->children
; 
 730         GtkPizzaChild 
*child 
= tmp_list
->data
; 
 731         tmp_list 
= tmp_list
->next
; 
 733         child
->widget
->allocation
.x 
+= dx
; 
 734         child
->widget
->allocation
.y 
+= dy
; 
 736         if (GTK_WIDGET_NO_WINDOW (child
->widget
) && 
 737             GTK_IS_CONTAINER (child
->widget
)) 
 739             gtk_container_forall (GTK_CONTAINER (child
->widget
), 
 740                                   gtk_pizza_adjust_allocations_recurse
, 
 747 gtk_pizza_scroll (GtkPizza 
*pizza
, gint dx
, gint dy
) 
 749     pizza
->xoffset 
+= dx
; 
 750     pizza
->yoffset 
+= dy
; 
 752     gtk_pizza_adjust_allocations (pizza
, -dx
, -dy
); 
 754     if (pizza
->bin_window
) 
 755         gdk_window_scroll( pizza
->bin_window
, -dx
, -dy 
); 
 760 #endif /* __cplusplus */