## 6.1 Write Primitive Widgets

To write your own primitive widget, you need to

- {select a default class name and instance name for your widget.}

- {provide the following 5 procedures

                 setup(EZ_Widget *widget);
           computeSize(EZ_Widget *widget,  int *w, int *h);
              freeData(EZ_Widget *widget);
                  draw(EZ_Widget *widget);
           eventHandle(EZ_Widget *widget, XEvent *event)

  `setup` is invoked whenever an instance of your widget is created. You should include here the widget or widget class initialization code and the code for parsing private resources. Primitive widgets are implemented internally through the `RawXWindow` widget. The widget data structure provides an array of 10 `EZ_UnknownDataType` for you to hook up private data to your widget.

  `computeSize` is invoked by the geometry manager to figure out the minimal size of your widget.

  `freeData` is invoked by `EZ_DestroyWidget` to clean up widget private data.

  `draw` is invoked whenever there are needs to repaint the widget window.

  `evehtHandle` is the default widget event handler.

- call `EZ_RegisterPrimitiveWidget` to register your widget. This function returns an integer identifier.

- write interface routines as needed.

After a widget has been registered, it can be instantiatd by calling `EZ_CreateWidgetXrm` with type the returned identifier.

### 6.1.1 Example: Locator

In this section, we implement the locator widget, a widget selects the mouse pointer coordinates in a rectangular area, scaled to [0,1]x[0,1].

Our locator has one private resources, `value`, which takes a pair of float point values in the range between 0.0 and 1.0.

We implement two interface routines for our locator. `ez_GetLocatorXY` and `ez_SetLocatorXY`. The first one returns the coordinates of the locator and the second one sets the coordinates of the locator.

Our locator will handle ButtonPress and KeyPress events. It also support MotionCallbacks, private event handlers and drag and drop.

    /************************* Locator Widget ****************************/
    #include "EZ.h"

    void ez_GetLocatorXY (EZ_Widget *widget, float *x, float *y);
    void ez_SetLocatorXY (EZ_Widget *widget, float x,  float y);
    /*********************************************************************/

    int  Locator_TYPE;

    /*********************************************************************/
    static void LocatorSetup       (EZ_Widget *widget);
    static void LocatorFreeData    (EZ_Widget *widget);
    static void LocatorComputeSize (EZ_Widget *widget, int *w, int *h);
    static void LocatorDraw        (EZ_Widget *widget);
    static void LocatorEventHandle (EZ_Widget *widget, XEvent *event);
    /*********************************************************************/

    static void LocatorSetup(EZ_Widget *widget)
    {
      /* this procedure is invoked when a new LOCATOR widget
       * is created. We initialize the locator through
       * resources and create a gc for rendering the crosshair.
       *
       * primitive widgets are implemented using the RAW_XWINDOW
       * widget. The RAW_XWINDOW widget data structure has
       * an array of 10 EZ_UnknownDataType reserved for
       * us to hook up private data.
       */
      EZ_UnknownDataType  *data = EZ_GetRawXWinPrivateData(widget);
      unsigned long       mask  = 0L;
      XGCValues           gcvalues;
      char                *value;   /* tmp return for resource */

      /* data is an array of 10 EZ_UnknownDataTypes */
      data[0].f = 0.5;
      data[1].f = 0.5;
      /* parse private resources */
      if(EZ_GetWidgetResource(widget, "value", &value))
        {
          float   x,y;
          if(sscanf(value, "%f %f", &x, &y) == 2)
            {
              if( x >= 0.0 && x <= 1.0) data[0].f = x;
              if( y >= 0.0 && y <= 1.0) data[1].f = y;
            }
        }
      data[2].p = (void *)EZ_GetGC(mask, &gcvalues);
    }
    /***************************************************************/

    static void LocatorFreeData(EZ_Widget *widget)
    {
      /* this procedure is called when a LOCATOR widget is
       * destroyed. We free the GC allocated for this widget
       */
      EZ_UnknownDataType  *data = EZ_GetRawXWinPrivateData(widget);
      GC gc = (GC)data[2].p;
      /* for gc created by EZ_GetGC, you must use EZ_FreeGC to free it ! */
      EZ_FreeGC(gc);
    }
    /***************************************************************/

    static void LocatorComputeSize(EZ_Widget *widget, int *w, int *h)
    {
      /* this procedure is invoked by the geometry manager to
       * figure out the minimal size of LOCATOR.
       */
      *w =100; *h =100;
    }
    /***************************************************************/

    static void LocatorDraw(EZ_Widget *widget)
    {
      int focus_pad = 0;   /* kbd focus highlight padding */
      int border    = 0;   /* border style */
      int bwidth    = 0;   /* border width */
      int padx, pady;      /* XY padding, not used */
      int width, height;   /* widget window width and height */
      Display  *dpy;       /* the display */
      Window   win;        /* widget window */
      int      depth;      /* depth of visual */
      int      tile;       /* is bg tiled ? */
      int      x, y, tmp, tmp2;
      Pixmap   pixmap;
      GC       gc;
      unsigned long pv;
      EZ_UnknownDataType  *data = EZ_GetRawXWinPrivateData(widget);

      EZ_GetWidgetPaddings(widget, &padx, &pady, &focus_pad); /* get all paddings       */
      EZ_GetWidgetBorder(widget, &bwidth, &border);           /* border characteristics */
      EZ_GetWidgetDimension(widget, &width, &height);         /* widget size            */
      dpy = EZ_GetDisplay();              /* display   */
      depth = EZ_GetDepth();             /*   visual    */
      win = EZ_GetWidgetWindow(widget); /* widget window */
      tmp = focus_pad + bwidth;
      tmp2 = tmp + tmp;

      /* draw into a tmp pixmap, copy to the widget window when done */
      pixmap = XCreatePixmap(dpy, win, width, height, depth);

      /* fill the background */
      EZ_GetBackgroundTileGC(widget, &gc, &pv, 0,0);
      XFillRectangle(dpy, pixmap, gc, 0, 0, width, height);

      /* fix kbd focus highlight border */
      EZ_FixFocusHighlightBorder(widget, pixmap, width, height, focus_pad);

      /* render the foreground, the crosshair */
      gc = (GC)data[2].p;
      pv = EZ_GetForegroundPV(widget);
      XSetForeground(dpy, gc, pv);

      x = tmp + (int)((width - tmp2) * data[0].f + 0.5);
      y = tmp + (int)((height- tmp2) * data[1].f + 0.5);

      XDrawLine(dpy, pixmap, gc, x, tmp, x, height -tmp);
      XDrawLine(dpy, pixmap, gc, tmp, y, width - tmp,y);

      /* render the widget border */
      EZ_DrawRectBorder(widget, pixmap);

      /* and finally  ... */
      XCopyArea(dpy,pixmap,win, gc,0,0,width,height,0,0);
      XFreePixmap(dpy, pixmap);
    }
    /***************************************************************/

    static void LocatorEventHandle(EZ_Widget *widget, XEvent *event)
    {
      if(event->type == Expose) LocatorDraw(widget);
      else if(event->type == ButtonPress || event->type == KeyPress)
        {
          int padx, pady, focus_pad=0;                /* paddings  */
          int border = 0, bwidth = 0, bwpad, bwpad2;  /* border .. */
          int x,y, width, height;                     /* dimension */
          Display *dpy = EZ_GetDisplay();             /* display   */
          XEvent xevent;
          float save_x, save_y;
          EZ_UnknownDataType  *data = EZ_GetRawXWinPrivateData(widget);

          if(event->type == ButtonPress)
            {
              if(event->xbutton.button == EZ_Btn1)
                {
                  /* get paddings and  dimension */
                  EZ_GetWidgetPaddings(widget, &padx, &pady, &focus_pad);
                  EZ_GetWidgetBorder(widget, &bwidth, &border);
                  EZ_GetWidgetDimension(widget, &width, &height);
                  bwpad = focus_pad + bwidth;
                  bwpad2 = bwpad + bwpad;
                  x = event->xbutton.x;
                  y = event->xbutton.y;
                  save_x = data[0].f;
                  save_y = data[1].f;
                  while(1)
                    {
                      /* loop and wait for ButtonRelease. Invoke
                       * private event handlers on other events. Handle
                       * MotionNotify events and execute MotionCallbacks
                       * if needed.
                       */
                      if(x >= 0)
                        {
                          data[0].f = (float)(x-bwpad)/(float)(width  - bwpad2);
                          data[1].f = (float)(y-bwpad)/(float)(height - bwpad2);
                          if(data[0].f < 0.0) data[0].f = 0.0; else if(data[0].f > 1.0) data[0].f=1.0;
                          if(data[1].f < 0.0) data[1].f = 0.0; else if(data[1].f > 1.0) data[1].f=1.0;
                          LocatorDraw(widget);
                          /* if locator has changed it state, invoke motion callbacks */
                          if(save_x != data[0].f || save_y != data[1].f)
                            EZ_CallWidgetMotionCallbacks(widget);
                        }
                      XNextEvent(dpy, &xevent);

                      /* tricky! We want to give private EHandlers a chance  */
                      if(EZ_FilterEvent(&xevent)) EZ_InvokePrivateEventHandler(&xevent);
                      if(xevent.type == Expose)
                        {
                          EZ_WidgetDispatchEvent(&xevent);
                          EZ_RemoveEvent(&xevent);
                        }
                      if(xevent.type == MotionNotify)
                        {
                          x = xevent.xmotion.x;
                          y = xevent.xmotion.y;
                          while(XCheckTypedEvent(dpy, MotionNotify, &xevent));
                        }
                      else if(xevent.type == ButtonRelease && xevent.xbutton.button == Button1)
                        break;
                      else x = -1111;
                    }
                }
              else if(event->xbutton.button == EZ_Btn3)
                {
                  EZ_HandleDnDEvents(widget, event);
                }
            }
          else /* key press */
            {
              char              tmpbuffer[8];
              int               buffersize = 8, xmove_unit, ymove_unit, count;
              KeySym            keysym;
              XComposeStatus    compose;

              count = XLookupString(&(event->xkey), tmpbuffer, buffersize, &keysym, &compose);
              tmpbuffer[count] = '\0';
              xmove_unit = 0;
              ymove_unit = 0;

              switch(keysym)
                {
                case XK_Left: case XK_b:  case XK_h: case XK_B:  case XK_H:
    #ifdef XK_KP_Left
                case XK_KP_Left:
    #endif
                  if(event->xkey.state & ShiftMask)        xmove_unit = -4;
                  else if(event->xkey.state & Mod1Mask)    xmove_unit = -10 ;
                  else if(event->xkey.state & ControlMask) xmove_unit = -20;
                  else xmove_unit = -1;
                  break;
                case XK_Right: case XK_f: case XK_l: case XK_F: case XK_L:
    #ifdef XK_KP_Right
                case XK_KP_Right:
    #endif
                  if(event->xkey.state & ShiftMask)         xmove_unit = 4;
                  else if(event->xkey.state & Mod1Mask)     xmove_unit = 10 ;
                  else if(event->xkey.state & ControlMask)  xmove_unit = 20;
                  else xmove_unit = 1;
                  break;
                case XK_Up: case XK_k:  case XK_p: case XK_K:  case XK_P:
    #ifdef XK_KP_Up
                case XK_KP_Up:
    #endif
                  if(event->xkey.state & ShiftMask)         ymove_unit = -4;
                  else if(event->xkey.state & Mod1Mask)     ymove_unit = -10 ;
                  else if(event->xkey.state & ControlMask)  ymove_unit = -20;
                  else ymove_unit = -1;
                  break;
                case XK_Down: case XK_n: case XK_j: case XK_N: case XK_J:
    #ifdef XK_KP_Down
                case XK_KP_Down:
    #endif
                  if(event->xkey.state & ShiftMask)         ymove_unit = 4;
                  else if(event->xkey.state & Mod1Mask)     ymove_unit = 10 ;
                  else if(event->xkey.state & ControlMask)  ymove_unit = 20;
                  else ymove_unit = 1;
                  break;
                default:
                  break;
                }

              if(xmove_unit | ymove_unit)
                {
                  data[0].f += (xmove_unit) * 0.01;
                  data[1].f += (ymove_unit) * 0.01;
                  if(data[0].f < 0.0) data[0].f = 0.0; else if(data[0].f > 1.0) data[0].f=1.0;
                  if(data[1].f < 0.0) data[1].f = 0.0; else if(data[1].f > 1.0) data[1].f=1.0;
                  LocatorDraw(widget);
                  EZ_CallWidgetMotionCallbacks(widget);
                }
              /* normally there are too many KeyPress events ... */
              while(XCheckTypedEvent(dpy, KeyPress, &xevent));
            }
        }
    }
    /**********************************************************************/

    void ez_GetLocatorXY(EZ_Widget *widget, float *x, float *y)
    {
      /* return the coordinates of the locator */
      EZ_UnknownDataType  *data = EZ_GetRawXWinPrivateData(widget);
      *x = data[0].f;  *y = data[1].f;
    }
    /**********************************************************************/

    void ez_SetLocatorXY(EZ_Widget *widget, float x, float y)
    {
      /* set the location of the locator */
      EZ_UnknownDataType  *data = EZ_GetRawXWinPrivateData(widget);
      if(x < 0.0) x = 0.0; else if(x > 1.0) x = 1.0;
      if(y < 0.0) y = 0.0; else if(y > 1.0) y = 1.0;
      if(x != data[0].f || y != data[1].f)
        {
          data[0].f = x;
          data[1].f = y;
          if(EZ_WidgetIsViewable(widget)) EZ_DrawWidget(widget);
          EZ_CallWidgetMotionCallbacks(widget);
        }
    }
    /**********************************************************************/
    /* TEST */
    main(int ac, char **av)
    {
      EZ_Widget *locator;
      EZ_Initialize(ac, av, 0);
      LocatorType = EZ_RegisterPrimitiveWidget("locator", "Locator",
                                 LocatorSetup, LocatorComputeSize,
    		             LocatorDraw, LocatorFreeData, LocatorEventHandle);
      locator = EZ_CreateWidgetXrm(LocatorType, NULL,
                                   "aLocator", "ALocator",
                                   0);
      EZ_DisplayWidget(locator);
      EZ_EventMainLoop();
    }
    /**********************************************************************/

### 6.1.2 Help Functions

In this section, we list the functions that are useful for writing primitive widgets.

    void EZ_GetWidgetPaddings(EZ_Widget *widget, int *padx, int *pady, int *focusPd);

return the paddings of a widget.

    void EZ_GetWidgetBorder(EZ_Widget *widget, int *bwidth, int *btype));

