Logo Search packages:      
Sourcecode: vertex version File versions

menubutton.c

#include <gtk/gtk.h>
#ifdef __MSW__
# include <gdk/gdkwin32.h>
#else
# include <gdk/gdkx.h>
#endif

#include "guiutils.h"
#include "menubutton.h"


#define MENU_BUTTON_KEY       "menu_button_key"


/*
 *    Menu button structure.
 */
typedef struct {

      GtkWidget *toplevel;
      GtkWidget *menu;

      gint map_type;                /* One of MENU_BUTTON_MAP_TYPE_*. */

      gboolean is_pressed;          /* Is button pressed? */
      gboolean menu_map_state;      /* Menu map state? */

      gint press_x, press_y;        /* Initial button press position. */

} menu_button_struct;


static void MenuButtonDestroyCB(GtkObject *object, gpointer data);
static gint MenuButtonMotionNotifyCB(
        GtkWidget *widget, GdkEventMotion *motion, gpointer data
);
static void MenuButtonMapPositionCB(
        GtkMenu *menu, gint *x, gint *y, gpointer data
);
static void MenuButtonDoMapMenu(menu_button_struct *mb);
static void MenuButtonPressedCB(GtkWidget *widget, gpointer data);
static void MenuButtonReleasedCB(GtkWidget *widget, gpointer data);
static void MenuButtonMenuHideCB(GtkWidget *widget, gpointer data);

static GtkWidget *MenuButtonNewNexus(
        const gchar *label, guchar **icon_data,
        GtkWidget **menu_rtn, gint orientation
);
GtkWidget *MenuButtonNewH(
        const gchar *label, guchar **icon_data,
        GtkWidget **menu_rtn
);
GtkWidget *MenuButtonNewV(
        const gchar *label, guchar **icon_data,
        GtkWidget **menu_rtn
);
GtkWidget *MenuButtonNewPixmap(
        guchar **icon_data, GtkWidget **menu_rtn
);
GtkWidget *MenuButtonGetMenu(GtkWidget *w);
void MenuButtonSetMenu(GtkWidget *w, GtkMenu *menu);
void MenuButtonSetMapTrigger(
        GtkWidget *w, gint map_type
);



/*
 *    Menu button GtkButton "destroy" signal callback.
 */
static void MenuButtonDestroyCB(GtkObject *object, gpointer data)
{
      GtkWidget *menu;
        menu_button_struct *mb = (menu_button_struct *)data;
        if(mb == NULL)
            return;

      menu = mb->menu;
      mb->menu = NULL;
      if(menu != NULL)
          gtk_widget_destroy(menu);
/* printf("Destroyed 0x%.8x\n", (guint)mb); */
      g_free(mb);
}

/*
 *    Button "motion_notify_event" signal callback.
 */
static gint MenuButtonMotionNotifyCB(
      GtkWidget *widget, GdkEventMotion *motion, gpointer data
)
{
        menu_button_struct *mb = (menu_button_struct *)data;
        if((widget == NULL) || (mb == NULL))
            return(FALSE);

      if(!mb->is_pressed)
          return(TRUE);

      /* if the map type is MENU_BUTTON_MAP_TYPE_PRESSED_DRAG then
       * check if the motion has gone beyond the drag tolorance,
       * if it has then the menu needs to be mapped.
         */
        if((mb->map_type == MENU_BUTTON_MAP_TYPE_PRESSED_DRAG) &&
         !mb->menu_map_state
      )
      {
          gint    dx = (gint)motion->x - mb->press_x,
                  dy = (gint)motion->y - mb->press_y;
          if((dx > 2) || (dx < -2) ||
               (dy > 2) || (dy < -2)
          )
            MenuButtonDoMapMenu(mb);
      }

      return(TRUE);
}

/*
 *    Menu map position callback.
 */
static void MenuButtonMapPositionCB(
        GtkMenu *menu, gint *x, gint *y, gpointer data
)
{
        gint rx, ry, mx, my, mwidth, mheight, root_width, root_height;
      GdkWindow *root = GDK_ROOT_PARENT();
        GtkWidget *w, *rel_widget = (GtkWidget *)data;
        if((menu == NULL) || (rel_widget == NULL) || (root == NULL))
            return;

        /* Get position of relative widget, and then calculate the
         * new menu mapping position.
         */
        w = rel_widget;
        if(GTK_WIDGET_NO_WINDOW(w))
            return;
        GUIGetWindowRootPosition(w->window, &rx, &ry);
        mx = rx;
        my = ry + w->allocation.height;

      /* Get menu size. */
        w = GTK_WIDGET(menu);
        mwidth = w->allocation.width;
        mheight = w->allocation.height;

      /* Get root window size. */
      gdk_window_get_size(root, &root_width, &root_height);

      /* Clip new menu mapping position to stay on root window. */
      if((mx + mwidth) > root_width)
          mx = root_width - mwidth;
        if(mx < 0)
            mx = 0;
        if((my + mheight) > root_height)
            my = root_height - mheight;
        if(my < 0)
            my = 0;

      /* Update return position. */
      if(x != NULL)
          *x = mx;
      if(y != NULL)
          *y = my;
}

