/* Copyright (C) 2002 Bjoern Beutel. */
/* Description. =============================================================*/
/* Read in and display Malaga Variables. */
/* Includes. ================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <setjmp.h>
#include <string.h>
#include <gtk/gtk.h>
#include "basic.h"
#include "scanner.h"
#include "input.h"
#include "canvas.h"
#include "tree.h"
/* Constants. ===============================================================*/
enum {FIRST_STATE, NEXT_STATE, PREV_STATE, LAST_STATE};
enum {PATH_BEGIN, PATH_END, FROM_ROOT}; /* Items of the tree's pop-up menu. */
/* Types. ===================================================================*/
typedef enum {INTER_NODE, BREAK_NODE, FINAL_NODE, UNFINAL_NODE,
PRUNED_NODE} tree_node_type_t;
typedef enum {ELEMENT_NODE, ELEMENT_LINK, ELEMENT_RULE} element_t;
/* Elements of a node that may be hit by the mouse pointer. */
typedef struct tree_node
{
struct tree_node *parent; /* Parent of this tree node. */
struct tree_node *sibling; /* Next sibling of this tree node. */
struct tree_node *child; /* First successor of this tree node. */
int_t state_index; /* State index of this tree node. */
tree_node_type_t type; /* Type of this node. */
string_t rule_name; /* Name of rule that created this node. */
string_t link_surf;
string_t link_feat;
string_t result_surf;
string_t result_feat;
string_t rule_set;
bool_t result_path;
/* The following items are used to display the tree node. */
bool_t in_path; /* TRUE iff this node is in displayed path. */
int_t x, y; /* Node position. */
pos_string_t *pos_rule_name; /* Positioned rule name. */
pos_string_t *pos_link_surf; /* Positioned link surface. */
pos_string_t *pos_state_index; /* Positioned state index. */
} tree_node_t;
typedef struct
{
list_node_t *next; /* Next path node in this list. */
pos_string_t *state_index;
pos_string_t *link_surf;
pos_value_t *link_feat;
pos_string_t *rule_name;
pos_string_t *result_surf;
pos_value_t *result_feat;
pos_string_t *rule_set;
} path_node_t;
/* Global variables. ========================================================*/
rectangle_t tree_geometry, path_geometry;
string_t tree_font_family, path_font_family;
int_t tree_font_size, path_font_size;
tree_mode_t tree_mode;
bool_t show_state_indexes;
bool_t inline_path;
/* Variables. ===============================================================*/
/* Information in Tree window. */
static pos_string_t *result_surf;
static tree_node_t *tree_nodes;
static tree_node_t **states; /* Dynamic array of nodes representing states. */
static int_t state_count; /* Size of the array STATES. */
static int_t tree_width, tree_height; /* Width and height of tree canvas. */
static canvas_t *tree_canvas;
static tree_node_t *popup_menu_node;
static bool_t tree_show_state_indexes;
/* Controls whether state indexes are shown in tree. */
/* Information in Path window. */
static list_t path_nodes;
static canvas_t *path_canvas;
static pos_string_t *plus;
static pos_string_t *comma;
static tree_node_t *path_begin, *path_end;
static bool_t path_show_state_indexes;
/* Controls whether state indexes are shown in path. */
/* Forward declarations. ====================================================*/
static void goto_state( canvas_t *canvas, guint action );
/* Functions. ===============================================================*/
static string_t
parse_optional_value( string_t *input )
/* Parse "{}" or "{VALUE}" in *INPUT and return NULL or VALUE, resp.
* Update *INPUT. The result must bee freed after use. */
{
string_t s, value;
if (**input != '{')
complain( "Missing \"{\"." );
for (s = *input + 1; *s != '}'; s++)
{
if (*s == EOS)
complain( "Missing \"}\"." );
if (*s == '\"')
{
for (s++; *s != '\"'; s++)
{
if (*s == EOS)
complain( "Missing '\"'." );
if (s[0] == '\\' && s[1] == '\"')
s++;
}
}
}
if (s > *input + 1)
value = new_string( *input + 1, s );
else
value = NULL;
*input = s + 1;
parse_whitespace( input );
return value;
}
/*---------------------------------------------------------------------------*/
static void
configure_path( canvas_t *canvas, int_t *width_p, int_t *height_p )
/* Do the layout of the path CANVAS.
* Return the canvas' total size in *WIDTH_P and *HEIGHT_P. */
{
int_t path_width, path_height, x, x_dist;
path_node_t *node;
bool_t is_first;
int_t space_width = get_space_width( canvas );
int_t font_height = get_font_height( canvas );
int_t font_ascent = get_font_ascent( canvas );
int_t border_width = get_border_width( canvas );
config_pos_string( plus, canvas );
config_pos_string( comma, canvas );
x_dist = space_width + comma->width;
path_width = path_height = border_width;
is_first = TRUE;
FOREACH( node, path_nodes )
{
if (node->link_feat != NULL)
{
if (is_first)
is_first = FALSE;
else
path_height += font_height;
config_pos_string( node->link_surf, canvas );
node->link_surf->x = border_width + plus->width + space_width;
path_width = MAX( path_width,
node->link_surf->x + node->link_surf->width );
config_pos_value( node->link_feat, canvas );
node->link_feat->x = node->link_surf->x;
if (inline_path)
{
node->link_surf->y = (path_height
+ node->link_feat->ascent - font_ascent);
node->link_feat->x += node->link_surf->width + x_dist;
}
else
{
node->link_surf->y = path_height;
path_height += font_height;
}
node->link_feat->y = path_height;
path_width = MAX( path_width,
node->link_feat->x + node->link_feat->width );
path_height += node->link_feat->height;
}
if (node->result_feat != NULL)
{
if (is_first)
is_first = FALSE;
else
path_height += font_height;
x = border_width;
/* Configure rule name. */
if (node != (path_node_t *) path_nodes.first)
{
config_pos_string( node->rule_name, canvas );
node->rule_name->x = x;
x += node->rule_name->width + 2 * space_width;
if (! inline_path)
node->rule_name->y = path_height;
}
/* Configure state index. */
if (path_show_state_indexes)
{
config_pos_string( node->state_index, canvas );
node->state_index->x = x;
if (inline_path)
x += node->state_index->width + x_dist;
else
{
node->state_index->y = path_height;
path_width = MAX( path_width, x + node->state_index->width );
path_height += font_height;
}
}
/* Configure result surface. */
config_pos_string( node->result_surf, canvas );
node->result_surf->x = x;
if (inline_path)
x += node->result_surf->width + x_dist;
else
{
node->result_surf->y = path_height;
path_width = MAX( path_width, x + node->result_surf->width );
path_height += font_height;
}
/* Configure result feature structure. */
config_pos_value( node->result_feat, canvas );
node->result_feat->x = x;
node->result_feat->y = path_height;
if (inline_path)
x += node->result_feat->width + x_dist;
else
{
path_width = MAX( path_width, x + node->result_feat->width );
path_height += node->result_feat->height;
}
/* Configure rule set. */
config_pos_string( node->rule_set, canvas );
node->rule_set->x = x;
if (inline_path)
{
node->rule_name->y
= node->state_index->y
= node->result_surf->y
= node->rule_set->y
= path_height + node->result_feat->ascent - font_ascent;
path_height += node->result_feat->height;
}
else
{
node->rule_set->y = path_height;
path_height += font_height;
}
path_width = MAX( path_width, x + node->rule_set->width );
}
}
*width_p = path_width + border_width;
*height_p = path_height + border_width;
}
/*---------------------------------------------------------------------------*/
static void
expose_path( canvas_t *canvas, rectangle_t *area )
{
path_node_t *node;
int_t x1, x2, y;
int_t space_width = get_space_width( canvas );
int_t font_height = get_font_height( canvas );
int_t border_width = get_border_width( canvas );
int_t line_width = get_line_width( canvas );
set_color( BLACK );
FOREACH( node, path_nodes )
{
if (node->link_feat != NULL)
{
plus->x = border_width;
plus->y = node->link_surf->y;
draw_pos_string( plus, canvas );
draw_pos_string( node->link_surf, canvas );
draw_pos_value( node->link_feat, canvas );
if (inline_path)
{
comma->x = node->link_surf->x + node->link_surf->width;
comma->y = node->link_surf->y;
draw_pos_string( comma, canvas );
}
}
if (node->result_feat != NULL)
{
if (node != (path_node_t *) path_nodes.first)
{
x1 = node->rule_name->x;
x2 = x1 + node->rule_name->width + space_width;
y = node->rule_name->y + font_height + line_width;
draw_pos_string( node->rule_name, canvas );
/* Draw arrow. */
draw_lines( 2, x1, y, x2, y );
draw_lines( 3,
x2 - space_width, y - space_width / 2,
x2, y,
x2 - space_width, y + space_width / 2 );
}
if (path_show_state_indexes)
draw_pos_string( node->state_index, canvas );
draw_pos_string( node->result_surf, canvas );
draw_pos_value( node->result_feat, canvas );
draw_pos_string( node->rule_set, canvas );
if (inline_path)
{
comma->x = node->result_surf->x + node->result_surf->width;
comma->y = node->result_surf->y;
draw_pos_string( comma, canvas );
comma->x = node->result_feat->x + node->result_feat->width;
draw_pos_string( comma, canvas );
}
}
}
}
/*---------------------------------------------------------------------------*/
static void
unmark_tree_nodes( tree_node_t *node )
/* Mark NODE and all its children and siblings *not* to be part of the path. */
{
for (; node != NULL; node = node->sibling)
{
node->in_path = FALSE;
unmark_tree_nodes( node->child );
}
}
/*---------------------------------------------------------------------------*/
static void
free_path( void )
{
path_node_t *path_node;
/* Clear old path nodes. */
FOREACH_FREE( path_node, path_nodes )
{
free_pos_string( &path_node->state_index );
free_pos_value( &path_node->link_feat );
free_pos_value( &path_node->result_feat );
}
unmark_tree_nodes( tree_nodes );
}
/*---------------------------------------------------------------------------*/
static void
close_path( canvas_t *canvas )
{
free_path();
path_begin = path_end = NULL;
redraw_canvas( tree_canvas );
}
/*---------------------------------------------------------------------------*/
static void
set_inline_path( canvas_t *canvas, guint action, GtkWidget *item )
{
inline_path = GTK_CHECK_MENU_ITEM( item )->active;
configure_canvas( canvas );
}
/*---------------------------------------------------------------------------*/
static void
path_index_option( canvas_t *canvas, guint action, GtkWidget *item )
{
path_show_state_indexes = GTK_CHECK_MENU_ITEM( item )->active;
configure_canvas( canvas );
}
/*---------------------------------------------------------------------------*/
static GtkItemFactoryEntry path_items[] =
{
{ "/Style/Inline", NULL, set_inline_path, 0, "<ToggleItem>" },
{ "/Path", NULL, NULL, 0, "<Branch>" },
{ "/Path/Show State Indexes", NULL, path_index_option, 0, "<ToggleItem>" },
{ "/End States", NULL, NULL, 0, "<Branch>" },
{ "/End States/Show First End State", "<Control>F", goto_state, FIRST_STATE,
NULL },
{ "/End States/Show Previous End State", "<Control>P", goto_state, PREV_STATE,
NULL },
{ "/End States/Show Next End State", "<Control>N", goto_state, NEXT_STATE,
NULL },
{ "/End States/Show Last End State", "<Control>L", goto_state, LAST_STATE,
NULL }
};
/*---------------------------------------------------------------------------*/
static void
display_path( void )
{
tree_node_t *tree_node;
path_node_t *path_node;
string_t state_index, string;
free_path();
/* Create the path nodes. We go backwards from the path end to the path
* beginning, since this direction is easier to traverse. */
for (tree_node = path_end; ; tree_node = tree_node->parent)
{
tree_node->in_path = TRUE;
path_node = new_node( &path_nodes, sizeof( path_node_t ), LIST_START );
if (path_begin != NULL && tree_node->result_feat != NULL)
{
/* Create a new state in the path. */
state_index = int_to_string( tree_node->state_index );
string = concat_strings( "State ", state_index, ":", NULL );
path_node->state_index = new_pos_string( string );
free_mem( &string );
free_mem( &state_index );
path_node->rule_name = new_pos_string( tree_node->rule_name );
path_node->result_surf = new_pos_string( tree_node->result_surf );
set_scanner_input( tree_node->result_feat );
path_node->result_feat = parse_pos_value();
parse_token( EOF );
set_scanner_input( NULL );
path_node->rule_set = new_pos_string( tree_node->rule_set );
}
if (tree_node == path_begin)
break;
if (tree_node->link_feat != NULL)
{
/* Create a new link in the path. */
path_node->link_surf = new_pos_string( tree_node->link_surf );
set_scanner_input( tree_node->link_feat );
path_node->link_feat = parse_pos_value();
parse_token( EOF );
set_scanner_input( NULL );
}
if (path_begin == NULL)
break;
}
if (path_canvas == NULL)
{
path_canvas = create_canvas(
"Malaga Path", "path.eps", &path_geometry, configure_path, expose_path,
close_path, NULL, TRUE, path_items, ARRAY_LENGTH( path_items ) );
path_show_state_indexes = show_state_indexes;
if (path_show_state_indexes)
activate_menu_item( path_canvas, "/Path/Show State Indexes" );
if (inline_path)
activate_menu_item( path_canvas, "/Style/Inline" );
}
else
{
configure_canvas( path_canvas );
show_canvas( path_canvas );
}
redraw_canvas( tree_canvas );
}
/*---------------------------------------------------------------------------*/
static void
configure_node( canvas_t *canvas, tree_node_t *node, int_t x, int_t y )
{
int_t w, node_width;
tree_node_t *subnode;
bool_t is_first;
int_t font_height = get_font_height( canvas );
int_t space_width = get_space_width( canvas );
int_t line_width = get_line_width( canvas );
if (tree_show_state_indexes && node->state_index != -1)
{
config_pos_string( node->pos_state_index, canvas );
node_width = MAX( node->pos_state_index->width + space_width,
font_height );
node->pos_state_index->x
= x + (node_width - node->pos_state_index->width) / 2;
node->pos_state_index->y = tree_height - font_height * 3 / 2 + 1;
}
else
node_width = font_height;
node->x = x + node_width / 2;
node->y = y;
x += node_width;
tree_width = MAX( tree_width, x );
is_first = TRUE;
for (subnode = node->child; subnode != NULL; subnode = subnode->sibling)
{
if (tree_mode == NO_DEAD_ENDS && subnode->type == BREAK_NODE)
continue;
if (tree_mode == RESULT_PATHS && ! subnode->result_path)
continue;
if (is_first)
is_first = FALSE;
else
tree_height += (5 * font_height) / 2;
config_pos_string( subnode->pos_link_surf, canvas );
config_pos_string( subnode->pos_rule_name, canvas );
w = MAX( subnode->pos_link_surf->width, subnode->pos_rule_name->width );
w = MAX( w, space_width );
subnode->pos_link_surf->x = x + (w - subnode->pos_link_surf->width) / 2;
subnode->pos_link_surf->y = tree_height - font_height - line_width;
subnode->pos_rule_name->x = x + (w - subnode->pos_rule_name->width) / 2;
subnode->pos_rule_name->y = tree_height + line_width;
configure_node( canvas, subnode, x + w, tree_height );
}
}
/*---------------------------------------------------------------------------*/
static void
configure_tree( canvas_t *canvas, int_t *width_p, int_t *height_p )
{
int_t font_height = get_font_height( canvas );
int_t border_width = get_border_width( canvas );
tree_width = tree_height = border_width;
/* Configure result surface. */
config_pos_string( result_surf, canvas );
result_surf->x = border_width;
result_surf->y = border_width;
tree_width = MAX( tree_width, border_width + result_surf->width );
tree_height += font_height;
if (tree_mode != RESULT_PATHS || tree_nodes->result_path)
{
/* Configure tree. */
tree_height += 2 * font_height;
configure_node( canvas, tree_nodes, border_width, tree_height );
}
/* Return width and height. */
*width_p = tree_width + border_width;
*height_p = tree_height + font_height + border_width;
}
/*---------------------------------------------------------------------------*/
static void
expose_node( canvas_t *canvas, tree_node_t *node, int_t font_height )
{
tree_node_t *subnode;
const int_t x = node->x;
const int_t y = node->y;
const int_t radius1 = font_height / 2;
const int_t radius2 = (radius1 * 3) / 5;
int_t middle = y;
int_t bottom = y;
/* Draw all subnodes. */
for (subnode = node->child; subnode != NULL; subnode = subnode->sibling)
{
if (tree_mode == NO_DEAD_ENDS && subnode->type == BREAK_NODE)
continue;
if (tree_mode == RESULT_PATHS && ! subnode->result_path)
continue;
/* Draw horizontal line. */
bottom = subnode->y;
if (node->in_path && subnode->in_path)
{
middle = subnode->y;
set_color( RED );
}
else
set_color( BLACK );
draw_lines( 2, x, subnode->y, subnode->x, subnode->y );
if (path_begin == NULL && subnode->in_path)
set_color( RED );
draw_pos_string( subnode->pos_link_surf, canvas );
set_color( BLACK );
draw_pos_string( subnode->pos_rule_name, canvas );
expose_node( canvas, subnode, font_height );
}
/* Draw vertical line. */
set_color( BLACK );
draw_lines( 2, x, middle, x, bottom );
if (middle != y)
{
set_color( RED );
draw_lines( 2, x, y, x, middle );
}
/* Draw the node itself. */
if (node->type == BREAK_NODE)
{
set_color( (node->in_path && path_begin != NULL ? RED : BLACK) );
draw_rectangle( x - radius1 , y - radius1, 2 * radius1, 2 * radius1 );
}
else
{
set_color( WHITE );
draw_circle( TRUE, x, y, radius1 );
set_color( (node->in_path && path_begin != NULL ? RED : BLACK) );
draw_circle( FALSE, x, y, radius1 );
if (node->type == FINAL_NODE || node->type == UNFINAL_NODE)
draw_circle( FALSE, x, y, radius2 );
if (node->type == PRUNED_NODE || node->type == UNFINAL_NODE)
{
draw_lines( 2, x - radius1, y - radius1, x + radius1, y + radius1 );
draw_lines( 2, x + radius1, y - radius1, x - radius1, y + radius1 );
}
}
/* Draw the state index. */
if (tree_show_state_indexes && node->state_index != -1)
{
set_color( BLUE );
draw_pos_string( node->pos_state_index, canvas );
}
}
/*---------------------------------------------------------------------------*/
static void
expose_tree( canvas_t *canvas, rectangle_t *area )
/* Expose AREA on tree CANVAS. */
{
int_t font_height = get_font_height( canvas );
if (result_surf != NULL)
{
set_color( BLACK );
draw_pos_string( result_surf, canvas );
}
if (tree_nodes == NULL)
return;
if (tree_mode != RESULT_PATHS || tree_nodes->result_path)
expose_node( canvas, tree_nodes, font_height );
}
/*---------------------------------------------------------------------------*/
static bool_t
in_circle( int_t x, int_t y, int_t circle_x, int_t circle_y, int_t radius )
/* Return TRUE if position (X,Y) lies in disc with center (CIRCLE_X, CIRCLE_Y)
* and RADIUS. */
{
int_t diff_x = x - circle_x;
int_t diff_y = y - circle_y;
/* Taking DIFF_X or DIFF_Y to the 2nd power may result in an overflow,
* so we will check first whether DIFF_X and DIFF_Y are small enough. */
return (ABS( diff_x ) < radius && ABS( diff_y ) < radius
&& diff_x * diff_x + diff_y * diff_y <= radius * radius);
}
/*---------------------------------------------------------------------------*/
static bool_t
in_pos_string( int_t x, int_t y, pos_string_t *pos_string, int_t height )
/* Return TRUE if position (X,Y) lies in bounding box of POS_STRING, using font
* height HEIGHT. */
{
return (x >= pos_string->x
&& x < pos_string->x + pos_string->width
&& y >= pos_string->y
&& y < pos_string->y + height);
}
/*---------------------------------------------------------------------------*/
static bool_t
in_rectangle( int_t x, int_t y,
int_t rect_x, int_t rect_y, int_t width, int_t height )
/* Return TRUE if position (X,Y) lies in rectangle with upper left corner
* (RECT_X, RECT_Y), width WIDTH and height HEIGHT. */
{
return (x >= rect_x
&& x <= rect_x + width
&& y >= rect_y
&& y <= rect_y + height);
}
/*---------------------------------------------------------------------------*/
static void
set_tree( canvas_t *canvas, guint mode )
/* Set the tree's display mode to MODE. */
{
tree_mode = mode;
if (path_end != NULL
&& ((tree_mode == NO_DEAD_ENDS && path_end->type == BREAK_NODE)
|| (tree_mode == RESULT_PATHS && ! path_end->result_path)))
{
hide_canvas( path_canvas );
}
configure_canvas( tree_canvas );
}
/*---------------------------------------------------------------------------*/
static void
goto_state( canvas_t *canvas, guint action )
/* Display a certain state, according to ACTION, in the path window. */
{
int_t i = 0;
bool_t inverse = FALSE; /* TRUE if we search in inverse direction. */
if (action == FIRST_STATE || action == LAST_STATE
|| (path_begin != NULL && path_begin->type == FINAL_NODE))
{
switch (action)
{
case FIRST_STATE:
i = 0;
break;
case PREV_STATE:
i = path_begin->state_index - 1;
inverse = TRUE;
break;
case NEXT_STATE:
i = path_begin->state_index + 1;
break;
case LAST_STATE:
i = state_count - 1;
inverse = TRUE;
break;
}
while (i >= 0 && i < state_count)
{
if (states[i] != NULL && states[i]->type == FINAL_NODE)
{
path_begin = path_end = states[i];
make_visible( tree_canvas, path_begin->x, path_begin->y );
display_path();
break;
}
if (inverse)
i--;
else
i++;
}
}
}
/*---------------------------------------------------------------------------*/
static tree_node_t *
tree_node_at_position( tree_node_t *node, int_t x, int_t y, int_t font_height,
element_t *element_p)
{
tree_node_t *subnode;
if (node == NULL)
return NULL;
for (; node != NULL; node = node->sibling)
{
if (tree_mode == NO_DEAD_ENDS && node->type == BREAK_NODE)
continue;
if (tree_mode == RESULT_PATHS && ! node->result_path)
continue;
if ((node->type == BREAK_NODE
&& in_rectangle( x, y,
node->x - font_height / 2, node->y - font_height / 2,
font_height, font_height ))
|| (node->type != BREAK_NODE
&& in_circle( x, y, node->x, node->y, font_height / 2 ) ))
{
*element_p = ELEMENT_NODE;
return node;
}
if (in_pos_string( x, y, node->pos_link_surf, font_height ))
{
*element_p = ELEMENT_LINK;
return node;
}
if (in_pos_string( x, y, node->pos_rule_name, font_height ))
{
*element_p = ELEMENT_RULE;
return node;
}
subnode = tree_node_at_position( node->child, x, y, font_height,
element_p );
if (subnode != NULL)
return subnode;
}
return NULL;
}
/*---------------------------------------------------------------------------*/
static void
display_node( canvas_t *canvas, guint action )
{
tree_node_t *node;
if (action == PATH_BEGIN)
path_begin = popup_menu_node;
else if (action == FROM_ROOT)
path_begin = tree_nodes;
if (action == PATH_END || action == FROM_ROOT)
path_end = popup_menu_node;
/* Check whether there is a part from PATH_BEGIN to PATH_END. */
node = path_end;
while (node != NULL && node != path_begin)
node = node->parent;
if (node == NULL)
path_begin = path_end = popup_menu_node;
display_path();
}
/*---------------------------------------------------------------------------*/
static bool_t
mouse_event( canvas_t *canvas, int_t x, int_t y, int_t button )
/* Called if mouse has moved to position (X,Y). */
{
tree_node_t *node;
element_t element;
node = tree_node_at_position( tree_nodes, x, y, get_font_height( canvas ),
&element );
if (node == NULL)
{
set_cursor( canvas, FALSE );
return FALSE;
}
/* No button pressed: we should just update the cursor. */
if (button == 0)
{
set_cursor( canvas, TRUE );
return TRUE;
}
switch (element)
{
case ELEMENT_NODE:
if (button == 1)
{
path_begin = path_end = node;
display_path();
}
else if (button == 3)
{
popup_menu_node = node;
popup_menu( canvas );
}
break;
case ELEMENT_LINK:
path_begin = NULL;
path_end = node;
display_path();
break;
case ELEMENT_RULE:
path_begin = node->parent;
path_end = node;
display_path();
break;
}
return TRUE;
}
/*---------------------------------------------------------------------------*/
static void
tree_index_option( canvas_t *canvas, guint action, GtkWidget *item )
{
tree_show_state_indexes = GTK_CHECK_MENU_ITEM( item )->active;
configure_canvas( canvas );
}
/*---------------------------------------------------------------------------*/
static GtkItemFactoryEntry tree_items[] =
{
{ "/Tree", NULL, NULL, 0, "<Branch>" },
{ "/Tree/Full Tree", NULL, set_tree, FULL_TREE, "<RadioItem>" },
{ "/Tree/No Dead Ends", NULL, set_tree, NO_DEAD_ENDS, "/Tree/Full Tree" },
{ "/Tree/Complete Paths", NULL, set_tree, RESULT_PATHS, "/Tree/Full Tree" },
{ "/Tree/sep1", NULL, NULL, 0, "<Separator>" },
{ "/Tree/Show State Indexes", NULL, tree_index_option, 0, "<ToggleItem>" },
{ "/End States", NULL, NULL, 0, "<Branch>" },
{ "/End States/Show First End State", "<Control>F", goto_state, FIRST_STATE,
NULL },
{ "/End States/Show Previous End State", "<Control>P", goto_state, PREV_STATE,
NULL },
{ "/End States/Show Next End State", "<Control>N", goto_state, NEXT_STATE,
NULL },
{ "/End States/Show Last End State", "<Control>L", goto_state, LAST_STATE,
NULL }
};
/*---------------------------------------------------------------------------*/
static GtkItemFactoryEntry popup_items[] =
{
{ "/Set Path Begin", NULL, display_node, PATH_BEGIN, "<Item>" },
{ "/Set Path End", NULL, display_node, PATH_END, "<Item>" },
{ "/Show Path From Root", NULL, display_node, FROM_ROOT, "<Item>" }
};
/*---------------------------------------------------------------------------*/
static void
free_tree_nodes( tree_node_t *node )
/* Free *NODE_P and all its siblings and children. Set *NODE_P to NULL. */
{
tree_node_t *next;
for (; node != NULL; node = next)
{
/* Remove node from state index. */
if (node->state_index != -1)
states[ node->state_index ] = NULL;
free_tree_nodes( node->child );
free_mem( &node->rule_name );
free_mem( &node->link_surf );
free_mem( &node->link_feat );
free_mem( &node->result_surf );
free_mem( &node->result_feat );
free_mem( &node->rule_set );
free_pos_string( &node->pos_link_surf );
free_pos_string( &node->pos_rule_name );
free_pos_string( &node->pos_state_index );
next = node->sibling;
free( node );
}
}
/*---------------------------------------------------------------------------*/
static void
free_tree( void )
{
/* Clear old variables. */
free_pos_string( &result_surf );
free_pos_string( &plus );
free_pos_string( &comma );
free_tree_nodes( tree_nodes );
tree_nodes = NULL;
}
/*---------------------------------------------------------------------------*/
static void
close_tree( canvas_t *canvas )
/* Called by "canvas.c" when CANVAS gets hidden. */
{
if (path_canvas != NULL)
hide_canvas( path_canvas );
free_tree();
}
/*---------------------------------------------------------------------------*/
void
read_tree( void )
/* Read new tree from STDIN. */
{
string_t line; /* A line of input from STDIN. */
string_t line_p; /* Part of LINE which is yet to parse. */
string_t type; /* Node type. */
string_t state_index;
tree_node_t *node;
int_t parent_state_index, i;
tree_node_t **node_p;
if (path_canvas != NULL)
hide_canvas( path_canvas );
free_tree();
/* Allocate new nodes array. */
if (state_count == 0)
{
state_count = 100;
states = new_vector( sizeof( tree_node_t * ), state_count );
}
/* Read new input surface. */
line = read_line( stdin );
result_surf = new_pos_string( line );
free_mem( &line );
/* Read new tree. */
while (TRUE)
{
line = read_line( stdin );
if (line == NULL)
complain( "Premature EOF." );
if (strcmp_no_case( line, "end" ) == 0)
break;
node = new_mem( sizeof( tree_node_t ) );
line_p = line;
node->state_index = parse_int( &line_p );
type = parse_word( &line_p );
if (strcmp_no_case( type, "inter" ) == 0)
node->type = INTER_NODE;
else if (strcmp_no_case( type, "break" ) == 0)
node->type = BREAK_NODE;
else if (strcmp_no_case( type, "final" ) == 0)
node->type = FINAL_NODE;
else if (strcmp_no_case( type, "unfinal" ) == 0)
node->type = UNFINAL_NODE;
else if (strcmp_no_case( type, "pruned" ) == 0)
node->type = PRUNED_NODE;
else
complain( "Unknown node type \"%s\".", type );
free_mem( &type );
parent_state_index = parse_int( &line_p );
node->rule_name = parse_word( &line_p );
node->link_surf = parse_optional_value( &line_p );
node->link_feat = parse_optional_value( &line_p );
node->result_surf = parse_optional_value( &line_p );
node->result_feat = parse_optional_value( &line_p );
node->rule_set = parse_word( &line_p );
parse_end( &line_p );
free_mem( &line );
node->pos_link_surf = new_pos_string( node->link_surf );
node->pos_rule_name = new_pos_string( node->rule_name );
if (node->state_index != -1)
{
state_index = int_to_string( node->state_index );
node->pos_state_index = new_pos_string( state_index );
free_mem( &state_index );
}
/* Find parent node. */
if (parent_state_index == -1)
tree_nodes = node; /* This is the root node. */
else if (parent_state_index < 0 || parent_state_index >= state_count
|| states[ parent_state_index ] == NULL)
{
complain( "Display data corrupted." );
}
else
{
node->parent = states[ parent_state_index ];
/* Add NODE to be a child of NODE->PARENT. */
node_p = &node->parent->child;
while (*node_p != NULL)
node_p = &(*node_p)->sibling;
*node_p = node;
}
/* Add node to the STATES array. */
if (node->state_index >= state_count)
{
renew_vector( &states, sizeof( tree_node_t * ), 2 * node->state_index );
for (i = state_count; i < 2 * node->state_index; i++)
states[i] = NULL;
state_count = 2 * node->state_index;
}
if (node->state_index != -1)
states[ node->state_index ] = node;
/* If this is a final node, mark it and all its predecessors. */
if (node->type == FINAL_NODE)
{
for (; node != NULL; node = node->parent)
node->result_path = TRUE;
}
}
free_mem( &line );
if (tree_nodes == NULL)
complain( "Missing root node in tree." );
plus = new_pos_string( "+" );
comma = new_pos_string( "," );
if (tree_canvas == NULL)
{
tree_canvas = create_canvas(
"Malaga Tree", "tree.eps", &tree_geometry, configure_tree, expose_tree,
close_tree, mouse_event, FALSE, tree_items, ARRAY_LENGTH( tree_items ) );
set_popup_menu( tree_canvas, popup_items, ARRAY_LENGTH( popup_items ) );
tree_show_state_indexes = show_state_indexes;
if (tree_show_state_indexes )
activate_menu_item( tree_canvas, "/Tree/Show State Indexes" );
}
else
{
configure_canvas( tree_canvas );
show_canvas( tree_canvas );
}
}
/* End of file. =============================================================*/