return the border characteristics.

    void EZ_GetWidgetDimension(EZ_Widget *widget, int *w, int *h);

return the dimension of a widget.

    void EZ_GetWidgetMinDimension(EZ_Widget *widget, int *w, int *h);

return the minimal dimension of a widget.

    void EZ_GetWidgetPosition(EZ_Widget *widget, int *x, int *y);

return the location of a widget.

    void EZ_GetWidgetGeometry(EZ_Widget *widget, int *x,int *y, int *w, int *h);

return the geometry of a widget.

    int EZ_GetBackgroundTileGC(EZ_Widget *widget, GC *gc, unsigned long *pv,
                               int highlight, int force_highlight);

get background GC, return 1 if tile is used (with tile origin properly set).

    int EZ_GetBackgroundGC(EZ_Widget *widget, GC *gc, unsigned long *pv,
                            int highlight, int force_highlight);

get background GC, return 1 if tile is used (with tile origin unspecified).

    int EZ_GetDarkBrightNormalBDGC(EZ_Widget *widget, GC *dark,
                                   GC *bright, GC *normal);

get border GCs, return 1 if tile is used (with tile origin unspecified).

    void EZ_GetWidgetTileOrigin(EZ_Widget *widget, int *x, int *y);

if bg tile is used, return the tile origin.

    int EZ_GetWidgetOrientation(EZ_Widget *widget);