/*
 *    Ungrabs the button and holds it down, while mapping the menu.
 *
 *    The menu_map_state is not checked, however it will be set to
 *    TRUE.
 */
static void MenuButtonDoMapMenu(menu_button_struct *mb)
{
      GtkWidget *w;


      /* Mark menu as mapped without checking. */
      mb->menu_map_state = TRUE;

      /* Map menu, this needs to be done first before the button
       * widget is held down.
       */
        w = mb->menu;
        if(w != NULL)
        {
          GtkMenu *menu = GTK_MENU(w);
          gtk_menu_popup(
            menu, NULL, NULL,
            MenuButtonMapPositionCB, mb->toplevel,
            1, GDK_CURRENT_TIME
          );
      }

      /* Ungrab the button widget and hold it down by generating a
       * "pressed" signal.
       */
      w = mb->toplevel;
      if(w != NULL)
      {
          GtkButton *button = GTK_BUTTON(w);

          gtk_grab_remove(w);

          /* Need to flush GTK+ events. */
          while(gtk_events_pending() > 0)
            gtk_main_iteration();

          button->in_button = 1;
          button->button_down = 1;
          gtk_signal_emit_by_name(GTK_OBJECT(w), "pressed");
      }
}

/*
 *    Button "pressed" signal callback.
 */
static void MenuButtonPressedCB(GtkWidget *widget, gpointer data)
{
      gint x, y;
      GdkModifierType mask;
      GdkWindow *window;
      menu_button_struct *mb = (menu_button_struct *)data;
        if((widget == NULL) || (mb == NULL))
            return;

      if(mb->is_pressed)
          return;

      window = widget->window;
      if(window == NULL)
          return;

      /* Get pointer position. */
        gdk_window_get_pointer(window, &x, &y, &mask);

        /* Record button pressed state. */
        mb->is_pressed = TRUE;
        mb->press_x = x;
        mb->press_y = y;

      /* if the map type is MENU_BUTTON_MAP_TYPE_PRESSED_DRAG then
       * do not map menu here, instead allow the motion callback to
       * map it.
       */
      if(mb->map_type == MENU_BUTTON_MAP_TYPE_PRESSED_DRAG)
          return;

      MenuButtonDoMapMenu(mb);
}

/*
 *      Button "released" signal callback.
 */
static void MenuButtonReleasedCB(GtkWidget *widget, gpointer data)
{
        menu_button_struct *mb = (menu_button_struct *)data;
        if((widget == NULL) || (mb == NULL))
            return;

      if(mb->is_pressed)
      {
          /* Record button released state. */
            mb->is_pressed = FALSE;
            mb->press_x = 0;
            mb->press_y = 0;

          /* If the menu was not mapped when the button is released
           * and the map type is MENU_BUTTON_MAP_TYPE_PRESSED_DRAG
           * then a clicked signal needs to be reported.
           */
/* Seems like since no manipulation of button states are done a
   "clicked" signal is already generated so we do not need this.

          if(!mb->menu_map_state &&
             (mb->map_type == MENU_BUTTON_MAP_TYPE_PRESSED_DRAG)
          )
            gtk_signal_emit_by_name(GTK_OBJECT(widget), "clicked");
 */
      }
}

/*
 *      Menu "hide" signal callback.
 */
static void MenuButtonMenuHideCB(GtkWidget *widget, gpointer data)
{
        GtkWidget *w;
        menu_button_struct *mb = (menu_button_struct *)data;
        if((widget == NULL) || (mb == NULL))
            return;

      /* Release button if it is pressed. */
      if(mb->is_pressed)
      {
          w = mb->toplevel;
          if(w != NULL)
          {
            GtkButton *button = GTK_BUTTON(w);
            button->in_button = 0;
            gtk_signal_emit_by_name(GTK_OBJECT(w), "released");
          }
      }

        /* Mark menu as no longer mapped, this needs to be set after
       * button release reporting above so that the button "released"
       * signal callback can detect if the menu was mapped.
       */
        mb->menu_map_state = FALSE;
}

/*
 *    Menu detach callback.
 */
/*
static void MenuButtonMenuDetachCB(GtkWidget *attach_widget, GtkMenu *menu)
{

}
 */

/*
 *    Creates a new menu button widget.
 */
