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