diff options
Diffstat (limited to 'engines/sci/gfx/widgets.c')
-rw-r--r-- | engines/sci/gfx/widgets.c | 2600 |
1 files changed, 2600 insertions, 0 deletions
diff --git a/engines/sci/gfx/widgets.c b/engines/sci/gfx/widgets.c new file mode 100644 index 0000000000..f81a717f21 --- /dev/null +++ b/engines/sci/gfx/widgets.c @@ -0,0 +1,2600 @@ +/*************************************************************************** + widgets.c Copyright (C) 2000,01 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ + +#include <sci_memory.h> +#include <gfx_widgets.h> + +#undef GFXW_DEBUG_DIRTY /* Enable to debug dirty rectangle propagation (writes to stderr) */ + +#ifdef GFXW_DEBUG_DIRTY +# define DDIRTY fprintf(stderr, "%s:%5d| ", __FILE__, __LINE__); fprintf +#else +# define DDIRTY if (0) fprintf +#endif + +point_t gfxw_point_zero = {0, 0}; + +#define MAX_SERIAL_NUMBER 0x7fffffff +static int widget_serial_number_counter = 0x10000; /* Avoid confusion with IDs */ + +#ifdef GFXW_DEBUG_WIDGETS + +gfxw_widget_t *debug_widgets[GFXW_DEBUG_WIDGETS]; +int debug_widget_pos = 0; + +static void +_gfxw_debug_add_widget(gfxw_widget_t *widget) +{ + if (debug_widget_pos == GFXW_DEBUG_WIDGETS) { + GFXERROR("WIDGET DEBUG: Allocated the maximum number of %d widgets- Aborting!\n", GFXW_DEBUG_WIDGETS); + BREAKPOINT(); + } + debug_widgets[debug_widget_pos++] = widget; +} + +static void +_gfxw_debug_remove_widget(gfxw_widget_t *widget) { + int i; + int found = 0; + for (i = 0; i < debug_widget_pos; i++) { + if (debug_widgets[i] == widget) { + memmove(debug_widgets + i, debug_widgets + i + 1, + (sizeof (gfxw_widget_t *)) * (debug_widget_pos - i - 1)); + debug_widgets[debug_widget_pos--] = NULL; + found++; + } + } + + if (found > 1) { + GFXERROR("While removing widget: Found it %d times!\n", found); + BREAKPOINT(); + } + + if (found == 0) { + GFXERROR("Attempted removal of unregistered widget!\n"); + BREAKPOINT(); + } +} +#else /* !GFXW_DEBUG_WIDGETS */ +#define _gfxw_debug_add_widget(a) +#define _gfxw_debug_remove_widget(a) +#endif + + +static inline void +indent(int indentation) +{ + int i; + for (i = 0; i < indentation; i++) + sciprintf(" "); +} + +static void +_gfxw_print_widget(gfxw_widget_t *widget, int indentation) +{ + unsigned int i; + char flags_list[] = "VOCDTMI"; + + indent(indentation); + + if (widget->magic == GFXW_MAGIC_VALID) { + if (widget->visual) + sciprintf("v "); + else + sciprintf("NoVis "); + } else if (widget->magic == GFXW_MAGIC_INVALID) + sciprintf("INVALID "); + + sciprintf("S%08x", widget->serial); + + if (widget->ID != GFXW_NO_ID) { + sciprintf("#%x", widget->ID); + + if (widget->subID != GFXW_NO_ID) + sciprintf(":%x ", widget->subID); + else + sciprintf(" "); + } + + sciprintf("[(%d,%d)(%dx%d)]", widget->bounds.x, widget->bounds.y, widget->bounds.xl, widget->bounds.yl); + + for (i = 0; i < strlen(flags_list); i++) + if (widget->flags & (1 << i)) + sciprintf("%c", flags_list[i]); + + sciprintf(" "); +} + +static int +_gfxwop_print_empty(gfxw_widget_t *widget, int indentation) +{ + _gfxw_print_widget(widget, indentation); + sciprintf("<untyped #%d>", widget->type); + + return 0; +} + + +gfxw_widget_t * +_gfxw_new_widget(int size, gfxw_widget_type_t type) +{ + gfxw_widget_t *widget = (gfxw_widget_t*)sci_malloc(size); +#ifdef SATISFY_PURIFY + memset(widget, 0, size); +#endif + + widget->magic = GFXW_MAGIC_VALID; + widget->parent = NULL; + widget->visual = NULL; + widget->next = NULL; + widget->type = type; + widget->bounds = gfx_rect(0, 0, 0, 0); + widget->flags = GFXW_FLAG_DIRTY; + widget->ID = GFXW_NO_ID; + widget->subID = GFXW_NO_ID; + widget->serial = widget_serial_number_counter++; + widget->widget_priority = -1; + + widget_serial_number_counter &= MAX_SERIAL_NUMBER; + + widget->draw = NULL; + widget->widfree = NULL; + widget->tag = NULL; + widget->print = _gfxwop_print_empty; + widget->should_replace = NULL; + widget->compare_to = widget->equals = widget->superarea_of = NULL; + + _gfxw_debug_add_widget(widget); + + return widget; +} + + +static inline int +verify_widget(gfxw_widget_t *widget) +{ + if (!widget) { + GFXERROR("Attempt to use NULL widget\n"); +#ifdef GFXW_DEBUG_WIDGETS + BREAKPOINT(); +#endif /* GFXW_DEBUG_WIDGETS */ + return 1; + } else if (widget->magic != GFXW_MAGIC_VALID) { + if (widget->magic == GFXW_MAGIC_INVALID) { + GFXERROR("Attempt to use invalidated widget\n"); + } else { + GFXERROR("Attempt to use non-widget\n"); + } +#ifdef GFXW_DEBUG_WIDGETS + BREAKPOINT(); +#endif /* GFXW_DEBUG_WIDGETS */ + return 1; + } + return 0; +} + +#define VERIFY_WIDGET(w) \ + if (verify_widget((gfxw_widget_t *)(w))) { GFXERROR("Error occured while validating widget\n"); } + +static void +_gfxw_unallocate_widget(gfx_state_t *state, gfxw_widget_t *widget) +{ + if (GFXW_IS_TEXT(widget)) { + gfxw_text_t *text = (gfxw_text_t *) widget; + + if (text->text_handle) { + if (!state) { + GFXERROR("Attempt to free text without supplying mode to free it from!\n"); + BREAKPOINT(); + } else { + gfxop_free_text(state, text->text_handle); + text->text_handle = NULL; + } + } + } + + widget->magic = GFXW_MAGIC_INVALID; + free(widget); + _gfxw_debug_remove_widget(widget); +} + +#define GFX_ASSERT(_x) \ + { \ + int retval = (_x); \ + if (retval == GFX_ERROR) { \ + GFXERROR("Error occured while drawing widget!\n"); \ + return 1; \ + } else if (retval == GFX_FATAL) { \ + GFXERROR("Fatal error occured while drawing widget!\nGraphics state invalid; aborting program..."); \ + exit(1); \ + } \ + } + + +/**********************************/ +/*********** Widgets **************/ +/**********************************/ + +/* Base class operations and common stuff */ + +/* Assertion for drawing */ +#define DRAW_ASSERT(widget, exp_type) \ + if (!(widget)) { \ + sciprintf("L%d: NULL widget!\n", __LINE__); \ + return 1; \ + } \ + if (!(widget)->print) { \ + sciprintf("L%d: Widget of type %d does not have print function!\n", __LINE__, \ + (widget)->type); \ + } \ + if ((widget)->type != (exp_type)) { \ + sciprintf("L%d: Error in widget: Expected type " # exp_type "(%d) but got %d\n", \ + __LINE__, exp_type, (widget)->type); \ + sciprintf("Erroneous widget: "); \ + widget->print(widget, 4); \ + sciprintf("\n"); \ + return 1; \ + } \ + if (!(widget->flags & GFXW_FLAG_VISIBLE)) \ + return 0; \ + if (!(widget->type == GFXW_VISUAL || widget->visual)) { \ + sciprintf("L%d: Error while drawing widget: Widget has no visual\n", __LINE__); \ + sciprintf("Erroneous widget: "); \ + widget->print(widget, 1); \ + sciprintf("\n"); \ + return 1; \ + } + + +static inline int +_color_equals(gfx_color_t a, gfx_color_t b) +{ + if (a.mask != b.mask) + return 0; + + if (a.mask & GFX_MASK_VISUAL) { + if (a.visual.r != b.visual.r + || a.visual.g != b.visual.g + || a.visual.b != b.visual.b + || a.alpha != b.alpha) + return 0; + } + + if (a.mask & GFX_MASK_PRIORITY) + if (a.priority != b.priority) + return 0; + + if (a.mask & GFX_MASK_CONTROL) + if (a.control != b.control) + return 0; + + return 1; +} + +static int +_gfxwop_basic_set_visual(gfxw_widget_t *widget, gfxw_visual_t *visual) +{ + widget->visual = visual; + + if (widget->parent) { + DDIRTY(stderr,"basic_set_visual: DOWNWARDS rel(%d,%d,%d,%d, 1)\n", + GFX_PRINT_RECT(widget->bounds)); + widget->parent->add_dirty_rel(widget->parent, widget->bounds, 1); + } + + return 0; +} + + +static int +_gfxwop_basic_should_replace(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + return 0; +} + +static inline void +_gfxw_set_ops(gfxw_widget_t *widget, gfxw_point_op *draw, gfxw_op *free, gfxw_op *tag, gfxw_op_int *print, + gfxw_bin_op *compare_to, gfxw_bin_op *equals, gfxw_bin_op *superarea_of) +{ + widget->draw = draw; + widget->widfree = free; + widget->tag = tag; + widget->print = print; + widget->compare_to = compare_to; + widget->equals = equals; + widget->superarea_of = superarea_of; + + widget->should_replace = _gfxwop_basic_should_replace; + widget->set_visual = _gfxwop_basic_set_visual; +} + +void +gfxw_remove_widget_from_container(gfxw_container_t *container, gfxw_widget_t *widget) +{ + gfxw_widget_t **seekerp; + + if (!container) { + GFXERROR("Attempt to remove widget from NULL container!\n"); + BREAKPOINT(); + } + + seekerp = &(container->contents); + + if (GFXW_IS_LIST(widget) && GFXW_IS_PORT(container)) { + gfxw_port_t *port = (gfxw_port_t *) container; + if (port->decorations == (gfxw_list_t *) widget) { + port->decorations = NULL; + return; + } + } + + while (*seekerp && *seekerp != widget) + seekerp = &((*seekerp)->next); + + if (!*seekerp) { + GFXERROR("Internal error: Attempt to remove widget from container it was not contained in!\n"); + sciprintf("Widget:"); + widget->print(GFXW(widget), 1); + sciprintf("Container:"); + widget->print(GFXW(container), 1); + BREAKPOINT(); + return; + } + + if (container->nextpp == &(widget->next)) + container->nextpp = seekerp; + + *seekerp = widget->next; /* Remove it */ + widget->parent = NULL; + widget->next = NULL; +} + +static int +_gfxwop_basic_free(gfxw_widget_t *widget) +{ + gfxw_visual_t *visual = widget->visual; + gfx_state_t *state = (visual)? visual->gfx_state : NULL; + + DDIRTY(stderr, "BASIC-FREE: SomeAddDirty\n"); + + if (widget->parent) { + if (GFXW_IS_CONTAINER(widget)) + widget->parent->add_dirty_abs(widget->parent, widget->bounds, 1); + else + widget->parent->add_dirty_rel(widget->parent, widget->bounds, 1); + + gfxw_remove_widget_from_container(widget->parent, widget); + } + + _gfxw_unallocate_widget(state, widget); + + + return 0; +} + + +static int +_gfxwop_basic_tag(gfxw_widget_t *widget) +{ + widget->flags |= GFXW_FLAG_TAGGED; + + return 0; +} + + +static int +_gfxwop_basic_compare_to(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + return 1; +} + + +static int +_gfxwop_basic_equals(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + return 0; +} + + +static int +_gfxwop_basic_superarea_of(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + return (widget == other); +} + +/*-------------*/ +/**** Boxes ****/ +/*-------------*/ + +static inline rect_t +_move_rect(rect_t rect, point_t point) +{ + return gfx_rect(rect.x + point.x, rect.y + point.y, rect.xl, rect.yl); +} + +static inline void +_split_rect(rect_t rect, point_t *p1, point_t *p2) +{ + p1->x = rect.x; + p1->y = rect.y; + p2->x = rect.x + rect.xl; + p2->y = rect.y + rect.yl; +} + +static inline point_t +_move_point(rect_t rect, point_t point) +{ + return gfx_point(rect.x + point.x, rect.y + point.y); +} + +static int +_gfxwop_box_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_box_t *box = (gfxw_box_t *) widget; + DRAW_ASSERT(widget, GFXW_BOX); + GFX_ASSERT(gfxop_draw_box(box->visual->gfx_state, _move_rect(box->bounds, pos), box->color1, + box->color2, box->shade_type)); + + return 0; +} + +static int +_gfxwop_box_print(gfxw_widget_t *widget, int indentation) +{ + _gfxw_print_widget(widget, indentation); + sciprintf("BOX"); + return 0; +} + +static int +_gfxwop_box_superarea_of(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_box_t *box = (gfxw_box_t *) widget; + + if (box->color1.alpha) + return 0; + + if (box->shade_type != GFX_BOX_SHADE_FLAT && box->color2.alpha) + return 0; + + if (!gfx_rect_subset(other->bounds, box->bounds)) + return 0; + + return 1; +} + +static int +_gfxwop_box_equals(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_box_t *wbox = (gfxw_box_t *) widget, *obox; + if (other->type != GFXW_BOX) + return 0; + + obox = (gfxw_box_t *) other; + + if (!gfx_rect_equals(wbox->bounds, obox->bounds)) + return 0; + + if (!_color_equals(wbox->color1, obox->color1)) + return 0; + + if (wbox->shade_type != obox->shade_type) + return 0; + + if (wbox->shade_type != GFX_BOX_SHADE_FLAT + && _color_equals(wbox->color2, obox->color2)) + return 0; + + return 1; +} + +void +_gfxw_set_ops_BOX(gfxw_widget_t *widget) +{ + _gfxw_set_ops(GFXW(widget), _gfxwop_box_draw, + _gfxwop_basic_free, + _gfxwop_basic_tag, + _gfxwop_box_print, + _gfxwop_basic_compare_to, + _gfxwop_box_equals, + _gfxwop_box_superarea_of); +} + +static inline int +_gfxw_color_get_priority(gfx_color_t color) +{ + return (color.mask & GFX_MASK_PRIORITY)? color.priority : -1; +} + +gfxw_box_t * +gfxw_new_box(gfx_state_t *state, rect_t area, gfx_color_t color1, gfx_color_t color2, gfx_box_shade_t shade_type) +{ + gfxw_box_t *widget = (gfxw_box_t *) _gfxw_new_widget(sizeof(gfxw_box_t), GFXW_BOX); + + widget->widget_priority = _gfxw_color_get_priority(color1); + widget->bounds = area; + widget->color1 = color1; + widget->color2 = color2; + widget->shade_type = shade_type; + + widget->flags |= GFXW_FLAG_VISIBLE; + + if ((color1.mask & GFX_MASK_VISUAL) + && ((state && (state->driver->mode->palette)) + || (!color1.alpha && !color2.alpha))) + widget->flags |= GFXW_FLAG_OPAQUE; + + _gfxw_set_ops_BOX(GFXW(widget)); + + return widget; +} + + +static inline gfxw_primitive_t * +_gfxw_new_primitive(rect_t area, gfx_color_t color, gfx_line_mode_t mode, + gfx_line_style_t style, gfxw_widget_type_t type) +{ + gfxw_primitive_t *widget = (gfxw_primitive_t *) _gfxw_new_widget(sizeof(gfxw_primitive_t), type); + + widget->widget_priority = _gfxw_color_get_priority(color); + widget->bounds = area; + widget->color = color; + widget->line_mode = mode; + widget->line_style = style; + + widget->flags |= GFXW_FLAG_VISIBLE; + return widget; +} + +/*------------------*/ +/**** Rectangles ****/ +/*------------------*/ + +static int +_gfxwop_primitive_equals(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_primitive_t *wprim = (gfxw_primitive_t *) widget, *oprim; + if (widget->type != other->type) + return 0; + + oprim = (gfxw_primitive_t *) other; + + if (!gfx_rect_equals(wprim->bounds, oprim->bounds)) + return 0; + + if (!_color_equals(wprim->color, oprim->color)) + return 0; + + if (wprim->line_mode != oprim->line_mode) + return 0; + + if (wprim->line_style != oprim->line_style) + return 0; + + return 1; +} + +static int +_gfxwop_rect_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_primitive_t *rect = (gfxw_primitive_t *) widget; + DRAW_ASSERT(widget, GFXW_RECT); + + GFX_ASSERT(gfxop_draw_rectangle(rect->visual->gfx_state, + gfx_rect(rect->bounds.x + pos.x, rect->bounds.y + pos.y, + rect->bounds.xl - 1, rect->bounds.yl - 1), + rect->color, rect->line_mode, rect->line_style)); + + return 0; +} + +static int +_gfxwop_rect_print(gfxw_widget_t *rect, int indentation) +{ + _gfxw_print_widget(GFXW(rect), indentation); + sciprintf("RECT"); + return 0; +} + + +void +_gfxw_set_ops_RECT(gfxw_widget_t *prim) +{ + _gfxw_set_ops(GFXW(prim), _gfxwop_rect_draw, + _gfxwop_basic_free, + _gfxwop_basic_tag, + _gfxwop_rect_print, + _gfxwop_basic_compare_to, + _gfxwop_primitive_equals, + _gfxwop_basic_superarea_of); +} + +gfxw_primitive_t * +gfxw_new_rect(rect_t rect, gfx_color_t color, gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + gfxw_primitive_t *prim = _gfxw_new_primitive(rect, color, line_mode, line_style, GFXW_RECT); + prim->bounds.xl++; + prim->bounds.yl++; /* Since it is actually one pixel bigger in each direction */ + + _gfxw_set_ops_RECT(GFXW(prim)); + + return prim; +} + + +/*-------------*/ +/**** Lines ****/ +/*-------------*/ + +static int +_gfxwop_line_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_primitive_t *line = (gfxw_primitive_t *) widget; + rect_t linepos = widget->bounds; + point_t p1, p2; + + linepos.xl--; + linepos.yl--; + + if (widget->type == GFXW_INVERSE_LINE) { + linepos.x += linepos.xl; + linepos.xl = -linepos.xl; + } else { + DRAW_ASSERT(widget, GFXW_LINE); + } + + + _split_rect(_move_rect(linepos, pos), &p1, &p2); + GFX_ASSERT(gfxop_draw_line(line->visual->gfx_state, p1, p2, + line->color, line->line_mode, line->line_style)); + + return 0; +} + +static int +_gfxwop_line_print(gfxw_widget_t *widget, int indentation) +{ + _gfxw_print_widget(widget, indentation); + if (widget->type == GFXW_INVERSE_LINE) + sciprintf("INVERSE-LINE"); + else + sciprintf("LINE"); + return 0; +} + +void +_gfxw_set_ops_LINE(gfxw_widget_t *prim) +{ + _gfxw_set_ops(GFXW(prim), _gfxwop_line_draw, + _gfxwop_basic_free, + _gfxwop_basic_tag, + _gfxwop_line_print, + _gfxwop_basic_compare_to, + _gfxwop_primitive_equals, + _gfxwop_basic_superarea_of); + +} + +gfxw_primitive_t * +gfxw_new_line(point_t start, point_t end, gfx_color_t color, gfx_line_mode_t line_mode, gfx_line_style_t line_style) +{ + gfxw_primitive_t *prim; + /* Encode into internal representation */ + rect_t line = gfx_rect (start.x, start.y, end.x - start.x, end.y - start.y); + + byte inverse = 0; + + if (line.xl < 0) { + line.x += line.xl; + line.y += line.yl; + line.xl = -line.xl; + line.yl = -line.yl; + } + + if (line.yl < 0) { + inverse = 1; + line.x += line.xl; + line.xl = -line.xl; + } + + line.xl++; + line.yl++; + + prim = _gfxw_new_primitive(line, color, line_mode, line_style, inverse? GFXW_INVERSE_LINE : GFXW_LINE); + + _gfxw_set_ops_LINE(GFXW(prim)); + + return prim; +} + +/*------------------------------*/ +/**** Views and static views ****/ +/*------------------------------*/ + + +gfxw_view_t * +_gfxw_new_simple_view(gfx_state_t *state, point_t pos, int view, int loop, int cel, int palette, int priority, int control, + gfx_alignment_t halign, gfx_alignment_t valign, int size, gfxw_widget_type_t type) +{ + gfxw_view_t *widget; + int width, height; + point_t offset; + + if (!state) { + GFXERROR("Attempt to create view widget with NULL state!\n"); + return NULL; + } + + if (gfxop_get_cel_parameters(state, view, loop, cel, &width, &height, &offset)) { + GFXERROR("Attempt to retreive cel parameters for (%d/%d/%d) failed (Maybe the values weren't checked beforehand?)\n", + view, cel, loop); + return NULL; + } + + widget = (gfxw_view_t *) _gfxw_new_widget(size, type); + + widget->widget_priority = priority; + widget->pos = pos; + widget->color.mask = + ((priority < 0)? 0 : GFX_MASK_PRIORITY) + | ((control < 0)? 0 : GFX_MASK_CONTROL); + widget->color.priority = priority; + widget->color.control = control; + widget->view = view; + widget->loop = loop; + widget->cel = cel; + widget->palette = palette; + + if (halign == ALIGN_CENTER) + widget->pos.x -= width >> 1; + else if (halign == ALIGN_RIGHT) + widget->pos.x -= width; + + if (valign == ALIGN_CENTER) + widget->pos.y -= height >> 1; + else if (valign == ALIGN_BOTTOM) + widget->pos.y -= height; + + widget->bounds = gfx_rect(widget->pos.x - offset.x, widget->pos.y - offset.y, width, height); + + widget->flags |= GFXW_FLAG_VISIBLE; + + return widget; +} + +int +_gfxwop_view_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_view_t *view = (gfxw_view_t *) widget; + DRAW_ASSERT(widget, GFXW_VIEW); + + GFX_ASSERT(gfxop_draw_cel(view->visual->gfx_state, view->view, view->loop, + view->cel, gfx_point(view->pos.x + pos.x, view->pos.y + pos.y), + view->color, view->palette)); + + return 0; +} + +static int +_gfxwop_static_view_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_view_t *view = (gfxw_view_t *) widget; + DRAW_ASSERT(widget, GFXW_VIEW); + + GFX_ASSERT(gfxop_draw_cel_static(view->visual->gfx_state, view->view, view->loop, + view->cel, _move_point(view->bounds, pos), + view->color, view->palette)); + + return 0; +} + +static int +_w_gfxwop_view_print(gfxw_widget_t *widget, const char *name, int indentation) +{ + gfxw_view_t *view = (gfxw_view_t *) widget; + _gfxw_print_widget(widget, indentation); + + sciprintf(name); + sciprintf("(%d/%d/%d)@(%d,%d)[p:%d,c:%d]", view->view, view->loop, view->cel, view->pos.x, view->pos.y, + (view->color.mask & GFX_MASK_PRIORITY)? view->color.priority : -1, + (view->color.mask & GFX_MASK_CONTROL)? view->color.control : -1); + + return 0; +} + +static int +_gfxwop_view_print(gfxw_widget_t *widget, int indentation) +{ + return _w_gfxwop_view_print(widget, "VIEW", indentation); +} + +static int +_gfxwop_static_view_print(gfxw_widget_t *widget, int indentation) +{ + return _w_gfxwop_view_print(widget, "PICVIEW", indentation); +} + +void +_gfxw_set_ops_VIEW(gfxw_widget_t *view, char stat) +{ + _gfxw_set_ops(GFXW(view), (stat) ? _gfxwop_static_view_draw : _gfxwop_view_draw, + _gfxwop_basic_free, + _gfxwop_basic_tag, + (stat) ? _gfxwop_static_view_print : _gfxwop_view_print, + _gfxwop_basic_compare_to, + _gfxwop_basic_equals, + _gfxwop_basic_superarea_of); +} + +gfxw_view_t * +gfxw_new_view(gfx_state_t *state, point_t pos, int view_nr, int loop, int cel, int palette, int priority, int control, + gfx_alignment_t halign, gfx_alignment_t valign, int flags) +{ + gfxw_view_t *view; + + if (flags & GFXW_VIEW_FLAG_DONT_MODIFY_OFFSET) { + int foo; + point_t offset; + gfxop_get_cel_parameters(state, view_nr, loop, cel, &foo, &foo, &offset); + pos.x += offset.x; + pos.y += offset.y; + } + + view = _gfxw_new_simple_view(state, pos, view_nr, loop, cel, palette, priority, control, halign, valign, + sizeof(gfxw_view_t), (flags & GFXW_VIEW_FLAG_STATIC) ? GFXW_STATIC_VIEW : GFXW_VIEW); + + _gfxw_set_ops_VIEW(GFXW(view), (char)(flags & GFXW_VIEW_FLAG_STATIC)); + + return view; +} + +/*---------------------*/ +/**** Dynamic Views ****/ +/*---------------------*/ + +static int +_gfxwop_dyn_view_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) widget; + DRAW_ASSERT(widget, GFXW_DYN_VIEW); + + GFX_ASSERT(gfxop_draw_cel(view->visual->gfx_state, view->view, view->loop, + view->cel, _move_point(view->draw_bounds, pos), + view->color, view->palette)); + + /* + gfx_color_t red; red.visual.r = 0xff; red.visual.g = red.visual.b = 0; red.mask = GFX_MASK_VISUAL; + GFX_ASSERT(gfxop_draw_rectangle(view->visual->gfx_state, + gfx_rect(view->bounds.x + pos.x, view->bounds.y + pos.y, + view->bounds.xl - 1, view->bounds.yl - 1), + red, 0, 0)); + */ + + + return 0; + +} + +static int +_gfxwop_draw_nop(gfxw_widget_t *widget, point_t pos) +{ + return 0; +} + +static int +_gfxwop_pic_view_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) widget; + DRAW_ASSERT(widget, GFXW_PIC_VIEW); + + GFX_ASSERT(gfxop_set_clip_zone(view->visual->gfx_state, view->parent->zone)); + GFX_ASSERT(gfxop_draw_cel_static_clipped(view->visual->gfx_state, + view->view, view->loop, + view->cel, + _move_point(view->draw_bounds, pos), + view->color, view->palette)); + + /* Draw again on the back buffer */ + GFX_ASSERT(gfxop_draw_cel(view->visual->gfx_state, + view->view, view->loop, + view->cel, + _move_point(view->draw_bounds, pos), + view->color, view->palette)); + + + widget->draw = _gfxwop_draw_nop; /* No more drawing needs to be done */ + + + return 0; +} + +static int +_gfxwop_some_view_print(gfxw_widget_t *widget, int indentation, const char *type_string) +{ + gfxw_dyn_view_t *view = (gfxw_dyn_view_t *) widget; + + _gfxw_print_widget(widget, indentation); + + sciprintf(type_string); + sciprintf(" SORT=%d z=%d seq=%d (%d/%d/%d)@(%d,%d)[p:%d,c:%d]; sig[%04x@%04x]", view->force_precedence, view->z, + view->sequence, view->view, view->loop, view->cel, view->pos.x, view->pos.y, + (view->color.mask & GFX_MASK_PRIORITY)? view->color.priority : -1, + (view->color.mask & GFX_MASK_CONTROL)? view->color.control : -1, + view->signal, view->signalp); + + return 0; +} + +static int +_gfxwop_dyn_view_print(gfxw_widget_t *widget, int indentation) +{ + return _gfxwop_some_view_print(widget, indentation, "DYNVIEW"); +} + +static int +_gfxwop_pic_view_print(gfxw_widget_t *widget, int indentation) +{ + return _gfxwop_some_view_print(widget, indentation, "PICVIEW"); +} + + +static int +_gfxwop_dyn_view_equals(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_dyn_view_t *wview = (gfxw_dyn_view_t *) widget, *oview; + if (!GFXW_IS_DYN_VIEW(other)) + return 0; + + oview = (gfxw_dyn_view_t *) other; + + if (wview->pos.x != oview->pos.x + || wview->pos.y != oview->pos.y + || wview->z != oview->z) + return 0; + + if (wview->view != oview->view + || wview->loop != oview->loop + || wview->cel != oview->cel) + return 0; + + if (!_color_equals(wview->color, oview->color)) + return 0; + + if (wview->flags != oview->flags) + return 0; + + return 1; +} + +static int +_gfxwop_dyn_view_compare_to(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + int retval; + gfxw_dyn_view_t *wview = (gfxw_dyn_view_t *) widget, *oview; + if (!GFXW_IS_DYN_VIEW(other)) + return 1; + + oview = (gfxw_dyn_view_t *) other; + + retval = wview->force_precedence - oview->force_precedence; + if (retval) + return retval; + + retval = wview->pos.y - oview->pos.y; + if (retval) + return retval; + + retval = (wview->z - oview->z); + if (retval) + return retval; + + return -(wview->sequence - oview->sequence); +} + + +void +_gfxw_set_ops_DYNVIEW(gfxw_widget_t *widget) +{ + _gfxw_set_ops(GFXW(widget), _gfxwop_dyn_view_draw, + _gfxwop_basic_free, + _gfxwop_basic_tag, + _gfxwop_dyn_view_print, + _gfxwop_dyn_view_compare_to, + _gfxwop_dyn_view_equals, + _gfxwop_basic_superarea_of); +} + +void +_gfxw_set_ops_PICVIEW(gfxw_widget_t *widget) +{ + _gfxw_set_ops_DYNVIEW(widget); + widget->draw = _gfxwop_pic_view_draw; + widget->print = _gfxwop_pic_view_print; +} + +gfxw_dyn_view_t * +gfxw_new_dyn_view(gfx_state_t *state, point_t pos, int z, int view, int loop, int cel, int palette, int priority, int control, + gfx_alignment_t halign, gfx_alignment_t valign, int sequence) +{ + gfxw_dyn_view_t *widget; + int width, height; + int xalignmod, yalignmod; + point_t offset; + + if (!state) { + GFXERROR("Attempt to create view widget with NULL state!\n"); + return NULL; + } + + if (gfxop_get_cel_parameters(state, view, loop, cel, &width, &height, &offset)) { + GFXERROR("Attempt to retreive cel parameters for (%d/%d/%d) failed (Maybe the values weren't checked beforehand?)\n", + view, cel, loop); + return NULL; + } + + widget = (gfxw_dyn_view_t *) _gfxw_new_widget(sizeof(gfxw_dyn_view_t), GFXW_DYN_VIEW); + + widget->pos = pos; + widget->color.mask = + ((priority < 0)? 0 : GFX_MASK_PRIORITY) + | ((control < 0)? 0 : GFX_MASK_CONTROL); + widget->widget_priority = priority; + widget->color.priority = priority; + widget->color.control = control; + widget->color.alpha = 0; + widget->color.visual.global_index = 0; + widget->color.visual.r = 0; + widget->color.visual.g = 0; + widget->color.visual.b = 0; + widget->view = view; + widget->loop = loop; + widget->cel = cel; + widget->sequence = sequence; + widget->force_precedence = 0; + widget->palette = palette; + + if (halign == ALIGN_CENTER) + xalignmod = width >> 1; + else if (halign == ALIGN_RIGHT) + xalignmod = width; + else + xalignmod = 0; + + if (valign == ALIGN_CENTER) + yalignmod = height >> 1; + else if (valign == ALIGN_BOTTOM) + yalignmod = height; + else + yalignmod = 0; + + widget->z = z; + + widget->draw_bounds = gfx_rect(widget->pos.x - xalignmod, + widget->pos.y - yalignmod - z, width, height); + widget->bounds = gfx_rect(widget->pos.x - offset.x - xalignmod, + widget->pos.y - offset.y - yalignmod - z, width, height); + + widget->flags |= GFXW_FLAG_VISIBLE; + + _gfxw_set_ops_DYNVIEW(GFXW(widget)); + + widget->signalp = NULL; + widget->signal = 0; + + return widget; +} + +/*------------*/ +/**** Text ****/ +/*------------*/ + +static int +_gfxwop_text_free(gfxw_widget_t *widget) +{ + gfxw_text_t *text = (gfxw_text_t *) widget; + free(text->text); + return _gfxwop_basic_free(widget); +} + +static int +_gfxwop_text_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_text_t *text = (gfxw_text_t *) widget; + DRAW_ASSERT(widget, GFXW_TEXT); + + GFX_ASSERT(gfxop_draw_text(text->visual->gfx_state, text->text_handle, _move_rect(text->bounds, pos))); + + return 0; +} + +static int +_gfxwop_text_alloc_and_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_text_t *text = (gfxw_text_t *) widget; + DRAW_ASSERT(widget, GFXW_TEXT); + + text->text_handle = + gfxop_new_text(widget->visual->gfx_state, text->font_nr, text->text, text->bounds.xl, + text->halign, text->valign, text->color1, + text->color2, text->bgcolor, text->text_flags); + + text->draw = _gfxwop_text_draw; + + return _gfxwop_text_draw(widget, pos); +} + + +static int +_gfxwop_text_print(gfxw_widget_t *widget, int indentation) +{ + _gfxw_print_widget(widget, indentation); + sciprintf("TEXT:'%s'", ((gfxw_text_t *)widget)->text); + return 0; +} + + +static int +_gfxwop_text_equals(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_text_t *wtext = (gfxw_text_t *) widget, *otext; + if (other->type != GFXW_TEXT) + return 0; + + otext = (gfxw_text_t *) other; + + if ((wtext->bounds.x != otext->bounds.x) + || (wtext->bounds.y != otext->bounds.y)) + return 0; + + if (wtext->halign != otext->halign + || wtext->valign != otext->valign) + return 0; + + if (wtext->text_flags != otext->text_flags) + return 0; + + if (wtext->font_nr != otext->font_nr) + return 0; + + /* if (!(_color_equals(wtext->color1, otext->color1) + && _color_equals(wtext->color2, otext->color2) + && _color_equals(wtext->bgcolor, otext->bgcolor))) + return 0; */ + + return 1; +} + +static int +_gfxwop_text_should_replace(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + gfxw_text_t *wtext = (gfxw_text_t *) widget, *otext; + + if (other->type != GFXW_TEXT) + return 0; + + otext = (gfxw_text_t *) other; + + return strcmp(wtext->text, otext->text); +} + + +static int +_gfxwop_text_compare_to(gfxw_widget_t *widget, gfxw_widget_t *other) +{ + + return 1; +} + +void +_gfxw_set_ops_TEXT(gfxw_widget_t *widget) +{ + _gfxw_set_ops(GFXW(widget), _gfxwop_text_alloc_and_draw, + _gfxwop_text_free, + _gfxwop_basic_tag, + _gfxwop_text_print, + _gfxwop_text_compare_to, + _gfxwop_text_equals, + _gfxwop_basic_superarea_of); + widget->should_replace = _gfxwop_text_should_replace; +} + +gfxw_text_t * +gfxw_new_text(gfx_state_t *state, rect_t area, int font, const char *text, gfx_alignment_t halign, + gfx_alignment_t valign, gfx_color_t color1, gfx_color_t color2, + gfx_color_t bgcolor, int text_flags) +{ + gfxw_text_t *widget = (gfxw_text_t *) + _gfxw_new_widget(sizeof(gfxw_text_t), GFXW_TEXT); + + widget->widget_priority = _gfxw_color_get_priority(color1); + widget->font_nr = font; + widget->text = (char*)sci_malloc(strlen(text) + 1); + widget->halign = halign; + widget->valign = valign; + widget->color1 = color1; + widget->color2 = color2; + widget->bgcolor = bgcolor; + widget->text_flags = text_flags; + widget->text_handle = NULL; + + strcpy(widget->text, text); + + gfxop_get_text_params(state, font, text, area.xl, &(widget->width), + &(widget->height), text_flags, + &(widget->lines_nr), &(widget->lineheight), + &(widget->lastline_width)); + + /* FIXME: Window is too big + area.x += _calc_needmove(halign, area.xl, widget->width); + area.y += _calc_needmove(valign, area.yl, widget->height); + */ + + if (halign == ALIGN_LEFT) + area.xl = widget->width; + if (valign == ALIGN_TOP) + area.yl = widget->height; + + widget->bounds = area; + + widget->flags |= GFXW_FLAG_VISIBLE; + + _gfxw_set_ops_TEXT(GFXW(widget)); + + return widget; +} + + +void +gfxw_text_info(gfx_state_t *state, gfxw_text_t *text, int *lines, + int *lineheight, int *offset) +{ + if (lines) + *lines = text->lines_nr; + if (lineheight) + *lineheight = text->lineheight; + if (offset) + *offset = text->lastline_width; +} + + +/***********************/ +/*-- Container types --*/ +/***********************/ + +static int +_gfxwop_container_add_dirty_rel(gfxw_container_t *cont, rect_t rect, int propagate) +{ + DDIRTY(stderr, "->container_add_dirty_rel(%d,%d,%d,%d, %d)\n", GFX_PRINT_RECT(rect), propagate); + return cont->add_dirty_abs(cont, _move_rect(rect, gfx_point(cont->zone.x, cont->zone.y)), propagate); +} + +static inline void +_gfxw_set_container_ops(gfxw_container_t *container, gfxw_point_op *draw, gfxw_op *free, gfxw_op *tag, + gfxw_op_int *print, gfxw_bin_op *compare_to, gfxw_bin_op *equals, + gfxw_bin_op *superarea_of, gfxw_visual_op *set_visual, + gfxw_unary_container_op *free_tagged, gfxw_unary_container_op *free_contents, + gfxw_rect_op *add_dirty, gfxw_container_op *add) +{ + _gfxw_set_ops(GFXW(container), + draw, + free, + tag, + print, + compare_to, + equals, + superarea_of); + + container->free_tagged = free_tagged; + container->free_contents = free_contents; + container->add_dirty_abs = add_dirty; + container->add_dirty_rel = _gfxwop_container_add_dirty_rel; + container->add = add; + container->set_visual = set_visual; +} + +static int +_w_gfxwop_container_print_contents(const char *name, gfxw_widget_t *widget, int indentation) +{ + gfxw_widget_t *seeker = widget; + + indent(indentation); + + sciprintf("--%s:\n", name); + + while (seeker) { + seeker->print(seeker, indentation + 1); + sciprintf("\n"); + seeker = seeker->next; + } + + return 0; +} + +static int +_w_gfxwop_container_print(gfxw_widget_t *widget, int indentation) +{ + gfx_dirty_rect_t *dirty; + gfxw_container_t *container = (gfxw_container_t *) widget; + if (!GFXW_IS_CONTAINER(widget)) { + GFXERROR("_w_gfxwop_container_print() called on type %d widget\n", widget->type); + return 1; + } + + sciprintf(" viszone=((%d,%d),(%dx%d))\n", container->zone.x, container->zone.y, + container->zone.xl, container->zone.yl); + + indent(indentation); + sciprintf("--dirty:\n"); + + dirty = container->dirty; + while (dirty) { + indent(indentation + 1); + sciprintf("dirty(%d,%d, (%dx%d))\n", + dirty->rect.x, dirty->rect.y, dirty->rect.xl, dirty->rect.yl); + dirty = dirty->next; + } + + _w_gfxwop_container_print_contents("contents", container->contents, indentation); + + return 0; +} + + + +gfxw_container_t * +_gfxw_new_container_widget(rect_t area, int size, gfxw_widget_type_t type) +{ + gfxw_container_t *widget = (gfxw_container_t *) + _gfxw_new_widget(size, type); + + widget->bounds = widget->zone = area; + widget->contents = NULL; + widget->nextpp = &(widget->contents); + widget->dirty = NULL; + + widget->flags |= GFXW_FLAG_VISIBLE | GFXW_FLAG_CONTAINER; + + return widget; +} + + +static void +recursively_free_dirty_rects(gfx_dirty_rect_t *dirty) +{ + if (dirty) { + recursively_free_dirty_rects(dirty->next); + free(dirty); + } +} + + +int ti = 0; + +static inline int +_gfxw_dirty_rect_overlaps_normal_rect(rect_t port_zone, rect_t bounds, rect_t dirty) +{ + bounds.x += port_zone.x; + bounds.y += port_zone.y; + return gfx_rects_overlap(bounds, dirty); +} + +static int +_gfxwop_container_draw_contents(gfxw_widget_t *widget, gfxw_widget_t *contents) +{ + gfxw_container_t *container = (gfxw_container_t *) widget; + gfx_dirty_rect_t *dirty = container->dirty; + gfx_state_t *gfx_state = (widget->visual)? widget->visual->gfx_state : ((gfxw_visual_t *) widget)->gfx_state; + int draw_ports; + rect_t nullzone = {0,0,0,0}; + + if (!contents) + return 0; + + while (dirty) { + gfxw_widget_t *seeker = contents; + + while (seeker) { + if (_gfxw_dirty_rect_overlaps_normal_rect(GFXW_IS_CONTAINER(seeker)? nullzone : container->zone, + /* Containers have absolute coordinates, reflect this. */ + seeker->bounds, dirty->rect)) { + + if (GFXW_IS_CONTAINER(seeker)) {/* Propagate dirty rectangles /upwards/ */ + DDIRTY(stderr,"container_draw_contents: propagate upwards (%d,%d,%d,%d ,0)\n", GFX_PRINT_RECT(dirty->rect)); + ((gfxw_container_t *)seeker)->add_dirty_abs((gfxw_container_t *)seeker, dirty->rect, 0); + } + + seeker->flags |= GFXW_FLAG_DIRTY; + } + + seeker = seeker->next; + } + + dirty = dirty->next; + } + + /* The draw loop is executed twice: Once for normal data, and once for ports. */ + for (draw_ports = 0; draw_ports < 2; draw_ports++) { + + dirty = container->dirty; + + while (dirty) { + + gfxw_widget_t *seeker = contents; + while (seeker && (draw_ports || !GFXW_IS_PORT(seeker))) { + rect_t small_rect; + byte draw_noncontainers; + + memcpy(&small_rect, &(dirty->rect), sizeof(rect_t)); + draw_noncontainers = !_gfxop_clip(&small_rect, container->bounds); + + if (seeker->flags & GFXW_FLAG_DIRTY) { + + if (!GFXW_IS_CONTAINER(seeker) && draw_noncontainers) { + GFX_ASSERT(gfxop_set_clip_zone(gfx_state, small_rect)); + } + /* Clip zone must be reset after each element, because we might + ** descend into containers. + ** Doing this is relatively cheap, though. */ + if (draw_noncontainers || GFXW_IS_CONTAINER(seeker)) + seeker->draw(seeker, gfx_point(container->zone.x, container->zone.y)); + + if (!dirty->next) + seeker->flags &= ~GFXW_FLAG_DIRTY; + } + + seeker = seeker->next; + } + dirty = dirty->next; + } + } + /* Remember that the dirty rects should be freed afterwards! */ + + return 0; +} + +static int +_gfxwop_container_free(gfxw_widget_t *widget) +{ + gfxw_container_t *container = (gfxw_container_t *) widget; + gfxw_widget_t *seeker = container->contents; + + while (seeker) { + gfxw_widget_t *next = seeker->next; + seeker->widfree(seeker); + seeker = next; + } + + recursively_free_dirty_rects(container->dirty); + container->dirty = NULL; + + return _gfxwop_basic_free(widget); +} + +static int +_gfxwop_container_tag(gfxw_widget_t *widget) +{ + gfxw_container_t *container = (gfxw_container_t *) widget; + gfxw_widget_t *seeker = container->contents; + + while (seeker) { + seeker->tag(seeker); + seeker = seeker->next; + } + return 0; +} + + +static int +_w_gfxwop_container_set_visual_contents(gfxw_widget_t *contents, gfxw_visual_t *visual) +{ + while (contents) { + contents->set_visual(contents, visual); + contents = contents->next; + } + return 0; +} + +static int +_gfxwop_container_set_visual(gfxw_widget_t *widget, gfxw_visual_t *visual) +{ + gfxw_container_t *container = (gfxw_container_t *) widget; + + container->visual = visual; + if (widget->parent) { + if (!(GFXW_IS_LIST(widget) && !GFXWC(widget)->contents)) { + DDIRTY(stderr,"set_visual::DOWNWARDS abs(%d,%d,%d,%d, 1)\n", GFX_PRINT_RECT(widget->bounds)); + widget->parent->add_dirty_abs(widget->parent, widget->bounds, 1); + } + } + + return _w_gfxwop_container_set_visual_contents(container->contents, visual); +} + +static int +_gfxwop_container_free_tagged(gfxw_container_t *container) +{ + gfxw_widget_t *seekerp = (container->contents); + + while (seekerp) { + gfxw_widget_t *redshirt = seekerp; + + if (redshirt->flags & GFXW_FLAG_TAGGED) { + seekerp = (redshirt->next); + redshirt->widfree(redshirt); /* He's dead, Jim. */ + } else + seekerp = (seekerp)->next; + } + return 0; +} + +static int +_gfxwop_container_free_contents(gfxw_container_t *container) +{ + gfxw_widget_t *seeker = container->contents; + + while (seeker) { + gfxw_widget_t *next = seeker->next; + seeker->widfree(seeker); + seeker = next; + } + return 0; +} + +static void +_gfxw_dirtify_container(gfxw_container_t *container, gfxw_widget_t *widget) +{ + if (GFXW_IS_CONTAINER(widget)) + container->add_dirty_abs(GFXWC(container), widget->bounds, 1); + else + container->add_dirty_rel(GFXWC(container), widget->bounds, 1); +} + +static int +_parentize_widget(gfxw_container_t *container, gfxw_widget_t *widget) +{ + if (widget->parent) { + GFXERROR("_gfxwop_container_add(): Attempt to give second parent node to widget!\nWidget:"); + widget->print(GFXW(widget), 3); + sciprintf("\nContainer:"); + container->print(GFXW(container), 3); + + return 1; + } + + widget->parent = GFXWC(container); + + if (GFXW_IS_VISUAL(container)) + widget->set_visual(widget, (gfxw_visual_t *) container); + else if (container->visual) + widget->set_visual(widget, container->visual); + + return 0; +} + +static int +_gfxw_container_id_equals(gfxw_container_t *container, gfxw_widget_t *widget) +{ + gfxw_widget_t **seekerp = &(container->contents); + + if (GFXW_IS_PORT(widget)) + return 0; /* Don't match ports */ + + if (widget->ID == GFXW_NO_ID) + return 0; + + while (*seekerp + && ((*seekerp)->ID != widget->ID + || (*seekerp)->subID != widget->subID)) + seekerp = &((*seekerp)->next); + + if (!*seekerp) + return 0; + + if ((*seekerp)->equals(*seekerp, widget) + && !(*seekerp)->should_replace(*seekerp, widget)) { + widget->widfree(widget); + (*seekerp)->flags &= ~GFXW_FLAG_TAGGED; + return 1; + } else { + if (!(widget->flags & GFXW_FLAG_MULTI_ID)) + (*seekerp)->widfree(*seekerp); + return 0; + } +} + + +static int +_gfxwop_container_add_dirty(gfxw_container_t *container, rect_t dirty, int propagate) +{ +#if 0 + /* This code has been disabled because containers may contain sub-containers with + ** bounds greater than their own. */ + if (_gfxop_clip(&dirty, container->bounds)) + return 0; +#endif + + DDIRTY(stderr, "Effectively adding dirty %d,%d,%d,%d %d to ID %d\n", GFX_PRINT_RECT(dirty), propagate, container->ID); + container->dirty = gfxdr_add_dirty(container->dirty, dirty, GFXW_DIRTY_STRATEGY); + return 0; +} + + +static int +_gfxwop_container_add(gfxw_container_t *container, gfxw_widget_t *widget) +{ + if (_gfxw_container_id_equals(container, widget)) + return 0; + + if (_parentize_widget(container, widget)) + return 1; + + if (!(GFXW_IS_LIST(widget) && (!GFXWC(widget)->contents))) { /* Don't dirtify self on empty lists */ + DDIRTY(stderr, "container_add: dirtify DOWNWARDS (%d,%d,%d,%d, 1)\n", GFX_PRINT_RECT(widget->bounds)); + _gfxw_dirtify_container(container, widget); + } + + *(container->nextpp) = widget; + container->nextpp = &(widget->next); + + return 0; +} + +/*------------------------------*/ +/**** Lists and sorted lists ****/ +/*------------------------------*/ + +static int +_gfxwop_list_draw(gfxw_widget_t *list, point_t pos) +{ + DRAW_ASSERT(list, GFXW_LIST); + + _gfxwop_container_draw_contents(list, ((gfxw_list_t *)list)->contents); + recursively_free_dirty_rects(GFXWC(list)->dirty); + GFXWC(list)->dirty = NULL; + list->flags &= ~GFXW_FLAG_DIRTY; + return 0; +} + +static int +_gfxwop_sorted_list_draw(gfxw_widget_t *list, point_t pos) +{ + DRAW_ASSERT(list, GFXW_SORTED_LIST); + + _gfxwop_container_draw_contents(list, ((gfxw_list_t *)list)->contents); + recursively_free_dirty_rects(GFXWC(list)->dirty); + GFXWC(list)->dirty = NULL; + return 0; +} + +static inline int +_w_gfxwop_list_print(gfxw_widget_t *list, const char *name, int indentation) +{ + _gfxw_print_widget(list, indentation); + sciprintf(name); + return _w_gfxwop_container_print(list, indentation); +} + +static int +_gfxwop_list_print(gfxw_widget_t *list, int indentation) +{ + return _w_gfxwop_list_print(list, "LIST", indentation); +} + +static int +_gfxwop_sorted_list_print(gfxw_widget_t *list, int indentation) +{ + return _w_gfxwop_list_print(list, "SORTED_LIST", indentation); +} + +/* --- */ +#if 0 +struct gfxw_widget_list { + gfxw_widget_t *widget; + struct gfxw_widget_list *next; +}; + +static struct gfxw_widtet_list * +_gfxw_make_widget_list_recursive(gfxw_widget_t *widget) +{ + gfxw_widget_list *node; + + if (!widget) + return NULL; + + node = sci_malloc(sizeof(struct gfxw_widget_list)); + node->widget = widget; + node->next = _gfxw_make_widget_list_recursive(widget->next); + + return node; +} + +static struct gfxw_widget_list * +_gfxw_make_widget_list(gfxw_container_t *container) +{ + return _gfxw_make_widget_list_recursive(container->contents); +} +#endif +/* --- */ + + +static int +_gfxwop_list_equals(gfxw_widget_t *widget, gfxw_widget_t *other) + /* Requires identical order of list elements. */ +{ + gfxw_list_t *wlist, *olist; + + if (widget->type != other->type) + return 0; + + if (!GFXW_IS_LIST(widget)) { + GFXWARN("_gfxwop_list_equals(): Method called on non-list!\n"); + widget->print(widget, 0); + sciprintf("\n"); + return 0; + } + + wlist = (gfxw_list_t *) widget; + olist = (gfxw_list_t *) other; + + if (memcmp(&(wlist->bounds), &(olist->bounds), sizeof(rect_t))) + return 0; + + widget = wlist->contents; + other = olist->contents; + + while (widget && other) { + + if (!(widget->equals(widget, other) && !widget->should_replace(widget,other))) + return 0; + + widget = widget->next; + other = other->next; + } + + return (!widget && !other); /* True if both are finished now */ +} + +static int +_gfxwop_list_add_dirty(gfxw_container_t *container, rect_t dirty, int propagate) +{ + /* Lists add dirty boxes to both themselves and their parenting port/visual */ + + container->flags |= GFXW_FLAG_DIRTY; + + DDIRTY(stderr,"list_add_dirty %d,%d,%d,%d %d\n", GFX_PRINT_RECT(dirty), propagate); + if (propagate) + if (container->parent) { + DDIRTY(stderr, "->PROPAGATING\n"); + container->parent->add_dirty_abs(container->parent, dirty, 1); + } + + return _gfxwop_container_add_dirty(container, dirty, propagate); +} + +/* static inline */ int +_gfxwop_ordered_add(gfxw_container_t *container, gfxw_widget_t *widget, int compare_all) + /* O(n) */ +{ + gfxw_widget_t **seekerp = &(container->contents); + + if (widget->next) { + GFXERROR("_gfxwop_sorted_list_add(): Attempt to add widget to two lists!\nWidget:"); + widget->print(GFXW(widget), 3); + sciprintf("\nList:"); + container->print(GFXW(container), 3); + BREAKPOINT(); + + return 1; + } + + if (_gfxw_container_id_equals(container, widget)) + return 0; + + while (*seekerp && (compare_all || (widget->compare_to(widget, *seekerp) >= 0))) { + + if (widget->equals(GFXW(widget), GFXW(*seekerp))) { + if (compare_all) { + if ((*seekerp)->visual) + (*seekerp)->widfree(GFXW(*seekerp)); /* If it's a fresh widget */ + else + gfxw_annihilate(GFXW(*seekerp)); + + return _gfxwop_ordered_add(container, widget, compare_all); /* We might have destroyed the container's contents */ + } else { + widget->next = (*seekerp)->next; + (*seekerp)->widfree(GFXW(*seekerp)); + *seekerp = widget; + return (_parentize_widget(container, widget)); + } + } + + if (*seekerp) + seekerp = &((*seekerp)->next); + } + + widget->next = *seekerp; + *seekerp = widget; + + return _parentize_widget(container, widget); +} + +static int +_gfxwop_sorted_list_add(gfxw_container_t *container, gfxw_widget_t *widget) + /* O(n) */ +{ + return _gfxwop_ordered_add(container, widget, 0); +} + +void +_gfxw_set_ops_LIST(gfxw_container_t *list, char sorted) +{ + _gfxw_set_container_ops((gfxw_container_t *) list, + sorted? _gfxwop_sorted_list_draw : _gfxwop_list_draw, + _gfxwop_container_free, + _gfxwop_container_tag, + sorted? _gfxwop_sorted_list_print : _gfxwop_list_print, + _gfxwop_basic_compare_to, + sorted? _gfxwop_basic_equals : _gfxwop_list_equals, + _gfxwop_basic_superarea_of, + _gfxwop_container_set_visual, + _gfxwop_container_free_tagged, + _gfxwop_container_free_contents, + _gfxwop_list_add_dirty, + sorted? _gfxwop_sorted_list_add : _gfxwop_container_add); +} + +gfxw_list_t * +gfxw_new_list(rect_t area, int sorted) +{ + gfxw_list_t *list = (gfxw_list_t *) _gfxw_new_container_widget(area, sizeof(gfxw_list_t), + sorted? GFXW_SORTED_LIST : GFXW_LIST); + + _gfxw_set_ops_LIST(GFXWC(list), (char)sorted); + + return list; +} + + +/*---------------*/ +/**** Visuals ****/ +/*---------------*/ + +static int +_gfxwop_visual_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_visual_t *visual = (gfxw_visual_t *) widget; + gfx_dirty_rect_t *dirty = visual->dirty; + DRAW_ASSERT(widget, GFXW_VISUAL); + + while (dirty) { + int err = gfxop_clear_box(visual->gfx_state, dirty->rect); + + if (err) { + GFXERROR("Error while clearing dirty rect (%d,%d,(%dx%d))\n", dirty->rect.x, + dirty->rect.y, dirty->rect.xl, dirty->rect.yl); + if (err == GFX_FATAL) + return err; + } + + dirty = dirty->next; + } + + _gfxwop_container_draw_contents(widget, visual->contents); + + recursively_free_dirty_rects(visual->dirty); + visual->dirty = NULL; + widget->flags &= ~GFXW_FLAG_DIRTY; + + return 0; +} + +static int +_gfxwop_visual_free(gfxw_widget_t *widget) +{ + gfxw_visual_t *visual = (gfxw_visual_t *) widget; + gfxw_port_t **portrefs; + int retval; + + if (!GFXW_IS_VISUAL(visual)) { + GFXERROR("_gfxwop_visual_free() called on non-visual!Widget was: "); + widget->print(widget, 3); + return 1; + } + + portrefs = visual->port_refs; + + retval = _gfxwop_container_free(widget); + + free(portrefs); + return 0; +} + +static int +_gfxwop_visual_print(gfxw_widget_t *widget, int indentation) +{ + int i; + int comma = 0; + gfxw_visual_t *visual = (gfxw_visual_t *) widget; + + if (!GFXW_IS_VISUAL(visual)) { + GFXERROR("_gfxwop_visual_free() called on non-visual!Widget was: "); + widget->print(widget, 3); + return 1; + } + + _gfxw_print_widget(widget, indentation); + sciprintf("VISUAL; ports={"); + for (i = 0; i < visual->port_refs_nr; i++) + if (visual->port_refs[i]) { + if (comma) + sciprintf(","); + else + comma = 1; + + sciprintf("%d", i); + } + sciprintf("}\n"); + + return _w_gfxwop_container_print(widget, indentation); +} + +static int +_gfxwop_visual_set_visual(gfxw_widget_t *self, gfxw_visual_t *visual) +{ + if (self != GFXW(visual)) { + GFXWARN("Attempt to set a visual's parent visual to something else!\n"); + } else { + GFXWARN("Attempt to set a visual's parent visual!\n"); + } + return 1; +} + +void +_gfxw_set_ops_VISUAL(gfxw_container_t *visual) +{ + _gfxw_set_container_ops((gfxw_container_t *) visual, + _gfxwop_visual_draw, + _gfxwop_visual_free, + _gfxwop_container_tag, + _gfxwop_visual_print, + _gfxwop_basic_compare_to, + _gfxwop_basic_equals, + _gfxwop_basic_superarea_of, + _gfxwop_visual_set_visual, + _gfxwop_container_free_tagged, + _gfxwop_container_free_contents, + _gfxwop_container_add_dirty, + _gfxwop_container_add); +} + +gfxw_visual_t * +gfxw_new_visual(gfx_state_t *state, int font) +{ + gfxw_visual_t *visual = (gfxw_visual_t *) _gfxw_new_container_widget(gfx_rect(0, 0, 320, 200), sizeof(gfxw_visual_t), GFXW_VISUAL); + + visual->font_nr = font; + visual->gfx_state = state; + + visual->port_refs = (struct _gfxw_port**)sci_calloc(sizeof(gfxw_port_t), visual->port_refs_nr = 16); + + _gfxw_set_ops_VISUAL(GFXWC(visual)); + + return visual; +} + + +static int +_visual_find_free_ID(gfxw_visual_t *visual) +{ + int id = 0; + int newports = 16; + + while (visual->port_refs[id] && id < visual->port_refs_nr) + id++; + + if (id == visual->port_refs_nr) {/* Out of ports? */ + visual->port_refs = (struct _gfxw_port**)sci_realloc(visual->port_refs, visual->port_refs_nr += newports); + memset(visual->port_refs + id, 0, newports * sizeof(gfxw_port_t *)); /* Clear new port refs */ + } + + return id; +} + +static int +_gfxwop_add_dirty_rects(gfxw_container_t *dest, gfx_dirty_rect_t *src) +{ + DDIRTY(stderr, "Adding multiple dirty to #%d\n", dest->ID); + if (src) { + dest->dirty = gfxdr_add_dirty(dest->dirty, src->rect, GFXW_DIRTY_STRATEGY); + _gfxwop_add_dirty_rects(dest, src->next); + } + return 0; +} + +/*-------------*/ +/**** Ports ****/ +/*-------------*/ + +static int +_gfxwop_port_draw(gfxw_widget_t *widget, point_t pos) +{ + gfxw_port_t *port = (gfxw_port_t *) widget; + DRAW_ASSERT(widget, GFXW_PORT); + + if (port->decorations) { + DDIRTY(stderr, "Getting/applying deco dirty (multi)\n"); + _gfxwop_add_dirty_rects(GFXWC(port->decorations), port->dirty); + if (port->decorations->draw(GFXW(port->decorations), gfxw_point_zero)) { + port->decorations->dirty = NULL; + return 1; + } + port->decorations->dirty = NULL; + } + + _gfxwop_container_draw_contents(widget, port->contents); + + recursively_free_dirty_rects(port->dirty); + port->dirty = NULL; + widget->flags &= ~GFXW_FLAG_DIRTY; + return 0; +} + +static int +_gfxwop_port_free(gfxw_widget_t *widget) +{ + gfxw_port_t *port = (gfxw_port_t *) widget; + + if (port->visual) { + gfxw_visual_t *visual = port->visual; + int ID = port->ID; + + if (ID < 0 || ID >= visual->port_refs_nr) { + GFXWARN("Attempt to free port #%d; allowed: [0..%d]!\n", ID, visual->port_refs_nr); + return GFX_ERROR; + } + + if (visual->port_refs[ID] != port) { + GFXWARN("While freeing port %d: Port is at %p, but port list indicates %p!\n", + ID, port, visual->port_refs[ID]); + } else visual->port_refs[ID] = NULL; + + } + + if (port->decorations) + port->decorations->widfree(GFXW(port->decorations)); + + return _gfxwop_container_free(widget); +} + +static int +_gfxwop_port_print(gfxw_widget_t *widget, int indentation) +{ + gfxw_port_t *port = (gfxw_port_t *) widget; + + _gfxw_print_widget(widget, indentation); + sciprintf("PORT"); + sciprintf(" font=%d drawpos=(%d,%d)", port->font_nr, port->draw_pos.x, port->draw_pos.y); + if (port->gray_text) + sciprintf(" (gray)"); + _w_gfxwop_container_print(GFXW(port), indentation); + return _w_gfxwop_container_print_contents("decorations", GFXW(port->decorations), indentation); + +} + +static int +_gfxwop_port_superarea_of(gfxw_widget_t *self, gfxw_widget_t *other) +{ + gfxw_port_t *port = (gfxw_port_t *) self; + + if (!port->port_bg) + return _gfxwop_basic_superarea_of(self, other); + + return port->port_bg->superarea_of(port->port_bg, other); +} + +static int +_gfxwop_port_set_visual(gfxw_widget_t *widget, gfxw_visual_t *visual) +{ + gfxw_list_t *decorations = ((gfxw_port_t *) widget)->decorations; + widget->visual = visual; + + if (decorations) + if (decorations->set_visual(GFXW(decorations), visual)) { + GFXWARN("Setting the visual for decorations failed for port "); + widget->print(widget, 1); + return 1; + } + + return _gfxwop_container_set_visual(widget, visual); +} + +static int +_gfxwop_port_add_dirty(gfxw_container_t *widget, rect_t dirty, int propagate) +{ + gfxw_port_t *self = (gfxw_port_t *) widget; + + self->flags |= GFXW_FLAG_DIRTY; + + _gfxwop_container_add_dirty(widget, dirty, propagate); + + DDIRTY(stderr,"Added dirty to ID %d\n", widget->ID); + DDIRTY(stderr, "dirty= (%d,%d,%d,%d) bounds (%d,%d,%d,%d)\n", dirty.x, dirty.x, dirty.xl, dirty.yl, + widget->bounds.x, widget->bounds.y, widget->bounds.xl, widget->bounds.yl); +#if 0 + /* FIXME: This is a worthwhile optimization */ + if (self->port_bg) { + gfxw_widget_t foo; + + foo.bounds = dirty; /* Yeah, sub-elegant, I know */ + foo.bounds.x -= self->zone.x; + foo.bounds.y -= self->zone.y; + if (self->port_bg->superarea_of(self->port_bg, &foo)) { + gfxw_container_t *parent = self->parent; + while (parent) { + fprintf(stderr,"Dirtifying parent id %d\n", parent->ID); + parent->flags |= GFXW_FLAG_DIRTY; + parent = parent->parent; + } + return 0; + } + } /* else propagate to the parent, since we're not 'catching' the dirty rect */ +#endif + + if (propagate) + if (self->parent) { + DDIRTY(stderr, "PROPAGATE\n"); + return self->parent->add_dirty_abs(self->parent, dirty, 1); + } + + return 0; +} + +static int +_gfxwop_port_add(gfxw_container_t *container, gfxw_widget_t *widget) + /* O(n) */ +{ + return _gfxwop_ordered_add(container, widget, 1); +} + +void +_gfxw_set_ops_PORT(gfxw_container_t *widget) +{ + _gfxw_set_container_ops((gfxw_container_t *) widget, + _gfxwop_port_draw, + _gfxwop_port_free, + _gfxwop_container_tag, + _gfxwop_port_print, + _gfxwop_basic_compare_to, + _gfxwop_basic_equals, + _gfxwop_port_superarea_of, + _gfxwop_port_set_visual, + _gfxwop_container_free_tagged, + _gfxwop_container_free_contents, + _gfxwop_port_add_dirty, + _gfxwop_port_add); +} + +gfxw_port_t * +gfxw_new_port(gfxw_visual_t *visual, gfxw_port_t *predecessor, rect_t area, gfx_color_t fgcolor, gfx_color_t bgcolor) +{ + gfxw_port_t *widget = (gfxw_port_t *) + _gfxw_new_container_widget(area, sizeof(gfxw_port_t), GFXW_PORT); + + VERIFY_WIDGET(visual); + + widget->port_bg = NULL; + widget->parent = NULL; + widget->decorations = NULL; + widget->title_text = NULL; + widget->draw_pos = gfx_point(0, 0); + widget->gray_text = 0; + widget->color = fgcolor; + widget->bgcolor = bgcolor; + widget->font_nr = visual->font_nr; + widget->ID = _visual_find_free_ID(visual); + widget->chrono_port = 0; + visual->port_refs[widget->ID] = widget; + + _gfxw_set_ops_PORT(GFXWC(widget)); + + return widget; +} + +void gfxw_port_auto_restore_background(gfxw_visual_t *visual, gfxw_port_t *window, rect_t auto_rect) +{ + window->port_flags |= WINDOW_FLAG_AUTO_RESTORE; + window->restore_snap = gfxw_make_snapshot(visual, auto_rect); +} + +gfxw_port_t * +gfxw_remove_port(gfxw_visual_t *visual, gfxw_port_t *port) +{ + gfxw_port_t *parent; + VERIFY_WIDGET(visual); + VERIFY_WIDGET(port); + + if (!visual->contents) { + GFXWARN("Attempt to remove port from empty visual\n"); + return NULL; + } + + parent = (gfxw_port_t *) port->parent; + if (port->port_flags & WINDOW_FLAG_AUTO_RESTORE) + gfxw_restore_snapshot(visual, port->restore_snap); + + if (port->widfree(GFXW(port))) + return parent; + + while (parent && !GFXW_IS_PORT(parent)) + parent = (gfxw_port_t *) parent->parent; /* Ascend through ancestors */ + + return parent; +} + + +gfxw_port_t * +gfxw_find_port(gfxw_visual_t *visual, int ID) +{ + if (ID < 0 || ID >= visual->port_refs_nr) + return NULL; + + return visual->port_refs[ID]; +} + + +gfxw_port_t * +gfxw_find_default_port(gfxw_visual_t *visual) +{ + int id = visual->port_refs_nr; + + while (id--) { + gfxw_port_t *port = visual->port_refs[id]; + + if (port) + return port; + } + + return NULL; +} + + +/*** - other functions - ***/ + +gfxw_widget_t * +gfxw_set_id(gfxw_widget_t *widget, int ID, int subID) +{ + if (widget) { + widget->ID = ID; + widget->subID = subID; + } + + return widget; +} + +gfxw_dyn_view_t * +gfxw_dyn_view_set_params(gfxw_dyn_view_t *widget, int under_bits, void *under_bitsp, + int signal, void *signalp) +{ + if (!widget) + return NULL; + + widget->under_bits = under_bits; + widget->under_bitsp = under_bitsp; + widget->signal = signal; + widget->signalp = signalp; + + return widget; +} + +gfxw_widget_t * +gfxw_remove_id(gfxw_container_t *container, int ID, int subID) +{ + gfxw_widget_t **wp = &(container->contents); + + while (*wp) { + if ((*wp)->ID == ID + && (subID == GFXW_NO_ID + || (*wp)->subID == subID)) { + gfxw_widget_t *widget = *wp; + + *wp = (*wp)->next; + widget->next = NULL; + widget->parent = NULL; + widget->visual = NULL; + + return widget; + } + + wp = &((*wp)->next); + } + + return NULL; +} + + +gfxw_widget_t * +gfxw_hide_widget(gfxw_widget_t *widget) +{ + if (widget->flags & GFXW_FLAG_VISIBLE) { + widget->flags &= ~GFXW_FLAG_VISIBLE; + + if (widget->parent) + widget->parent->add_dirty_rel(widget->parent, widget->bounds, 1); + } + + return widget; +} + +gfxw_widget_t * +gfxw_show_widget(gfxw_widget_t *widget) +{ + if (!(widget->flags & GFXW_FLAG_VISIBLE)) { + widget->flags |= GFXW_FLAG_VISIBLE; + + if (widget->parent) + widget->parent->add_dirty_rel(widget->parent, widget->bounds, 1); + } + + return widget; +} + + +gfxw_snapshot_t * +gfxw_make_snapshot(gfxw_visual_t *visual, rect_t area) +{ + gfxw_snapshot_t *retval = (gfxw_snapshot_t*)sci_malloc(sizeof(gfxw_snapshot_t)); + + retval->serial = widget_serial_number_counter++; + + retval->area = area; + + /* Work around subset semantics in gfx_rect_subset. + This fixes the help icon in LSL5. */ + if (retval->area.xl == 320) retval->area.xl = 321; + + return retval; +} + + +int +gfxw_widget_matches_snapshot(gfxw_snapshot_t *snapshot, gfxw_widget_t *widget) +{ + int free_below = (snapshot->serial < widget_serial_number_counter)? 0: widget_serial_number_counter; + int free_above_eq = snapshot->serial; + rect_t bounds = widget->bounds; + + if (!GFXW_IS_CONTAINER(widget) && widget->parent) { + bounds.x += widget->parent->bounds.x; + bounds.y += widget->parent->bounds.y; + } + + return ((widget->serial >= free_above_eq + || widget->serial < free_below) + && gfx_rect_subset(bounds, snapshot->area)); +} + +#define MAGIC_FREE_NUMBER -42 + +void +_gfxw_free_contents_appropriately(gfxw_container_t *container, gfxw_snapshot_t *snapshot, int priority) +{ + gfxw_widget_t *widget = container->contents; + + while (widget) { + gfxw_widget_t *next = widget->next; + + if (gfxw_widget_matches_snapshot(snapshot, widget) && !(widget->flags & GFXW_FLAG_IMMUNE_TO_SNAPSHOTS) + && (priority == MAGIC_FREE_NUMBER || priority <= widget->widget_priority || widget->widget_priority == -1)) { + widget->widfree(widget); + } else { + if (GFXW_IS_CONTAINER(widget)) + _gfxw_free_contents_appropriately(GFXWC(widget), snapshot, priority); + } + + widget = next; + } +} + +gfxw_snapshot_t * +gfxw_restore_snapshot(gfxw_visual_t *visual, gfxw_snapshot_t *snapshot) +{ + _gfxw_free_contents_appropriately(GFXWC(visual), snapshot, MAGIC_FREE_NUMBER); + + return snapshot; +} + +void +gfxw_annihilate(gfxw_widget_t *widget) +{ + gfxw_visual_t *visual = widget->visual; + int widget_priority = 0; + int free_overdrawn = 0; + + gfxw_snapshot_t snapshot; + if (!GFXW_IS_CONTAINER(widget) && widget->parent && visual && (widget->flags & GFXW_FLAG_VISIBLE)) { + snapshot.serial = 0; + snapshot.area = widget->bounds; + snapshot.area.x += widget->parent->zone.x; + snapshot.area.y += widget->parent->zone.y; + free_overdrawn = 1; + widget_priority = widget->widget_priority; + } + + widget->widfree(GFXW(widget)); + + if (free_overdrawn) + _gfxw_free_contents_appropriately(GFXWC(visual), &snapshot, widget_priority); +} + + + +gfxw_dyn_view_t * +gfxw_picviewize_dynview(gfxw_dyn_view_t *dynview) +{ + dynview->type = GFXW_PIC_VIEW; + dynview->flags |= GFXW_FLAG_DIRTY; + + _gfxw_set_ops_PICVIEW(GFXW(dynview)); + + if (dynview->parent) + _gfxw_dirtify_container(dynview->parent, GFXW(dynview)); + + return dynview; +} + +/* Chrono-Ports (tm) */ + +gfxw_port_t * +gfxw_get_chrono_port(gfxw_visual_t *visual, gfxw_list_t **temp_widgets_list, int flags) +{ + gfxw_port_t *result = NULL; + gfx_color_t transparent = {0}; + int id = 0; + + if (!(flags & GFXW_CHRONO_NON_TOPMOST)) + { + result = gfxw_find_default_port(visual); + } else + { + id = visual->port_refs_nr; + while (id >= 0 && (!visual->port_refs[id] || + !visual->port_refs[id]->chrono_port)) + id--; + + if (id >= 0) + result = visual->port_refs[id]; + } + + if (!result || !result->chrono_port) + { + if (flags & GFXW_CHRONO_NO_CREATE) return NULL; + result = gfxw_new_port(visual, NULL, gfx_rect(0, 0, 320, 200), transparent, transparent); + *temp_widgets_list = gfxw_new_list(gfx_rect(0, 0, 320, 200), 1); + result->add(GFXWC(result), GFXW(*temp_widgets_list)); + result->chrono_port = 1; + if (temp_widgets_list) + *temp_widgets_list = GFXWC(result->contents); + return result; + }; + + if (temp_widgets_list) + *temp_widgets_list = GFXWC(result->contents); + return result; +} + +static int +gfxw_check_chrono_overlaps(gfxw_port_t *chrono, gfxw_widget_t *widget) +{ + gfxw_widget_t *seeker = GFXWC(chrono->contents)->contents; + + while (seeker) { + if (gfx_rect_equals(seeker->bounds, widget->bounds)) + { + gfxw_annihilate(GFXW(seeker)); + return 1; + } + + seeker = seeker->next; + } + + return 0; +} + +void +gfxw_add_to_chrono(gfxw_visual_t *visual, gfxw_widget_t *widget) +{ + gfxw_list_t *tw; + gfxw_port_t *chrono = + gfxw_get_chrono_port(visual, &tw, 0); + + gfxw_check_chrono_overlaps(chrono, widget); + chrono->add(GFXWC(chrono), widget); +} + +static gfxw_widget_t * +gfxw_widget_intersects_chrono(gfxw_list_t *tw, gfxw_widget_t *widget) +{ + gfxw_widget_t *seeker; + + assert(tw->type == GFXW_SORTED_LIST); + + seeker = tw->contents; + while (seeker) + { + point_t origin; + rect_t bounds = widget->bounds; + + bounds = widget->bounds; + origin.x = seeker->parent->zone.x; + origin.y = seeker->parent->zone.y; + gfx_rect_translate(bounds, origin); + + if (gfx_rects_overlap(bounds, seeker->bounds)) + return seeker; + + seeker = seeker->next; + } + + return 0; +} + +void +gfxw_widget_reparent_chrono(gfxw_visual_t *visual, gfxw_widget_t *view, gfxw_list_t *target) +{ + gfxw_list_t *tw; + gfxw_port_t *chrono; + gfxw_widget_t *intersector; + + chrono = gfxw_get_chrono_port(visual, &tw, GFXW_CHRONO_NO_CREATE); + if (chrono == NULL) return; + + intersector = gfxw_widget_intersects_chrono(tw, view); + if (intersector) + { + point_t origin = gfx_point(intersector->parent->zone.x, + intersector->parent->zone.y); + + gfxw_remove_widget_from_container(GFXWC(chrono), GFXW(tw)); + gfxw_remove_widget_from_container(GFXWC(chrono->parent), GFXW(chrono)); + gfxw_annihilate(GFXW(chrono)); + + gfx_rect_translate(tw->zone, origin); + target->add(GFXWC(target), GFXW(tw)); + } +} + +void +gfxw_widget_kill_chrono(gfxw_visual_t *visual, int window) +{ + int i; + + for (i=window; i < visual->port_refs_nr ; i++) + { + if (visual->port_refs[i] && visual->port_refs[i]->chrono_port) + gfxw_annihilate(GFXW(visual->port_refs[i])); + } +} |