Beispiel #1
0
/* Send a trigger that propagates to a widget's container until it's accepted */
int send_propagating_trigger(struct widget *w, s32 type,
			     union trigparam *param) {
  while (w && !send_trigger(w,type,param))
    w = widget_traverse(w,PG_TRAVERSE_CONTAINER,1);
  if (w)
    return 1;
  return 0;
}
Beispiel #2
0
/* Sends a trigger to all of a widget's children */
void r_send_trigger(struct widget *w, s32 type,
		    union trigparam *param, int *stop,int forward) {
  struct widget *bar;
  
  if (!w || (*stop) > 0)
    return;
  send_trigger(w,type,param);
  
  /* Also traverse the panelbar if there is one */
  if (!iserror(rdhandle((void**)&bar, PG_TYPE_WIDGET, w->owner, 
			widget_get(w,PG_WP_PANELBAR))) && bar) {
    r_send_trigger(bar, type, param, stop,0);
    if ((*stop) > 0)
      return;
  }
  
  r_send_trigger(widget_traverse(w,PG_TRAVERSE_CHILDREN,0),type,param,stop,1);
  if (forward)
    r_send_trigger(widget_traverse(w,PG_TRAVERSE_FORWARD,1),type,param,stop,1);
}
Beispiel #3
0
/* Traverse to other widgets in a given direction (PG_TRAVERSE_*) */
struct widget *widget_traverse(struct widget *w, int direction, int count) {
  struct widget *p;
  struct divnode *d;
  struct app_info **appinfo_p, *appinfo;
  
  switch (direction) {

    /* Traverse to the first child, then go forward 
     */
  case PG_TRAVERSE_CHILDREN:
    if (!w)
      return NULL;
    /* Make sure not only that this widget has an occupied child
     * attachment point, but make sure that the thing attached to it is
     * really the "in" connector on another widget. This is necessary to avoid
     * an improper result when this is called with a widget used inside another
     * widget as a container.
     */
    if (!w->sub || !*w->sub || (*w->sub)->owner->in != *w->sub)
      return NULL;
    return widget_traverse((*w->sub)->owner,PG_TRAVERSE_FORWARD,count);

    /* Go forward by 'count' widgets
     */
  case PG_TRAVERSE_FORWARD:
    for (;w && count;count--) {
      if (!w->out || !*w->out || (*w->out)->owner->in != *w->out)
	return NULL;
      w = (*w->out)->owner;
    }
    break;

    /* Go up by 'count' container levels 
     */
  case PG_TRAVERSE_CONTAINER:
    for (;w && count;count--) {
      if (iserror(rdhandle((void**) &w, PG_TYPE_WIDGET, -1, w->container)))
	return NULL;
    }
    break;

    /* Traversing backwards is harder, since there's no 'parent' pointer in the divnode
     */
  case PG_TRAVERSE_BACKWARD:
    for (;w && count;count--) {
      /* Find a suitable subtree */
      if (iserror(rdhandle((void**) &p, PG_TYPE_WIDGET, -1, w->container)))
	return NULL;
      if (p)
	d = p->in;
      else
        d = w->dt->head;

      /* Find parent divnode */
      d = divnode_findparent(d, w->in);
      if (!d)
	return NULL;
      if (d->next != w->in)   /* div child doesn't count */
	return NULL;
      w = d->owner;
    }
    break;

    /* Traverse through the application list */
  case PG_TRAVERSE_APP:
    if (w) {
      /* Find the appinfo structure associated with this widget */
      appinfo_p = appmgr_findapp(w);
      if (!appinfo_p)
	return NULL;
      
      /* Traverse the appinfo list */
      for (appinfo=*appinfo_p;appinfo && count;count--)
	appinfo = appinfo->next;
    }
    else {
      /* If they passed a 0 widget, start them off with the first app */
      appinfo = applist;
    }

    /* Return the root widget */
    if (!appinfo)
      return NULL;
    if (iserror(rdhandle((void**) &w, PG_TYPE_WIDGET, -1, appinfo->rootw)))
      return NULL;
    break;
  }

