2015-09-04 13:51:10 -05:00
// 4 september 2015
2015-10-08 17:24:09 -05:00
# include "uipriv_unix.h"
2015-12-17 15:31:56 -06:00
// notes:
// - G_DECLARE_DERIVABLE/FINAL_INTERFACE() requires glib 2.44 and that's starting with debian stretch (testing) (GTK+ 3.18) and ubuntu 15.04 (GTK+ 3.14) - debian jessie has 2.42 (GTK+ 3.14)
2015-10-08 17:24:09 -05:00
# define areaWidgetType (areaWidget_get_type())
# define areaWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), areaWidgetType, areaWidget))
# define isAreaWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), areaWidgetType))
# define areaWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), areaWidgetType, areaWidgetClass))
# define isAreaWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), areaWidget))
# define getAreaWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), areaWidgetType, areaWidgetClass))
typedef struct areaWidget areaWidget ;
typedef struct areaWidgetClass areaWidgetClass ;
struct areaWidget {
GtkDrawingArea parent_instance ;
2015-12-17 15:49:21 -06:00
uiArea * a ;
2015-12-17 18:59:33 -06:00
// construct-only parameters aare not set until after the init() function has returned
// we need this particular object available during init(), so put it here instead of in uiArea
// keep a pointer in uiArea for convenience, though
2018-04-15 21:26:51 -05:00
uiprivClickCounter cc ;
2015-10-08 17:24:09 -05:00
} ;
struct areaWidgetClass {
GtkDrawingAreaClass parent_class ;
} ;
struct uiArea {
uiUnixControl c ;
2015-12-17 16:37:58 -06:00
GtkWidget * widget ; // either swidget or areaWidget depending on whether it is scrolling
GtkWidget * swidget ;
2015-10-09 09:09:55 -05:00
GtkContainer * scontainer ;
GtkScrolledWindow * sw ;
2015-12-17 16:37:58 -06:00
2015-10-09 09:09:55 -05:00
GtkWidget * areaWidget ;
2015-10-08 17:24:09 -05:00
GtkDrawingArea * drawingArea ;
areaWidget * area ;
2015-09-04 13:51:10 -05:00
uiAreaHandler * ah ;
2015-12-17 16:37:58 -06:00
gboolean scrolling ;
2016-06-13 20:05:40 -05:00
int scrollWidth ;
int scrollHeight ;
2015-12-17 13:48:58 -06:00
2015-12-17 18:59:33 -06:00
// note that this is a pointer; see above
2018-04-15 21:26:51 -05:00
uiprivClickCounter * cc ;
2016-10-24 23:34:12 -05:00
// for user window drags
GdkEventButton * dragevent ;
2015-09-04 13:51:10 -05:00
} ;
2015-12-17 13:48:58 -06:00
G_DEFINE_TYPE ( areaWidget , areaWidget , GTK_TYPE_DRAWING_AREA )
2015-09-04 13:51:10 -05:00
2015-12-17 15:49:21 -06:00
static void areaWidget_init ( areaWidget * aw )
2015-09-04 13:51:10 -05:00
{
// for events
2015-12-17 15:49:21 -06:00
gtk_widget_add_events ( GTK_WIDGET ( aw ) ,
2015-09-04 13:51:10 -05:00
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_MOTION_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_KEY_PRESS_MASK |
2015-12-17 18:59:33 -06:00
GDK_KEY_RELEASE_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK ) ;
2015-09-04 13:51:10 -05:00
2015-12-17 15:49:21 -06:00
gtk_widget_set_can_focus ( GTK_WIDGET ( aw ) , TRUE ) ;
2015-09-11 11:38:37 -05:00
2018-04-15 21:26:51 -05:00
uiprivClickCounterReset ( & ( aw - > cc ) ) ;
2015-09-04 13:51:10 -05:00
}
static void areaWidget_dispose ( GObject * obj )
{
G_OBJECT_CLASS ( areaWidget_parent_class ) - > dispose ( obj ) ;
}
static void areaWidget_finalize ( GObject * obj )
{
G_OBJECT_CLASS ( areaWidget_parent_class ) - > finalize ( obj ) ;
}
static void areaWidget_size_allocate ( GtkWidget * w , GtkAllocation * allocation )
{
2015-12-17 15:49:21 -06:00
areaWidget * aw = areaWidget ( w ) ;
uiArea * a = aw - > a ;
2015-09-04 13:51:10 -05:00
// GtkDrawingArea has a size_allocate() implementation; we need to call it
// this will call gtk_widget_set_allocation() for us
GTK_WIDGET_CLASS ( areaWidget_parent_class ) - > size_allocate ( w , allocation ) ;
2015-12-17 15:49:21 -06:00
if ( ! a - > scrolling )
// we must redraw everything on resize because Windows requires it
2017-06-08 14:31:28 -05:00
// TODO https://developer.gnome.org/gtk3/3.10/GtkWidget.html#gtk-widget-set-redraw-on-allocate ?
// TODO drop this rule; it was stupid and documenting this was stupid — let platforms where it matters do it on their own
// TODO or do we not, for parity of performance?
2015-12-17 15:49:21 -06:00
gtk_widget_queue_resize ( w ) ;
2015-09-04 13:51:10 -05:00
}
2015-12-17 16:37:58 -06:00
static void loadAreaSize ( uiArea * a , double * width , double * height )
{
GtkAllocation allocation ;
* width = 0 ;
* height = 0 ;
// don't provide size information for scrolling areas
if ( ! a - > scrolling ) {
gtk_widget_get_allocation ( a - > areaWidget , & allocation ) ;
// these are already in drawing space coordinates
// for drawing, the size of drawing space has the same value as the widget allocation
// thanks to tristan in irc.gimp.net/#gtk+
* width = allocation . width ;
* height = allocation . height ;
}
}
2015-09-04 20:50:49 -05:00
static gboolean areaWidget_draw ( GtkWidget * w , cairo_t * cr )
{
2015-12-17 15:49:21 -06:00
areaWidget * aw = areaWidget ( w ) ;
uiArea * a = aw - > a ;
2015-09-06 15:20:37 -05:00
uiAreaDrawParams dp ;
double clipX0 , clipY0 , clipX1 , clipY1 ;
2018-05-12 12:14:39 -05:00
dp . Context = uiprivNewContext ( cr ,
2017-02-10 15:16:43 -06:00
gtk_widget_get_style_context ( a - > widget ) ) ;
2015-09-06 15:20:37 -05:00
2015-12-17 16:37:58 -06:00
loadAreaSize ( a , & ( dp . AreaWidth ) , & ( dp . AreaHeight ) ) ;
2015-09-06 15:20:37 -05:00
cairo_clip_extents ( cr , & clipX0 , & clipY0 , & clipX1 , & clipY1 ) ;
dp . ClipX = clipX0 ;
dp . ClipY = clipY0 ;
dp . ClipWidth = clipX1 - clipX0 ;
dp . ClipHeight = clipY1 - clipY0 ;
2015-10-11 11:36:48 -05:00
// no need to save or restore the graphics state to reset transformations; GTK+ does that for us
2015-12-17 15:49:21 -06:00
( * ( a - > ah - > Draw ) ) ( a - > ah , a , & dp ) ;
2015-09-04 20:50:49 -05:00
2018-05-12 12:14:39 -05:00
uiprivFreeContext ( dp . Context ) ;
2015-09-04 20:50:49 -05:00
return FALSE ;
}
2015-09-04 13:51:10 -05:00
2015-12-17 18:16:30 -06:00
// to do this properly for scrolling areas, we need to
// - return the same value for min and nat
// - call gtk_widget_queue_resize() when the size changes
// thanks to Company in irc.gimp.net/#gtk+
static void areaWidget_get_preferred_height ( GtkWidget * w , gint * min , gint * nat )
{
areaWidget * aw = areaWidget ( w ) ;
uiArea * a = aw - > a ;
// always chain up just in case
GTK_WIDGET_CLASS ( areaWidget_parent_class ) - > get_preferred_height ( w , min , nat ) ;
if ( a - > scrolling ) {
* min = a - > scrollHeight ;
* nat = a - > scrollHeight ;
}
}
static void areaWidget_get_preferred_width ( GtkWidget * w , gint * min , gint * nat )
{
areaWidget * aw = areaWidget ( w ) ;
uiArea * a = aw - > a ;
// always chain up just in case
GTK_WIDGET_CLASS ( areaWidget_parent_class ) - > get_preferred_width ( w , min , nat ) ;
if ( a - > scrolling ) {
* min = a - > scrollWidth ;
* nat = a - > scrollWidth ;
}
}
2015-09-04 13:51:10 -05:00
2015-09-11 11:38:37 -05:00
static guint translateModifiers ( guint state , GdkWindow * window )
{
GdkModifierType statetype ;
// GDK doesn't initialize the modifier flags fully; we have to explicitly tell it to (thanks to Daniel_S and daniels (two different people) in irc.gimp.net/#gtk+)
statetype = state ;
gdk_keymap_add_virtual_modifiers (
gdk_keymap_get_for_display ( gdk_window_get_display ( window ) ) ,
& statetype ) ;
return statetype ;
}
static uiModifiers toModifiers ( guint state )
{
uiModifiers m ;
m = 0 ;
2015-09-11 12:07:27 -05:00
if ( ( state & GDK_CONTROL_MASK ) ! = 0 )
2015-09-11 11:38:37 -05:00
m | = uiModifierCtrl ;
if ( ( state & GDK_META_MASK ) ! = 0 )
m | = uiModifierAlt ;
if ( ( state & GDK_MOD1_MASK ) ! = 0 ) // GTK+ itself requires this to be Alt (just read through gtkaccelgroup.c)
m | = uiModifierAlt ;
if ( ( state & GDK_SHIFT_MASK ) ! = 0 )
m | = uiModifierShift ;
if ( ( state & GDK_SUPER_MASK ) ! = 0 )
m | = uiModifierSuper ;
return m ;
}
2015-09-11 20:24:39 -05:00
// capture on drag is done automatically on GTK+
2015-12-17 15:49:21 -06:00
static void finishMouseEvent ( uiArea * a , uiAreaMouseEvent * me , guint mb , gdouble x , gdouble y , guint state , GdkWindow * window )
2015-09-11 11:38:37 -05:00
{
// on GTK+, mouse buttons 4-7 are for scrolling; if we got here, that's a mistake
if ( mb > = 4 & & mb < = 7 )
return ;
// if the button ID >= 8, continue counting from 4, as in the MouseEvent spec
if ( me - > Down > = 8 )
me - > Down - = 4 ;
if ( me - > Up > = 8 )
me - > Up - = 4 ;
state = translateModifiers ( state , window ) ;
me - > Modifiers = toModifiers ( state ) ;
// the mb != # checks exclude the Up/Down button from Held
me - > Held1To64 = 0 ;
if ( mb ! = 1 & & ( state & GDK_BUTTON1_MASK ) ! = 0 )
me - > Held1To64 | = 1 < < 0 ;
if ( mb ! = 2 & & ( state & GDK_BUTTON2_MASK ) ! = 0 )
me - > Held1To64 | = 1 < < 1 ;
if ( mb ! = 3 & & ( state & GDK_BUTTON3_MASK ) ! = 0 )
me - > Held1To64 | = 1 < < 2 ;
// don't check GDK_BUTTON4_MASK or GDK_BUTTON5_MASK because those are for the scrolling buttons mentioned above
// GDK expressly does not support any more buttons in the GdkModifierType; see https://git.gnome.org/browse/gtk+/tree/gdk/x11/gdkdevice-xi2.c#n763 (thanks mclasen in irc.gimp.net/#gtk+)
2015-10-14 07:49:06 -05:00
// these are already in drawing space coordinates
// the size of drawing space has the same value as the widget allocation
// thanks to tristan in irc.gimp.net/#gtk+
2015-09-11 11:38:37 -05:00
me - > X = x ;
me - > Y = y ;
2015-09-11 20:24:39 -05:00
2015-12-17 16:37:58 -06:00
loadAreaSize ( a , & ( me - > AreaWidth ) , & ( me - > AreaHeight ) ) ;
2015-09-11 11:38:37 -05:00
2015-12-17 15:49:21 -06:00
( * ( a - > ah - > MouseEvent ) ) ( a - > ah , a , me ) ;
2015-09-11 11:38:37 -05:00
}
static gboolean areaWidget_button_press_event ( GtkWidget * w , GdkEventButton * e )
{
2015-12-17 15:49:21 -06:00
areaWidget * aw = areaWidget ( w ) ;
uiArea * a = aw - > a ;
2015-09-11 11:38:37 -05:00
gint maxTime , maxDistance ;
GtkSettings * settings ;
uiAreaMouseEvent me ;
// clicking doesn't automatically transfer keyboard focus; we must do so manually (thanks tristan in irc.gimp.net/#gtk+)
gtk_widget_grab_focus ( w ) ;
// we handle multiple clicks ourselves here, in the same way as we do on Windows
if ( e - > type ! = GDK_BUTTON_PRESS )
// ignore GDK's generated double-clicks and beyond
return GDK_EVENT_PROPAGATE ;
settings = gtk_widget_get_settings ( w ) ;
g_object_get ( settings ,
" gtk-double-click-time " , & maxTime ,
" gtk-double-click-distance " , & maxDistance ,
NULL ) ;
2016-05-05 16:58:26 -05:00
// don't unref settings; it's transfer-none (thanks gregier in irc.gimp.net/#gtk+)
2016-06-13 20:37:50 -05:00
// e->time is guint32
// e->x and e->y are floating-point; just make them 32-bit integers
// maxTime and maxDistance... are gint, which *should* fit, hopefully...
2018-04-15 21:26:51 -05:00
me . Count = uiprivClickCounterClick ( a - > cc , me . Down ,
2015-09-11 11:38:37 -05:00
e - > x , e - > y ,
e - > time , maxTime ,
maxDistance , maxDistance ) ;
me . Down = e - > button ;
me . Up = 0 ;
2016-10-24 23:34:12 -05:00
// and set things up for window drags
a - > dragevent = e ;
2015-12-17 15:49:21 -06:00
finishMouseEvent ( a , & me , e - > button , e - > x , e - > y , e - > state , e - > window ) ;
2016-10-24 23:34:12 -05:00
a - > dragevent = NULL ;
2015-09-11 11:38:37 -05:00
return GDK_EVENT_PROPAGATE ;
}
static gboolean areaWidget_button_release_event ( GtkWidget * w , GdkEventButton * e )
{
2015-12-17 15:49:21 -06:00
areaWidget * aw = areaWidget ( w ) ;
uiArea * a = aw - > a ;
2015-09-11 11:38:37 -05:00
uiAreaMouseEvent me ;
me . Down = 0 ;
me . Up = e - > button ;
me . Count = 0 ;
2015-12-17 15:49:21 -06:00
finishMouseEvent ( a , & me , e - > button , e - > x , e - > y , e - > state , e - > window ) ;
2015-09-11 11:38:37 -05:00
return GDK_EVENT_PROPAGATE ;
}
static gboolean areaWidget_motion_notify_event ( GtkWidget * w , GdkEventMotion * e )
{
2015-12-17 15:49:21 -06:00
areaWidget * aw = areaWidget ( w ) ;
uiArea * a = aw - > a ;
2015-09-11 11:38:37 -05:00
uiAreaMouseEvent me ;
me . Down = 0 ;
me . Up = 0 ;
me . Count = 0 ;
2015-12-17 15:49:21 -06:00
finishMouseEvent ( a , & me , 0 , e - > x , e - > y , e - > state , e - > window ) ;
2015-09-11 11:38:37 -05:00
return GDK_EVENT_PROPAGATE ;
}
// we want switching away from the control to reset the double-click counter, like with WM_ACTIVATE on Windows
2015-12-17 15:49:21 -06:00
// according to tristan in irc.gimp.net/#gtk+, doing this on both enter-notify-event and leave-notify-event is correct (and it seems to be true in my own tests; plus the events DO get sent when switching programs with the keyboard (just pointing that out))
static gboolean onCrossing ( areaWidget * aw , int left )
2015-09-11 11:38:37 -05:00
{
2015-12-17 15:49:21 -06:00
uiArea * a = aw - > a ;
2015-09-11 11:38:37 -05:00
2015-12-17 18:19:08 -06:00
( * ( a - > ah - > MouseCrossed ) ) ( a - > ah , a , left ) ;
2018-04-15 21:26:51 -05:00
uiprivClickCounterReset ( a - > cc ) ;
2015-09-11 11:38:37 -05:00
return GDK_EVENT_PROPAGATE ;
}
2015-12-17 15:49:21 -06:00
static gboolean areaWidget_enter_notify_event ( GtkWidget * w , GdkEventCrossing * e )
{
return onCrossing ( areaWidget ( w ) , 0 ) ;
}
static gboolean areaWidget_leave_notify_event ( GtkWidget * w , GdkEventCrossing * e )
{
return onCrossing ( areaWidget ( w ) , 1 ) ;
}
2015-09-12 07:02:07 -05:00
// note: there is no equivalent to WM_CAPTURECHANGED on GTK+; there literally is no way to break a grab like that (at least not on X11 and Wayland)
// even if I invoke the task switcher and switch processes, the mouse grab will still be held until I let go of all buttons
2015-09-12 07:11:10 -05:00
// therefore, no DragBroken()
2015-09-12 07:02:07 -05:00
2015-09-13 14:21:21 -05:00
// we use GDK_KEY_Print as a sentinel because libui will never support the print screen key; that key belongs to the user
static const struct {
guint keyval ;
uiExtKey extkey ;
} extKeys [ ] = {
{ GDK_KEY_Escape , uiExtKeyEscape } ,
{ GDK_KEY_Insert , uiExtKeyInsert } ,
{ GDK_KEY_Delete , uiExtKeyDelete } ,
{ GDK_KEY_Home , uiExtKeyHome } ,
{ GDK_KEY_End , uiExtKeyEnd } ,
{ GDK_KEY_Page_Up , uiExtKeyPageUp } ,
{ GDK_KEY_Page_Down , uiExtKeyPageDown } ,
{ GDK_KEY_Up , uiExtKeyUp } ,
{ GDK_KEY_Down , uiExtKeyDown } ,
{ GDK_KEY_Left , uiExtKeyLeft } ,
{ GDK_KEY_Right , uiExtKeyRight } ,
{ GDK_KEY_F1 , uiExtKeyF1 } ,
{ GDK_KEY_F2 , uiExtKeyF2 } ,
{ GDK_KEY_F3 , uiExtKeyF3 } ,
{ GDK_KEY_F4 , uiExtKeyF4 } ,
{ GDK_KEY_F5 , uiExtKeyF5 } ,
{ GDK_KEY_F6 , uiExtKeyF6 } ,
{ GDK_KEY_F7 , uiExtKeyF7 } ,
{ GDK_KEY_F8 , uiExtKeyF8 } ,
{ GDK_KEY_F9 , uiExtKeyF9 } ,
{ GDK_KEY_F10 , uiExtKeyF10 } ,
{ GDK_KEY_F11 , uiExtKeyF11 } ,
{ GDK_KEY_F12 , uiExtKeyF12 } ,
// numpad numeric keys and . are handled in events.c
{ GDK_KEY_KP_Enter , uiExtKeyNEnter } ,
{ GDK_KEY_KP_Add , uiExtKeyNAdd } ,
{ GDK_KEY_KP_Subtract , uiExtKeyNSubtract } ,
{ GDK_KEY_KP_Multiply , uiExtKeyNMultiply } ,
{ GDK_KEY_KP_Divide , uiExtKeyNDivide } ,
{ GDK_KEY_Print , 0 } ,
} ;
static const struct {
guint keyval ;
uiModifiers mod ;
} modKeys [ ] = {
{ GDK_KEY_Control_L , uiModifierCtrl } ,
{ GDK_KEY_Control_R , uiModifierCtrl } ,
{ GDK_KEY_Alt_L , uiModifierAlt } ,
{ GDK_KEY_Alt_R , uiModifierAlt } ,
{ GDK_KEY_Meta_L , uiModifierAlt } ,
{ GDK_KEY_Meta_R , uiModifierAlt } ,
{ GDK_KEY_Shift_L , uiModifierShift } ,
{ GDK_KEY_Shift_R , uiModifierShift } ,
{ GDK_KEY_Super_L , uiModifierSuper } ,
{ GDK_KEY_Super_R , uiModifierSuper } ,
{ GDK_KEY_Print , 0 } ,
} ;
2015-12-17 15:49:21 -06:00
static int areaKeyEvent ( uiArea * a , int up , GdkEventKey * e )
2015-09-13 14:21:21 -05:00
{
uiAreaKeyEvent ke ;
guint state ;
int i ;
ke . Key = 0 ;
ke . ExtKey = 0 ;
ke . Modifier = 0 ;
state = translateModifiers ( e - > state , e - > window ) ;
ke . Modifiers = toModifiers ( state ) ;
ke . Up = up ;
for ( i = 0 ; extKeys [ i ] . keyval ! = GDK_KEY_Print ; i + + )
if ( extKeys [ i ] . keyval = = e - > keyval ) {
ke . ExtKey = extKeys [ i ] . extkey ;
goto keyFound ;
}
for ( i = 0 ; modKeys [ i ] . keyval ! = GDK_KEY_Print ; i + + )
if ( modKeys [ i ] . keyval = = e - > keyval ) {
ke . Modifier = modKeys [ i ] . mod ;
// don't include the modifier in ke.Modifiers
ke . Modifiers & = ~ ke . Modifier ;
goto keyFound ;
}
2018-04-15 21:31:17 -05:00
if ( uiprivFromScancode ( e - > hardware_keycode - 8 , & ke ) )
2015-09-13 14:21:21 -05:00
goto keyFound ;
// no supported key found; treat as unhandled
return 0 ;
keyFound :
2015-12-17 15:49:21 -06:00
return ( * ( a - > ah - > KeyEvent ) ) ( a - > ah , a , & ke ) ;
2015-09-13 14:21:21 -05:00
}
static gboolean areaWidget_key_press_event ( GtkWidget * w , GdkEventKey * e )
{
2015-12-17 15:49:21 -06:00
areaWidget * aw = areaWidget ( w ) ;
uiArea * a = aw - > a ;
2015-09-13 14:21:21 -05:00
2015-12-17 15:49:21 -06:00
if ( areaKeyEvent ( a , 0 , e ) )
2015-09-13 14:21:21 -05:00
return GDK_EVENT_STOP ;
return GDK_EVENT_PROPAGATE ;
}
static gboolean areaWidget_key_release_event ( GtkWidget * w , GdkEventKey * e )
{
2015-12-17 15:49:21 -06:00
areaWidget * aw = areaWidget ( w ) ;
uiArea * a = aw - > a ;
2015-09-13 14:21:21 -05:00
2015-12-17 15:49:21 -06:00
if ( areaKeyEvent ( a , 1 , e ) )
2015-09-13 14:21:21 -05:00
return GDK_EVENT_STOP ;
return GDK_EVENT_PROPAGATE ;
}
2015-09-04 13:51:10 -05:00
enum {
2015-12-17 15:49:21 -06:00
pArea = 1 ,
2015-09-04 13:51:10 -05:00
nProps ,
} ;
2015-12-17 15:49:21 -06:00
static GParamSpec * pspecArea ;
2015-09-04 20:12:41 -05:00
2015-09-04 13:51:10 -05:00
static void areaWidget_set_property ( GObject * obj , guint prop , const GValue * value , GParamSpec * pspec )
{
2015-12-17 15:49:21 -06:00
areaWidget * aw = areaWidget ( obj ) ;
2015-09-04 13:51:10 -05:00
switch ( prop ) {
2015-12-17 15:49:21 -06:00
case pArea :
aw - > a = ( uiArea * ) g_value_get_pointer ( value ) ;
2015-12-17 18:59:33 -06:00
aw - > a - > cc = & ( aw - > cc ) ;
2015-09-04 20:12:41 -05:00
return ;
2015-09-04 13:51:10 -05:00
}
G_OBJECT_WARN_INVALID_PROPERTY_ID ( obj , prop , pspec ) ;
}
static void areaWidget_get_property ( GObject * obj , guint prop , GValue * value , GParamSpec * pspec )
{
G_OBJECT_WARN_INVALID_PROPERTY_ID ( obj , prop , pspec ) ;
}
static void areaWidget_class_init ( areaWidgetClass * class )
{
G_OBJECT_CLASS ( class ) - > dispose = areaWidget_dispose ;
G_OBJECT_CLASS ( class ) - > finalize = areaWidget_finalize ;
G_OBJECT_CLASS ( class ) - > set_property = areaWidget_set_property ;
G_OBJECT_CLASS ( class ) - > get_property = areaWidget_get_property ;
GTK_WIDGET_CLASS ( class ) - > size_allocate = areaWidget_size_allocate ;
2015-09-04 20:50:49 -05:00
GTK_WIDGET_CLASS ( class ) - > draw = areaWidget_draw ;
2015-12-17 18:16:30 -06:00
GTK_WIDGET_CLASS ( class ) - > get_preferred_height = areaWidget_get_preferred_height ;
GTK_WIDGET_CLASS ( class ) - > get_preferred_width = areaWidget_get_preferred_width ;
2015-09-11 11:38:37 -05:00
GTK_WIDGET_CLASS ( class ) - > button_press_event = areaWidget_button_press_event ;
GTK_WIDGET_CLASS ( class ) - > button_release_event = areaWidget_button_release_event ;
GTK_WIDGET_CLASS ( class ) - > motion_notify_event = areaWidget_motion_notify_event ;
2015-12-17 15:49:21 -06:00
GTK_WIDGET_CLASS ( class ) - > enter_notify_event = areaWidget_enter_notify_event ;
GTK_WIDGET_CLASS ( class ) - > leave_notify_event = areaWidget_leave_notify_event ;
2015-09-13 14:21:21 -05:00
GTK_WIDGET_CLASS ( class ) - > key_press_event = areaWidget_key_press_event ;
GTK_WIDGET_CLASS ( class ) - > key_release_event = areaWidget_key_release_event ;
2015-09-04 13:51:10 -05:00
2015-12-17 15:49:21 -06:00
pspecArea = g_param_spec_pointer ( " libui-area " ,
" libui-area " ,
" uiArea. " ,
2015-09-04 20:12:41 -05:00
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS ) ;
2015-12-17 15:49:21 -06:00
g_object_class_install_property ( G_OBJECT_CLASS ( class ) , pArea , pspecArea ) ;
2015-09-04 13:51:10 -05:00
}
2015-09-04 20:12:41 -05:00
2015-10-08 17:24:09 -05:00
// control implementation
2016-04-25 19:19:57 -05:00
uiUnixControlAllDefaults ( uiArea )
2015-10-08 17:24:09 -05:00
2016-06-13 20:05:40 -05:00
void uiAreaSetSize ( uiArea * a , int width , int height )
2015-10-08 17:24:09 -05:00
{
2015-12-17 18:16:30 -06:00
if ( ! a - > scrolling )
2018-04-15 20:46:08 -05:00
uiprivUserBug ( " You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p) " , a ) ;
2015-12-17 18:16:30 -06:00
a - > scrollWidth = width ;
a - > scrollHeight = height ;
gtk_widget_queue_resize ( a - > areaWidget ) ;
2015-10-08 17:24:09 -05:00
}
2015-10-09 14:27:57 -05:00
void uiAreaQueueRedrawAll ( uiArea * a )
{
gtk_widget_queue_draw ( a - > areaWidget ) ;
}
2016-01-24 20:50:10 -06:00
void uiAreaScrollTo ( uiArea * a , double x , double y , double width , double height )
2016-01-24 20:41:34 -06:00
{
// TODO
// TODO adjust adjustments and find source for that
}
2016-10-24 23:34:12 -05:00
void uiAreaBeginUserWindowMove ( uiArea * a )
{
GtkWidget * toplevel ;
if ( a - > dragevent = = NULL )
2018-04-15 20:46:08 -05:00
uiprivUserBug ( " cannot call uiAreaBeginUserWindowMove() outside of a Mouse() with Down != 0 " ) ;
2016-10-24 23:34:12 -05:00
// TODO don't we have a libui function for this? did I scrap it?
// TODO widget or areaWidget?
toplevel = gtk_widget_get_toplevel ( a - > widget ) ;
if ( toplevel = = NULL ) {
// TODO
return ;
}
// the docs say to do this
if ( ! gtk_widget_is_toplevel ( toplevel ) ) {
// TODO
return ;
}
if ( ! GTK_IS_WINDOW ( toplevel ) ) {
// TODO
return ;
}
gtk_window_begin_move_drag ( GTK_WINDOW ( toplevel ) ,
a - > dragevent - > button ,
a - > dragevent - > x_root , // TODO are these correct?
a - > dragevent - > y_root ,
a - > dragevent - > time ) ;
}
static const GdkWindowEdge edges [ ] = {
[ uiWindowResizeEdgeLeft ] = GDK_WINDOW_EDGE_WEST ,
[ uiWindowResizeEdgeTop ] = GDK_WINDOW_EDGE_NORTH ,
[ uiWindowResizeEdgeRight ] = GDK_WINDOW_EDGE_EAST ,
[ uiWindowResizeEdgeBottom ] = GDK_WINDOW_EDGE_SOUTH ,
[ uiWindowResizeEdgeTopLeft ] = GDK_WINDOW_EDGE_NORTH_WEST ,
[ uiWindowResizeEdgeTopRight ] = GDK_WINDOW_EDGE_NORTH_EAST ,
[ uiWindowResizeEdgeBottomLeft ] = GDK_WINDOW_EDGE_SOUTH_WEST ,
[ uiWindowResizeEdgeBottomRight ] = GDK_WINDOW_EDGE_SOUTH_EAST ,
} ;
void uiAreaBeginUserWindowResize ( uiArea * a , uiWindowResizeEdge edge )
{
GtkWidget * toplevel ;
if ( a - > dragevent = = NULL )
2018-04-15 20:46:08 -05:00
uiprivUserBug ( " cannot call uiAreaBeginUserWindowResize() outside of a Mouse() with Down != 0 " ) ;
2016-10-24 23:34:12 -05:00
// TODO don't we have a libui function for this? did I scrap it?
// TODO widget or areaWidget?
toplevel = gtk_widget_get_toplevel ( a - > widget ) ;
if ( toplevel = = NULL ) {
// TODO
return ;
}
// the docs say to do this
if ( ! gtk_widget_is_toplevel ( toplevel ) ) {
// TODO
return ;
}
if ( ! GTK_IS_WINDOW ( toplevel ) ) {
// TODO
return ;
}
gtk_window_begin_resize_drag ( GTK_WINDOW ( toplevel ) ,
edges [ edge ] ,
a - > dragevent - > button ,
a - > dragevent - > x_root , // TODO are these correct?
a - > dragevent - > y_root ,
a - > dragevent - > time ) ;
}
2015-10-08 17:24:09 -05:00
uiArea * uiNewArea ( uiAreaHandler * ah )
2015-09-04 20:12:41 -05:00
{
2015-10-08 17:24:09 -05:00
uiArea * a ;
2016-04-25 19:19:57 -05:00
uiUnixNewControl ( uiArea , a ) ;
2015-10-08 17:24:09 -05:00
2015-12-17 18:59:33 -06:00
a - > ah = ah ;
2015-12-17 18:16:30 -06:00
a - > scrolling = FALSE ;
a - > areaWidget = GTK_WIDGET ( g_object_new ( areaWidgetType ,
" libui-area " , a ,
NULL ) ) ;
a - > drawingArea = GTK_DRAWING_AREA ( a - > areaWidget ) ;
a - > area = areaWidget ( a - > areaWidget ) ;
a - > widget = a - > areaWidget ;
return a ;
}
2016-06-13 20:05:40 -05:00
uiArea * uiNewScrollingArea ( uiAreaHandler * ah , int width , int height )
2015-12-17 18:16:30 -06:00
{
uiArea * a ;
2016-04-25 19:19:57 -05:00
uiUnixNewControl ( uiArea , a ) ;
2015-12-17 18:16:30 -06:00
2015-12-17 18:59:33 -06:00
a - > ah = ah ;
2015-12-17 18:16:30 -06:00
a - > scrolling = TRUE ;
a - > scrollWidth = width ;
a - > scrollHeight = height ;
a - > swidget = gtk_scrolled_window_new ( NULL , NULL ) ;
a - > scontainer = GTK_CONTAINER ( a - > swidget ) ;
a - > sw = GTK_SCROLLED_WINDOW ( a - > swidget ) ;
2015-10-09 09:09:55 -05:00
a - > areaWidget = GTK_WIDGET ( g_object_new ( areaWidgetType ,
2015-12-17 15:49:21 -06:00
" libui-area " , a ,
2015-09-04 20:12:41 -05:00
NULL ) ) ;
2015-10-09 09:09:55 -05:00
a - > drawingArea = GTK_DRAWING_AREA ( a - > areaWidget ) ;
a - > area = areaWidget ( a - > areaWidget ) ;
2015-09-04 20:27:32 -05:00
2015-12-17 18:16:30 -06:00
a - > widget = a - > swidget ;
2015-10-09 09:09:55 -05:00
gtk_container_add ( a - > scontainer , a - > areaWidget ) ;
// and make the area visible; only the scrolled window's visibility is controlled by libui
gtk_widget_show ( a - > areaWidget ) ;
2015-10-08 17:24:09 -05:00
return a ;
2015-09-04 20:27:32 -05:00
}