return the orientation of a widget.

    Window EZ_GetWidgetWindow(EZ_Widget *widget);

return the widget window.

    EZ_Widget *EZ_GetParentWidget(EZ_Widget *widget);

return the parent widget.

    EZ_Widget *EZ_GetChildrenWidget(EZ_Widget *widget);

return the first child, other children are siblings of the first child.

    EZ_Widget *EZ_GetSiblingWidget(EZ_Widget *widget);

return the sibling widget.

    unsigned long EZ_GetForegroundPV(EZ_Widget *widget);

return the pixel value of the foreground color.

    EZ_Bitmap *EZ_GetWidgetPixmap(EZ_Widget *widget);

return the label pixmap of a widget.

    void EZ_GetBrightBDpv(EZ_Widget *widget, unsigned long *pv);

return the pixel value of the bright side of the border.

    void EZ_GetDarkBDpv(EZ_Widget *widget, unsigned long *pv);

return the pixel value of the dark side of the border.

    XFontStruct *EZ_GetWidgetFont(EZ_Widget *widget);

return widget font.

    Display *EZ_GetDisplay(void);

return the display.

    void   EZ_GetDisplayInfo(Display **dpy, int *scrn, Visual **vis, int *depth);

return info about current display.

    void  EZ_GetDisplaySize(int *w, int *h);

return the display screen size.

    GC  EZ_GetGC(unsigned long mask, GCValues gcvalues);

create and return a GC.

    void  EZ_FreeGC(GC gc);

free a GC created by `EZ_GetGC`.

    int  EZ_RegisterPrimitiveWidget(char *iname, char *cname,
                    void (*Setup)(EZ_Widget *widget),
                    void (*ComputeSize)(EZ_Widget *widget, int *w, int *h),
                    void (*DrawWidget)(EZ_Widget *widget),
                    void (*FreeData)(EZ_Widget *widget),
                   void  (*EventHandle)(EZ_Widget *widget, XEvent *event));

register a primitive widget to EZwgl. This function returns an integer widget type identifier. This type identifier can be used by `EZ_CreateWidget` to instantiate the registered widget.

HTML Documentation Maintainance:`Arturo Espinosa <arturo@nuclecu.unam.mx>`