%{
/*
* This is a plug-in for GIMP.
*
* Generates clickable image maps.
*
* Copyright (C) 1998-2005 Maurits Rijk lpeek.mrijk@consunet.nl
*
* 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 3 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, see .
*
*/
#include
#include
#include
#include
#include "imap_circle.h"
#include "imap_file.h"
#include "imap_main.h"
#include "imap_polygon.h"
#include "imap_rectangle.h"
#include "imap_string.h"
extern int csim_lex(void);
extern int csim_restart(FILE *csim_in);
static void csim_error(char* s);
static gchar * unescape_text(gchar *input);
static enum {UNDEFINED, RECTANGLE, CIRCLE, POLYGON} current_type;
static Object_t *current_object;
static MapInfo_t *_map_info;
%}
%union {
int val;
double value;
char *id;
}
%token IMG SRC WIDTH HEIGHT BORDER USEMAP
%token START_MAP END_MAP NAME AREA SHAPE COORDS ALT HREF NOHREF
%token TARGET ONMOUSEOVER ONMOUSEOUT ONFOCUS ONBLUR
%token AUTHOR DESCRIPTION BEGIN_COMMENT END_COMMENT
%token FLOAT
%token STRING
%type integer_value
%%
csim_file : image start_map comment_lines area_list end_map
;
image : '<' IMG SRC '=' STRING image_tags xhtml_close
{
g_strreplace(&_map_info->image_name, $5);
g_free ($5);
}
;
image_tags : /* Empty */
| image_tags image_tag
;
image_tag : image_width
| image_height
| BORDER '=' integer_value {}
| USEMAP '=' STRING { g_free ($3); }
| ALT '=' STRING { g_free ($3); }
;
image_width : WIDTH '=' integer_value
{
_map_info->old_image_width = $3;
}
;
image_height : HEIGHT '=' integer_value
{
_map_info->old_image_height = $3;
}
;
integer_value : FLOAT
{
$$ = (gint) $1;
}
| STRING
{
$$ = (gint) g_ascii_strtod ($1, NULL);
g_free ($1);
}
;
start_map : '<' START_MAP NAME '=' STRING '>'
{
g_strreplace(&_map_info->title, $5);
g_free ($5);
}
;
comment_lines : /* empty */
| comment_lines comment_line
;
comment_line : author_line
| description_line
| real_comment
;
real_comment : BEGIN_COMMENT STRING END_COMMENT
{
g_free ($2);
}
;
author_line : AUTHOR STRING END_COMMENT
{
g_strreplace(&_map_info->author, $2);
g_free ($2);
}
;
description_line: DESCRIPTION STRING END_COMMENT
{
gchar *description;
description = g_strconcat(_map_info->description, $2, "\n",
NULL);
g_strreplace(&_map_info->description, description);
g_free ($2);
}
;
area_list : /* empty */
| area_list area
;
area : '<' AREA tag_list xhtml_close
{
if (current_type != UNDEFINED)
add_shape(current_object);
}
;
xhtml_close : '>'
| '/' '>'
;
tag_list : /* Empty */
| tag_list tag
;
tag : shape_tag
| coords_tag
| href_tag
| nohref_tag
| alt_tag
| target_tag
| onmouseover_tag
| onmouseout_tag
| onfocus_tag
| onblur_tag
;
shape_tag : SHAPE '=' STRING
{
if (!g_ascii_strcasecmp($3, "RECT")) {
current_object = create_rectangle(0, 0, 0, 0);
current_type = RECTANGLE;
} else if (!g_ascii_strcasecmp($3, "CIRCLE")) {
current_object = create_circle(0, 0, 0);
current_type = CIRCLE;
} else if (!g_ascii_strcasecmp($3, "POLY")) {
current_object = create_polygon(NULL);
current_type = POLYGON;
} else if (!g_ascii_strcasecmp($3, "DEFAULT")) {
current_type = UNDEFINED;
}
g_free ($3);
}
;
coords_tag : COORDS '=' STRING
{
char *p;
if (current_type == RECTANGLE) {
Rectangle_t *rectangle;
rectangle = ObjectToRectangle(current_object);
p = strtok($3, ",");
rectangle->x = atoi(p);
p = strtok(NULL, ",");
rectangle->y = atoi(p);
p = strtok(NULL, ",");
rectangle->width = atoi(p) - rectangle->x;
p = strtok(NULL, ",");
rectangle->height = atoi(p) - rectangle->y;
} else if (current_type == CIRCLE) {
Circle_t *circle;
circle = ObjectToCircle(current_object);
p = strtok($3, ",");
circle->x = atoi(p);
p = strtok(NULL, ",");
circle->y = atoi(p);
p = strtok(NULL, ",");
circle->r = atoi(p);
} else if (current_type == POLYGON) {
Polygon_t *polygon = ObjectToPolygon(current_object);
GList *points;
GdkPoint *point, *first;
gint x, y;
p = strtok($3, ",");
x = atoi(p);
p = strtok(NULL, ",");
y = atoi(p);
point = new_point(x, y);
points = g_list_append(NULL, (gpointer) point);
while(1) {
p = strtok(NULL, ",");
if (!p)
break;
x = atoi(p);
p = strtok(NULL, ",");
y = atoi(p);
point = new_point(x, y);
points = g_list_append(points, (gpointer) point);
}
/* Remove last point if duplicate */
first = (GdkPoint*) points->data;
polygon->points = points;
if (first->x == point->x && first->y == point->y)
polygon_remove_last_point(polygon);
polygon->points = points;
}
g_free ($3);
}
;
href_tag : HREF '=' STRING
{
if (current_type == UNDEFINED) {
g_strreplace(&_map_info->default_url, $3);
} else {
object_set_url(current_object, unescape_text($3));
}
g_free ($3);
}
;
nohref_tag : NOHREF optional_value
{
}
;
optional_value : /* Empty */
| '=' STRING
{
g_free ($2);
}
;
alt_tag : ALT '=' STRING
{
object_set_comment(current_object, unescape_text($3));
g_free ($3);
}
;
target_tag : TARGET '=' STRING
{
object_set_target(current_object, unescape_text($3));
g_free ($3);
}
;
onmouseover_tag : ONMOUSEOVER '=' STRING
{
object_set_mouse_over(current_object, unescape_text($3));
g_free ($3);
}
;
onmouseout_tag : ONMOUSEOUT '=' STRING
{
object_set_mouse_out(current_object, unescape_text($3));
g_free ($3);
}
;
onfocus_tag : ONFOCUS '=' STRING
{
object_set_focus(current_object, unescape_text($3));
g_free ($3);
}
;
onblur_tag : ONBLUR '=' STRING
{
object_set_blur(current_object, unescape_text($3));
g_free ($3);
}
;
end_map : '<' END_MAP '>'
;
%%
static void
csim_error(char* s)
{
extern FILE *csim_in;
csim_restart(csim_in);
}
gboolean
load_csim (const char* filename)
{
gboolean status;
extern FILE *csim_in;
csim_in = g_fopen(filename, "r");
if (csim_in) {
_map_info = get_map_info();
status = !csim_parse();
fclose(csim_in);
} else {
status = FALSE;
}
return status;
}
static gchar*
unescape_text (gchar *input)
{
/*
* We "unescape" simple things "in place", knowing that unescaped
* strings always are shorter than the original input.
*
* It is a shame there is no g_markup_unescape_text() function, but
* instead you have to create a full GMarkupParser/Context.
*/
struct token {
const char *escaped;
const char unescaped;
};
const struct token tab[] = {
{ """, '"' },
{ "'", '\'' },
{ "&", '&' },
{ "<", '<' },
{ ">", '>' }
};
size_t i;
for (i = 0; i < (sizeof tab / sizeof tab[0]); i++)
{
const size_t escaped_len = strlen (tab[i].escaped);
char *p;
/* FIXME: The following code does not perform a UTF-8 substring
search. */
for (p = strstr (input, tab[i].escaped);
p != NULL;
p = strstr (p, tab[i].escaped))
{
size_t copy_len;
*p++ = tab[i].unescaped;
copy_len = strlen (p) - escaped_len + 2;
memmove (p, p + escaped_len - 1, copy_len);
if (*p == 0)
break;
}
}
return input;
}