static GtkWidget *MenuButtonNewNexus(
        const gchar *label, guchar **icon_data,
        GtkWidget **menu_rtn, gint orientation
)
{
      GtkWidget *w;
      menu_button_struct *mb = (menu_button_struct *)g_malloc0(
          sizeof(menu_button_struct)
      );

      /* Reset values. */
      mb->map_type = MENU_BUTTON_MAP_TYPE_PRESSED;
      mb->is_pressed = FALSE;
      mb->menu_map_state = FALSE;
      mb->press_x = 0;
      mb->press_y = 0;

      /* Create button. */
      switch(orientation)
      {
        case 0:
          mb->toplevel = w = (GtkWidget *)GUIButtonPixmapLabelH(
            (u_int8_t **)icon_data, label, NULL
          );
          break;
        case 1:
            mb->toplevel = w = (GtkWidget *)GUIButtonPixmapLabelV(
                (u_int8_t **)icon_data, label, NULL
            );
            break;
          default:
            mb->toplevel = w = (GtkWidget *)GUIButtonPixmap(
                (u_int8_t **)icon_data
            );
            break;
      }
      gtk_widget_add_events(
          w,
          GDK_POINTER_MOTION_MASK
      );
      gtk_object_set_data(GTK_OBJECT(w), MENU_BUTTON_KEY, mb);
      gtk_signal_connect(
            GTK_OBJECT(w), "destroy",
            GTK_SIGNAL_FUNC(MenuButtonDestroyCB), mb
        );
        gtk_signal_connect_after(
            GTK_OBJECT(w), "pressed",
            GTK_SIGNAL_FUNC(MenuButtonPressedCB), mb
        );
        gtk_signal_connect_after(
            GTK_OBJECT(w), "released",
            GTK_SIGNAL_FUNC(MenuButtonReleasedCB), mb
        );
        gtk_signal_connect_after(
            GTK_OBJECT(w), "motion_notify_event",
            GTK_SIGNAL_FUNC(MenuButtonMotionNotifyCB), mb
        );

      /* Create menu only if the menu_rtn is given. */
      if(menu_rtn != NULL)
      {
          mb->menu = w = (GtkWidget *)GUIMenuCreate();
          gtk_signal_connect(
            GTK_OBJECT(w), "hide",
            GTK_SIGNAL_FUNC(MenuButtonMenuHideCB), mb
          );
/*
          gtk_menu_attach_to_widget(
            GTK_MENU(w), mb->toplevel, MenuButtonMenuDetachCB
          );
 */
          *menu_rtn = mb->menu;
      }

      return(mb->toplevel);
}
GtkWidget *MenuButtonNewH(
        const gchar *label, guchar **icon_data,
        GtkWidget **menu_rtn
)
{
      return(MenuButtonNewNexus(label, icon_data, menu_rtn, 0));
}
GtkWidget *MenuButtonNewV(
        const gchar *label, guchar **icon_data,
        GtkWidget **menu_rtn
)
{
        return(MenuButtonNewNexus(label, icon_data, menu_rtn, 1));
}
GtkWidget *MenuButtonNewPixmap(
      guchar **icon_data, GtkWidget **menu_rtn
)
{
        return(MenuButtonNewNexus(NULL, icon_data, menu_rtn, 2));
}

/*
 *    Returns the menu widget associated with the menu button.
 */
GtkWidget *MenuButtonGetMenu(GtkWidget *w)
{
      menu_button_struct *mb = (menu_button_struct *)((w != NULL) ?
          gtk_object_get_data(GTK_OBJECT(w), MENU_BUTTON_KEY) : NULL
      );
      return((mb != NULL) ? mb->menu : NULL);
}

/*
 *    Sets the given menu widget as the new menu for the menu button,
 *    the old menu will be destroyed.
 */
void MenuButtonSetMenu(GtkWidget *w, GtkMenu *menu)
{
        menu_button_struct *mb = (menu_button_struct *)((w != NULL) ?
            gtk_object_get_data(GTK_OBJECT(w), MENU_BUTTON_KEY) : NULL
        );
      if(mb == NULL)
          return;

      if(mb->menu != NULL)
          gtk_widget_destroy(mb->menu);
      mb->menu = (GtkWidget *)menu;
}

/*
 *    Sets the map trigger type.
 */
void MenuButtonSetMapTrigger(
        GtkWidget *w, gint map_type
)
{
        menu_button_struct *mb = (menu_button_struct *)((w != NULL) ?
            gtk_object_get_data(GTK_OBJECT(w), MENU_BUTTON_KEY) : NULL
        );
      if(mb != NULL)
          mb->map_type = map_type;
}

Generated by  Doxygen 1.6.0   Back to index