  return w;
}
Beispiel #4
0
g_error widget_base_set(struct widget *w, int property, glob data) {
  char *str;
  struct divnode *maindiv = w->in->div ? w->in->div : w->in;

  switch (property) {
    
    /* Set the size, assuming initial split at w->in.
     * Calls resize handler if it exists, and sets the
     * appropriate flags.
     */
  case PG_WP_SIDE:
    if (!VALID_SIDE(data)) return mkerror(PG_ERRT_BADPARAM,2);
    w->in->flags &= SIDEMASK;
    w->in->flags |= ((sidet)data) | DIVNODE_NEED_RECALC;
    resizewidget(w);
    w->dt->flags |= DIVTREE_NEED_RECALC;
    redraw_bg(w);

    if (w->auto_orientation) {
      /* Set orientation on all child widgets */

      struct widget *p;
      p = widget_traverse(w, PG_TRAVERSE_CHILDREN, 0);
      while (p) {
	switch (data) {

	case PG_S_LEFT:
	case PG_S_RIGHT:
	   
	  if (w->auto_orientation & PG_AUTO_DIRECTION)
	    widget_set(p, PG_WP_DIRECTION, PG_DIR_VERTICAL);

	  if (w->auto_orientation & PG_AUTO_SIDE)
	    switch (widget_get(p, PG_WP_SIDE)) {
		
	    case PG_S_LEFT:
	      widget_set(p, PG_WP_SIDE, PG_S_TOP);
	      break;
		
	    case PG_S_RIGHT:
	      widget_set(p, PG_WP_SIDE, PG_S_BOTTOM);
	      break;
		
	    }
	  break;

	case PG_S_TOP:
	case PG_S_BOTTOM:

	  if (w->auto_orientation & PG_AUTO_DIRECTION)
	    widget_set(p, PG_WP_DIRECTION, PG_DIR_HORIZONTAL);

	  if (w->auto_orientation & PG_AUTO_SIDE)
	    switch (widget_get(p, PG_WP_SIDE)) {
		
	    case PG_S_TOP:
	      widget_set(p, PG_WP_SIDE, PG_S_LEFT);
	      break;
		
	    case PG_S_BOTTOM:
	      widget_set(p, PG_WP_SIDE, PG_S_RIGHT);
	      break;
		
	    }
	  break;

	}
	p = widget_traverse(p, PG_TRAVERSE_FORWARD,1);
      }
    }
    break;

  case PG_WP_SIZE:
    if (data<0) {
      /* Automatic sizing */
      w->in->flags |= DIVNODE_SIZE_AUTOSPLIT;
      w->dt->flags |= DIVTREE_NEED_RESIZE;
    }
    else {
      w->in->split = data;
      w->in->flags &= ~DIVNODE_SIZE_AUTOSPLIT;     /* No auto resizing */
    }
    w->in->flags |= DIVNODE_NEED_RECALC;
    w->dt->flags |= DIVTREE_NEED_RECALC;
    redraw_bg(w);
    break;

  case PG_WP_SIZEMODE:
    w->in->flags &= ~DIVNODE_SIZE_AUTOSPLIT;     /* No auto resizing */
    w->in->flags &= ~PG_SZMODEMASK;
    w->in->flags |= data & PG_SZMODEMASK;
    redraw_bg(w);
    break;
      
  case PG_WP_SCROLL_X:
    if (data > w->in->child.w - w->in->r.w)
      data = w->in->child.w - w->in->r.w;
    if (data < 0)
      data = 0;
    if (maindiv->translation.x != -data) {
      maindiv->translation.x = -data;
      maindiv->flags |= DIVNODE_SCROLL_ONLY | DIVNODE_NEED_RECALC;
      w->dt->flags |= DIVTREE_NEED_REDRAW;
      hotspot_free();
    }
    maindiv->flags |= DIVNODE_DIVSCROLL | DIVNODE_EXTEND_WIDTH;
    break;

  case PG_WP_SCROLL_Y:
    if (data > w->in->child.h - w->in->r.h)
      data = w->in->child.h - w->in->r.h;
    if (data < 0)
      data = 0;
    if (maindiv->translation.y != -data) {
      maindiv->translation.y = -data;
      maindiv->flags |= DIVNODE_SCROLL_ONLY | DIVNODE_NEED_RECALC;
      w->dt->flags |= DIVTREE_NEED_REDRAW;
      hotspot_free();
    }
    maindiv->flags |= DIVNODE_DIVSCROLL | DIVNODE_EXTEND_HEIGHT;
    break;

  case PG_WP_NAME:
    if (iserror(rdhandle((void **)&str,PG_TYPE_PGSTRING,-1,data))) 
      return mkerror(PG_ERRT_HANDLE,18);
    w->name = handle_canonicalize((handle) data);
    break;

  case PG_WP_PUBLICBOX:
    w->publicbox = data;
    break;

  case PG_WP_BIND:
    w->scrollbind = data;
    break;

  case PG_WP_THOBJ:
    maindiv->state = data;
    resizewidget(w);
    w->in->flags |= DIVNODE_NEED_RECALC;
    w->dt->flags |= DIVTREE_NEED_RECALC;
    break;

  case PG_WP_TRIGGERMASK:
    w->trigger_mask = data;
    break;
     
  case PG_WP_HILIGHTED:
    {
      struct widget *p;
      
      //
      // Pass the message onto the other sub widgets
      //
      p = widget_traverse(w, PG_TRAVERSE_CHILDREN, 0);
      while (p) {
	widget_set(p, PG_WP_HILIGHTED, data);
	p = widget_traverse(p, PG_TRAVERSE_FORWARD,1);
      }
    }
    break;

  case PG_WP_AUTO_ORIENTATION:
    w->auto_orientation = data;
    break;

  default:
    return mkerror(PG_ERRT_BADPARAM,6);   /* Unknown property */
  }
  return success;
}
Beispiel #5
0
void widget_remove(struct widget *w) {
  struct widget *child;
  struct divnode **old_where;
  DBG("%p, type %d\n",w,w->type);

  /* Get out of the timer list */
  remove_timer(w);

  /* Detach the widget from the widget tree */
  old_where = w->where;
  widget_attach(w,NULL,NULL,0);

  /* Detach all children from this widget */
  while ((child = widget_traverse(w,PG_TRAVERSE_CHILDREN,0)) && child->where) {
    DBG("removing child %p, type %d. where %p\n",child,child->type,child->where);
    widget_attach(child,NULL,NULL,0);
  }

  /* Note that the widget may have it's 'sub' attachment point filled even
   * if it doesn't have any real children, if it was used inside another widget.
   * We need to reattach any child divnodes this widget still has back to its insertion
   * point, so that they are properly deleted when the widget owning this subtree is
   * finished removing its component pieces.
   * Note that we're attaching it to the widget's former "where" attachment point,
   * since by this time it's been detached from the widget tree and w->where
   * should be NULL.
   */
  if (w->out && *w->out && w->sub && *w->sub) {
    /* If this widget has two subtrees, we need to append them into just one
     * before we link this back into the parent's subtree. A messy process, but
     * cleaner than the alternative (voodoo memory management :)
     */

    struct divnode *n;
    n = *w->sub;
    while (n->next) n = n->next;
    n->next = *w->out;
    if (old_where)
      *old_where = *w->sub;
    *w->out = *w->sub = NULL;
  }
  /* If there's only one child, link it directly */
  else if (w->out && *w->out) {
    if (old_where)
      *old_where = *w->out;
    *w->out = NULL;
  }
  else if (w->sub && *w->sub) {
    if (old_where)
      *old_where = *w->sub;
    *w->sub = NULL;
  }

  /* Free the widget's private data and divnodes */
  if (w->def->remove) (*w->def->remove)(w);
  
  /* Free the array of subclass data */
  g_free(w->subclasses);

  /* Free the widget itself */
#ifdef DEBUG_KEYS
  num_widgets--;
#endif
  g_free(w);
}
Beispiel #6
0
/* Fillstyle interpreter- generates/refreshes a gropnode list */
g_error exec_fillstyle_inner(struct gropctxt *ctx,u16 state,
			     u16 property) {
  g_error e;
  u32 fssize;  /* Fillstyle size */
  unsigned char *fs;  /* Pointer to the actual fillstyle data */
  unsigned char *p,*plimit;
  unsigned char op;
  int r,g,b;          /* For color arithmetic */
  struct widget *w;
  int stackframe = fsstkpos-4;

  /* Look up the fillstyle */
  e = rdhandle((void**)&fs,PG_TYPE_FILLSTYLE,-1,theme_lookup(state,property));
  errorcheck;

  if (!fs) {
    
    /* When our best just isn't good enough... */
    if (property == PGTH_P_BACKDROP || property == PGTH_P_BORDER_FILL)
      return success;

    /* The default fillstyle, if no theme is loaded or no 
       theme has defined the property*/

    addgrop(ctx,PG_GROP_SETCOLOR);
    ctx->current->param[0] = VID(color_pgtohwr) (0x000000);
    
    switch (state) {

    case PGTH_O_BUTTON_ON:      /* 2 borders */
      addgropsz(ctx,PG_GROP_FRAME,ctx->r.x,ctx->r.y,ctx->r.w,ctx->r.h);
      ctx->r.x += 1; ctx->r.y += 1; ctx->r.w -= 2; ctx->r.h -= 2;
    default:                    /* 1 border */
      addgropsz(ctx,PG_GROP_FRAME,ctx->r.x,ctx->r.y,ctx->r.w,ctx->r.h);
      ctx->r.x += 1; ctx->r.y += 1; ctx->r.w -= 2; ctx->r.h -= 2;
    case PGTH_O_LABEL_SCROLL:   /* No border */
      addgrop(ctx,PG_GROP_SETCOLOR);
      ctx->current->param[0] = VID(color_pgtohwr) (theme_lookup(state,PGTH_P_BGCOLOR));
      addgropsz(ctx,PG_GROP_RECT,ctx->r.x,ctx->r.y,ctx->r.w,ctx->r.h);      
    }
    return success;
  }

  /* Process the opcodes */
  fssize = *(((u32 *)fs)++);
  p = fs;
  plimit = fs+fssize;
  while (p<plimit) {
    op = *(p++);
    
    /* These must occur in MSB to LSB order! (see constants.h) */
    if (op & PGTH_OPSIMPLE_GROP) {
      /* 1-byte gropnode */
      e = fsgrop(ctx,op & (PGTH_OPSIMPLE_GROP-1));
      errorcheck;
    }
    else if (op & PGTH_OPSIMPLE_LITERAL) {
      /* 1-byte literal */
      fsstack[fsstkpos++] = op & (PGTH_OPSIMPLE_LITERAL-1);
    }
    else if (op & PGTH_OPSIMPLE_CMDCODE) {
      /* Command code */
      switch (op) {

      case PGTH_OPCMD_LONGLITERAL:
	if ((plimit-p)<4)
	  return mkerror(PG_ERRT_BADPARAM,91);  /* Truncated opcode */
	 fsstack[fsstkpos++] = NEXTLONG;
	 p += 4;
	 break;

      case PGTH_OPCMD_LONGGROP:
	if ((plimit-p)<2)
	  return mkerror(PG_ERRT_BADPARAM,91);  /* Truncated opcode */
	e = fsgrop(ctx,NEXTSHORT);
	p += 2;
	errorcheck;
	break;

      case PGTH_OPCMD_LONGGET:
	if (plimit<=p)
	  return mkerror(PG_ERRT_BADPARAM,91);  /* Truncated opcode */
	e = fsget(*(p++)+stackframe);
	errorcheck;
	break;

      case PGTH_OPCMD_LONGSET:
	if (plimit<=p)
	  return mkerror(PG_ERRT_BADPARAM,91);  /* Truncated opcode */
	e = fsset(*(p++)+stackframe);
	errorcheck;
	break;

      case PGTH_OPCMD_PROPERTY:
	if ((plimit-p)<4)
	  return mkerror(PG_ERRT_BADPARAM,91);  /* Truncated opcode */
	fsa = NEXTSHORT;
	p += 2;
	fsb = NEXTSHORT;
	p += 2;
	fsstack[fsstkpos++] = theme_lookup(fsa,fsb);

#ifdef CONFIG_ANIMATION
	/* If it depends on time or randomness, turn on the animated flag in the divnode */
	if ((fsb==PGTH_P_TICKS || fsb==PGTH_P_RANDOM) && ctx->owner)
	  ctx->owner->flags |= DIVNODE_ANIMATED;
#endif
	break;

      case PGTH_OPCMD_LOCALPROP:
	if ((plimit-p)<2)
	  return mkerror(PG_ERRT_BADPARAM,91);  /* Truncated opcode */
	 fsa = NEXTSHORT;
	 p += 2;
#ifdef DEBUG_THEME
	 printf("Local theme lookup, property %d\n",(int)fsa);
#endif
	 fsstack[fsstkpos++] = theme_lookup(state,fsa);

#ifdef CONFIG_ANIMATION
	/* If it depends on time or randomness, turn on the animated flag in the divnode */
	if ((fsa==PGTH_P_TICKS || fsa==PGTH_P_RANDOM) && ctx->owner)
	  ctx->owner->flags |= DIVNODE_ANIMATED;
#endif
	break;

      case PGTH_OPCMD_PLUS:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa + fsb;
	break;

      case PGTH_OPCMD_MINUS:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa -  fsb;
	break;

      case PGTH_OPCMD_MULTIPLY:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa * fsb;
	break;

      case PGTH_OPCMD_SHIFTL:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa << fsb; 
	break;
	
      case PGTH_OPCMD_SHIFTR:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa >> fsb;
	break;

      case PGTH_OPCMD_OR:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa | fsb;
	break;

      case PGTH_OPCMD_AND:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa & fsb;
	break;

      case PGTH_OPCMD_EQ:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa == fsb;
	break;

      case PGTH_OPCMD_LT:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa < fsb;
	break;

      case PGTH_OPCMD_GT:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa > fsb;
	break;

      case PGTH_OPCMD_LOGICAL_OR:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa || fsb;
	break;

      case PGTH_OPCMD_LOGICAL_AND:
	e = fspopargs();
	errorcheck;
	fsstack[fsstkpos++] = fsa && fsb;
	break;

      case PGTH_OPCMD_LOGICAL_NOT:
	fsstack[fsstkpos-1] = !fsstack[fsstkpos-1];
	break;

      case PGTH_OPCMD_DIVIDE:   
	e = fspopargs();
	errorcheck;
	if (fsb)
	  fsstack[fsstkpos++] = fsa / fsb; 
	else
	  fsstack[fsstkpos++] = 0xFFFFFFFF;   /* limit of fsa/fsb as fsb approaches 0 */
	break;

      case PGTH_OPCMD_COLORADD:
	e = fspopargs();
	errorcheck;
	r = getred(fsa);
	g = getgreen(fsa);
	b = getblue(fsa);
	r += getred(fsb);
	g += getgreen(fsb);
	b += getblue(fsb);
	if (r>255) r = 255;
	if (g>255) g = 255;
	if (b>255) b = 255;
	fsstack[fsstkpos++] = mkcolor(r,g,b);
	break;

      case PGTH_OPCMD_COLORSUB:
	e = fspopargs();
	errorcheck;
	r = getred(fsa);
	g = getgreen(fsa);
	b = getblue(fsa);
	r -= getred(fsb);
	g -= getgreen(fsb);
	b -= getblue(fsb);
	if (r<0) r = 0;
	if (g<0) g = 0;
	if (b<0) b = 0;
	fsstack[fsstkpos++] = mkcolor(r,g,b);
	break;

      case PGTH_OPCMD_COLORDIV:
	e = fspopargs();
	errorcheck;
	r = getred(fsa);
	g = getgreen(fsa);
	b = getblue(fsa);
	r = getred(fsb) ? (r/getred(fsb)) : 0xFF;     /* Avoid divide by zero */
	g = getgreen(fsb) ? (g/getgreen(fsb)) : 0xFF; 
	b = getred(fsb) ? (b/getblue(fsb)) : 0xFF;
     	fsstack[fsstkpos++] = mkcolor(r,g,b);
	break;

      case PGTH_OPCMD_COLORMULT:
	e = fspopargs();
	errorcheck;
	r = getred(fsa);
	g = getgreen(fsa);
	b = getblue(fsa);
	r *= getred(fsb);
	g *= getgreen(fsb);
	b *= getblue(fsb);
	if (r>255) r = 255;
	if (g>255) g = 255;
	if (b>255) b = 255;
	fsstack[fsstkpos++] = mkcolor(r,g,b);
	break;

      case PGTH_OPCMD_QUESTIONCOLON:
	if (fsstkpos<3)
	  return mkerror(PG_ERRT_BADPARAM,88);  /* Stack underflow */
	fsstkpos -= 2;
	fsstack[fsstkpos-1] = fsstack[fsstkpos+1] ? 
	  fsstack[fsstkpos] : fsstack[fsstkpos-1];
	break;

      case PGTH_OPCMD_WIDGET:
	if (ctx->owner && ctx->owner->owner)
	  fsstack[fsstkpos++] = hlookup(ctx->owner->owner,NULL);
	else
	  fsstack[fsstkpos++] = 0;
	break;
	
      case PGTH_OPCMD_TRAVERSEWGT:
	if (fsstkpos<3)
	  return mkerror(PG_ERRT_BADPARAM,88);  /* Stack underflow */
	fsstkpos -= 2;
	e = rdhandle((void**)&w, PG_TYPE_WIDGET, -1, fsstack[fsstkpos+1]);
	errorcheck;
	if (w)
	  fsstack[fsstkpos-1] = hlookup(widget_traverse(w,fsstack[fsstkpos],fsstack[fsstkpos-1]),NULL);
	else
	  fsstack[fsstkpos-1] = 0;
	break;

      case PGTH_OPCMD_GETWIDGET:
	e = fspopargs();
	errorcheck;
	e = rdhandle((void**)&w, PG_TYPE_WIDGET, -1, fsa);
	errorcheck;
	if (w) 
	  fsstack[fsstkpos++] = widget_get(w,fsb);
	else
	  fsstack[fsstkpos++] = 0;
	break;

      case PGTH_OPCMD_CALL:
	if ((plimit-p)<4)
	  return mkerror(PG_ERRT_BADPARAM,91);  /* Truncated opcode */
	fsa = NEXTSHORT;
	p += 2;
	fsb = NEXTSHORT;
	p += 2;
	e = exec_fillstyle_inner(ctx,fsa,fsb);
	errorcheck;
	break;

      case PGTH_OPCMD_LOCALCALL:
	if ((plimit-p)<2)
	  return mkerror(PG_ERRT_BADPARAM,91);  /* Truncated opcode */
	fsb = NEXTSHORT;
	p += 2;
	e = exec_fillstyle_inner(ctx,state,fsb);
	errorcheck;
	break;

      case PGTH_OPCMD_EXTENDED:
	/* extended command */
	op = *(p++);
	switch (op) {

	case PGTH_EXCMD_SKIP_IF:
	  if (!fsstack[--fsstkpos]) {
	    --fsstkpos;
	    break;
	  }
	  /* else proceed to EXCMD_SKIP */

	case PGTH_EXCMD_SKIP:
	  p += (s32)fsstack[--fsstkpos];
	  break;

	}
	break;

      }
    }
    else if (op & PGTH_OPSIMPLE_GET) {
      /* 1-byte get */
      e = fsget((op & (PGTH_OPSIMPLE_GET-1)) + stackframe);
      errorcheck;
    }
    else {
      /* 1-byte set */
      e = fsset(op + stackframe);
      errorcheck;
    }

#ifdef DEBUG_THEME
    /* trace */
    printf("FILLSTYLE --- Op: 0x%02X Stk:",op);
    for (fsa=0;fsa<fsstkpos;fsa++)
      printf(" %d",(int)fsstack[fsa]);
    printf("\n"); 
#endif
    
    /* check for stack over/underflow */
    if (fsstkpos<0)
      return mkerror(PG_ERRT_BADPARAM,88);  /* Stack underflow */
    if (fsstkpos>=FSSTACKSIZE)
      return mkerror(PG_ERRT_BADPARAM,89);  /* Stack overflow */
  }
Beispiel #7
0
/* This is called whenever the widget is attached, after the attaching
 * process is complete. We use this as a hook for managing the tab and tab_bar.
 */
g_error tabpage_post_attach(struct widget *self, struct widget *parent, int rship) {
  struct widget *tab, *tab_bar, *parent_tab;
  g_error e;
  handle existing_bar = 0;

  /* Dereference handles */
  e = rdhandle((void**)&tab, PG_TYPE_WIDGET, self->owner, DATA->htab);
  errorcheck;
  e = rdhandle((void**)&tab_bar, PG_TYPE_WIDGET, self->owner, DATA->htab_bar);
  errorcheck;

  /* Detach our tab. It will be reattached later if necessary */
  e = widget_derive(&tab, &DATA->htab, tab->type, NULL, 0, 0, self->owner);
  errorcheck;

  /* If we already have a tab bar but it's empty, delete it */
  if (DATA->htab_bar && !widget_traverse(tab_bar, PG_TRAVERSE_CHILDREN, 0)) {
    handle_free(self->owner, DATA->htab_bar);
    DATA->htab_bar = 0;
  }

  /* Are we being attached rather than detached? */
  if (parent) {

    /* If we're attaching before or after another tab page, share its tab bar */
    if (parent->type==PG_WIDGET_TABPAGE &&
	(rship==PG_DERIVE_BEFORE || rship==PG_DERIVE_AFTER)) {
      struct widget *self = parent;
      existing_bar = DATA->htab_bar;
    }
    DATA->htab_bar = existing_bar;

    /* Otherwise, create a new tab bar */
    if (!DATA->htab_bar) {
      tab_bar = NULL;
      e = widget_derive(&tab_bar, &DATA->htab_bar, PG_WIDGET_TOOLBAR,
			self, self->h, PG_DERIVE_BEFORE, self->owner);
      errorcheck;
      e = widget_set(tab_bar, PG_WP_THOBJ, PGTH_O_TAB_BAR);
      errorcheck;
      tab_bar->auto_orientation = PG_AUTO_SIDE;
    }

    /* If we're attaching on an existing bar, attach the tab in the same
     * relative order as the tab pages themselves.
     */
    parent_tab = NULL;
    rdhandle((void**)&parent_tab, PG_TYPE_WIDGET, self->owner, widget_get(parent, PG_WP_TAB));
    if (existing_bar && parent_tab) {
      e = widget_derive(&tab, &DATA->htab, tab->type,
			parent_tab, parent_tab->h, rship, self->owner);
      errorcheck;
    }
    /* Otherwise just put it in our tab bar directly */
    else {
      e = widget_derive(&tab, &DATA->htab, tab->type,
			tab_bar, tab_bar->h, PG_DERIVE_INSIDE, self->owner);
      errorcheck;
    }      
		
    /* If we were here first, make ourselves active */
    if (!existing_bar) {
      e = widget_set(self, PG_WP_ON, 1);
      errorcheck;
    }
  }

  return success;
}