1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  This file is part of the GtkHTML library.
3  *
4  *  Copyright (C) 1997 Martin Jones (mjones@kde.org)
5  *  Copyright (C) 1997 Torben Weis (weis@kde.org)
6  *  Copyright (C) 1999 Helix Code, Inc.
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Library General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Library General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Library General Public License
19  *  along with this library; see the file COPYING.LIB.  If not, write to
20  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  *  Boston, MA 02110-1301, USA.
22 */
23 
24 #include <config.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <gtk/gtk.h>
28 #include <glib/gi18n-lib.h>
29 #include "gtkhtml.h"
30 #include "gtkhtml-embedded.h"
31 #include "htmlembedded.h"
32 #include "htmlframe.h"
33 #include "htmliframe.h"
34 #include "htmlpainter.h"
35 #include "htmlengine.h"
36 /*For use converter based on g_iconv*/
37 #include "htmltokenizer.h"
38 
39 HTMLEmbeddedClass html_embedded_class;
40 static HTMLObjectClass *parent_class = NULL;
41 
42 #define d(x)
43 
44 static void
copy(HTMLObject * self,HTMLObject * dest)45 copy (HTMLObject *self,
46       HTMLObject *dest)
47 {
48 	(* HTML_OBJECT_CLASS (parent_class)->copy) (self, dest);
49 
50 	/* FIXME g_warning ("HTMLEmbedded::copy is not complete."); */
51 
52 	HTML_EMBEDDED (dest)->name = g_strdup (HTML_EMBEDDED (self)->name);
53 	HTML_EMBEDDED (dest)->value = g_strdup (HTML_EMBEDDED (self)->value);
54 	HTML_EMBEDDED (dest)->form = HTML_EMBEDDED (self)->form;
55 
56 	HTML_EMBEDDED (dest)->widget = NULL;
57 	HTML_EMBEDDED (dest)->parent = NULL;
58 
59 	HTML_EMBEDDED (dest)->abs_x = HTML_EMBEDDED (self)->abs_x;
60 	HTML_EMBEDDED (dest)->abs_y = HTML_EMBEDDED (self)->abs_y;
61 }
62 
63 static void
draw(HTMLObject * o,HTMLPainter * p,gint x,gint y,gint width,gint height,gint tx,gint ty)64 draw (HTMLObject *o,
65       HTMLPainter *p,
66       gint x,
67       gint y,
68       gint width,
69       gint height,
70       gint tx,
71       gint ty)
72 {
73 	HTMLEmbedded *element = HTML_EMBEDDED (o);
74 	gint new_x, new_y;
75 
76 	d (printf ("draw embedded %p\n", element));
77 	if (!element->widget)
78 		return;
79 
80 	if (element->parent) {
81 		GtkWidget *parent;
82 		new_x = o->x + tx;
83 		new_y = o->y + ty - o->ascent;
84 
85 		if ((parent = gtk_widget_get_parent (element->widget))) {
86 			if (new_x != element->abs_x || new_y != element->abs_y) {
87 				d (printf ("element: %p moveto: %d,%d shown: %d\n", element, new_x, new_y, GTK_WIDGET_VISIBLE (element->widget)));
88 				gtk_layout_move (GTK_LAYOUT (parent), element->widget, new_x, new_y);
89 			} else if (!GTK_HTML (parent)->engine->expose)
90 				gtk_widget_queue_draw (element->widget);
91 		}
92 
93 		element->abs_x = new_x;
94 		element->abs_y = new_y;
95 
96 		if (!parent) {
97 			d (printf ("element: %p put: %d,%d shown: %d\n", element, new_x, new_y, GTK_WIDGET_VISIBLE (element->widget)));
98 			gtk_layout_put (GTK_LAYOUT (element->parent), element->widget, new_x, new_y);
99 		}
100 	}
101 
102 	d (printf ("draw embedded %p - call painter tx %d ty %d\n", element, tx, ty));
103 	html_painter_draw_embedded (p, element, tx, ty);
104 }
105 
106 static void
destroy(HTMLObject * o)107 destroy (HTMLObject *o)
108 {
109 	HTMLEmbedded *element;
110 
111 	d (printf ("destroy embedded %p\n", o));
112 	element = HTML_EMBEDDED (o);
113 
114 	if (element->name)
115 		g_free (element->name);
116 	if (element->value)
117 		g_free (element->value);
118 	if (element->widget) {
119 		GtkWidget *parent;
120 
121 		gtk_widget_hide (element->widget);
122 		parent = gtk_widget_get_parent (element->widget);
123 		g_signal_handlers_disconnect_matched (element->widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, element);
124 		if (element->changed_id > 0)
125 			g_signal_handler_disconnect (element->widget, element->changed_id);
126 		g_object_set_data (G_OBJECT (element->widget), "embeddedelement", NULL);
127 		if (parent && element->parent) {
128 			g_assert (parent == element->parent);
129 			gtk_container_remove (GTK_CONTAINER (element->parent), element->widget);
130 		} else {
131 			g_object_ref_sink (element->widget);
132 			g_object_unref (element->widget);
133 		}
134 	}
135 
136 	HTML_OBJECT_CLASS (parent_class)->destroy (o);
137 }
138 
139 static void
reset(HTMLEmbedded * e)140 reset (HTMLEmbedded *e)
141 {
142 	/* Nothing to do?  */
143 }
144 
145 static gint
calc_min_width(HTMLObject * self,HTMLPainter * painter)146 calc_min_width (HTMLObject *self,
147                 HTMLPainter *painter)
148 {
149 	GtkRequisition requisition;
150 	GtkWidget *widget;
151 	gint pixel_size;
152 	gint min_width;
153 
154 	widget = HTML_EMBEDDED (self)->widget;
155 
156 	if (widget == NULL || !gtk_widget_get_visible (widget))
157 		return 0;
158 
159 	requisition.width = requisition.height = 0;
160 	gtk_widget_get_preferred_size (widget, &requisition, NULL);
161 	pixel_size = html_painter_get_pixel_size (painter);
162 
163 	min_width = requisition.width * pixel_size;
164 
165 	return min_width;
166 }
167 
168 static gboolean
html_embedded_real_calc_size(HTMLObject * self,HTMLPainter * painter,GList ** changed_objs)169 html_embedded_real_calc_size (HTMLObject *self,
170                               HTMLPainter *painter,
171                               GList **changed_objs)
172 {
173 	GtkWidget *widget;
174 	HTMLEmbedded *emb = HTML_EMBEDDED (self);
175 	gint pixel_size;
176 	gint old_width, old_ascent;
177 	GtkRequisition requisition;
178 
179 	widget = emb->widget;
180 	if (widget == NULL)
181 		return FALSE;
182 
183 	pixel_size = html_painter_get_pixel_size (painter);
184 
185 	old_width = self->width;
186 	old_ascent = self->ascent;
187 
188 	requisition.width = requisition.height = 0;
189 	gtk_widget_get_preferred_size (widget, &requisition, NULL);
190 
191 	if (GTK_IS_HTML_EMBEDDED (widget))
192 		self->descent = GTK_HTML_EMBEDDED (widget)->descent * pixel_size;
193 	else
194 		self->descent = 0;
195 
196 	self->width  = requisition.width  * pixel_size;
197 	self->ascent = requisition.height * pixel_size - self->descent;
198 
199 	if (old_width != self->width || old_ascent != self->ascent || old_ascent != self->descent)
200 		return TRUE;
201 
202 	return FALSE;
203 }
204 
205 static gboolean
accepts_cursor(HTMLObject * o)206 accepts_cursor (HTMLObject *o)
207 {
208 	return TRUE;
209 }
210 
211 static gchar *
encode(HTMLEmbedded * e,const gchar * codepage)212 encode (HTMLEmbedded *e,
213         const gchar *codepage)
214 {
215 	return g_strdup ("");
216 }
217 
218 void
html_embedded_reset(HTMLEmbedded * e)219 html_embedded_reset (HTMLEmbedded *e)
220 {
221 	HTML_EMBEDDED_CLASS (HTML_OBJECT (e)->klass)->reset (e);
222 }
223 
224 gchar *
html_embedded_encode(HTMLEmbedded * e,const gchar * codepage)225 html_embedded_encode (HTMLEmbedded *e,
226                       const gchar *codepage)
227 {
228 	return HTML_EMBEDDED_CLASS (HTML_OBJECT (e)->klass)->encode (e, codepage);
229 }
230 
231 void
html_embedded_reparent(HTMLEmbedded * e,GtkWidget * new_parent)232 html_embedded_reparent (HTMLEmbedded *e,
233                         GtkWidget *new_parent)
234 {
235 	HTML_EMBEDDED_CLASS (HTML_OBJECT (e)->klass)->reparent (e, new_parent);
236 }
237 
238 void
html_embedded_set_form(HTMLEmbedded * e,HTMLForm * form)239 html_embedded_set_form (HTMLEmbedded *e,
240                         HTMLForm *form)
241 {
242 	e->form = form;
243 }
244 
245 gchar *
html_embedded_encode_string(const gchar * before,const gchar * codepage)246 html_embedded_encode_string (const gchar *before,
247                              const gchar *codepage)
248 {
249 	    const gchar * str = before;
250 	    static const gchar *safe = "$-._!*(),"; /* RFC 1738 */
251 	unsigned pos = 0;
252 	GString *encoded = g_string_new ("");
253 	gchar buffer[5], *ptr;
254 		guchar c;
255 
256 	    GIConv iconv_cd = generate_iconv_to (codepage);
257 	    if (is_valid_g_iconv (iconv_cd))
258 	    {
259 		str= convert_text_encoding (iconv_cd, before);
260 		g_iconv_close (iconv_cd);
261 	    }
262 
263 	while (pos < strlen (str)) {
264 
265 		c = (guchar) str[pos];
266 
267 		if ((( c >= 'A') && (c <= 'Z')) ||
268 		    (( c >= 'a') && (c <= 'z')) ||
269 		    (( c >= '0') && (c <= '9')) ||
270 		    (strchr (safe, c))) {
271 			encoded = g_string_append_c (encoded, c);
272 		} else if (c == ' ') {
273 			encoded = g_string_append_c (encoded, '+');
274 		} else if (c == '\n') {
275 			encoded = g_string_append (encoded, "%0D%0A");
276 		} else if (c != '\r') {
277 			sprintf (buffer, "%%%02X", (gint) c);
278 			encoded = g_string_append (encoded, buffer);
279 		}
280 		pos++;
281 	}
282 
283 	ptr = encoded->str;
284 
285 	g_string_free (encoded, FALSE);
286 
287     return ptr;
288 }
289 
290 void
html_embedded_type_init(void)291 html_embedded_type_init (void)
292 {
293 	html_embedded_class_init (&html_embedded_class, HTML_TYPE_EMBEDDED, sizeof (HTMLEmbedded));
294 }
295 
296 void
html_embedded_class_init(HTMLEmbeddedClass * klass,HTMLType type,guint size)297 html_embedded_class_init (HTMLEmbeddedClass *klass,
298                           HTMLType type,
299                           guint size)
300 {
301 	HTMLObjectClass *object_class;
302 
303 	g_return_if_fail (klass != NULL);
304 
305 	object_class = HTML_OBJECT_CLASS (klass);
306 	html_object_class_init (object_class, type, size);
307 
308 	/* HTMLEmbedded methods.   */
309 	klass->reset = reset;
310 	klass->encode = encode;
311 
312 	/* HTMLObject methods.   */
313 	object_class->destroy = destroy;
314 	object_class->copy = copy;
315 	object_class->draw = draw;
316 	object_class->accepts_cursor = accepts_cursor;
317 	object_class->calc_size = html_embedded_real_calc_size;
318 	object_class->calc_min_width = calc_min_width;
319 
320 	parent_class = &html_object_class;
321 }
322 
323 void
html_embedded_init(HTMLEmbedded * element,HTMLEmbeddedClass * klass,GtkWidget * parent,const gchar * name,const gchar * value)324 html_embedded_init (HTMLEmbedded *element,
325                    HTMLEmbeddedClass *klass,
326                    GtkWidget *parent,
327                    const gchar *name,
328                    const gchar *value)
329 {
330 	HTMLObject *object;
331 
332 	d (printf ("embedded %p init\n", element));
333 
334 	object = HTML_OBJECT (element);
335 	html_object_init (object, HTML_OBJECT_CLASS (klass));
336 
337 	element->form = NULL;
338 	if (name)
339 		element->name = g_strdup (name);
340 	else
341 		element->name = g_strdup ("");
342 	if (value)
343 		element->value = g_strdup (value);
344 	else
345 		element->value = g_strdup ("");
346 	element->widget = NULL;
347 	element->parent = parent;
348 	element->width  = 0;
349 	element->height = 0;
350 	element->abs_x  = element->abs_y = -1;
351 	element->changed_id = 0;
352 }
353 
354 static gboolean
html_embedded_grab_cursor(GtkWidget * eb,GdkEvent * event)355 html_embedded_grab_cursor (GtkWidget *eb,
356                            GdkEvent *event)
357 {
358 	/* Keep the focus! Fight the power */
359 	return TRUE;
360 }
361 
362 /* called when some state in an embedded html object has changed ... do a redraw */
363 static void
html_embedded_object_changed(GtkHTMLEmbedded * eb,HTMLEngine * e)364 html_embedded_object_changed (GtkHTMLEmbedded *eb,
365                               HTMLEngine *e)
366 {
367 	HTMLObject *object;
368 
369 	object = HTML_OBJECT (g_object_get_data (G_OBJECT (eb), "embeddedelement"));
370 	if (object)
371 		html_object_calc_size (object, e->painter, NULL);
372 
373 	html_engine_schedule_update (e);
374 }
375 
376 HTMLEmbedded *
html_embedded_new_widget(GtkWidget * parent,GtkHTMLEmbedded * eb,HTMLEngine * engine)377 html_embedded_new_widget (GtkWidget *parent,
378                           GtkHTMLEmbedded *eb,
379                           HTMLEngine *engine)
380 {
381 	HTMLEmbedded *em;
382 
383 	em = g_new0 (HTMLEmbedded, 1);
384 	d (printf ("embedded %p new widget\n", em));
385 
386 	html_embedded_init (em, HTML_EMBEDDED_CLASS (&html_embedded_class), parent, eb->name, "");
387 	html_embedded_set_widget (em, GTK_WIDGET (eb));
388 
389 	/* pass em as the user_data so that the handler will disconnect
390 	 * when the object is destoyed
391 	 */
392 	g_signal_connect (eb, "button_press_event", G_CALLBACK (html_embedded_grab_cursor), em);
393 	em->changed_id = g_signal_connect (eb, "changed", G_CALLBACK (html_embedded_object_changed), engine);
394 	/* printf ("id %u\n", em->changed_id); */
395 
396 	return em;
397 }
398 
399 static void
html_embedded_allocate(GtkWidget * w,GtkAllocation * allocation,HTMLEmbedded * e)400 html_embedded_allocate (GtkWidget *w,
401                         GtkAllocation *allocation,
402                         HTMLEmbedded *e)
403 {
404 	GtkWidget *parent;
405 
406 	parent = gtk_widget_get_parent (w);
407 
408 	if (e->width != allocation->width || e->height != allocation->height) {
409 		if (e->width != allocation->width) {
410 			html_object_change_set (HTML_OBJECT (e), HTML_CHANGE_ALL_CALC);
411 			e->width = allocation->width;
412 		}
413 		e->height = allocation->height;
414 
415 		if (GTK_IS_HTML (parent))
416 			html_engine_schedule_update (GTK_HTML (parent)->engine);
417 	}
418 }
419 
420 void
html_embedded_set_widget(HTMLEmbedded * emb,GtkWidget * w)421 html_embedded_set_widget (HTMLEmbedded *emb,
422                           GtkWidget *w)
423 {
424 	emb->widget = w;
425 
426 	d (printf ("set embedded widget: %p widget: %p\n", emb, w));
427 	gtk_widget_show (w);
428 
429 	g_object_set_data (G_OBJECT (w), "embeddedelement", emb);
430 	g_signal_connect (w, "size_allocate", G_CALLBACK (html_embedded_allocate), emb);
431 }
432 
433 GtkWidget *
html_embedded_get_widget(HTMLEmbedded * e)434 html_embedded_get_widget (HTMLEmbedded *e)
435 {
436 	return e->widget;
437 }
438 
439 gboolean
html_object_is_embedded(HTMLObject * o)440 html_object_is_embedded (HTMLObject *o)
441 {
442 	gboolean rv = FALSE;
443 
444 	switch (o->klass->type) {
445 	case HTML_TYPE_EMBEDDED:
446 	case HTML_TYPE_TEXTINPUT:
447 	case HTML_TYPE_BUTTON:
448 	case HTML_TYPE_IMAGEINPUT:
449 	case HTML_TYPE_TEXTAREA:
450 	case HTML_TYPE_HIDDEN:
451 	case HTML_TYPE_RADIO:
452 	case HTML_TYPE_CHECKBOX:
453 	case HTML_TYPE_SELECT:
454 	case HTML_TYPE_IFRAME:
455 	case HTML_TYPE_FRAME:
456 		rv = TRUE;
457 	default:
458 		;
459 	}
460 
461 	return rv;
462 }
463 
464 gboolean
html_object_is_frame(HTMLObject * o)465 html_object_is_frame (HTMLObject *o)
466 {
467 	return HTML_IS_FRAME (o) || HTML_IS_IFRAME (o);
468 }
469