On Fri, 5 Nov 1999, Cyrille Chepelov (home) wrote:
> This improves on my previous "snarf.diff" patch by removing about 100
> lines in each child object type : now the only remaining stuff is the
> Object* structures and the new, overriding routines.
I didn't comment this the last time, but i don't like this way of solving
the problems. If there is some helper function or even full object that is
so useful it should be moved over to lib/ and institutionalized as a
generic base class. Relying on some arbritary object for help makes for a
fraigile dependency. And if you want to use a method of a base class its
symbol should be clearly visible (i.e. in lib/) so that you could just
put it in the ops struct. None of this fixup stuff.
While I'm in complain mode i also like to say that i still *don't* like
the idea of having very generic base elements and then limiting the
visiblility or editability of their properties in base classes. This makes
for overly complex base classes and is contrary to typical OO. Having a
zigzagline that does rounded corner might look ok, but then someone wants
it to support wavy lines too, which is possible after some work, but the
code gets horribly comples (waved curves anyone?). Then people want more
features leading to hard to understand unmaintainable code.
But instead of all-powerfull base classes it must be very easy to create
subclasses. I think the way to solve this is by having generic helper
functions that help doing the grunt work of objects (mostly persistance
and property editing). My idea is partly like James property proposal, but
a bit different.
Essentially you describe the layout of your object, and then you put this
description in the ObjectType. Then you just replace the properties and
load/save methods with generic functions that uses the object
descriptions to work. To make this possible some new extra ObjectOps must
be added:
update_data() (exists in all objects anyway) is called to update
object internal data when some property has been changed.
get_state() and
set_state() - Are needed for objects that use the generic property
helper functions.
I've added a new version of the zigzagline using this method. It is now
336 lines.
Some objects have properties that will never be possible to handle with
generic helper functions. These will still have to define their properties
so that they might be used in scripts and multi-selected-object properties
editing, but the properties that are non-describable has to be marked of
type PROPERTY_OPAQUE or something.
Further comments:
> My roadmap (comments and help welcome):
> [1] build an object inheritance mechanism to limit code
> duplication.
I don't like this.
> 2 Eliminate register_sheets(), handle the filling of sheets
> through improved index.sheet (built so that a user-defined sheet takes
> precedence over dia-supplied ones).
Yes, this should really be done. This removes some extra code from object
libraries and make it easier to change the sheets and even make your own.
> 3 Bring a property list in struct Object, so that each property
> has value/min/max/visible/editeable fields.
> 4 Provide default Load/Save/Edit routines using property lists
See above example.
> 5 tweak the midclick menu system for step 6 (perhaps not as
> radically as I had stated last sunday).
> 6 de-couple object implementation from object type definition ;
> put object type definition in XML files (define a suitable format, similar
> to the way shapes are defined). Property list contents and midclick
> menu appearance are defined in the object type definition file
Why? I don't understand this.
> 7 Convert objects so that they actually use the properties, and
> stop using custom Load/Save/Edit routines (wherever applicable). Define
> the properties in a way clever enough to allow file-level compatibility
> with older binaries (at least ascending).
Yes. All object would need to be converted to the new system.
> 8 While doing 7, possibly try to merge object implementations
> wherever practical.
Well, if it is possible and really wanted yes.
By the way, sharing object implementations for object that are much alike
is actually possible right now by using the user_data argument of
object_create(). You can place different arguments in different sheet
objects refering to the same object. This is used for example in the class
and template class case.
/ Alex
/* Dia -- an diagram creation/manipulation program
* Copyright (C) 1998 Alexander Larsson
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <assert.h>
#include <gtk/gtk.h>
#include <math.h>
#include "config.h"
#include "intl.h"
#include "object.h"
#include "orth_conn.h"
#include "connectionpoint.h"
#include "render.h"
#include "attributes.h"
#include "widgets.h"
#include "message.h"
#include "pixmaps/zigzag.xpm"
typedef struct _ZigzaglineState ZigzaglineState;
struct _ZigzaglineState {
ObjectState obj_state;
Color line_color;
LineStyle line_style;
real dashlength;
real line_width;
Arrow start_arrow, end_arrow;
};
typedef struct _Zigzagline {
OrthConn orth;
Color line_color;
LineStyle line_style;
real dashlength;
real line_width;
Arrow start_arrow, end_arrow;
} Zigzagline;
static real default_dashlen = DEFAULT_LINESTYLE_DASHLEN;
static real default_linewidth = 0.5;
static Arrow default_arrow = { /* ... */ };
Properties[] zigzagline_properties = {
{ "line_color", PROPERTY_COLOR, G_STRUCT_OFFSET(Zigzagline, line_color),
&color_black, N_("Color:")},
{ "line_style", PROPERTY_LINESTYLE, G_STRUCT_OFFSET(Zigzagline, line_style),
GINT_TO_POINTER(LINESTYLE_SOLID), N_("Line Style:")},
{ "line_width", PROPERTY_REAL, G_STRUCT_OFFSET(Zigzagline, line_width),
&default_linewidth, N_("Line width:")},
{ "dashlength", PROPERTY_REAL, G_STRUCT_OFFSET(Zigzagline, dashlength),
&default_dashlen, N_("Dash length:")},
{ "start_arrow", PROPERTY_ARROW, G_STRUCT_OFFSET(Zigzagline, start_arrow),
&default_arrow, N_("Start arrow:")},
{ "end_arrow", PROPERTY_ARROW, G_STRUCT_OFFSET(Zigzagline, end_arrow),
&default_arrow, N_("End arrow:")},
{ NULL}
};
static void zigzagline_select(Zigzagline *zigzagline, Point *clicked_point,
Renderer *interactive_renderer);
static void zigzagline_draw(Zigzagline *zigzagline, Renderer *renderer);
static Object *zigzagline_create(Point *startpoint, void *user_data,
Handle **handle1, Handle **handle2);
static real zigzagline_distance_from(Zigzagline *zigzagline, Point *point);
static void zigzagline_update_data(Zigzagline *zigzagline);
static Object *zigzagline_copy(Zigzagline *zigzagline);
static DiaMenu *zigzagline_get_object_menu(Zigzagline *zigzagline,
Point *clickedpoint);
static ZigzaglineState *zigzagline_get_state(Zigzagline *zigzagline);
static void zigzagline_set_state(Zigzagline *zigzagline, ZigzaglineState *state);
static ObjectTypeOps zigzagline_type_ops =
{
(CreateFunc)zigzagline_create, /* create */
(LoadFunc) properties_load, /* load */
(SaveFunc) properties_save, /* save */
(GetDefaultsFunc) NULL /*zigzagline_get_defaults*/,
(ApplyDefaultsFunc) NULL /*zigzagline_apply_defaults*/
};
static ObjectType zigzagline_type =
{
"Standard - ZigZagLine", /* name */
0, /* version */
(char **) zigzag_xpm, /* pixmap */
&zigzagline_type_ops, /* ops */
&zigzagline_properties
};
ObjectType *_zigzagline_type = (ObjectType *) &zigzagline_type;
static ObjectOps zigzagline_ops = {
(DestroyFunc) orthconn_destroy,
(DrawFunc) zigzagline_draw,
(DistanceFunc) zigzagline_distance_from,
(SelectFunc) zigzagline_select,
(CopyFunc) zigzagline_copy,
(MoveFunc) orthconn_move,
(MoveHandleFunc) orthconn_move_handle,
(GetPropertiesFunc) properties_get_properties,
(ApplyPropertiesFunc) properties_apply_properties,
(ObjectMenuFunc) zigzagline_get_object_menu,
(UpdateDataFunc) zigzagline_update_data,
(GetStateFunc) zigzagline_get_state,
(SetStateFunc) zigzagline_set_state
};
static real
zigzagline_distance_from(Zigzagline *zigzagline, Point *point)
{
return orthconn_distance_from((OrthConn *)zigzagline, point,
zigzagline->line_width);
}
static void
zigzagline_select(Zigzagline *zigzagline, Point *clicked_point,
Renderer *interactive_renderer)
{
orthconn_update_data(&zigzagline->orth);
}
static void
zigzagline_draw(Zigzagline *zigzagline, Renderer *renderer)
{
OrthConn *orth = &zigzagline->orth;
Point *points;
int n;
points = &orth->points[0];
n = orth->numpoints;
renderer->ops->set_linewidth(renderer, zigzagline->line_width);
renderer->ops->set_linestyle(renderer, zigzagline->line_style);
renderer->ops->set_dashlength(renderer, zigzagline->dashlength);
renderer->ops->set_linejoin(renderer, LINEJOIN_MITER);
renderer->ops->set_linecaps(renderer, LINECAPS_BUTT);
renderer->ops->draw_polyline(renderer, points, n, &zigzagline->line_color);
if (zigzagline->start_arrow.type != ARROW_NONE) {
arrow_draw(renderer, zigzagline->start_arrow.type,
&points[0], &points[1],
zigzagline->start_arrow.length, zigzagline->start_arrow.width,
zigzagline->line_width,
&zigzagline->line_color, &color_white);
}
if (zigzagline->end_arrow.type != ARROW_NONE) {
arrow_draw(renderer, zigzagline->end_arrow.type,
&points[n-1], &points[n-2],
zigzagline->end_arrow.length, zigzagline->end_arrow.width,
zigzagline->line_width,
&zigzagline->line_color, &color_white);
}
}
static Object *
zigzagline_create(Point *startpoint,
void *user_data,
Handle **handle1,
Handle **handle2)
{
Zigzagline *zigzagline;
OrthConn *orth;
Object *obj;
zigzagline = g_malloc(sizeof(Zigzagline));
orth = &zigzagline->orth;
obj = (Object *) zigzagline;
obj->type = &zigzagline_type;
obj->ops = &zigzagline_ops;
orthconn_init(orth, startpoint);
zigzagline_update_data(zigzagline);
zigzagline->line_width = attributes_get_default_linewidth();
zigzagline->line_color = attributes_get_foreground();
attributes_get_default_line_style(&zigzagline->line_style,
&zigzagline->dashlength);
zigzagline->start_arrow = attributes_get_default_start_arrow();
zigzagline->end_arrow = attributes_get_default_end_arrow();
*handle1 = orth->handles[0];
*handle2 = orth->handles[orth->numpoints-2];
return (Object *)zigzagline;
}
static Object *
zigzagline_copy(Zigzagline *zigzagline)
{
Zigzagline *newzigzagline;
OrthConn *orth, *neworth;
Object *newobj;
orth = &zigzagline->orth;
newzigzagline = g_malloc(sizeof(Zigzagline));
neworth = &newzigzagline->orth;
newobj = (Object *) newzigzagline;
orthconn_copy(orth, neworth);
newzigzagline->line_color = zigzagline->line_color;
newzigzagline->line_width = zigzagline->line_width;
newzigzagline->line_style = zigzagline->line_style;
newzigzagline->start_arrow = zigzagline->start_arrow;
newzigzagline->end_arrow = zigzagline->end_arrow;
return (Object *)newzigzagline;
}
static ZigzaglineState *
zigzagline_get_state(Zigzagline *zigzagline)
{
ZigzaglineState *state = g_new(ZigzaglineState, 1);
state->obj_state.free = NULL;
state->line_color = zigzagline->line_color;
state->line_style = zigzagline->line_style;
state->dashlength = zigzagline->dashlength;
state->line_width = zigzagline->line_width;
state->start_arrow = zigzagline->start_arrow;
state->end_arrow = zigzagline->end_arrow;
return state;
}
static void
zigzagline_set_state(Zigzagline *zigzagline, ZigzaglineState *state)
{
zigzagline->line_color = state->line_color;
zigzagline->line_style = state->line_style;
zigzagline->dashlength = state->dashlength;
zigzagline->line_width = state->line_width;
zigzagline->start_arrow = state->start_arrow;
zigzagline->end_arrow = state->end_arrow;
g_free(state);
zigzagline_update_data(zigzagline);
}
static void
zigzagline_update_data(Zigzagline *zigzagline)
{
OrthConn *orth = &zigzagline->orth;
Object *obj = (Object *) zigzagline;
orthconn_update_data(&zigzagline->orth);
orthconn_update_boundingbox(orth);
/* fix boundingbox for line_width: */
obj->bounding_box.top -= zigzagline->line_width/2;
obj->bounding_box.left -= zigzagline->line_width/2;
obj->bounding_box.bottom += zigzagline->line_width/2;
obj->bounding_box.right += zigzagline->line_width/2;
/* Fix boundingbox for arrowheads */
if (zigzagline->start_arrow.type != ARROW_NONE ||
zigzagline->end_arrow.type != ARROW_NONE) {
real arrow_width = 0.0;
if (zigzagline->start_arrow.type != ARROW_NONE)
arrow_width = zigzagline->start_arrow.width;
if (zigzagline->end_arrow.type != ARROW_NONE)
arrow_width = MAX(arrow_width, zigzagline->start_arrow.width);
obj->bounding_box.top -= arrow_width;
obj->bounding_box.left -= arrow_width;
obj->bounding_box.bottom += arrow_width;
obj->bounding_box.right += arrow_width;
}
}
static ObjectChange *
zigzagline_add_segment_callback(Object *obj, Point *clicked, gpointer data)
{
ObjectChange *change;
change = orthconn_add_segment((OrthConn *)obj, clicked);
zigzagline_update_data((Zigzagline *)obj);
return change;
}
static ObjectChange *
zigzagline_delete_segment_callback(Object *obj, Point *clicked, gpointer data)
{
ObjectChange *change;
change = orthconn_delete_segment((OrthConn *)obj, clicked);
zigzagline_update_data((Zigzagline *)obj);
return change;
}
static DiaMenuItem object_menu_items[] = {
{ N_("Add segment"), zigzagline_add_segment_callback, NULL, 1 },
{ N_("Delete segment"), zigzagline_delete_segment_callback, NULL, 1 },
};
static DiaMenu object_menu = {
"Zigzagline",
sizeof(object_menu_items)/sizeof(DiaMenuItem),
object_menu_items,
NULL
};
static DiaMenu *
zigzagline_get_object_menu(Zigzagline *zigzagline, Point *clickedpoint)
{
OrthConn *orth;
orth = &zigzagline->orth;
/* Set entries sensitive/selected etc here */
object_menu_items[0].active = orthconn_can_add_segment(orth, clickedpoint);
object_menu_items[1].active = orthconn_can_delete_segment(orth, clickedpoint);
return &object_menu;
}