421 lines
8.6 KiB
Plaintext
421 lines
8.6 KiB
Plaintext
%{
|
|
/*
|
|
* This is a plug-in for PIKA.
|
|
*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#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<val> IMG SRC WIDTH HEIGHT BORDER USEMAP
|
|
%token<val> START_MAP END_MAP NAME AREA SHAPE COORDS ALT HREF NOHREF
|
|
%token<val> TARGET ONMOUSEOVER ONMOUSEOUT ONFOCUS ONBLUR ONCLICK
|
|
%token<val> ACCESSKEY TABINDEX AUTHOR DESCRIPTION BEGIN_COMMENT END_COMMENT
|
|
%token<value> FLOAT
|
|
%token<id> STRING
|
|
|
|
%type<val> 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
|
|
| onclick_tag
|
|
| accesskey_tag
|
|
| tabindex_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);
|
|
}
|
|
;
|
|
|
|
onclick_tag : ONCLICK '=' STRING
|
|
{
|
|
object_set_click(current_object, unescape_text($3));
|
|
g_free ($3);
|
|
}
|
|
;
|
|
|
|
accesskey_tag : ACCESSKEY '=' STRING
|
|
{
|
|
object_set_accesskey(current_object, unescape_text($3));
|
|
g_free ($3);
|
|
}
|
|
;
|
|
|
|
tabindex_tag : TABINDEX '=' STRING
|
|
{
|
|
object_set_tabindex(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;
|
|
}
|