/* 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; }
/* 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); }
/* 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; }
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; }
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); }
/* 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 */ }
/* 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; }