mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-27 12:16:59 +00:00
7c84cca81d
svn-id: r41198
1822 lines
49 KiB
C++
1822 lines
49 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "sci/sci.h"
|
|
#include "sci/gfx/gfx_gui.h" // for kWindowAutoRestore
|
|
#include "sci/gfx/gfx_widgets.h"
|
|
#include "sci/gfx/gfx_state_internal.h"
|
|
|
|
namespace Sci {
|
|
|
|
#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
|
|
|
|
Common::Point 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
|
|
|
|
GfxWidget *debug_widgets[GFXW_DEBUG_WIDGETS];
|
|
int debug_widget_pos = 0;
|
|
|
|
static void _gfxw_debug_add_widget(GfxWidget *widget) {
|
|
if (debug_widget_pos == GFXW_DEBUG_WIDGETS)
|
|
error("WIDGET DEBUG: Allocated the maximum number of %d widgets- Aborting!\n", GFXW_DEBUG_WIDGETS);
|
|
debug_widgets[debug_widget_pos++] = widget;
|
|
}
|
|
|
|
static void _gfxw_debug_remove_widget(GfxWidget *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(GfxWidget *)) * (debug_widget_pos - i - 1));
|
|
debug_widgets[debug_widget_pos--] = NULL;
|
|
found++;
|
|
}
|
|
}
|
|
|
|
if (found > 1) {
|
|
error("While removing widget: Found it %d times!\n", found);
|
|
}
|
|
|
|
if (found == 0) {
|
|
error("Attempted removal of unregistered widget!\n");
|
|
}
|
|
}
|
|
#else // !GFXW_DEBUG_WIDGETS
|
|
#define _gfxw_debug_add_widget(a)
|
|
#define _gfxw_debug_remove_widget(a)
|
|
#endif
|
|
|
|
|
|
static void indent(int indentation) {
|
|
for (int i = 0; i < indentation; i++)
|
|
sciprintf(" ");
|
|
}
|
|
|
|
void GfxWidget::printIntern(int indentation) const {
|
|
unsigned int i;
|
|
char flags_list[] = "VOCDTMI";
|
|
|
|
indent(indentation);
|
|
|
|
if (_magic == GFXW_MAGIC_VALID) {
|
|
if (_visual)
|
|
sciprintf("v ");
|
|
else
|
|
sciprintf("NoVis ");
|
|
} else if (_magic == GFXW_MAGIC_INVALID)
|
|
sciprintf("INVALID ");
|
|
|
|
sciprintf("S%08x", _serial);
|
|
|
|
if (_ID != GFXW_NO_ID) {
|
|
sciprintf("#%x", _ID);
|
|
|
|
if (_subID != GFXW_NO_ID)
|
|
sciprintf(":%x ", _subID);
|
|
else
|
|
sciprintf(" ");
|
|
}
|
|
|
|
sciprintf("[(%d,%d)(%dx%d)]", _bounds.x, _bounds.y, _bounds.width, _bounds.height);
|
|
|
|
for (i = 0; i < strlen(flags_list); i++)
|
|
if (_flags & (1 << i))
|
|
sciprintf("%c", flags_list[i]);
|
|
|
|
sciprintf(" ");
|
|
}
|
|
|
|
void GfxWidget::print(int indentation) const {
|
|
printIntern(indentation);
|
|
sciprintf("<untyped #%d>", _type);
|
|
}
|
|
|
|
GfxWidget::GfxWidget(gfxw_widget_type_t type_) {
|
|
_magic = GFXW_MAGIC_VALID;
|
|
|
|
_serial = widget_serial_number_counter++;
|
|
widget_serial_number_counter &= MAX_SERIAL_NUMBER;
|
|
|
|
_flags = GFXW_FLAG_DIRTY;
|
|
_type = type_;
|
|
_bounds = gfx_rect(0, 0, 0, 0);
|
|
_next = NULL;
|
|
_ID = GFXW_NO_ID;
|
|
_subID = GFXW_NO_ID;
|
|
_parent = NULL;
|
|
_visual = NULL;
|
|
_widgetPriority = -1;
|
|
|
|
compare_to = NULL;
|
|
equals = NULL;
|
|
should_replace = NULL;
|
|
superarea_of = NULL;
|
|
|
|
_gfxw_debug_add_widget(this);
|
|
}
|
|
|
|
static int verify_widget(GfxWidget *widget) {
|
|
if (!widget) {
|
|
GFXERROR("Attempt to use NULL widget\n");
|
|
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");
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define VERIFY_WIDGET(w) \
|
|
if (verify_widget((GfxWidget *)(w))) { GFXERROR("Error occured while validating widget\n"); }
|
|
|
|
#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) { \
|
|
error("Fatal error occured while drawing widget!\nGraphics state invalid; aborting program..."); \
|
|
} \
|
|
}
|
|
|
|
//********** Widgets *************
|
|
|
|
// Base class operations and common stuff
|
|
|
|
// Assertion for drawing
|
|
#define DRAW_ASSERT(widget, exp_type) \
|
|
if (!(widget)) { \
|
|
sciprintf("L%d: NULL widget", __LINE__); \
|
|
return 1; \
|
|
} \
|
|
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(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(1); \
|
|
sciprintf("\n"); \
|
|
return 1; \
|
|
}
|
|
|
|
|
|
// TODO: Turn this into an operator==
|
|
static 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;
|
|
}
|
|
|
|
int GfxWidget::setVisual(GfxVisual *visual) {
|
|
_visual = visual;
|
|
|
|
if (_parent) {
|
|
DDIRTY(stderr, "GfxWidget::setVisual: DOWNWARDS rel(%d,%d,%d,%d, 1)\n", GFX_PRINT_RECT(_bounds));
|
|
_parent->add_dirty_rel(_parent, _bounds, 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _gfxwop_basic_should_replace(GfxWidget *widget, GfxWidget *other) {
|
|
return 0;
|
|
}
|
|
|
|
static void _gfxw_set_ops(GfxWidget *widget,
|
|
gfxw_bin_op *compare_to, gfxw_bin_op *equals, gfxw_bin_op *superarea_of) {
|
|
widget->compare_to = compare_to;
|
|
widget->equals = equals;
|
|
widget->superarea_of = superarea_of;
|
|
|
|
widget->should_replace = _gfxwop_basic_should_replace;
|
|
}
|
|
|
|
void gfxw_remove_widget_from_container(GfxContainer *container, GfxWidget *widget) {
|
|
GfxWidget **seekerp;
|
|
|
|
if (!container) {
|
|
GFXERROR("Attempt to remove widget from NULL container!\n");
|
|
error("gfxw_remove_widget_from_container() failed. Breakpoint in %s, line %d", __FILE__, __LINE__);
|
|
}
|
|
|
|
seekerp = &(container->_contents);
|
|
|
|
if (GFXW_IS_LIST(widget) && GFXW_IS_PORT(container)) {
|
|
GfxPort *port = (GfxPort *) container;
|
|
if (port->_decorations == (GfxList *) 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(1);
|
|
sciprintf("Container:");
|
|
widget->print(1);
|
|
error("gfxw_remove_widget_from_container() failed. Breakpoint in %s, line %d", __FILE__, __LINE__);
|
|
return;
|
|
}
|
|
|
|
if (container->_nextpp == &(widget->_next))
|
|
container->_nextpp = seekerp;
|
|
|
|
*seekerp = widget->_next; // Remove it
|
|
widget->_parent = NULL;
|
|
widget->_next = NULL;
|
|
}
|
|
|
|
GfxWidget::~GfxWidget() {
|
|
DDIRTY(stderr, "BASIC-FREE: SomeAddDirty\n"); // FIXME: What is this?
|
|
|
|
if (_parent) {
|
|
if (GFXW_IS_CONTAINER(this))
|
|
_parent->add_dirty_abs(_parent, _bounds, 1);
|
|
else
|
|
_parent->add_dirty_rel(_parent, _bounds, 1);
|
|
|
|
gfxw_remove_widget_from_container(_parent, this);
|
|
}
|
|
|
|
_magic = GFXW_MAGIC_INVALID;
|
|
_gfxw_debug_remove_widget(this);
|
|
}
|
|
|
|
static int _gfxwop_basic_compare_to(GfxWidget *widget, GfxWidget *other) {
|
|
return 1;
|
|
}
|
|
|
|
static int _gfxwop_basic_equals(GfxWidget *widget, GfxWidget *other) {
|
|
return 0;
|
|
}
|
|
|
|
static int _gfxwop_basic_superarea_of(GfxWidget *widget, GfxWidget *other) {
|
|
return (widget == other);
|
|
}
|
|
|
|
//*** Boxes ***
|
|
|
|
static rect_t _move_rect(rect_t rect, Common::Point point) {
|
|
return gfx_rect(rect.x + point.x, rect.y + point.y, rect.width, rect.height);
|
|
}
|
|
|
|
static void _split_rect(rect_t rect, Common::Point *p1, Common::Point *p2) {
|
|
p1->x = rect.x;
|
|
p1->y = rect.y;
|
|
p2->x = rect.x + rect.width;
|
|
p2->y = rect.y + rect.height;
|
|
}
|
|
|
|
static Common::Point _move_point(rect_t rect, Common::Point point) {
|
|
return Common::Point(rect.x + point.x, rect.y + point.y);
|
|
}
|
|
|
|
int GfxBox::draw(const Common::Point &pos) {
|
|
DRAW_ASSERT(this, GFXW_BOX);
|
|
GFX_ASSERT(gfxop_draw_box(_visual->_gfxState, _move_rect(_bounds, pos), _color1, _color2, _shadeType));
|
|
return 0;
|
|
}
|
|
|
|
void GfxBox::print(int indentation) const {
|
|
printIntern(indentation);
|
|
sciprintf("BOX");
|
|
}
|
|
|
|
static int _gfxwop_box_superarea_of(GfxWidget *widget, GfxWidget *other) {
|
|
GfxBox *box = (GfxBox *) widget;
|
|
|
|
if (box->_color1.alpha)
|
|
return 0;
|
|
|
|
if (box->_shadeType != GFX_BOX_SHADE_FLAT && box->_color2.alpha)
|
|
return 0;
|
|
|
|
// Note: the check for box->_bounds and other->_bounds is NOT the same as contains()
|
|
// in Common::Rect (this one includes equality too)
|
|
if (!(box->_bounds.x <= other->_bounds.x && box->_bounds.y <= other->_bounds.y &&
|
|
box->_bounds.x + box->_bounds.width >= other->_bounds.x + other->_bounds.width &&
|
|
box->_bounds.y + box->_bounds.height >= other->_bounds.y + other->_bounds.height))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _gfxwop_box_equals(GfxWidget *widget, GfxWidget *other) {
|
|
GfxBox *wbox = (GfxBox *)widget, *obox;
|
|
if (other->_type != GFXW_BOX)
|
|
return 0;
|
|
|
|
obox = (GfxBox *) other;
|
|
|
|
if (!toCommonRect(wbox->_bounds).equals(toCommonRect(obox->_bounds)))
|
|
return 0;
|
|
|
|
if (!_color_equals(wbox->_color1, obox->_color1))
|
|
return 0;
|
|
|
|
if (wbox->_shadeType != obox->_shadeType)
|
|
return 0;
|
|
|
|
if (wbox->_shadeType != GFX_BOX_SHADE_FLAT
|
|
&& _color_equals(wbox->_color2, obox->_color2))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void _gfxw_set_ops_BOX(GfxWidget *widget) {
|
|
_gfxw_set_ops(widget, _gfxwop_basic_compare_to, _gfxwop_box_equals, _gfxwop_box_superarea_of);
|
|
}
|
|
|
|
static int _gfxw_color_get_priority(gfx_color_t color) {
|
|
return (color.mask & GFX_MASK_PRIORITY) ? color.priority : -1;
|
|
}
|
|
|
|
GfxBox *gfxw_new_box(GfxState *state, rect_t area, gfx_color_t color1, gfx_color_t color2, gfx_box_shade_t shade_type) {
|
|
return new GfxBox(state, area, color1, color2, shade_type);
|
|
}
|
|
|
|
GfxBox::GfxBox(GfxState *state, rect_t area, gfx_color_t color1, gfx_color_t color2, gfx_box_shade_t shade_type)
|
|
: GfxWidget(GFXW_BOX) {
|
|
|
|
_widgetPriority = _gfxw_color_get_priority(color1);
|
|
_bounds = area;
|
|
_color1 = color1;
|
|
_color2 = color2;
|
|
_shadeType = shade_type;
|
|
|
|
_flags |= GFXW_FLAG_VISIBLE;
|
|
|
|
if ((_color1.mask & GFX_MASK_VISUAL) && ((state && (state->driver->mode->palette)) || (!_color1.alpha && !_color2.alpha)))
|
|
_flags |= GFXW_FLAG_OPAQUE;
|
|
|
|
_gfxw_set_ops_BOX(this);
|
|
}
|
|
|
|
GfxPrimitive::GfxPrimitive(rect_t area, gfx_color_t color_, gfx_line_mode_t mode,
|
|
gfx_line_style_t style, gfxw_widget_type_t type_)
|
|
: GfxWidget(type_) {
|
|
|
|
_widgetPriority = _gfxw_color_get_priority(color_);
|
|
_bounds = area;
|
|
_color = color_;
|
|
_lineMode = mode;
|
|
_lineStyle = style;
|
|
|
|
_flags |= GFXW_FLAG_VISIBLE;
|
|
}
|
|
|
|
//*** Rectangles ***
|
|
|
|
struct GfxRect : public GfxPrimitive {
|
|
GfxRect(rect_t rect, gfx_color_t color, gfx_line_mode_t line_mode, gfx_line_style_t line_style);
|
|
|
|
virtual int draw(const Common::Point &pos);
|
|
virtual void print(int indentation) const;
|
|
};
|
|
|
|
static int _gfxwop_primitive_equals(GfxWidget *widget, GfxWidget *other) {
|
|
GfxPrimitive *wprim = (GfxPrimitive *) widget, *oprim;
|
|
if (widget->_type != other->_type)
|
|
return 0;
|
|
|
|
oprim = (GfxPrimitive *) other;
|
|
|
|
// Check if the two primitives are equal (note: the primitives aren't always rectangles, so
|
|
// the "width" and the "height" here could be negative)
|
|
if (wprim->_bounds.x != oprim->_bounds.x || wprim->_bounds.y != oprim->_bounds.y ||
|
|
wprim->_bounds.width != oprim->_bounds.width || wprim->_bounds.height != oprim->_bounds.height)
|
|
return 0;
|
|
|
|
if (!_color_equals(wprim->_color, oprim->_color))
|
|
return 0;
|
|
|
|
if (wprim->_lineMode != oprim->_lineMode)
|
|
return 0;
|
|
|
|
if (wprim->_lineStyle != oprim->_lineStyle)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int GfxRect::draw(const Common::Point &pos) {
|
|
DRAW_ASSERT(this, GFXW_RECT);
|
|
|
|
GFX_ASSERT(gfxop_draw_rectangle(_visual->_gfxState, gfx_rect(_bounds.x + pos.x, _bounds.y + pos.y,
|
|
_bounds.width - 1, _bounds.height - 1), _color, _lineMode, _lineStyle));
|
|
return 0;
|
|
}
|
|
|
|
void GfxRect::print(int indentation) const {
|
|
printIntern(indentation);
|
|
sciprintf("RECT");
|
|
}
|
|
|
|
void _gfxw_set_ops_RECT(GfxWidget *prim) {
|
|
_gfxw_set_ops(prim,
|
|
_gfxwop_basic_compare_to, _gfxwop_primitive_equals, _gfxwop_basic_superarea_of);
|
|
}
|
|
|
|
GfxPrimitive *gfxw_new_rect(rect_t rect, gfx_color_t color, gfx_line_mode_t line_mode, gfx_line_style_t line_style) {
|
|
return new GfxRect(rect, color, line_mode, line_style);
|
|
}
|
|
|
|
GfxRect::GfxRect(rect_t rect, gfx_color_t color_, gfx_line_mode_t line_mode_, gfx_line_style_t line_style_)
|
|
: GfxPrimitive(rect, color_, line_mode_, line_style_, GFXW_RECT) {
|
|
|
|
_bounds.width++;
|
|
_bounds.height++; // Since it is actually one pixel bigger in each direction
|
|
|
|
_gfxw_set_ops_RECT(this);
|
|
}
|
|
|
|
//*** Lines ***
|
|
|
|
struct GfxLine : public GfxPrimitive {
|
|
GfxLine(Common::Point start, Common::Point end, gfx_color_t color, gfx_line_mode_t line_mode, gfx_line_style_t line_style);
|
|
|
|
virtual int draw(const Common::Point &pos);
|
|
virtual void print(int indentation) const;
|
|
};
|
|
|
|
int GfxLine::draw(const Common::Point &pos) {
|
|
rect_t linepos = _bounds;
|
|
Common::Point p1, p2;
|
|
|
|
linepos.width--;
|
|
linepos.height--;
|
|
|
|
DRAW_ASSERT(this, GFXW_LINE);
|
|
|
|
_split_rect(_move_rect(linepos, pos), &p1, &p2);
|
|
GFX_ASSERT(gfxop_draw_line(_visual->_gfxState, p1, p2, _color, _lineMode, _lineStyle));
|
|
return 0;
|
|
}
|
|
|
|
void GfxLine::print(int indentation) const {
|
|
printIntern(indentation);
|
|
// sciprintf("LINE");
|
|
}
|
|
|
|
void _gfxw_set_ops_LINE(GfxWidget *prim) {
|
|
_gfxw_set_ops(prim,
|
|
_gfxwop_basic_compare_to, _gfxwop_primitive_equals, _gfxwop_basic_superarea_of);
|
|
}
|
|
|
|
GfxPrimitive *gfxw_new_line(Common::Point start, Common::Point end, gfx_color_t color, gfx_line_mode_t line_mode, gfx_line_style_t line_style) {
|
|
return new GfxLine(start, end, color, line_mode, line_style);
|
|
}
|
|
|
|
GfxLine::GfxLine(Common::Point start, Common::Point end, gfx_color_t color_, gfx_line_mode_t line_mode_, gfx_line_style_t line_style_)
|
|
: GfxPrimitive(gfx_rect(start.x, start.y, end.x - start.x + 1, end.y - start.y + 1), color_, line_mode_, line_style_, GFXW_LINE) {
|
|
_gfxw_set_ops_LINE(this);
|
|
}
|
|
|
|
//*** Views and static views ***
|
|
|
|
|
|
GfxView::GfxView(GfxState *state, Common::Point pos_, int view_, int loop_, int cel_, int palette_, int priority, int control,
|
|
gfx_alignment_t halign, gfx_alignment_t valign, int flags_)
|
|
: GfxWidget((flags_ & GFXW_VIEW_FLAG_STATIC) ? GFXW_STATIC_VIEW : GFXW_VIEW) {
|
|
|
|
int width, height;
|
|
Common::Point offset;
|
|
|
|
if (!state) {
|
|
error("Attempt to create view widget with NULL state");
|
|
}
|
|
|
|
if (gfxop_get_cel_parameters(state, view_, loop_, cel_, &width, &height, &offset)) {
|
|
error("Attempt to retrieve cel parameters for (%d/%d/%d) failed (Maybe the values weren't checked beforehand?)",
|
|
view_, cel_, loop_);
|
|
}
|
|
|
|
_pos = pos_;
|
|
_color.mask = ((priority < 0) ? 0 : GFX_MASK_PRIORITY) | ((control < 0) ? 0 : GFX_MASK_CONTROL);
|
|
_widgetPriority = priority;
|
|
_color.priority = priority;
|
|
_color.control = control;
|
|
_view = view_;
|
|
_loop = loop_;
|
|
_cel = cel_;
|
|
_palette = palette_;
|
|
|
|
if (halign == ALIGN_CENTER)
|
|
_pos.x -= width >> 1;
|
|
else if (halign == ALIGN_RIGHT)
|
|
_pos.x -= width;
|
|
|
|
if (valign == ALIGN_CENTER)
|
|
_pos.y -= height >> 1;
|
|
else if (valign == ALIGN_BOTTOM)
|
|
_pos.y -= height;
|
|
|
|
_bounds = gfx_rect(_pos.x - offset.x, _pos.y - offset.y, width, height);
|
|
|
|
_flags |= GFXW_FLAG_VISIBLE;
|
|
}
|
|
|
|
int GfxView::draw(const Common::Point &pos) {
|
|
if (_type == GFXW_VIEW) {
|
|
DRAW_ASSERT(this, GFXW_VIEW);
|
|
GFX_ASSERT(gfxop_draw_cel(_visual->_gfxState, _view, _loop, _cel,
|
|
Common::Point(_pos.x + pos.x, _pos.y + pos.y), _color, _palette));
|
|
} else {
|
|
// FIXME: _gfxwop_static_view_draw checked for GFXW_VIEW here, instead of GFXW_STATIC_VIEW.
|
|
//DRAW_ASSERT(this, GFXW_VIEW);
|
|
DRAW_ASSERT(this, GFXW_STATIC_VIEW);
|
|
GFX_ASSERT(gfxop_draw_cel_static(_visual->_gfxState, _view, _loop, _cel,
|
|
_move_point(_bounds, pos), _color, _palette));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void GfxView::print(int indentation) const {
|
|
printIntern(indentation);
|
|
|
|
if (_type == GFXW_STATIC_VIEW)
|
|
sciprintf("STATICVIEW");
|
|
else if (_type == GFXW_VIEW)
|
|
sciprintf("VIEW");
|
|
else
|
|
error("GfxView::print: Invalid type %d", _type);
|
|
|
|
sciprintf("(%d/%d/%d)@(%d,%d)[p:%d,c:%d]", _view, _loop, _cel, _pos.x, _pos.y,
|
|
(_color.mask & GFX_MASK_PRIORITY) ? _color.priority : -1,
|
|
(_color.mask & GFX_MASK_CONTROL) ? _color.control : -1);
|
|
}
|
|
|
|
void _gfxw_set_ops_VIEW(GfxWidget *view, char stat) {
|
|
_gfxw_set_ops(view, _gfxwop_basic_compare_to, _gfxwop_basic_equals, _gfxwop_basic_superarea_of);
|
|
}
|
|
|
|
GfxView *gfxw_new_view(GfxState *state, Common::Point pos, int view_nr, int loop, int cel, int palette, int priority, int control,
|
|
gfx_alignment_t halign, gfx_alignment_t valign, int flags) {
|
|
GfxView *view;
|
|
|
|
if (flags & GFXW_VIEW_FLAG_DONT_MODIFY_OFFSET) {
|
|
int foo;
|
|
Common::Point offset;
|
|
gfxop_get_cel_parameters(state, view_nr, loop, cel, &foo, &foo, &offset);
|
|
pos.x += offset.x;
|
|
pos.y += offset.y;
|
|
}
|
|
|
|
view = new GfxView(state, pos, view_nr, loop, cel, palette, priority, control, halign, valign,
|
|
(flags & GFXW_VIEW_FLAG_STATIC) ? GFXW_STATIC_VIEW : GFXW_VIEW);
|
|
|
|
_gfxw_set_ops_VIEW(view, (char)(flags & GFXW_VIEW_FLAG_STATIC));
|
|
|
|
return view;
|
|
}
|
|
|
|
//*** Dynamic Views ***
|
|
|
|
int GfxDynView::draw(const Common::Point &pos) {
|
|
if (_type == GFXW_DYN_VIEW) {
|
|
DRAW_ASSERT(this, GFXW_DYN_VIEW);
|
|
|
|
GFX_ASSERT(gfxop_draw_cel(_visual->_gfxState, _view, _loop, _cel,
|
|
_move_point(draw_bounds, pos), _color, _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->_gfxState,
|
|
gfx_rect(view->_bounds.x + pos.x, view->_bounds.y + pos.y, view->_bounds.width - 1, view->_bounds.height - 1), red, 0, 0));
|
|
*/
|
|
} else {
|
|
DRAW_ASSERT(this, GFXW_PIC_VIEW);
|
|
|
|
if (_isDrawn)
|
|
return 0;
|
|
|
|
GFX_ASSERT(gfxop_set_clip_zone(_visual->_gfxState, _parent->zone));
|
|
GFX_ASSERT(gfxop_draw_cel_static_clipped(_visual->_gfxState, _view, _loop,
|
|
_cel, _move_point(draw_bounds, pos), _color, _palette));
|
|
|
|
// Draw again on the back buffer
|
|
GFX_ASSERT(gfxop_draw_cel(_visual->_gfxState, _view, _loop, _cel,
|
|
_move_point(draw_bounds, pos), _color, _palette));
|
|
|
|
|
|
_isDrawn = true; // No more drawing needs to be done
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void GfxDynView::print(int indentation) const {
|
|
printIntern(indentation);
|
|
|
|
if (_type == GFXW_DYN_VIEW)
|
|
sciprintf("DYNVIEW");
|
|
else if (_type == GFXW_PIC_VIEW)
|
|
sciprintf("PICVIEW");
|
|
else
|
|
error("GfxDynView::print: Invalid type %d", _type);
|
|
|
|
sciprintf(" SORT=%d z=%d seq=%d (%d/%d/%d)@(%d,%d)[p:%d,c:%d]; sig[%04x@%p]", force_precedence, _z,
|
|
sequence, _view, _loop, _cel, _pos.x, _pos.y,
|
|
(_color.mask & GFX_MASK_PRIORITY) ? _color.priority : -1,
|
|
(_color.mask & GFX_MASK_CONTROL) ? _color.control : -1, signal, signalp);
|
|
}
|
|
|
|
static int _gfxwop_dyn_view_equals(GfxWidget *widget, GfxWidget *other) {
|
|
GfxDynView *wview = (GfxDynView *)widget, *oview;
|
|
if (!GFXW_IS_DYN_VIEW(other))
|
|
return 0;
|
|
|
|
oview = (GfxDynView *)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(GfxWidget *widget, GfxWidget *other) {
|
|
int retval;
|
|
GfxDynView *wview = (GfxDynView *) widget, *oview;
|
|
if (!GFXW_IS_DYN_VIEW(other))
|
|
return 1;
|
|
|
|
oview = (GfxDynView *) 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(GfxWidget *widget) {
|
|
_gfxw_set_ops(widget, _gfxwop_dyn_view_compare_to, _gfxwop_dyn_view_equals, _gfxwop_basic_superarea_of);
|
|
}
|
|
|
|
void _gfxw_set_ops_PICVIEW(GfxWidget *widget) {
|
|
_gfxw_set_ops_DYNVIEW(widget);
|
|
}
|
|
|
|
GfxDynView *gfxw_new_dyn_view(GfxState *state, Common::Point 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) {
|
|
|
|
return new GfxDynView(state, pos, z, view, loop, cel, palette, priority, control, halign, valign, sequence);
|
|
}
|
|
|
|
GfxDynView::GfxDynView(GfxState *state, Common::Point 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_)
|
|
: GfxView(state, pos_, view_, loop_, cel_, palette_, priority, control, halign, valign, 0) {
|
|
int width, height;
|
|
int xalignmod, yalignmod;
|
|
Common::Point offset;
|
|
|
|
_type = GFXW_DYN_VIEW;
|
|
|
|
if (!state) {
|
|
error("Attempt to create view widget with NULL state");
|
|
}
|
|
|
|
if (gfxop_get_cel_parameters(state, view_, loop_, cel_, &width, &height, &offset)) {
|
|
error("Attempt to retrieve cel parameters for (%d/%d/%d) failed (Maybe the values weren't checked beforehand?)",
|
|
view_, cel_, loop_);
|
|
}
|
|
|
|
_pos = pos_;
|
|
_color.mask = ((priority < 0) ? 0 : GFX_MASK_PRIORITY) | ((control < 0) ? 0 : GFX_MASK_CONTROL);
|
|
_widgetPriority = priority;
|
|
_color.priority = priority;
|
|
_color.control = control;
|
|
_view = view_;
|
|
_loop = loop_;
|
|
_cel = cel_;
|
|
_palette = palette_;
|
|
|
|
_color.alpha = 0;
|
|
_color.visual = PaletteEntry(0,0,0); // FIXME: black!
|
|
|
|
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;
|
|
|
|
draw_bounds = gfx_rect(_pos.x - xalignmod, _pos.y - yalignmod - z_, width, height);
|
|
_bounds = gfx_rect(_pos.x - offset.x - xalignmod, _pos.y - offset.y - yalignmod - z_, width, height);
|
|
|
|
under_bitsp = NULL;
|
|
under_bits = 0;
|
|
signalp = NULL;
|
|
signal = 0;
|
|
_z = z_;
|
|
sequence = sequence_;
|
|
force_precedence = 0;
|
|
|
|
_isDrawn = false;
|
|
|
|
_flags |= GFXW_FLAG_VISIBLE;
|
|
|
|
_gfxw_set_ops_DYNVIEW(this);
|
|
}
|
|
|
|
//*** Text ***
|
|
|
|
GfxText::~GfxText() {
|
|
if (_textHandle) {
|
|
GfxState *state = _visual ? _visual->_gfxState : NULL;
|
|
if (!state) {
|
|
GFXERROR("Attempt to free text without supplying mode to free it from!\n");
|
|
error("GfxText destructor failed. Breakpoint in %s, line %d", __FILE__, __LINE__);
|
|
} else {
|
|
gfxop_free_text(state, _textHandle);
|
|
_textHandle = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int GfxText::draw(const Common::Point &pos) {
|
|
DRAW_ASSERT(this, GFXW_TEXT);
|
|
|
|
if (_textHandle == 0) {
|
|
_textHandle = gfxop_new_text(_visual->_gfxState, _font, _text, _bounds.width,
|
|
halign, valign, _color1, _color2, _bgcolor, _textFlags);
|
|
}
|
|
|
|
GFX_ASSERT(gfxop_draw_text(_visual->_gfxState, _textHandle, _move_rect(_bounds, pos)));
|
|
return 0;
|
|
}
|
|
|
|
void GfxText::print(int indentation) const {
|
|
printIntern(indentation);
|
|
sciprintf("TEXT:'%s'", _text.c_str());
|
|
}
|
|
|
|
static int _gfxwop_text_equals(GfxWidget *widget, GfxWidget *other) {
|
|
GfxText *wtext = (GfxText *)widget, *otext;
|
|
if (other->_type != GFXW_TEXT)
|
|
return 0;
|
|
|
|
otext = (GfxText *)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->_textFlags != otext->_textFlags)
|
|
return 0;
|
|
|
|
if (wtext->_font != otext->_font)
|
|
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(GfxWidget *widget, GfxWidget *other) {
|
|
GfxText *wtext = (GfxText *)widget, *otext;
|
|
|
|
if (other->_type != GFXW_TEXT)
|
|
return 0;
|
|
|
|
otext = (GfxText *)other;
|
|
|
|
return wtext->_text != otext->_text;
|
|
}
|
|
|
|
static int _gfxwop_text_compare_to(GfxWidget *widget, GfxWidget *other) {
|
|
return 1;
|
|
}
|
|
|
|
void _gfxw_set_ops_TEXT(GfxWidget *widget) {
|
|
_gfxw_set_ops(widget,
|
|
_gfxwop_text_compare_to, _gfxwop_text_equals,
|
|
_gfxwop_basic_superarea_of);
|
|
widget->should_replace = _gfxwop_text_should_replace;
|
|
}
|
|
|
|
GfxText *gfxw_new_text(GfxState *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) {
|
|
return new GfxText(state, area, font, text, halign, valign, color1, color2, bgcolor, text_flags);
|
|
}
|
|
|
|
GfxText::GfxText(GfxState *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_)
|
|
: GfxWidget(GFXW_TEXT) {
|
|
|
|
_widgetPriority = _gfxw_color_get_priority(color1_);
|
|
_font = font;
|
|
_text = text;
|
|
halign = halign_;
|
|
valign = valign_;
|
|
_color1 = color1_;
|
|
_color2 = color2_;
|
|
_bgcolor = bgcolor_;
|
|
_textFlags = text_flags_;
|
|
_textHandle = NULL;
|
|
|
|
gfxop_get_text_params(state, font, text, area.width, &width, &height, _textFlags,
|
|
&lines_nr, &lineheight, &lastline_width);
|
|
|
|
/* FIXME: Window is too big
|
|
area.x += _calc_needmove(halign, area.width, width);
|
|
area.y += _calc_needmove(valign, area.height, height);
|
|
*/
|
|
|
|
if (halign == ALIGN_LEFT)
|
|
area.width = width;
|
|
if (valign == ALIGN_TOP)
|
|
area.height = height;
|
|
|
|
_bounds = area;
|
|
|
|
_flags |= GFXW_FLAG_VISIBLE;
|
|
|
|
_gfxw_set_ops_TEXT(this);
|
|
}
|
|
|
|
void gfxw_text_info(GfxState *state, GfxText *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(GfxContainer *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, Common::Point(cont->zone.x, cont->zone.y)), propagate);
|
|
}
|
|
|
|
static void _gfxw_set_container_ops(GfxContainer *container,
|
|
gfxw_bin_op *compare_to, gfxw_bin_op *equals,
|
|
gfxw_bin_op *superarea_of,
|
|
gfxw_unary_container_op *free_tagged, gfxw_unary_container_op *free_contents,
|
|
gfxw_rect_op *add_dirty, gfxw_container_op *add) {
|
|
_gfxw_set_ops(container, 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;
|
|
}
|
|
|
|
static int _w_gfxwop_container_print_contents(const char *name, GfxWidget *widget, int indentation) {
|
|
GfxWidget *seeker = widget;
|
|
|
|
indent(indentation);
|
|
|
|
sciprintf("--%s:\n", name);
|
|
|
|
while (seeker) {
|
|
seeker->print(indentation + 1);
|
|
sciprintf("\n");
|
|
seeker = seeker->_next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void GfxContainer::print(int indentation) const {
|
|
sciprintf(" viszone=((%d,%d),(%dx%d))\n", zone.x, zone.y, zone.width, zone.height);
|
|
|
|
indent(indentation);
|
|
sciprintf("--dirty:\n");
|
|
|
|
for (DirtyRectList::const_iterator dirty = _dirtyRects.begin(); dirty != _dirtyRects.end(); ++dirty) {
|
|
indent(indentation + 1);
|
|
sciprintf("dirty(%d,%d, (%dx%d))\n", dirty->x, dirty->y, dirty->width, dirty->height);
|
|
}
|
|
|
|
_w_gfxwop_container_print_contents("contents", _contents, indentation);
|
|
}
|
|
|
|
|
|
GfxContainer::GfxContainer(rect_t area, gfxw_widget_type_t type_)
|
|
: GfxWidget(type_) {
|
|
_bounds = zone = area;
|
|
_contents = NULL;
|
|
_nextpp = &_contents;
|
|
|
|
free_tagged = NULL;
|
|
free_contents = NULL;
|
|
add_dirty_abs = NULL;
|
|
add_dirty_rel = NULL;
|
|
add = NULL;
|
|
|
|
_flags |= GFXW_FLAG_VISIBLE | GFXW_FLAG_CONTAINER;
|
|
}
|
|
|
|
static 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(GfxWidget *widget, GfxWidget *contents) {
|
|
GfxContainer *container = (GfxContainer *)widget;
|
|
GfxState *gfx_state = (widget->_visual) ? widget->_visual->_gfxState : ((GfxVisual *) widget)->_gfxState;
|
|
int draw_ports;
|
|
rect_t nullzone = {0, 0, 0, 0};
|
|
|
|
if (!contents)
|
|
return 0;
|
|
|
|
DirtyRectList::iterator dirty = container->_dirtyRects.begin();
|
|
while (dirty != container->_dirtyRects.end()) {
|
|
GfxWidget *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)) {
|
|
|
|
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));
|
|
((GfxContainer *)seeker)->add_dirty_abs((GfxContainer *)seeker, *dirty, 0);
|
|
}
|
|
|
|
seeker->_flags |= GFXW_FLAG_DIRTY;
|
|
}
|
|
|
|
seeker = seeker->_next;
|
|
}
|
|
|
|
++dirty;
|
|
}
|
|
|
|
// 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->_dirtyRects.begin();
|
|
while (dirty != container->_dirtyRects.end()) {
|
|
GfxWidget *seeker = contents;
|
|
// FIXME: Ugly hack to check whether this is the last dirty rect or not
|
|
DirtyRectList::iterator next = dirty;
|
|
++next;
|
|
const bool isLastDirtyRect = (next == container->_dirtyRects.end());
|
|
|
|
while (seeker && (draw_ports || !GFXW_IS_PORT(seeker))) {
|
|
rect_t small_rect = *dirty;
|
|
bool 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(Common::Point(container->zone.x, container->zone.y));
|
|
|
|
if (isLastDirtyRect)
|
|
seeker->_flags &= ~GFXW_FLAG_DIRTY;
|
|
}
|
|
|
|
seeker = seeker->_next;
|
|
}
|
|
++dirty;
|
|
}
|
|
}
|
|
// Remember that the dirty rects should be freed afterwards!
|
|
|
|
return 0;
|
|
}
|
|
|
|
GfxContainer::~GfxContainer() {
|
|
GfxWidget *seeker = _contents;
|
|
|
|
while (seeker) {
|
|
GfxWidget *next = seeker->_next;
|
|
delete seeker;
|
|
seeker = next;
|
|
}
|
|
|
|
_dirtyRects.clear();
|
|
}
|
|
|
|
void GfxContainer::tag() {
|
|
// FIXME: Should we also tag this object itself?
|
|
GfxWidget *seeker = _contents;
|
|
while (seeker) {
|
|
seeker->tag();
|
|
seeker = seeker->_next;
|
|
}
|
|
}
|
|
|
|
int GfxContainer::setVisual(GfxVisual *visual) {
|
|
_visual = visual;
|
|
if (_parent) {
|
|
if (!(GFXW_IS_LIST(this) && !_contents)) {
|
|
DDIRTY(stderr, "set_visual::DOWNWARDS abs(%d,%d,%d,%d, 1)\n", GFX_PRINT_RECT(_bounds));
|
|
_parent->add_dirty_abs(_parent, _bounds, 1);
|
|
}
|
|
}
|
|
|
|
GfxWidget *seeker = _contents;
|
|
while (seeker) {
|
|
seeker->setVisual(visual);
|
|
seeker = seeker->_next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int _gfxwop_container_free_tagged(GfxContainer *container) {
|
|
GfxWidget *seeker = container->_contents;
|
|
|
|
while (seeker) {
|
|
GfxWidget *next = seeker->_next;
|
|
if (seeker->_flags & GFXW_FLAG_TAGGED)
|
|
delete seeker;
|
|
seeker = next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _gfxwop_container_free_contents(GfxContainer *container) {
|
|
GfxWidget *seeker = container->_contents;
|
|
|
|
while (seeker) {
|
|
GfxWidget *next = seeker->_next;
|
|
delete seeker;
|
|
seeker = next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void _gfxw_dirtify_container(GfxContainer *container, GfxWidget *widget) {
|
|
if (GFXW_IS_CONTAINER(widget))
|
|
container->add_dirty_abs(container, widget->_bounds, 1);
|
|
else
|
|
container->add_dirty_rel(container, widget->_bounds, 1);
|
|
}
|
|
|
|
static int _parentize_widget(GfxContainer *container, GfxWidget *widget) {
|
|
if (widget->_parent) {
|
|
GFXERROR("_gfxwop_container_add(): Attempt to give second parent node to widget!\nWidget:");
|
|
widget->print(3);
|
|
sciprintf("\nContainer:");
|
|
container->print(3);
|
|
|
|
return 1;
|
|
}
|
|
|
|
widget->_parent = container;
|
|
|
|
if (GFXW_IS_VISUAL(container))
|
|
widget->setVisual((GfxVisual *)container);
|
|
else if (container->_visual)
|
|
widget->setVisual(container->_visual);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _gfxw_container_id_equals(GfxContainer *container, GfxWidget *widget) {
|
|
GfxWidget **seekerp = &(container->_contents);
|
|
|
|
if (GFXW_IS_PORT(widget))
|
|
return 0;
|
|
|
|
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)) {
|
|
delete widget;
|
|
(*seekerp)->_flags &= ~GFXW_FLAG_TAGGED;
|
|
return 1;
|
|
} else {
|
|
if (!(widget->_flags & GFXW_FLAG_MULTI_ID))
|
|
delete *seekerp;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int _gfxwop_container_add_dirty(GfxContainer *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);
|
|
gfxdr_add_dirty(container->_dirtyRects, dirty, GFXW_DIRTY_STRATEGY);
|
|
return 0;
|
|
}
|
|
|
|
static int _gfxwop_container_add(GfxContainer *container, GfxWidget *widget) {
|
|
if (_gfxw_container_id_equals(container, widget))
|
|
return 0;
|
|
|
|
if (_parentize_widget(container, widget))
|
|
return 1;
|
|
|
|
if (!(GFXW_IS_LIST(widget) && (!((GfxContainer *)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 ***
|
|
|
|
int GfxList::draw(const Common::Point &pos) {
|
|
if (_type == GFXW_LIST) {
|
|
DRAW_ASSERT(this, GFXW_LIST);
|
|
|
|
_gfxwop_container_draw_contents(this, _contents);
|
|
_flags &= ~GFXW_FLAG_DIRTY;
|
|
} else {
|
|
DRAW_ASSERT(this, GFXW_SORTED_LIST);
|
|
|
|
_gfxwop_container_draw_contents(this, _contents);
|
|
}
|
|
_dirtyRects.clear();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void GfxList::print(int indentation) const {
|
|
printIntern(indentation);
|
|
|
|
if (_type == GFXW_LIST)
|
|
sciprintf("LIST");
|
|
else if (_type == GFXW_SORTED_LIST)
|
|
sciprintf("SORTED_LIST");
|
|
else
|
|
error("GfxList::print: Invalid type %d", _type);
|
|
|
|
GfxContainer::print(indentation);
|
|
}
|
|
|
|
static int _gfxwop_list_equals(GfxWidget *widget, GfxWidget *other) {
|
|
// Requires identical order of list elements.
|
|
GfxList *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(0);
|
|
sciprintf("\n");
|
|
return 0;
|
|
}
|
|
|
|
wlist = (GfxList *)widget;
|
|
olist = (GfxList *)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(GfxContainer *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);
|
|
}
|
|
|
|
int _gfxwop_ordered_add(GfxContainer *container, GfxWidget *widget, int compare_all) {
|
|
// O(n)
|
|
GfxWidget **seekerp = &(container->_contents);
|
|
|
|
if (widget->_next) {
|
|
GFXERROR("_gfxwop_sorted_list_add(): Attempt to add widget to two lists!\nWidget:");
|
|
widget->print(3);
|
|
sciprintf("\nList:");
|
|
container->print(3);
|
|
error("Breakpoint in %s, line %d", __FILE__, __LINE__);
|
|
}
|
|
|
|
if (_gfxw_container_id_equals(container, widget))
|
|
return 0;
|
|
|
|
while (*seekerp && (compare_all || (widget->compare_to(widget, *seekerp) >= 0))) {
|
|
|
|
if (widget->equals(widget, *seekerp)) {
|
|
if (compare_all) {
|
|
if ((*seekerp)->_visual)
|
|
delete *seekerp; // If it's a fresh widget
|
|
else
|
|
gfxw_annihilate(*seekerp);
|
|
|
|
return _gfxwop_ordered_add(container, widget, compare_all); // We might have destroyed the container's contents
|
|
} else {
|
|
widget->_next = (*seekerp)->_next;
|
|
delete *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(GfxContainer *container, GfxWidget *widget) {
|
|
// O(n)
|
|
return _gfxwop_ordered_add(container, widget, 0);
|
|
}
|
|
|
|
void _gfxw_set_ops_LIST(GfxContainer *list, char sorted) {
|
|
_gfxw_set_container_ops((GfxContainer *)list,
|
|
_gfxwop_basic_compare_to, sorted ? _gfxwop_basic_equals : _gfxwop_list_equals,
|
|
_gfxwop_basic_superarea_of,
|
|
_gfxwop_container_free_tagged, _gfxwop_container_free_contents,
|
|
_gfxwop_list_add_dirty, sorted ? _gfxwop_sorted_list_add : _gfxwop_container_add);
|
|
}
|
|
|
|
GfxList *gfxw_new_list(rect_t area, int sorted) {
|
|
return new GfxList(area, sorted);
|
|
}
|
|
|
|
GfxList::GfxList(rect_t area, bool sorted)
|
|
: GfxContainer(area, sorted ? GFXW_SORTED_LIST : GFXW_LIST) {
|
|
|
|
_gfxw_set_ops_LIST(this, sorted);
|
|
}
|
|
|
|
|
|
|
|
//*** Visuals ***
|
|
|
|
int GfxVisual::draw(const Common::Point &pos) {
|
|
DRAW_ASSERT(this, GFXW_VISUAL);
|
|
|
|
for (DirtyRectList::iterator dirty = _dirtyRects.begin(); dirty != _dirtyRects.end(); ++dirty) {
|
|
int err = gfxop_clear_box(_gfxState, *dirty);
|
|
|
|
if (err) {
|
|
GFXERROR("Error while clearing dirty rect (%d,%d,(%dx%d))\n", dirty->x,
|
|
dirty->y, dirty->width, dirty->height);
|
|
if (err == GFX_FATAL)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
_gfxwop_container_draw_contents(this, _contents);
|
|
|
|
_dirtyRects.clear();
|
|
_flags &= ~GFXW_FLAG_DIRTY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void GfxVisual::print(int indentation) const {
|
|
printIntern(indentation);
|
|
sciprintf("VISUAL; ports={");
|
|
for (uint i = 0; i < _portRefs.size(); i++) {
|
|
if (_portRefs[i]) {
|
|
if (i != 0)
|
|
sciprintf(",");
|
|
sciprintf("%d", i);
|
|
}
|
|
}
|
|
sciprintf("}\n");
|
|
|
|
GfxContainer::print(indentation);
|
|
}
|
|
|
|
int GfxVisual::setVisual(GfxVisual *visual) {
|
|
if (this != visual) {
|
|
warning("Attempt to set a visual's parent visual to something else");
|
|
} else {
|
|
warning("Attempt to set a visual's parent visual to itself");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void _gfxw_set_ops_VISUAL(GfxContainer *visual) {
|
|
_gfxw_set_container_ops((GfxContainer *)visual,
|
|
_gfxwop_basic_compare_to,
|
|
_gfxwop_basic_equals, _gfxwop_basic_superarea_of,
|
|
_gfxwop_container_free_tagged, _gfxwop_container_free_contents,
|
|
_gfxwop_container_add_dirty, _gfxwop_container_add);
|
|
}
|
|
|
|
GfxVisual::GfxVisual(GfxState *state, int font)
|
|
: GfxContainer(gfx_rect(0, 0, 320, 200), GFXW_VISUAL) {
|
|
|
|
_font = font;
|
|
_gfxState = state;
|
|
|
|
_gfxw_set_ops_VISUAL(this);
|
|
}
|
|
|
|
GfxVisual::~GfxVisual() {
|
|
// HACK: We must dispose all content *here* already, because our child widgets
|
|
// still may have references to this object, and will try to invoke methods
|
|
// of this object which try to access the already cleared _portRefs array
|
|
// when they are destroyed.
|
|
GfxWidget *seeker = _contents;
|
|
|
|
while (seeker) {
|
|
GfxWidget *next = seeker->_next;
|
|
delete seeker;
|
|
seeker = next;
|
|
}
|
|
_contents = 0;
|
|
}
|
|
|
|
static int _visual_find_free_ID(GfxVisual *visual) {
|
|
uint id = 0;
|
|
|
|
while (id < visual->_portRefs.size() && visual->_portRefs[id])
|
|
id++;
|
|
|
|
if (id == visual->_portRefs.size()) { // Out of ports?
|
|
visual->_portRefs.push_back(0);
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
//*** Ports ***
|
|
|
|
int GfxPort::draw(const Common::Point &pos) {
|
|
DRAW_ASSERT(this, GFXW_PORT);
|
|
|
|
if (_decorations) {
|
|
DDIRTY(stderr, "Getting/applying deco dirty (multi)\n");
|
|
|
|
DDIRTY(stderr, "Adding multiple dirty to #%d\n", _decorations->_ID);
|
|
for (DirtyRectList::iterator dirty = _dirtyRects.begin(); dirty != _dirtyRects.end(); ++dirty) {
|
|
gfxdr_add_dirty(_decorations->_dirtyRects, *dirty, GFXW_DIRTY_STRATEGY);
|
|
}
|
|
|
|
if (_decorations->draw(gfxw_point_zero)) {
|
|
_decorations->_dirtyRects.clear();
|
|
return 1; // error
|
|
}
|
|
_decorations->_dirtyRects.clear();
|
|
}
|
|
|
|
_gfxwop_container_draw_contents(this, _contents);
|
|
|
|
_dirtyRects.clear();
|
|
_flags &= ~GFXW_FLAG_DIRTY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
GfxPort::~GfxPort() {
|
|
if (_visual) {
|
|
if (_ID < 0 || _ID >= (int)_visual->_portRefs.size()) {
|
|
error("Attempt to free port #%d; allowed: [0..%d]", _ID, _visual->_portRefs.size());
|
|
}
|
|
|
|
if (_visual->_portRefs[_ID] != this) {
|
|
GFXWARN("While freeing port %d: Port is at %p, but port list indicates %p", _ID, (void *)this, (void *)_visual->_portRefs[_ID]);
|
|
} else
|
|
_visual->_portRefs[_ID] = NULL;
|
|
|
|
}
|
|
|
|
delete _decorations;
|
|
}
|
|
|
|
void GfxPort::print(int indentation) const {
|
|
printIntern(indentation);
|
|
sciprintf("PORT");
|
|
sciprintf(" font=%d drawpos=(%d,%d)", _font, draw_pos.x, draw_pos.y);
|
|
if (gray_text)
|
|
sciprintf(" (gray)");
|
|
|
|
GfxContainer::print(indentation);
|
|
_w_gfxwop_container_print_contents("decorations", _decorations, indentation);
|
|
}
|
|
|
|
static int _gfxwop_port_superarea_of(GfxWidget *self, GfxWidget *other) {
|
|
GfxPort *port = (GfxPort *) self;
|
|
|
|
if (!port->port_bg)
|
|
return _gfxwop_basic_superarea_of(self, other);
|
|
|
|
return port->port_bg->superarea_of(port->port_bg, other);
|
|
}
|
|
|
|
int GfxPort::setVisual(GfxVisual *visual) {
|
|
_visual = visual;
|
|
|
|
if (_decorations)
|
|
if (_decorations->setVisual(visual)) {
|
|
GFXWARN("Setting the visual for decorations failed for port ");
|
|
this->print(1);
|
|
return 1;
|
|
}
|
|
|
|
return GfxContainer::setVisual(visual);
|
|
}
|
|
|
|
static int _gfxwop_port_add_dirty(GfxContainer *widget, rect_t dirty, int propagate) {
|
|
GfxPort *self = (GfxPort *) 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.width, dirty.height,
|
|
widget->_bounds.x, widget->_bounds.y, widget->_bounds.width, widget->_bounds.height);
|
|
#if 0
|
|
// FIXME: This is a worthwhile optimization
|
|
if (self->port_bg) {
|
|
GfxWidget 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)) {
|
|
GfxContainer *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(GfxContainer *container, GfxWidget *widget) {
|
|
// O(n)
|
|
return _gfxwop_ordered_add(container, widget, 1);
|
|
}
|
|
|
|
void _gfxw_set_ops_PORT(GfxContainer *widget) {
|
|
_gfxw_set_container_ops((GfxContainer *)widget,
|
|
_gfxwop_basic_compare_to, _gfxwop_basic_equals, _gfxwop_port_superarea_of,
|
|
_gfxwop_container_free_tagged, _gfxwop_container_free_contents,
|
|
_gfxwop_port_add_dirty, _gfxwop_port_add);
|
|
}
|
|
|
|
GfxPort::GfxPort(GfxVisual *visual_, rect_t area, gfx_color_t fgcolor, gfx_color_t bgcolor_)
|
|
: GfxContainer(area, GFXW_PORT) {
|
|
VERIFY_WIDGET(visual_);
|
|
|
|
port_bg = NULL;
|
|
_parent = NULL;
|
|
_decorations = NULL;
|
|
title_text = NULL;
|
|
draw_pos = Common::Point(0, 0);
|
|
gray_text = 0;
|
|
_color = fgcolor;
|
|
_bgcolor = bgcolor_;
|
|
_font = visual_->_font;
|
|
_ID = _visual_find_free_ID(visual_);
|
|
visual_->_portRefs[_ID] = this;
|
|
|
|
_gfxw_set_ops_PORT(this);
|
|
}
|
|
|
|
void gfxw_port_auto_restore_background(GfxVisual *visual, GfxPort *window, rect_t auto_rect) {
|
|
window->port_flags |= kWindowAutoRestore;
|
|
window->restore_snap = gfxw_make_snapshot(visual, auto_rect);
|
|
}
|
|
|
|
GfxPort *gfxw_remove_port(GfxVisual *visual, GfxPort *port) {
|
|
GfxPort *parent;
|
|
VERIFY_WIDGET(visual);
|
|
VERIFY_WIDGET(port);
|
|
|
|
if (!visual->_contents) {
|
|
GFXWARN("Attempt to remove port from empty visual\n");
|
|
return NULL;
|
|
}
|
|
|
|
parent = (GfxPort *)port->_parent;
|
|
if (port->port_flags & kWindowAutoRestore)
|
|
gfxw_restore_snapshot(visual, port->restore_snap);
|
|
|
|
delete port;
|
|
|
|
while (parent && !GFXW_IS_PORT(parent))
|
|
parent = (GfxPort *)parent->_parent; // Ascend through ancestors
|
|
|
|
return parent;
|
|
}
|
|
|
|
GfxPort *gfxw_find_default_port(GfxVisual *visual) {
|
|
int id = visual->_portRefs.size();
|
|
|
|
while (id--) {
|
|
GfxPort *port = visual->_portRefs[id];
|
|
|
|
if (port)
|
|
return port;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// - other functions -
|
|
|
|
GfxWidget *gfxw_set_id(GfxWidget *widget, int ID, int subID) {
|
|
if (widget) {
|
|
widget->_ID = ID;
|
|
widget->_subID = subID;
|
|
}
|
|
|
|
return widget;
|
|
}
|
|
|
|
GfxDynView *gfxw_dyn_view_set_params(GfxDynView *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;
|
|
}
|
|
|
|
GfxWidget *gfxw_remove_id(GfxContainer *container, int ID, int subID) {
|
|
GfxWidget **wp = &(container->_contents);
|
|
|
|
while (*wp) {
|
|
if ((*wp)->_ID == ID && (subID == GFXW_NO_ID || (*wp)->_subID == subID)) {
|
|
GfxWidget *widget = *wp;
|
|
|
|
*wp = (*wp)->_next;
|
|
widget->_next = NULL;
|
|
widget->_parent = NULL;
|
|
widget->_visual = NULL;
|
|
|
|
return widget;
|
|
}
|
|
|
|
wp = &((*wp)->_next);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GfxWidget *gfxw_hide_widget(GfxWidget *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;
|
|
}
|
|
|
|
GfxWidget *gfxw_show_widget(GfxWidget *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(GfxVisual *visual, rect_t area) {
|
|
gfxw_snapshot_t *retval = (gfxw_snapshot_t*)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.width == 320)
|
|
retval->area.width = 321;
|
|
|
|
return retval;
|
|
}
|
|
|
|
int gfxw_widget_matches_snapshot(gfxw_snapshot_t *snapshot, GfxWidget *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;
|
|
}
|
|
|
|
// Note: the check for snapshot->area and bounds is NOT the same as contains() in Common::Rect
|
|
// (this one includes equality too)
|
|
return ((widget->_serial >= free_above_eq || widget->_serial < free_below) &&
|
|
(snapshot->area.x <= bounds.x && snapshot->area.y <= bounds.y &&
|
|
snapshot->area.width >= bounds.width &&
|
|
snapshot->area.height >= bounds.height));
|
|
}
|
|
|
|
#define MAGIC_FREE_NUMBER -42
|
|
|
|
void _gfxw_free_contents_appropriately(GfxContainer *container, gfxw_snapshot_t *snapshot, int priority) {
|
|
GfxWidget *widget = container->_contents;
|
|
|
|
while (widget) {
|
|
GfxWidget *next = widget->_next;
|
|
|
|
if (gfxw_widget_matches_snapshot(snapshot, widget) && !(widget->_flags & GFXW_FLAG_IMMUNE_TO_SNAPSHOTS)
|
|
&& (priority == MAGIC_FREE_NUMBER || priority <= widget->_widgetPriority || widget->_widgetPriority == -1)) {
|
|
delete widget;
|
|
} else {
|
|
if (GFXW_IS_CONTAINER(widget))
|
|
_gfxw_free_contents_appropriately((GfxContainer *)widget, snapshot, priority);
|
|
}
|
|
|
|
widget = next;
|
|
}
|
|
}
|
|
|
|
gfxw_snapshot_t *gfxw_restore_snapshot(GfxVisual *visual, gfxw_snapshot_t *snapshot) {
|
|
_gfxw_free_contents_appropriately((GfxContainer *)visual, snapshot, MAGIC_FREE_NUMBER);
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
void gfxw_annihilate(GfxWidget *widget) {
|
|
GfxVisual *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->_widgetPriority;
|
|
}
|
|
|
|
delete widget;
|
|
|
|
if (free_overdrawn)
|
|
_gfxw_free_contents_appropriately((GfxContainer *)visual, &snapshot, widget_priority);
|
|
}
|
|
|
|
GfxDynView *gfxw_picviewize_dynview(GfxDynView *dynview) {
|
|
dynview->_type = GFXW_PIC_VIEW;
|
|
dynview->_flags |= GFXW_FLAG_DIRTY;
|
|
|
|
_gfxw_set_ops_PICVIEW(dynview);
|
|
|
|
if (dynview->_parent)
|
|
_gfxw_dirtify_container(dynview->_parent, dynview);
|
|
|
|
return dynview;
|
|
}
|
|
|
|
} // End of namespace Sci
|