Add to git.
[monolith.git] / src / ml_window.c
1 /* Monolith window class.
2  * - by Richard W.M. Jones <rich@annexia.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * $Id: ml_window.c,v 1.8 2003/04/30 13:15:34 rich Exp $
19  */
20
21 #include "config.h"
22
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #endif
26
27 #include <pool.h>
28 #include <vector.h>
29 #include <pstring.h>
30
31 #include <pthr_http.h>
32
33 #include "monolith.h"
34 #include "ml_window.h"
35
36 struct ml_window
37 {
38   pool pool;                    /* Pool for allocations. */
39   const char *windowid;         /* Window ID. */
40
41   /* For ordinary windows: */
42   ml_widget w;                  /* Packed widget. */
43   int headers_flag;             /* If set, emit start, end of page. */
44   const char *title;            /* Title of the window. */
45   const char *stylesheet;       /* Stylesheet for the window. */
46   const char *charset;          /* Character encoding. */
47   int refresh;                  /* Refresh period (0 = no refresh). */
48   int scroll_to_x, scroll_to_y; /* Scroll to (x, y). */
49
50   /* For framesets: */
51   const char *rows, *cols;      /* Layout. */
52   vector frames;                /* Frames (vector of struct
53                                  * ml_frame_description). */
54   vector actions;               /* Action IDs (one per frame). */
55
56   /* For redirects. */
57   const char *uri;              /* URI to redirect to. */
58
59   /* Javascript cookies to set (see _ml_window_set_cookie_with_javascript). */
60   vector js_cookies;
61 };
62
63 static int next_window_id = 0;
64
65 ml_window
66 new_ml_window (ml_session session, pool pool)
67 {
68   ml_window w = pmalloc (pool, sizeof *w);
69
70   w->pool = pool;
71   w->w = 0;
72   w->headers_flag = 1;
73   w->title = 0;
74   /* XXX Eventually take this from the default theme for the application? */
75   w->stylesheet = "/ml-styles/default.css";
76   w->charset = "utf-8";
77   w->refresh = 0;
78   w->scroll_to_x = w->scroll_to_y = 0;
79
80   w->rows = w->cols = 0;
81   w->frames = 0;
82   w->actions = 0;
83
84   w->uri = 0;
85   w->js_cookies = new_vector (pool, const char *);
86
87   /* Register and set current window. */
88   w->windowid = pitoa (pool, ++next_window_id);
89   _ml_session_set_current_window (session, w, w->windowid);
90
91   return w;
92 }
93
94 void
95 ml_window_pack (ml_window w, ml_widget _w)
96 {
97   w->w = _w;
98 }
99
100 void
101 ml_window_set_headers_flag (ml_window w, int headers_flag)
102 {
103   w->headers_flag = headers_flag;
104 }
105
106 int
107 ml_window_get_headers_flag (ml_window w)
108 {
109   return w->headers_flag;
110 }
111
112 void
113 ml_window_set_title (ml_window w, const char *title)
114 {
115   w->title = title;
116 }
117
118 const char *
119 ml_window_get_title (ml_window w)
120 {
121   return w->title;
122 }
123
124 void
125 ml_window_set_stylesheet (ml_window w, const char *stylesheet)
126 {
127   w->stylesheet = stylesheet;
128 }
129
130 const char *
131 ml_window_get_stylesheet (ml_window w)
132 {
133   return w->stylesheet;
134 }
135
136 void
137 ml_window_set_charset (ml_window w, const char *_charset)
138 {
139   w->charset = _charset;
140 }
141
142 const char *
143 ml_window_get_charset (ml_window w)
144 {
145   return w->charset;
146 }
147
148 void
149 ml_window_set_refresh (ml_window w, int refresh)
150 {
151   w->refresh = refresh;
152 }
153
154 int
155 ml_window_get_refresh (ml_window w)
156 {
157   return w->refresh;
158 }
159
160 void
161 ml_window_scroll_to (ml_window w, int x, int y)
162 {
163   w->scroll_to_x = x;
164   w->scroll_to_y = y;
165 }
166
167 static void update_actions (ml_window w, ml_session session);
168
169 ml_window
170 new_ml_frameset (ml_session session, pool pool,
171                  const char *rows, const char *cols, vector frames)
172 {
173   ml_window w = new_ml_window (session, pool);
174
175   w->rows = rows;
176   w->cols = cols;
177   w->frames = frames;
178   update_actions (w, session);
179
180   return w;
181 }
182
183 void
184 ml_frameset_set_description (ml_window w, ml_session session,
185                              const char *rows, const char *cols, vector frames)
186 {
187   w->rows = rows;
188   w->cols = cols;
189   w->frames = frames;
190   update_actions (w, session);
191 }
192
193 void
194 ml_frameset_set_title (ml_window w, const char *title)
195 {
196   w->title = title;
197 }
198
199 const char *
200 ml_frameset_get_title (ml_window w)
201 {
202   return w->title;
203 }
204
205 static void
206 update_actions (ml_window w, ml_session session)
207 {
208   int i;
209   const char *actionid;
210   struct ml_frame_description frame;
211
212   if (w->actions)
213     {
214       /* Unregister the old actions. */
215       for (i = 0; i < vector_size (w->actions); ++i)
216         {
217           vector_get (w->actions, i, actionid);
218           ml_unregister_action (session, actionid);
219         }
220       vector_clear (w->actions);
221     }
222   else
223     w->actions = new_vector (w->pool, const char *);
224
225   if (w->frames && vector_size (w->frames) > 0)
226     {
227       /* Register new actions. */
228       for (i = 0; i < vector_size (w->frames); ++i)
229         {
230           vector_get (w->frames, i, frame);
231           actionid = ml_register_action (session, frame.fn, frame.data);
232           vector_push_back (w->actions, actionid);
233         }
234     }
235 }
236
237 ml_window
238 new_ml_redirect (ml_session session, pool pool,
239                  const char *uri)
240 {
241   ml_window w = new_ml_window (session, pool);
242
243   w->uri = uri;
244
245   return w;
246 }
247
248 void
249 ml_redirect_set_uri (ml_window w, const char *uri)
250 {
251   w->uri = uri;
252 }
253
254 const char *
255 ml_redirect_get_uri (ml_window w)
256 {
257   return w->uri;
258 }
259
260 void
261 _ml_window_notify_begin_response (ml_window w)
262 {
263   vector_clear (w->js_cookies);
264 }
265
266 int
267 _ml_window_get_response_code (ml_window w)
268 {
269   if (! w->uri) return 200;
270   else return 301;
271 }
272
273 const char *
274 _ml_window_get_response_name (ml_window w)
275 {
276   if (! w->uri) return "OK";
277   else return "Moved Permanently";
278 }
279
280 /* To allow shared object scripts to be embedded in pages using
281  * mod_include we must set the session cookie using Javascript. This
282  * is because mod_include doesn't propagate the Set-Cookie header from
283  * a #include to the outer set of headers.
284  *
285  * NB. 'cookie' is allocated on the thread pool.
286  */
287 void
288 _ml_window_set_cookie_with_javascript (ml_window w,
289                                        const char *cookie)
290 {
291   vector_push_back (w->js_cookies, cookie);
292 }
293
294 static inline void
295 set_cookies_with_javascript (ml_window w, io_handle io)
296 {
297   while (vector_size (w->js_cookies) > 0)
298     {
299       const char *cookie;
300
301       vector_pop_front (w->js_cookies, cookie);
302       io_fprintf (io,
303                   "<script language=\"javascript\"><!--\n"
304                   "document.cookie = '%s';\n"
305                   "//--></script>\n",
306                   cookie);
307     }
308 }
309
310 void
311 _ml_window_send_headers (ml_window w, pool thread_pool,
312                          http_response http_response)
313 {
314   if (! w->uri)
315     {
316       /* Send the Content-Type: header. */
317       http_response_send_header
318         (http_response,
319          "Content-Type", psprintf (thread_pool,
320                                    "text/html; charset=%s",
321                                    w->charset));
322
323       /* Refresh period? */
324       if (w->refresh != 0)
325         http_response_send_header
326           (http_response,
327            "Refresh", pitoa (thread_pool, w->refresh));
328     }
329   else
330     {
331       /* Send the Location: header. */
332       http_response_send_header (http_response, "Location", w->uri);
333
334       /* Send the Content-Length: header. */
335       http_response_send_header (http_response, "Content-Length", "0");
336     }
337 }
338
339 const char *
340 _ml_window_get_windowid (ml_window w)
341 {
342   return w->windowid;
343 }
344
345 void
346 _ml_window_repaint (ml_window w, ml_session session, io_handle io)
347 {
348   if (!w->frames && !w->uri)    /* Ordinary window. */
349     {
350       if (w->headers_flag)
351         {
352           io_fprintf
353             (io,
354              "<!DOCTYPE html "
355              "PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
356              "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
357              "<html xmlns=\"http://www.w3.org/1999/xhtml\" "
358              "xml:lang=\"en\" lang=\"en\">\n"
359              "<head>\n");
360
361           if (w->title)
362             io_fprintf (io, "<title>%s</title>\n", w->title);
363
364           if (w->stylesheet)
365             io_fprintf (io,
366                         "<link rel=\"stylesheet\" "
367                         "href=\"%s\" type=\"text/css\">\n",
368                         w->stylesheet);
369
370           io_fprintf (io, "</head><body>\n");
371         }
372
373       set_cookies_with_javascript (w, io);
374
375       if (w->w)
376         ml_widget_repaint (w->w, session, w->windowid, io);
377
378       if (w->scroll_to_x > 0 || w->scroll_to_y > 0)
379         {
380           io_fprintf
381             (io,
382              "<script language=\"javascript\"><!--\n"
383              "window.scrollTo (%d, %d);\n"
384              "//--></script>\n",
385              w->scroll_to_x, w->scroll_to_y);
386         }
387
388       if (w->headers_flag)
389         io_fprintf (io, "</body></html>\n");
390     }
391   else if (w->frames)           /* Frameset. */
392     {
393       int i;
394
395       io_fprintf
396         (io,
397          "<!DOCTYPE html "
398          "PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" "
399          "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">\n"
400          "<html xmlns=\"http://www.w3.org/1999/xhtml\" "
401          "xml:lang=\"en\" lang=\"en\">\n"
402          "<head>\n");
403
404       if (w->title)
405         io_fprintf (io, "<title>%s</title>\n", w->title);
406
407       io_fprintf (io, "</head><frameset");
408
409       if (w->rows)
410         io_fprintf (io, " rows=\"%s\"", w->rows);
411       if (w->cols)
412         io_fprintf (io, " cols=\"%s\"", w->cols);
413
414       io_fprintf (io, ">");
415
416       for (i = 0; i < vector_size (w->frames); ++i)
417         {
418           /* struct ml_frame_description frame; */
419           const char *actionid;
420
421           /* vector_get (w->frames, i, frame); */
422           vector_get (w->actions, i, actionid);
423
424           io_fprintf (io, "<frame src=\"%s?ml_action=%s\" />",
425                       ml_session_script_name (session), actionid);
426         }
427
428       io_fprintf (io, "</frameset></html>\n");
429     }
430   else                          /* Redirect. */
431     {
432       /* Do nothing. */
433     }
434 }