Codebase list dillo / 17ecdf9
Imported Upstream version 3~hg20110720 Axel Beckert 12 years ago
707 changed file(s) with 64067 addition(s) and 60619 deletion(s). Raw diff Collapse all Expand all
Binary diff not shown
0 default
0 39732d8f7abd1597cd10bafdf3602797a345485e 2142
1 39732d8f7abd1597cd10bafdf3602797a345485e default
2 aa944fb43c5686ad5de0b759011883889df1fee2 dillo2
0 2142 39732d8f7abd1597cd10bafdf3602797a345485e 3f61660576a94a9dbf113f35461fe7fad13793fa
1
2 aa944fb43c5686ad5de0b759011883889df1fee2 start
3 65ee806d51728780211db61ba387988ae1a3874b release-2_1_1
4 009c4cf944331789ae47689d4dbefeb845900fb6 release-2_0
5 e02128d96c8ab24b424a582ed3a8fc4699bb8aa1 release-2_1
6 26fc17b38ef26c5e069cebae62f85c5254b3dfc8 release-2_2
7 3cc3dd19e50da74eddfe67fa41ab888f8fb8cb0f release-2_2-rc2
8 96a14e974cfbca56647e2f503698f196a4b614b4 2.1-noCss
9 0c2299a1362da47b74093c4ac0a58331b8c9f470 release-2_2-rc1
10 93e4655f8a92d2d3421949e7f91a96c521255106 release-2_2-rc3
Binary diff not shown
0 [paths]
1 default = http://hg.dillo.org/dillo_port1.3
0 revlogv1
1 store
2 fncache
3 dotencode
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 data/doc/Cache.txt.i
1 data/src/bookmark.h.i
2 data/src/capi.c.i
3 data/src/dicache.c.i
4 data/src/misc.h.i
5 data/dpi/dpiutil.h.i
6 data/src/paths.cc.i
7 data/src/prefs.c.i
8 data/test/dw_lists.cc.i
9 data/doc/dw-style-box-model.png.i
10 data/test/dw_example.cc.i
11 data/doc/fltk-problems.doc.i
12 data/dpi/datauri.c.i
13 data/dw/events.hh.i
14 data/src/timeout.hh.i
15 data/src/html.hh.i
16 data/doc/dw-textblock-collapsing-spaces-1-2.png.i
17 data/doc/CCCwork.txt.i
18 data/test/form.cc.i
19 data/src/binaryconst.h.i
20 data/dpi/dpiutil.c.i
21 data/d_size.h.i
22 data/dw/layout.hh.i
23 data/doc/dw-style-length-percentage.png.i
24 data/src/dns.h.i
25 data/src/history.c.i
26 data/dpi/hello.c.i
27 data/src/css.hh.i
28 data/lout/Makefile.am.i
29 data/test/dw_imgbuf_mem_test.cc.i
30 data/configure.in.i
31 data/dw/listitem.hh.i
32 data/src/url.h.i
33 data/dw/fltkpreview.cc.i
34 data/dw/view.hh.i
35 data/dw/alignedtextblock.hh.i
36 data/doc/dw-style-length-absolute.png.i
37 data/ChangeLog.i
38 data/doc/rounding-errors.doc.i
39 data/src/plain.cc.i
40 data/src/cache.h.i
41 data/src/dgif.h.i
42 data/test/Makefile.am.i
43 data/dpi/file.c.i
44 data/doc/HtmlParser.txt.i
45 data/src/image.cc.i
46 data/src/utf8.cc.i
47 data/src/dir.c.i
48 data/doc/dw-map.doc.i
49 data/dw/bullet.hh.i
50 data/doc/DwTable.txt.i
51 data/dpi/https.c.i
52 data/dpid/dpi.c.i
53 data/dw/iterator.cc.i
54 data/src/styleengine.cc.i
55 data/src/styleengine.hh.i
56 data/lout/identity.cc.i
57 data/dpid/dpi_socket_dir.h.i
58 data/src/IO/iowatch.hh.i
59 data/src/uicmd.cc.i
60 data/doc/README.i
61 data/src/IO/dpi.c.i
62 data/doc/uml-legend.doc.i
63 data/src/dir.h.i
64 data/dpid/dpid_common.c.i
65 data/dw/style.cc.i
66 data/lout/object.cc.i
67 data/src/bitvec.c.i
68 data/src/form.hh.i
69 data/lout/debug.hh.i
70 data/src/bookmark.c.i
71 data/.hgtags.i
72 data/test/dw_images_scaled.cc.i
73 data/src/prefsparser.cc.i
74 data/src/dns.c.i
75 data/doc/dw-textblock-collapsing-spaces-2-1.png.i
76 data/doc/DwPage.txt.i
77 data/doc/dw-viewport-without-scrollbar.png.i
78 data/src/png.c.i
79 data/src/cookies.c.i
80 data/test/shapes.cc.i
81 data/INSTALL.i
82 data/README-port.i
83 data/dw/image.hh.i
84 data/src/table.cc.i
85 data/dpid/dpid.h.i
86 data/src/auth.c.i
87 data/dpi/downloads.cc.i
88 data/doc/Dpid.txt.i
89 data/src/chain.c.i
90 data/test/dw_resource_test.cc.i
91 data/doc/Dw.txt.i
92 data/doc/dw-usage.doc.i
93 data/src/colors.h.i
94 data/src/chain.h.i
95 data/dpip/Makefile.am.i
96 data/src/findbar.hh.i
97 data/dw/iterator.hh.i
98 data/src/keys.cc.i
99 data/src/cache.c.i
100 data/dw/widget.cc.i
101 data/dw/fltkimgbuf.cc.i
102 data/dpid/misc_new.c.i
103 data/src/html_common.hh.i
104 data/src/jpeg.c.i
105 data/dillorc.i
106 data/dw/selection.cc.i
107 data/dw/fltkcore.hh.i
108 data/dw/fltkui.hh.i
109 data/dw/fltkpreview.hh.i
110 data/test/cookies.c.i
111 data/dw/platform.hh.i
112 data/src/IO/IO.c.i
113 data/src/paths.hh.i
114 data/Makefile.am.i
115 data/dw/textblock.cc.i
116 data/dlib/dlib.h.i
117 data/dillo2rc.i
118 data/src/imgbuf.cc.i
119 data/doc/dw-widget-sizes.doc.i
120 data/test/dw_images_simple.cc.i
121 data/dw/tablecell.hh.i
122 data/doc/index.doc.i
123 data/src/history.h.i
124 data/dw/core.hh.i
125 data/src/djpeg.h.i
126 data/dw/fltkviewport.hh.i
127 data/dpi/ftp.c.i
128 data/src/keys.hh.i
129 data/src/klist.h.i
130 data/dpid/dpi_service.c.i
131 data/src/cookies.h.i
132 data/src/dpiapi.h.i
133 data/src/auth.h.i
134 data/src/pixmaps.h.i
135 data/dw/ruler.hh.i
136 data/src/utf8.hh.i
137 data/dpi/bookmarks.c.i
138 data/dw/layout.cc.i
139 data/doc/dw-textblock-collapsing-spaces-2-2.png.i
140 data/doc/NC_design.txt.i
141 data/doc/dw-size-of-widget.png.i
142 data/dw/widget.hh.i
143 data/src/cssparser.hh.i
144 data/src/image.hh.i
145 data/src/bw.c.i
146 data/README.i
147 data/src/dpiapi.c.i
148 data/dw/fltkui.cc.i
149 data/dw/selection.hh.i
150 data/src/cssparser.cc.i
151 data/src/imgbuf.hh.i
152 data/lout/misc.hh.i
153 data/src/gif.c.i
154 data/test/dw_table_aligned.cc.i
155 data/src/msg.h.i
156 data/src/bitvec.h.i
157 data/src/capi.h.i
158 data/AUTHORS.i
159 data/doc/Cookies.txt.i
160 data/doc/dw-changes.doc.i
161 data/doc/dw-textblock-collapsing-spaces-1-1.png.i
162 data/dpid/dpidc.i
163 data/dw/fltkviewport.cc.i
164 data/src/findbar.cc.i
165 data/doc/dw-images-and-backgrounds.doc.i
166 data/src/xembed.hh.i
167 data/dpid/dpidrc.in.i
168 data/dw/fltkmisc.cc.i
169 data/dw/types.hh.i
170 data/dw/ui.cc.i
171 data/dw/fltkflatview.hh.i
172 data/dpi/cookies.c.i
173 data/dw/findtext.cc.i
174 data/src/decode.c.i
175 data/dw/fltkflatview.cc.i
176 data/dw/listitem.cc.i
177 data/doc/dw-layout-widgets.doc.i
178 data/src/list.h.i
179 data/src/IO/Url.h.i
180 data/src/IO/mime.c.i
181 data/src/dicache.h.i
182 data/doc/Dillo.txt.i
183 data/dw/fltkcomplexbutton.hh.i
184 data/src/IO/proto.c.i
185 data/Doxyfile.i
186 data/doc/dw-viewport-with-scrollbar.png.i
187 data/src/dillo.cc.i
188 data/doc/Makefile.am.i
189 data/src/table.hh.i
190 data/src/colors.c.i
191 data/src/IO/IO.h.i
192 data/lout/container.cc.i
193 data/doc/DwStyle.txt.i
194 data/test/dw_links.cc.i
195 data/test/dw_links2.cc.i
196 data/doc/DwWidget.txt.i
197 data/dpid/dpi_service.h.i
198 data/src/keysrc.i
199 data/doc/Imgbuf.txt.i
200 data/doc/dillo.1.i
201 data/dw/fltkmisc.hh.i
202 data/install-dpi-local.i
203 data/.hgignore.i
204 data/dw/table.cc.i
205 data/dw/types.cc.i
206 data/dpid/TODO.i
207 data/doc/lout.doc.i
208 data/src/nav.c.i
209 data/dpid/dpid.c.i
210 data/doc/IO.txt.i
211 data/dpid/Makefile.am.i
212 data/dpi/vsource.c.i
213 data/dpid/misc_new.h.i
214 data/src/IO/mime.h.i
215 data/src/IO/iowatch.cc.i
216 data/src/menu.cc.i
217 data/src/dialog.cc.i
218 data/dw/fltkviewbase.hh.i
219 data/src/prefsparser.hh.i
220 data/src/web.hh.i
221 data/dw/textblock.hh.i
222 data/test/dw_images_scaled2.cc.i
223 data/src/srch.i
224 data/test/dw_ui_test.cc.i
225 data/src/form.cc.i
226 data/src/uicmd.hh.i
227 data/dw/fltkplatform.cc.i
228 data/doc/dw-layout-views.doc.i
229 data/src/klist.c.i
230 data/lout/identity.hh.i
231 data/autogen.sh.i
232 data/src/html.cc.d
233 data/src/decode.h.i
234 data/dw/preview.xbm.i
235 data/src/bw.h.i
236 data/dw/image.cc.i
237 data/dlib/dlib.c.i
238 data/lout/misc.cc.i
239 data/dillorc2.i
240 data/src/IO/about.c.i
241 data/src/chg.i
242 data/src/html.cc.i
243 data/test/dw_table.cc.i
244 data/dw/table.hh.i
245 data/src/ui.hh.i
246 data/dpip/dpip.h.i
247 data/doc/dw-style-length-relative.png.i
248 data/src/web.cc.i
249 data/src/dpng.h.i
250 data/dw/ui.hh.i
251 data/lout/container.hh.i
252 data/test/dw_anchors_test.cc.i
253 data/src/prefs.h.i
254 data/src/url.c.i
255 data/dw/imgbuf.hh.i
256 data/test/dw_border_test.cc.i
257 data/dlib/Makefile.am.i
258 data/doc/Selection.txt.i
259 data/src/ui.cc.i
260 data/test/dw_find_test.cc.i
261 data/src/IO/http.c.i
262 data/dpid/main.c.i
263 data/COPYING.i
264 data/test/fltk_browser.cc.i
265 data/dw/fltkcomplexbutton.cc.i
266 data/dw/tablecell.cc.i
267 data/dpid/dpidc.c.i
268 data/dw/alignedtextblock.cc.i
269 data/dw/ruler.cc.i
270 data/src/doctree.hh.i
271 data/lout/signal.hh.i
272 data/src/IO/Makefile.am.i
273 data/dpi/Makefile.am.i
274 data/dpid/dpid_common.h.i
275 data/NEWS.i
276 data/lout/signal.cc.i
277 data/src/xembed.cc.i
278 data/src/menu.hh.i
279 data/lout/object.hh.i
280 data/src/debug.h.i
281 data/src/css.cc.i
282 data/dw/fltkimgbuf.hh.i
283 data/src/misc.c.i
284 data/dw/Makefile.am.i
285 data/dw/fltkviewbase.cc.i
286 data/src/Makefile.am.i
287 data/doc/Images.txt.i
288 data/doc/user_help.html.i
289 data/doc/DwImage.txt.i
290 data/dw/bullet.cc.i
291 data/doc/DwRender.txt.i
292 data/dpip/dpip.c.i
293 data/dw/style.hh.i
294 data/test/form.hh.i
295 data/dw/fltkplatform.hh.i
296 data/src/dialog.hh.i
297 data/doc/dw-example-screenshot.png.i
298 data/dpid/dpi_socket_dir.c.i
299 data/src/nav.h.i
300 data/dw/findtext.hh.i
301 data/doc/dw-overview.doc.i
302 data/config.h.in.i
303 data/dpid/dpi.h.i
304 data/lout/msg.h.i
305 data/src/timeout.cc.i
Binary diff not shown
0 default
0 0
1 pull
2 http://hg.dillo.org/dillo_port1.3
(New empty file)
0 (^|/)CVS($|/)
1 (^|/)\.hg($|/)
2 (^|/)\.deps($|/)
3 (^|/)html($|/)
4 \.a$
5 \.o$
6 ^config\.
7 ^autom4te\.cache$
8 (^|/)Makefile$
9 (^|/)Makefile\.in$
10 ^configure$
11 ^aclocal\.m4$
12 ^depcomp$
13 ^install-sh$
14 ^stamp-h1$
15 ^missing$
16 (^|/)tags$
17 ^src/dillo$
18 ^dpi/[^/]*\.dpi$
19 ^dpid/dpid$
20 ^dpid/dpidc$
21 ^dpid/dpidrc$
22 ^test/cookies$
23 ^test/dw-anchors-test$
24 ^test/dw-border-test$
25 ^test/dw-example$
26 ^test/dw-find-test$
27 ^test/dw-images-scaled$
28 ^test/dw-images-scaled2$
29 ^test/dw-images-simple$
30 ^test/dw-imgbuf-mem-test$
31 ^test/dw-links$
32 ^test/dw-links2$
33 ^test/dw-lists$
34 ^test/dw-resource-test$
35 ^test/dw-table$
36 ^test/dw-table-aligned$
37 ^test/dw-ui-test$
38 ^test/fltk-browser$
39 ^test/shapes$
0 009c4cf944331789ae47689d4dbefeb845900fb6 release-2_0
1 aa944fb43c5686ad5de0b759011883889df1fee2 start
2 96a14e974cfbca56647e2f503698f196a4b614b4 2.1-noCss
3
4 e02128d96c8ab24b424a582ed3a8fc4699bb8aa1 release-2_1
5 65ee806d51728780211db61ba387988ae1a3874b release-2_1_1
6 0c2299a1362da47b74093c4ac0a58331b8c9f470 release-2_2-rc1
7 3cc3dd19e50da74eddfe67fa41ab888f8fb8cb0f release-2_2-rc2
8 93e4655f8a92d2d3421949e7f91a96c521255106 release-2_2-rc3
9 26fc17b38ef26c5e069cebae62f85c5254b3dfc8 release-2_2
00
11 _________________________________________________________________
2
2
33 Dillo project's team
44 _________________________________________________________________
5
5
66 Project maintainer:
7 * Jorge Arellano Cid
8
7 * Jorge Arellano Cid
8
99 Core Developers:
10 * Jorge Arellano Cid
11 * Sebastian Geerken
12 * Luca Rota
13
10 * Jorge Arellano Cid
11 * Sebastian Geerken
12 * Johannes Hofmann
13 * Place (aka corvid)
14 * Luca Rota
15
1416 Steady developers:
1517 * Livio Baldini
16 * Eric Gaudet
17 * Jörgen Viksell
18
18 * Eric Gaudet
19 * Jeremy Henty
20 * Jörgen Viksell
21
1922 Contributors:
2023 * Lars Clausen
21 * Geoff Lane
22 * Sammy Mannaert
23 * James McCollough
24
24 * Ferdi Franceschini
25 * Matthias Franz
26 * Geoff Lane
27 * Sammy Mannaert
28 * James McCollough
29 * Justus Winter
30
2531 Patches:
2632 * Philip Blundell
2733 * Francis Daly
28 * Sam Dennis
34 * Sam Dennis
2935 * Melvin Hadasht
3036 * Madis Janson
31 * Andrew McPherson
37 * Frank de Lange
38 * Andrew McPherson
3239 * Sean 'Shaleh' Perry
3340 * Marcos Ramírez
3441 * Adam Sampson
3542 * Andreas Schweitzer
36 * Dominic Wong
43 * Dominic Wong
3744 _________________________________________________________________
38
45
3946 Web site logo:
4047 * Eric Gaudet
41 * Jarrod Henry
42
48 * Jarrod Henry
49
4350 Gzilla author:
44 * Raph Levien
51 * Raph Levien
4552 _________________________________________________________________
4653
4754
+623
-291
COPYING less more
0
10 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991
3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1 Version 3, 29 June 2007
2
3 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
64 Everyone is permitted to copy and distribute verbatim copies
75 of this license document, but changing it is not allowed.
86
97 Preamble
108
11 The licenses for most software are designed to take away your
12 freedom to share and change it. By contrast, the GNU General Public
13 License is intended to guarantee your freedom to share and change free
14 software--to make sure the software is free for all its users. This
15 General Public License applies to most of the Free Software
16 Foundation's software and to any other program whose authors commit to
17 using it. (Some other Free Software Foundation software is covered by
18 the GNU Library General Public License instead.) You can apply it to
9 The GNU General Public License is a free, copyleft license for
10 software and other kinds of works.
11
12 The licenses for most software and other practical works are designed
13 to take away your freedom to share and change the works. By contrast,
14 the GNU General Public License is intended to guarantee your freedom to
15 share and change all versions of a program--to make sure it remains free
16 software for all its users. We, the Free Software Foundation, use the
17 GNU General Public License for most of our software; it applies also to
18 any other work released this way by its authors. You can apply it to
1919 your programs, too.
2020
2121 When we speak of free software, we are referring to freedom, not
2222 price. Our General Public Licenses are designed to make sure that you
2323 have the freedom to distribute copies of free software (and charge for
24 this service if you wish), that you receive source code or can get it
25 if you want it, that you can change the software or use pieces of it
26 in new free programs; and that you know you can do these things.
27
28 To protect your rights, we need to make restrictions that forbid
29 anyone to deny you these rights or to ask you to surrender the rights.
30 These restrictions translate to certain responsibilities for you if you
31 distribute copies of the software, or if you modify it.
24 them if you wish), that you receive source code or can get it if you
25 want it, that you can change the software or use pieces of it in new
26 free programs, and that you know you can do these things.
27
28 To protect your rights, we need to prevent others from denying you
29 these rights or asking you to surrender the rights. Therefore, you have
30 certain responsibilities if you distribute copies of the software, or if
31 you modify it: responsibilities to respect the freedom of others.
3232
3333 For example, if you distribute copies of such a program, whether
34 gratis or for a fee, you must give the recipients all the rights that
35 you have. You must make sure that they, too, receive or can get the
36 source code. And you must show them these terms so they know their
37 rights.
38
39 We protect your rights with two steps: (1) copyright the software, and
40 (2) offer you this license which gives you legal permission to copy,
41 distribute and/or modify the software.
42
43 Also, for each author's protection and ours, we want to make certain
44 that everyone understands that there is no warranty for this free
45 software. If the software is modified by someone else and passed on, we
46 want its recipients to know that what they have is not the original, so
47 that any problems introduced by others will not reflect on the original
48 authors' reputations.
49
50 Finally, any free program is threatened constantly by software
51 patents. We wish to avoid the danger that redistributors of a free
52 program will individually obtain patent licenses, in effect making the
53 program proprietary. To prevent this, we have made it clear that any
54 patent must be licensed for everyone's free use or not licensed at all.
34 gratis or for a fee, you must pass on to the recipients the same
35 freedoms that you received. You must make sure that they, too, receive
36 or can get the source code. And you must show them these terms so they
37 know their rights.
38
39 Developers that use the GNU GPL protect your rights with two steps:
40 (1) assert copyright on the software, and (2) offer you this License
41 giving you legal permission to copy, distribute and/or modify it.
42
43 For the developers' and authors' protection, the GPL clearly explains
44 that there is no warranty for this free software. For both users' and
45 authors' sake, the GPL requires that modified versions be marked as
46 changed, so that their problems will not be attributed erroneously to
47 authors of previous versions.
48
49 Some devices are designed to deny users access to install or run
50 modified versions of the software inside them, although the manufacturer
51 can do so. This is fundamentally incompatible with the aim of
52 protecting users' freedom to change the software. The systematic
53 pattern of such abuse occurs in the area of products for individuals to
54 use, which is precisely where it is most unacceptable. Therefore, we
55 have designed this version of the GPL to prohibit the practice for those
56 products. If such problems arise substantially in other domains, we
57 stand ready to extend this provision to those domains in future versions
58 of the GPL, as needed to protect the freedom of users.
59
60 Finally, every program is threatened constantly by software patents.
61 States should not allow patents to restrict development and use of
62 software on general-purpose computers, but in those that do, we wish to
63 avoid the special danger that patents applied to a free program could
64 make it effectively proprietary. To prevent this, the GPL assures that
65 patents cannot be used to render the program non-free.
5566
5667 The precise terms and conditions for copying, distribution and
5768 modification follow.
58
59 GNU GENERAL PUBLIC LICENSE
60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
62 0. This License applies to any program or other work which contains
63 a notice placed by the copyright holder saying it may be distributed
64 under the terms of this General Public License. The "Program", below,
65 refers to any such program or work, and a "work based on the Program"
66 means either the Program or any derivative work under copyright law:
67 that is to say, a work containing the Program or a portion of it,
68 either verbatim or with modifications and/or translated into another
69 language. (Hereinafter, translation is included without limitation in
70 the term "modification".) Each licensee is addressed as "you".
71
72 Activities other than copying, distribution and modification are not
73 covered by this License; they are outside its scope. The act of
74 running the Program is not restricted, and the output from the Program
75 is covered only if its contents constitute a work based on the
76 Program (independent of having been made by running the Program).
77 Whether that is true depends on what the Program does.
78
79 1. You may copy and distribute verbatim copies of the Program's
80 source code as you receive it, in any medium, provided that you
81 conspicuously and appropriately publish on each copy an appropriate
82 copyright notice and disclaimer of warranty; keep intact all the
83 notices that refer to this License and to the absence of any warranty;
84 and give any other recipients of the Program a copy of this License
85 along with the Program.
86
87 You may charge a fee for the physical act of transferring a copy, and
88 you may at your option offer warranty protection in exchange for a fee.
89
90 2. You may modify your copy or copies of the Program or any portion
91 of it, thus forming a work based on the Program, and copy and
92 distribute such modifications or work under the terms of Section 1
93 above, provided that you also meet all of these conditions:
94
95 a) You must cause the modified files to carry prominent notices
96 stating that you changed the files and the date of any change.
97
98 b) You must cause any work that you distribute or publish, that in
99 whole or in part contains or is derived from the Program or any
100 part thereof, to be licensed as a whole at no charge to all third
101 parties under the terms of this License.
102
103 c) If the modified program normally reads commands interactively
104 when run, you must cause it, when started running for such
105 interactive use in the most ordinary way, to print or display an
106 announcement including an appropriate copyright notice and a
107 notice that there is no warranty (or else, saying that you provide
108 a warranty) and that users may redistribute the program under
109 these conditions, and telling the user how to view a copy of this
110 License. (Exception: if the Program itself is interactive but
111 does not normally print such an announcement, your work based on
112 the Program is not required to print an announcement.)
113
114 These requirements apply to the modified work as a whole. If
115 identifiable sections of that work are not derived from the Program,
116 and can be reasonably considered independent and separate works in
117 themselves, then this License, and its terms, do not apply to those
118 sections when you distribute them as separate works. But when you
119 distribute the same sections as part of a whole which is a work based
120 on the Program, the distribution of the whole must be on the terms of
121 this License, whose permissions for other licensees extend to the
122 entire whole, and thus to each and every part regardless of who wrote it.
123
124 Thus, it is not the intent of this section to claim rights or contest
125 your rights to work written entirely by you; rather, the intent is to
126 exercise the right to control the distribution of derivative or
127 collective works based on the Program.
128
129 In addition, mere aggregation of another work not based on the Program
130 with the Program (or with a work based on the Program) on a volume of
131 a storage or distribution medium does not bring the other work under
132 the scope of this License.
133
134 3. You may copy and distribute the Program (or a work based on it,
135 under Section 2) in object code or executable form under the terms of
136 Sections 1 and 2 above provided that you also do one of the following:
137
138 a) Accompany it with the complete corresponding machine-readable
139 source code, which must be distributed under the terms of Sections
140 1 and 2 above on a medium customarily used for software interchange; or,
141
142 b) Accompany it with a written offer, valid for at least three
143 years, to give any third party, for a charge no more than your
144 cost of physically performing source distribution, a complete
145 machine-readable copy of the corresponding source code, to be
146 distributed under the terms of Sections 1 and 2 above on a medium
147 customarily used for software interchange; or,
148
149 c) Accompany it with the information you received as to the offer
150 to distribute corresponding source code. (This alternative is
151 allowed only for noncommercial distribution and only if you
152 received the program in object code or executable form with such
153 an offer, in accord with Subsection b above.)
154
155 The source code for a work means the preferred form of the work for
156 making modifications to it. For an executable work, complete source
157 code means all the source code for all modules it contains, plus any
158 associated interface definition files, plus the scripts used to
159 control compilation and installation of the executable. However, as a
160 special exception, the source code distributed need not include
161 anything that is normally distributed (in either source or binary
162 form) with the major components (compiler, kernel, and so on) of the
163 operating system on which the executable runs, unless that component
164 itself accompanies the executable.
165
166 If distribution of executable or object code is made by offering
167 access to copy from a designated place, then offering equivalent
168 access to copy the source code from the same place counts as
169 distribution of the source code, even though third parties are not
170 compelled to copy the source along with the object code.
171
172 4. You may not copy, modify, sublicense, or distribute the Program
173 except as expressly provided under this License. Any attempt
174 otherwise to copy, modify, sublicense or distribute the Program is
175 void, and will automatically terminate your rights under this License.
176 However, parties who have received copies, or rights, from you under
177 this License will not have their licenses terminated so long as such
178 parties remain in full compliance.
179
180 5. You are not required to accept this License, since you have not
181 signed it. However, nothing else grants you permission to modify or
182 distribute the Program or its derivative works. These actions are
183 prohibited by law if you do not accept this License. Therefore, by
184 modifying or distributing the Program (or any work based on the
185 Program), you indicate your acceptance of this License to do so, and
186 all its terms and conditions for copying, distributing or modifying
187 the Program or works based on it.
188
189 6. Each time you redistribute the Program (or any work based on the
190 Program), the recipient automatically receives a license from the
191 original licensor to copy, distribute or modify the Program subject to
192 these terms and conditions. You may not impose any further
193 restrictions on the recipients' exercise of the rights granted herein.
194 You are not responsible for enforcing compliance by third parties to
69
70 TERMS AND CONDITIONS
71
72 0. Definitions.
73
74 "This License" refers to version 3 of the GNU General Public License.
75
76 "Copyright" also means copyright-like laws that apply to other kinds of
77 works, such as semiconductor masks.
78
79 "The Program" refers to any copyrightable work licensed under this
80 License. Each licensee is addressed as "you". "Licensees" and
81 "recipients" may be individuals or organizations.
82
83 To "modify" a work means to copy from or adapt all or part of the work
84 in a fashion requiring copyright permission, other than the making of an
85 exact copy. The resulting work is called a "modified version" of the
86 earlier work or a work "based on" the earlier work.
87
88 A "covered work" means either the unmodified Program or a work based
89 on the Program.
90
91 To "propagate" a work means to do anything with it that, without
92 permission, would make you directly or secondarily liable for
93 infringement under applicable copyright law, except executing it on a
94 computer or modifying a private copy. Propagation includes copying,
95 distribution (with or without modification), making available to the
96 public, and in some countries other activities as well.
97
98 To "convey" a work means any kind of propagation that enables other
99 parties to make or receive copies. Mere interaction with a user through
100 a computer network, with no transfer of a copy, is not conveying.
101
102 An interactive user interface displays "Appropriate Legal Notices"
103 to the extent that it includes a convenient and prominently visible
104 feature that (1) displays an appropriate copyright notice, and (2)
105 tells the user that there is no warranty for the work (except to the
106 extent that warranties are provided), that licensees may convey the
107 work under this License, and how to view a copy of this License. If
108 the interface presents a list of user commands or options, such as a
109 menu, a prominent item in the list meets this criterion.
110
111 1. Source Code.
112
113 The "source code" for a work means the preferred form of the work
114 for making modifications to it. "Object code" means any non-source
115 form of a work.
116
117 A "Standard Interface" means an interface that either is an official
118 standard defined by a recognized standards body, or, in the case of
119 interfaces specified for a particular programming language, one that
120 is widely used among developers working in that language.
121
122 The "System Libraries" of an executable work include anything, other
123 than the work as a whole, that (a) is included in the normal form of
124 packaging a Major Component, but which is not part of that Major
125 Component, and (b) serves only to enable use of the work with that
126 Major Component, or to implement a Standard Interface for which an
127 implementation is available to the public in source code form. A
128 "Major Component", in this context, means a major essential component
129 (kernel, window system, and so on) of the specific operating system
130 (if any) on which the executable work runs, or a compiler used to
131 produce the work, or an object code interpreter used to run it.
132
133 The "Corresponding Source" for a work in object code form means all
134 the source code needed to generate, install, and (for an executable
135 work) run the object code and to modify the work, including scripts to
136 control those activities. However, it does not include the work's
137 System Libraries, or general-purpose tools or generally available free
138 programs which are used unmodified in performing those activities but
139 which are not part of the work. For example, Corresponding Source
140 includes interface definition files associated with source files for
141 the work, and the source code for shared libraries and dynamically
142 linked subprograms that the work is specifically designed to require,
143 such as by intimate data communication or control flow between those
144 subprograms and other parts of the work.
145
146 The Corresponding Source need not include anything that users
147 can regenerate automatically from other parts of the Corresponding
148 Source.
149
150 The Corresponding Source for a work in source code form is that
151 same work.
152
153 2. Basic Permissions.
154
155 All rights granted under this License are granted for the term of
156 copyright on the Program, and are irrevocable provided the stated
157 conditions are met. This License explicitly affirms your unlimited
158 permission to run the unmodified Program. The output from running a
159 covered work is covered by this License only if the output, given its
160 content, constitutes a covered work. This License acknowledges your
161 rights of fair use or other equivalent, as provided by copyright law.
162
163 You may make, run and propagate covered works that you do not
164 convey, without conditions so long as your license otherwise remains
165 in force. You may convey covered works to others for the sole purpose
166 of having them make modifications exclusively for you, or provide you
167 with facilities for running those works, provided that you comply with
168 the terms of this License in conveying all material for which you do
169 not control copyright. Those thus making or running the covered works
170 for you must do so exclusively on your behalf, under your direction
171 and control, on terms that prohibit them from making any copies of
172 your copyrighted material outside their relationship with you.
173
174 Conveying under any other circumstances is permitted solely under
175 the conditions stated below. Sublicensing is not allowed; section 10
176 makes it unnecessary.
177
178 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
179
180 No covered work shall be deemed part of an effective technological
181 measure under any applicable law fulfilling obligations under article
182 11 of the WIPO copyright treaty adopted on 20 December 1996, or
183 similar laws prohibiting or restricting circumvention of such
184 measures.
185
186 When you convey a covered work, you waive any legal power to forbid
187 circumvention of technological measures to the extent such circumvention
188 is effected by exercising rights under this License with respect to
189 the covered work, and you disclaim any intention to limit operation or
190 modification of the work as a means of enforcing, against the work's
191 users, your or third parties' legal rights to forbid circumvention of
192 technological measures.
193
194 4. Conveying Verbatim Copies.
195
196 You may convey verbatim copies of the Program's source code as you
197 receive it, in any medium, provided that you conspicuously and
198 appropriately publish on each copy an appropriate copyright notice;
199 keep intact all notices stating that this License and any
200 non-permissive terms added in accord with section 7 apply to the code;
201 keep intact all notices of the absence of any warranty; and give all
202 recipients a copy of this License along with the Program.
203
204 You may charge any price or no price for each copy that you convey,
205 and you may offer support or warranty protection for a fee.
206
207 5. Conveying Modified Source Versions.
208
209 You may convey a work based on the Program, or the modifications to
210 produce it from the Program, in the form of source code under the
211 terms of section 4, provided that you also meet all of these conditions:
212
213 a) The work must carry prominent notices stating that you modified
214 it, and giving a relevant date.
215
216 b) The work must carry prominent notices stating that it is
217 released under this License and any conditions added under section
218 7. This requirement modifies the requirement in section 4 to
219 "keep intact all notices".
220
221 c) You must license the entire work, as a whole, under this
222 License to anyone who comes into possession of a copy. This
223 License will therefore apply, along with any applicable section 7
224 additional terms, to the whole of the work, and all its parts,
225 regardless of how they are packaged. This License gives no
226 permission to license the work in any other way, but it does not
227 invalidate such permission if you have separately received it.
228
229 d) If the work has interactive user interfaces, each must display
230 Appropriate Legal Notices; however, if the Program has interactive
231 interfaces that do not display Appropriate Legal Notices, your
232 work need not make them do so.
233
234 A compilation of a covered work with other separate and independent
235 works, which are not by their nature extensions of the covered work,
236 and which are not combined with it such as to form a larger program,
237 in or on a volume of a storage or distribution medium, is called an
238 "aggregate" if the compilation and its resulting copyright are not
239 used to limit the access or legal rights of the compilation's users
240 beyond what the individual works permit. Inclusion of a covered work
241 in an aggregate does not cause this License to apply to the other
242 parts of the aggregate.
243
244 6. Conveying Non-Source Forms.
245
246 You may convey a covered work in object code form under the terms
247 of sections 4 and 5, provided that you also convey the
248 machine-readable Corresponding Source under the terms of this License,
249 in one of these ways:
250
251 a) Convey the object code in, or embodied in, a physical product
252 (including a physical distribution medium), accompanied by the
253 Corresponding Source fixed on a durable physical medium
254 customarily used for software interchange.
255
256 b) Convey the object code in, or embodied in, a physical product
257 (including a physical distribution medium), accompanied by a
258 written offer, valid for at least three years and valid for as
259 long as you offer spare parts or customer support for that product
260 model, to give anyone who possesses the object code either (1) a
261 copy of the Corresponding Source for all the software in the
262 product that is covered by this License, on a durable physical
263 medium customarily used for software interchange, for a price no
264 more than your reasonable cost of physically performing this
265 conveying of source, or (2) access to copy the
266 Corresponding Source from a network server at no charge.
267
268 c) Convey individual copies of the object code with a copy of the
269 written offer to provide the Corresponding Source. This
270 alternative is allowed only occasionally and noncommercially, and
271 only if you received the object code with such an offer, in accord
272 with subsection 6b.
273
274 d) Convey the object code by offering access from a designated
275 place (gratis or for a charge), and offer equivalent access to the
276 Corresponding Source in the same way through the same place at no
277 further charge. You need not require recipients to copy the
278 Corresponding Source along with the object code. If the place to
279 copy the object code is a network server, the Corresponding Source
280 may be on a different server (operated by you or a third party)
281 that supports equivalent copying facilities, provided you maintain
282 clear directions next to the object code saying where to find the
283 Corresponding Source. Regardless of what server hosts the
284 Corresponding Source, you remain obligated to ensure that it is
285 available for as long as needed to satisfy these requirements.
286
287 e) Convey the object code using peer-to-peer transmission, provided
288 you inform other peers where the object code and Corresponding
289 Source of the work are being offered to the general public at no
290 charge under subsection 6d.
291
292 A separable portion of the object code, whose source code is excluded
293 from the Corresponding Source as a System Library, need not be
294 included in conveying the object code work.
295
296 A "User Product" is either (1) a "consumer product", which means any
297 tangible personal property which is normally used for personal, family,
298 or household purposes, or (2) anything designed or sold for incorporation
299 into a dwelling. In determining whether a product is a consumer product,
300 doubtful cases shall be resolved in favor of coverage. For a particular
301 product received by a particular user, "normally used" refers to a
302 typical or common use of that class of product, regardless of the status
303 of the particular user or of the way in which the particular user
304 actually uses, or expects or is expected to use, the product. A product
305 is a consumer product regardless of whether the product has substantial
306 commercial, industrial or non-consumer uses, unless such uses represent
307 the only significant mode of use of the product.
308
309 "Installation Information" for a User Product means any methods,
310 procedures, authorization keys, or other information required to install
311 and execute modified versions of a covered work in that User Product from
312 a modified version of its Corresponding Source. The information must
313 suffice to ensure that the continued functioning of the modified object
314 code is in no case prevented or interfered with solely because
315 modification has been made.
316
317 If you convey an object code work under this section in, or with, or
318 specifically for use in, a User Product, and the conveying occurs as
319 part of a transaction in which the right of possession and use of the
320 User Product is transferred to the recipient in perpetuity or for a
321 fixed term (regardless of how the transaction is characterized), the
322 Corresponding Source conveyed under this section must be accompanied
323 by the Installation Information. But this requirement does not apply
324 if neither you nor any third party retains the ability to install
325 modified object code on the User Product (for example, the work has
326 been installed in ROM).
327
328 The requirement to provide Installation Information does not include a
329 requirement to continue to provide support service, warranty, or updates
330 for a work that has been modified or installed by the recipient, or for
331 the User Product in which it has been modified or installed. Access to a
332 network may be denied when the modification itself materially and
333 adversely affects the operation of the network or violates the rules and
334 protocols for communication across the network.
335
336 Corresponding Source conveyed, and Installation Information provided,
337 in accord with this section must be in a format that is publicly
338 documented (and with an implementation available to the public in
339 source code form), and must require no special password or key for
340 unpacking, reading or copying.
341
342 7. Additional Terms.
343
344 "Additional permissions" are terms that supplement the terms of this
345 License by making exceptions from one or more of its conditions.
346 Additional permissions that are applicable to the entire Program shall
347 be treated as though they were included in this License, to the extent
348 that they are valid under applicable law. If additional permissions
349 apply only to part of the Program, that part may be used separately
350 under those permissions, but the entire Program remains governed by
351 this License without regard to the additional permissions.
352
353 When you convey a copy of a covered work, you may at your option
354 remove any additional permissions from that copy, or from any part of
355 it. (Additional permissions may be written to require their own
356 removal in certain cases when you modify the work.) You may place
357 additional permissions on material, added by you to a covered work,
358 for which you have or can give appropriate copyright permission.
359
360 Notwithstanding any other provision of this License, for material you
361 add to a covered work, you may (if authorized by the copyright holders of
362 that material) supplement the terms of this License with terms:
363
364 a) Disclaiming warranty or limiting liability differently from the
365 terms of sections 15 and 16 of this License; or
366
367 b) Requiring preservation of specified reasonable legal notices or
368 author attributions in that material or in the Appropriate Legal
369 Notices displayed by works containing it; or
370
371 c) Prohibiting misrepresentation of the origin of that material, or
372 requiring that modified versions of such material be marked in
373 reasonable ways as different from the original version; or
374
375 d) Limiting the use for publicity purposes of names of licensors or
376 authors of the material; or
377
378 e) Declining to grant rights under trademark law for use of some
379 trade names, trademarks, or service marks; or
380
381 f) Requiring indemnification of licensors and authors of that
382 material by anyone who conveys the material (or modified versions of
383 it) with contractual assumptions of liability to the recipient, for
384 any liability that these contractual assumptions directly impose on
385 those licensors and authors.
386
387 All other non-permissive additional terms are considered "further
388 restrictions" within the meaning of section 10. If the Program as you
389 received it, or any part of it, contains a notice stating that it is
390 governed by this License along with a term that is a further
391 restriction, you may remove that term. If a license document contains
392 a further restriction but permits relicensing or conveying under this
393 License, you may add to a covered work material governed by the terms
394 of that license document, provided that the further restriction does
395 not survive such relicensing or conveying.
396
397 If you add terms to a covered work in accord with this section, you
398 must place, in the relevant source files, a statement of the
399 additional terms that apply to those files, or a notice indicating
400 where to find the applicable terms.
401
402 Additional terms, permissive or non-permissive, may be stated in the
403 form of a separately written license, or stated as exceptions;
404 the above requirements apply either way.
405
406 8. Termination.
407
408 You may not propagate or modify a covered work except as expressly
409 provided under this License. Any attempt otherwise to propagate or
410 modify it is void, and will automatically terminate your rights under
411 this License (including any patent licenses granted under the third
412 paragraph of section 11).
413
414 However, if you cease all violation of this License, then your
415 license from a particular copyright holder is reinstated (a)
416 provisionally, unless and until the copyright holder explicitly and
417 finally terminates your license, and (b) permanently, if the copyright
418 holder fails to notify you of the violation by some reasonable means
419 prior to 60 days after the cessation.
420
421 Moreover, your license from a particular copyright holder is
422 reinstated permanently if the copyright holder notifies you of the
423 violation by some reasonable means, this is the first time you have
424 received notice of violation of this License (for any work) from that
425 copyright holder, and you cure the violation prior to 30 days after
426 your receipt of the notice.
427
428 Termination of your rights under this section does not terminate the
429 licenses of parties who have received copies or rights from you under
430 this License. If your rights have been terminated and not permanently
431 reinstated, you do not qualify to receive new licenses for the same
432 material under section 10.
433
434 9. Acceptance Not Required for Having Copies.
435
436 You are not required to accept this License in order to receive or
437 run a copy of the Program. Ancillary propagation of a covered work
438 occurring solely as a consequence of using peer-to-peer transmission
439 to receive a copy likewise does not require acceptance. However,
440 nothing other than this License grants you permission to propagate or
441 modify any covered work. These actions infringe copyright if you do
442 not accept this License. Therefore, by modifying or propagating a
443 covered work, you indicate your acceptance of this License to do so.
444
445 10. Automatic Licensing of Downstream Recipients.
446
447 Each time you convey a covered work, the recipient automatically
448 receives a license from the original licensors, to run, modify and
449 propagate that work, subject to this License. You are not responsible
450 for enforcing compliance by third parties with this License.
451
452 An "entity transaction" is a transaction transferring control of an
453 organization, or substantially all assets of one, or subdividing an
454 organization, or merging organizations. If propagation of a covered
455 work results from an entity transaction, each party to that
456 transaction who receives a copy of the work also receives whatever
457 licenses to the work the party's predecessor in interest had or could
458 give under the previous paragraph, plus a right to possession of the
459 Corresponding Source of the work from the predecessor in interest, if
460 the predecessor has it or can get it with reasonable efforts.
461
462 You may not impose any further restrictions on the exercise of the
463 rights granted or affirmed under this License. For example, you may
464 not impose a license fee, royalty, or other charge for exercise of
465 rights granted under this License, and you may not initiate litigation
466 (including a cross-claim or counterclaim in a lawsuit) alleging that
467 any patent claim is infringed by making, using, selling, offering for
468 sale, or importing the Program or any portion of it.
469
470 11. Patents.
471
472 A "contributor" is a copyright holder who authorizes use under this
473 License of the Program or a work on which the Program is based. The
474 work thus licensed is called the contributor's "contributor version".
475
476 A contributor's "essential patent claims" are all patent claims
477 owned or controlled by the contributor, whether already acquired or
478 hereafter acquired, that would be infringed by some manner, permitted
479 by this License, of making, using, or selling its contributor version,
480 but do not include claims that would be infringed only as a
481 consequence of further modification of the contributor version. For
482 purposes of this definition, "control" includes the right to grant
483 patent sublicenses in a manner consistent with the requirements of
195484 this License.
196485
197 7. If, as a consequence of a court judgment or allegation of patent
198 infringement or for any other reason (not limited to patent issues),
199 conditions are imposed on you (whether by court order, agreement or
486 Each contributor grants you a non-exclusive, worldwide, royalty-free
487 patent license under the contributor's essential patent claims, to
488 make, use, sell, offer for sale, import and otherwise run, modify and
489 propagate the contents of its contributor version.
490
491 In the following three paragraphs, a "patent license" is any express
492 agreement or commitment, however denominated, not to enforce a patent
493 (such as an express permission to practice a patent or covenant not to
494 sue for patent infringement). To "grant" such a patent license to a
495 party means to make such an agreement or commitment not to enforce a
496 patent against the party.
497
498 If you convey a covered work, knowingly relying on a patent license,
499 and the Corresponding Source of the work is not available for anyone
500 to copy, free of charge and under the terms of this License, through a
501 publicly available network server or other readily accessible means,
502 then you must either (1) cause the Corresponding Source to be so
503 available, or (2) arrange to deprive yourself of the benefit of the
504 patent license for this particular work, or (3) arrange, in a manner
505 consistent with the requirements of this License, to extend the patent
506 license to downstream recipients. "Knowingly relying" means you have
507 actual knowledge that, but for the patent license, your conveying the
508 covered work in a country, or your recipient's use of the covered work
509 in a country, would infringe one or more identifiable patents in that
510 country that you have reason to believe are valid.
511
512 If, pursuant to or in connection with a single transaction or
513 arrangement, you convey, or propagate by procuring conveyance of, a
514 covered work, and grant a patent license to some of the parties
515 receiving the covered work authorizing them to use, propagate, modify
516 or convey a specific copy of the covered work, then the patent license
517 you grant is automatically extended to all recipients of the covered
518 work and works based on it.
519
520 A patent license is "discriminatory" if it does not include within
521 the scope of its coverage, prohibits the exercise of, or is
522 conditioned on the non-exercise of one or more of the rights that are
523 specifically granted under this License. You may not convey a covered
524 work if you are a party to an arrangement with a third party that is
525 in the business of distributing software, under which you make payment
526 to the third party based on the extent of your activity of conveying
527 the work, and under which the third party grants, to any of the
528 parties who would receive the covered work from you, a discriminatory
529 patent license (a) in connection with copies of the covered work
530 conveyed by you (or copies made from those copies), or (b) primarily
531 for and in connection with specific products or compilations that
532 contain the covered work, unless you entered into that arrangement,
533 or that patent license was granted, prior to 28 March 2007.
534
535 Nothing in this License shall be construed as excluding or limiting
536 any implied license or other defenses to infringement that may
537 otherwise be available to you under applicable patent law.
538
539 12. No Surrender of Others' Freedom.
540
541 If conditions are imposed on you (whether by court order, agreement or
200542 otherwise) that contradict the conditions of this License, they do not
201 excuse you from the conditions of this License. If you cannot
202 distribute so as to satisfy simultaneously your obligations under this
203 License and any other pertinent obligations, then as a consequence you
204 may not distribute the Program at all. For example, if a patent
205 license would not permit royalty-free redistribution of the Program by
206 all those who receive copies directly or indirectly through you, then
207 the only way you could satisfy both it and this License would be to
208 refrain entirely from distribution of the Program.
209
210 If any portion of this section is held invalid or unenforceable under
211 any particular circumstance, the balance of the section is intended to
212 apply and the section as a whole is intended to apply in other
213 circumstances.
214
215 It is not the purpose of this section to induce you to infringe any
216 patents or other property right claims or to contest validity of any
217 such claims; this section has the sole purpose of protecting the
218 integrity of the free software distribution system, which is
219 implemented by public license practices. Many people have made
220 generous contributions to the wide range of software distributed
221 through that system in reliance on consistent application of that
222 system; it is up to the author/donor to decide if he or she is willing
223 to distribute software through any other system and a licensee cannot
224 impose that choice.
225
226 This section is intended to make thoroughly clear what is believed to
227 be a consequence of the rest of this License.
228
229 8. If the distribution and/or use of the Program is restricted in
230 certain countries either by patents or by copyrighted interfaces, the
231 original copyright holder who places the Program under this License
232 may add an explicit geographical distribution limitation excluding
233 those countries, so that distribution is permitted only in or among
234 countries not thus excluded. In such case, this License incorporates
235 the limitation as if written in the body of this License.
236
237 9. The Free Software Foundation may publish revised and/or new versions
238 of the General Public License from time to time. Such new versions will
543 excuse you from the conditions of this License. If you cannot convey a
544 covered work so as to satisfy simultaneously your obligations under this
545 License and any other pertinent obligations, then as a consequence you may
546 not convey it at all. For example, if you agree to terms that obligate you
547 to collect a royalty for further conveying from those to whom you convey
548 the Program, the only way you could satisfy both those terms and this
549 License would be to refrain entirely from conveying the Program.
550
551 13. Use with the GNU Affero General Public License.
552
553 Notwithstanding any other provision of this License, you have
554 permission to link or combine any covered work with a work licensed
555 under version 3 of the GNU Affero General Public License into a single
556 combined work, and to convey the resulting work. The terms of this
557 License will continue to apply to the part which is the covered work,
558 but the special requirements of the GNU Affero General Public License,
559 section 13, concerning interaction through a network will apply to the
560 combination as such.
561
562 14. Revised Versions of this License.
563
564 The Free Software Foundation may publish revised and/or new versions of
565 the GNU General Public License from time to time. Such new versions will
239566 be similar in spirit to the present version, but may differ in detail to
240567 address new problems or concerns.
241568
242 Each version is given a distinguishing version number. If the Program
243 specifies a version number of this License which applies to it and "any
244 later version", you have the option of following the terms and conditions
245 either of that version or of any later version published by the Free
246 Software Foundation. If the Program does not specify a version number of
247 this License, you may choose any version ever published by the Free Software
248 Foundation.
249
250 10. If you wish to incorporate parts of the Program into other free
251 programs whose distribution conditions are different, write to the author
252 to ask for permission. For software which is copyrighted by the Free
253 Software Foundation, write to the Free Software Foundation; we sometimes
254 make exceptions for this. Our decision will be guided by the two goals
255 of preserving the free status of all derivatives of our free software and
256 of promoting the sharing and reuse of software generally.
257
258 NO WARRANTY
259
260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 REPAIR OR CORRECTION.
269
270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 POSSIBILITY OF SUCH DAMAGES.
569 Each version is given a distinguishing version number. If the
570 Program specifies that a certain numbered version of the GNU General
571 Public License "or any later version" applies to it, you have the
572 option of following the terms and conditions either of that numbered
573 version or of any later version published by the Free Software
574 Foundation. If the Program does not specify a version number of the
575 GNU General Public License, you may choose any version ever published
576 by the Free Software Foundation.
577
578 If the Program specifies that a proxy can decide which future
579 versions of the GNU General Public License can be used, that proxy's
580 public statement of acceptance of a version permanently authorizes you
581 to choose that version for the Program.
582
583 Later license versions may give you additional or different
584 permissions. However, no additional obligations are imposed on any
585 author or copyright holder as a result of your choosing to follow a
586 later version.
587
588 15. Disclaimer of Warranty.
589
590 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
591 APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
592 HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
593 OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
594 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
595 PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
596 IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
597 ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
598
599 16. Limitation of Liability.
600
601 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
602 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
603 THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
604 GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
605 USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
606 DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
607 PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
608 EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
609 SUCH DAMAGES.
610
611 17. Interpretation of Sections 15 and 16.
612
613 If the disclaimer of warranty and limitation of liability provided
614 above cannot be given local legal effect according to their terms,
615 reviewing courts shall apply local law that most closely approximates
616 an absolute waiver of all civil liability in connection with the
617 Program, unless a warranty or assumption of liability accompanies a
618 copy of the Program in return for a fee.
279619
280620 END OF TERMS AND CONDITIONS
281
621
282622 How to Apply These Terms to Your New Programs
283623
284624 If you develop a new program, and you want it to be of the greatest
287627
288628 To do so, attach the following notices to the program. It is safest
289629 to attach them to the start of each source file to most effectively
290 convey the exclusion of warranty; and each file should have at least
630 state the exclusion of warranty; and each file should have at least
291631 the "copyright" line and a pointer to where the full notice is found.
292632
293633 <one line to give the program's name and a brief idea of what it does.>
294 Copyright (C) 19yy <name of author>
295
296 This program is free software; you can redistribute it and/or modify
634 Copyright (C) <year> <name of author>
635
636 This program is free software: you can redistribute it and/or modify
297637 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or
638 the Free Software Foundation, either version 3 of the License, or
299639 (at your option) any later version.
300640
301641 This program is distributed in the hope that it will be useful,
304644 GNU General Public License for more details.
305645
306646 You should have received a copy of the GNU General Public License
307 along with this program; if not, write to the Free Software
308 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309
647 along with this program. If not, see <http://www.gnu.org/licenses/>.
310648
311649 Also add information on how to contact you by electronic and paper mail.
312650
313 If the program is interactive, make it output a short notice like this
314 when it starts in an interactive mode:
315
316 Gnomovision version 69, Copyright (C) 19yy name of author
317 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
651 If the program does terminal interaction, make it output a short
652 notice like this when it starts in an interactive mode:
653
654 <program> Copyright (C) <year> <name of author>
655 This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318656 This is free software, and you are welcome to redistribute it
319657 under certain conditions; type `show c' for details.
320658
321659 The hypothetical commands `show w' and `show c' should show the appropriate
322 parts of the General Public License. Of course, the commands you use may
323 be called something other than `show w' and `show c'; they could even be
324 mouse-clicks or menu items--whatever suits your program.
325
326 You should also get your employer (if you work as a programmer) or your
327 school, if any, to sign a "copyright disclaimer" for the program, if
328 necessary. Here is a sample; alter the names:
329
330 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331 `Gnomovision' (which makes passes at compilers) written by James Hacker.
332
333 <signature of Ty Coon>, 1 April 1989
334 Ty Coon, President of Vice
335
336 This General Public License does not permit incorporating your program into
337 proprietary programs. If your program is a subroutine library, you may
338 consider it more useful to permit linking proprietary applications with the
339 library. If this is what you want to do, use the GNU Library General
340 Public License instead of this License.
341
660 parts of the General Public License. Of course, your program's commands
661 might be different; for a GUI interface, you would use an "about box".
662
663 You should also get your employer (if you work as a programmer) or school,
664 if any, to sign a "copyright disclaimer" for the program, if necessary.
665 For more information on this, and how to apply and follow the GNU GPL, see
666 <http://www.gnu.org/licenses/>.
667
668 The GNU General Public License does not permit incorporating your program
669 into proprietary programs. If your program is a subroutine library, you
670 may consider it more useful to permit linking proprietary applications with
671 the library. If this is what you want to do, use the GNU Lesser General
672 Public License instead of this License. But first, please read
673 <http://www.gnu.org/philosophy/why-not-lgpl.html>.
0 =============================================================================
1 Dillo project
2 =============================================================================
3
4 dillo-3.0 [not released yet]
5
6 +- Ported Dillo to FLTK-1.3.
7 Patch: corvid, Johannes Hofmann, Jorge Arellano Cid
8 +- Default binding for close-all changed from Alt-q to Ctrl-q.
9 - Default binding for close-tab changed from Ctrl-q to Ctrl-w.
10 - Add right_click_closes_tab preference (default is middle click).
11 - 'hide-panels' key action now hides the findbar if present, and toggles
12 display of the control panels otherwise.
13 - Remove 'fullscreen' key action.
14 Patches: Jorge Arellano Cid
15 +- Remove --enable-ansi configure option.
16 - Limit saved cookie size.
17 - Allow binding to non-ASCII keys and multimedia keys.
18 - Enable line wrapping for <textarea>. (BUG#903)
19 Patches: corvid
20
21 -----------------------------------------------------------------------------
22
23 dillo-2.2.1 [not released yet]
24
25 +- Implemented "View source" as a dpi.
26 - Accept application/xhtml+xml.
27 - Small caps support.
28 - Border-collapse, border-style properties.
29 Patches: Jorge Arellano Cid
30 +- Configurable User-Agent HTTP header.
31 Patch: Alexander Voigt, corvid
32 +- Include Accept header in HTTP queries.
33 - Work with libpng-1.4.
34 - Handle zero-width space.
35 - Fix segfault closing window from WM.
36 - Limit total number of cookies.
37 - Use the suffix/subdomain field in cookies.txt.
38 - Follow most specific matching rule in cookiesrc.
39 - Fix segfault with form inputs and repush for stylesheets.
40 - Handle white-space: pre-wrap and pre-line.
41 - Support for the word-spacing property.
42 - Fix segfault with https and self-signed certificates.
43 - Text-indent property.
44 Patches: corvid
45 +- Reintroduce bg_color dillorc option.
46 - Make Dillo compile with Clang.
47 - Fix Textblock flushing.
48 - Support !important in style attributes.
49 Patches: Johannes Hofmann
50 +- Implement line-height.
51 - Draw image maps when image not loaded.
52 Patches: Johannes Hofmann, corvid
53 +- Support @media rules.
54 - Implement media-conditional @import rules.
55 - Configure/Makefile cleanup.
56 - Fix meta refresh looping.
57 Patches: Jeremy Henty
58
59 -----------------------------------------------------------------------------
60
61 dillo-2.2 [Feb 11, 2010]
62
63 +- Added keybindings for scrolling.
64 - Help button and local help file.
65 Patches: corvid, Jorge Arellano Cid
66 +- Add support for multiple class names in CSS.
67 - Fix X11 coordinate overflows with huge borders.
68 - Improve CSS font parsing.
69 - Enable font face setting via <font> element.
70 - Ignore XML comment markers in CSS.
71 - Split up long lines in plain.cc to avoid X11 coordinate overflows.
72 - Fix user agent style for nested <ul>.
73 - Add support for CSS property list-style-position.
74 - Support border-width: thin | medium | thick.
75 - Fix CSS_SHORTHAND_DIRECTIONS case in CssParser.
76 - Add quirk to reset font properties in tables (fixes e.g. gmail).
77 Patches: Johannes Hofmann
78 +- Cleaned up system includes in dpid directory.
79 - Fixed CustProgressBox() for systems without weak symbols.
80 - Handle signed chars. Added dIsspace() and dIsalnum() to dlib.
81 - Added a_Dpip_get_attr_l() to DPIP's API.
82 - Changed the CCCs to build in one step (for both HTTP and DPI). This
83 is simpler and helps to avoid race conditions.
84 - Updated CCCwork.txt to the new scheme.
85 - Fixed a bug with OPTION element (it was parsing entities twice).
86 - Bugfix: remove the empty cache entry lingering after connection abort.
87 - Switched capi to use dlib's Dlist instead of a_List_* methods.
88 - Remove empty cache entries on Stop-button press and new link request!
89 - Fixed URL unescaping in the datauri DPI.
90 - Changed and reimplemented the DPI API.
91 * Fixed bugs and updated all DPI programs:
92 * Reimplemented the file dpi using select(). No pthreads-based anymore.
93 * Fixed ftp dpi: downloads, streamed transfer, error feedback.
94 * Fixed a bug in dillo with lingering cache entries.
95 * Made dpidc a C language program.
96 * Made the internal dsh implementation use unique functions for read/write.
97 * Removed the write/fwrite mix in DPIP.
98 * Made the DPIP API token-based. Packet assembling is coded inside DPIP!
99 * Several cleanups and more error handling sprinkled all over too.
100 Patches: Jorge Arellano Cid
101 +- Fix segfault from AREA when MAP is missing name attribute.
102 - Fix image map coordinates when margin/border/padding present.
103 - Handle stylesheet @charset.
104 - Fix cache segfault when cache entry removed.
105 - Split words that contain whitespace as numeric character references.
106 - Allow linebreaks around Chinese/Japanese characters.
107 - Fix segfault in Html_parse_doctype (BUG#918).
108 - Change exit code used for bad command line argument.
109 - By default, do not use proxy for localhost (BUG 921).
110 - Fix scrolling for text search.
111 - Added 'save' key action (not bound by default).
112 - Tooltips
113 - Fix segfault when radio button lacks name attribute.
114 - Enable popup menu below bottom of page content (BUG#856).
115 - Handle JPEGs with CMYK color space.
116 - Allow keysyms in keysrc.
117 - Explicitly check installation bindir for dpid (BUG 930)
118 - General cookies overhaul.
119 Patches: corvid
120 +- Support for the letter-spacing property.
121 Patch: Johannes Hofmann, corvid
122 +- Fixed a bug in w3c_mode. In fact it wasn't working at all.
123 - Improve stylesheet menu.
124 Patches: Jeremy Henty
125 +- Limit number of simultaneous connections (BUG 685).
126 Patch: Johannes Hofmann, Jorge Arellano Cid
127
128 -----------------------------------------------------------------------------
129
130 dillo-2.1.1 [Jul 3, 2009]
131
132 +- Add additional size checks for images.
133 Patch: Jorge Arellano Cid, Johannes Hofmann, corvid
134 +- Fixed a bug in parsing RGB color values (CSS).
135 - Added support for css colors of the form rgb(255, 255, 255).
136 - Assert that SimpleVector size is positive.
137 Patches: Johannes Hofmann
138 +- Removed redundant system includes.
139 - Added the "nop" keybinding (nop = NO_OPERATION; cancels a default hook).
140 - Added 'stop' key action (not bound by default).
141 - Fixed segfault when URL is NULL and dpis can't be found.
142 Patches: place (AKA corvid)
143 +- Reduced 'warning: ignoring return value of ...'
144 Patch: Michal Nowak, Jorge Arellano Cid
145 +- Check chdir() return code in Paths::init.
146 - Removed return from a_Nav_unref_buf()
147 - Do not build proto.c (file is empty); GCC warning
148 Patches: Michal Nowak
149
150 -----------------------------------------------------------------------------
151
152 dillo-2.1 [Jun 15, 2009]
153
154 +- Added ipv6 addresses iteration and ipv4 fallback.
155 Patch: James Turner, Jorge Arellano Cid
156 +- Added support for numeric IPv6 addresses entered into the url bar.
157 - Made the DNS resolver report in numeric address notation.
158 - Used the URL authority part instead of stripped default port in HTTP query.
159 - Fixed Bookmarks modify's HTML so it wraps nicely on handhelds.
160 Patches: Justus Winter
161 +- Implemented "search previous" in string searches.
162 Patch: João Ricardo Lourenço
163 +- Fix for file inputs without values (forms).
164 - Tuned input width a bit.
165 - Cleaned up resource embedding (forms)
166 - Made cookierc parsing more robust.
167 - Switched a_UIcmd_save() to take its URL from history (not location bar).
168 - Set prefs.vw_fontname as default font for the UI.
169 - Fix: recover page focus when clicking outside of a widget.
170 - Fixed a segfault bug in the test/ directory.
171 - Set middle click to submit in a new TAB. (Helps to keep form data!)
172 - Added support for the Q element. BUG#343
173 - Cleaned up Html_pop_tag().
174 - Ported the command line interface from dillo1
175 - Switched file dpi error messages to HTML.
176 - Added a right-click menu to form controls (show hiddens, submit, reset)
177 - Remove now-redundant generate_submit pref
178 - Added the "http_language" dillorc option for setting HTTP's Accept-Language.
179 - Refactored prefs.c to a much smaller size!
180 - Fixed a SEGFAULT bug on redirections without Location.
181 - Obey SELECT's size attribute.
182 - Replace image loading button and page menu option with a tools menu option.
183 - Implemented the "overline" text-decoration.
184 - Enhanced and cleaned up text decorations for SUB and SUP.
185 - Added "View Stylesheets" to the page menu.
186 - Remove standard_widget_colors dillorc option.
187 - Added dillo(1) man page.
188 - Proxy support for HTTPS.
189 - System config files have moved to sysconfdir/dillo/
190 - Add keysrc.
191 Patches: place (AKA corvid)
192 +- Switched SSL-enabled to configure.in (./configure --enable-ssl).
193 - Standardised the installation of dpid/dpidrc with auto* tools.
194 - Set the ScrollGroup as the resizable widget in downloads dpi.
195 - Cleaned up and normalized D_SUN_LEN usage.
196 - Fixed incorrect use of VOIDP2INT in Dialog_user_password_cb().
197 - Ensure that the dlib dStr* functions are used everywhere.
198 - Fixed a memory leak in Html_tag_open_link().
199 - Fixed a memory leak in Klist().
200 - Fix the comment for DLWin::del() (dpi/downloads.cc).
201 - Removed redundant caller NULL checks already in the API.
202 Patches: Jeremy Henty
203 +- Implemented Basic authentication!
204 Patch: Jeremy Henty, Jorge Arellano Cid
205 +- Added "-fno-rtti -fno-exceptions" to CXXFLAGS (reduces binary size).
206 Patch: Jorge Arellano Cid, place (AKA corvid)
207 +- Allowed compilation with older machines by removing a few C99isms.
208 - Added use of inttypes.h when stdint.h isn't found.
209 Patches: Dan Fandrich
210 +- Reduced warnings with gcc-4.3.
211 Patch: Thomas Orgis
212 +- Made the parser recognize "[^ ]/>"-terminated XML elements.
213 - Implemented basic CSS infrastructure.
214 - Brought in Sebastian's CSS parser from dillo-0.8.0-css-3.
215 - Read user style from ~/.dillo/style.css.
216 - Added support for descendant and child selectors.
217 - Improved CSS selector matching performance using hash tables.
218 - Support selector specificity.
219 - Add support for font-size and font-weight enum values.
220 - Added "font_max_size", "font_min_size" dillorc options.
221 - Add workaround for fltk bug #2062.
222 - Reduce number of styleEngine::style0() calls.
223 - Replace bg_color dillorc option.
224 - Remove text_color, link_color, and force_my_colors dillorc options.
225 - Fix CSS string parsing bug.
226 - Replace visited_color dillorc option.
227 - Add support for negative numbers in CSS parser.
228 - Fix allow_white_bg dillorc option.
229 - Load <style></style> content only if applicable.
230 - Allow negative values for specific CSS properties only.
231 - Disable negative margins for now as dw/* does not support them yet.
232 - Support CSS @import directive.
233 - Disable form widgets while stylesheets are loading.
234 - Fix image scaling on reload with border, margin, or padding > 0.
235 - Implement --xid command line option (used by claws mail client).
236 - Make tab expansion in plain text utf8 aware.
237 Patches: Johannes Hofmann
238 +- Updated the GPL copyright note in the source files.
239 Patch: Detlef Riekenberg
240 +- Implemented a close-tab button for the GUI.
241 Patch: João Ricardo Lourenço, Jorge Arellano Cid
242 +- Added the "middle_click_drags_page" dillorc option.
243 Patch: Jorge Arellano Cid, Thomas Orgis
244 +- Added configurable keybindings! (in ~/.dillo/keysrc)
245 Patch: Jorge Arellano Cid, Tim Nieradzik, place (AKA corvid)
246 +- Fixed a memory leak with DilloImage structures.
247 Patch: Johannes Hofmann, place (AKA corvid)
248 +- Set the File menu label to hide when the File menu-button is shown.
249 - Set a new iconv() test in configure.in.
250 - Allowed the rc parser to skip whitespace around the equal sign.
251 - Fixed the parser not to call Html_tag_close_* functions twice.
252 - Implemented loading of remote CSS Stylesheet.
253 - Made a big cleanup of cache.c WRT charset decoding (fixes bugs).
254 - Made an extensive cleanup/fixup of the whole image handling process.
255 - Implemented the tools button with a couple CSS options.
256 - Removed the nav.h dependency from html.cc
257 - Made the repush() operation more general and suited for CSS use.
258 - Fixed collapsing of whitespace entities in HTML mode.
259 - Updated the URL resolver to comply with RFC-3986.
260 - Fixed handling of META's content-type with no MIME type (e.g. only charset).
261 - Added support for a quoted URL in META refresh.
262 - Added instant client-side redirects (aka. zero-delay META refresh).
263 Patches: Jorge Arellano Cid
264
265 dw
266
267 +- Moved clicked from ButtonResource to Resource.
268 Patch: place (AKA corvid)
269 +- Cleaned up unused code in fltkviewbase.
270 Patch: Johannes Hofmann
271 +- Added lout/msg.h and normalized debug messages to use it.
272 Patch: Jorge Arellano Cid
273
274 -----------------------------------------------------------------------------
275
276 dillo-2.0 [Oct 14, 2008]
277
278 +- Ported Dillo from GTK1 to FLTK2.
279 - Ported a susbstantial part of the code from C to C++ (FLTK2 is in C++).
280 - Wrote a new library: Dlib. With "Dlib" Dillo doesn't need glib anymore.
281 - Ported all the code to Dlib.
282 - Fixed Http_must_use_proxy() to be case insensitive.
283 - Fixed some leaks and bugs in the cookies dpi.
284 - Made Dillo's UI Control Panel resizable on-the-fly.
285 - Implemented a new, simpler, dillorc parser.
286 - Added handling of "localhost" in file URIs.
287 - Fix: recognize "http://foo" and "http://foo/" as the same URL (BUG#497).
288 - Reimplemented the Concomitant Callback chains into a uniform scheme!
289 (two query branches and a single answer branch). It simplifies a lot the
290 former CCC paths and allows for easier error control.
291 - Added a new method for internally-generated urls: a_Cache_entry_inject().
292 - Switched the cache to use Dlib's Dstr for its data storage.
293 - Removed threads from IO. Now it only uses select-based watches.
294 - Reimplemented IO.c and dpi.c to use Dlib's Dstr as its main buffer.
295 - Turned Klist into a sorted list.
296 - Removed one data-copy stage in Html_write_raw().
297 - Switched gcc's "fmt..." syntax to ISO C __VA_ARGS__.
298 - Fixed Dillo and its dpis to work from "/tmp" (for easy device unmount).
299 - Simplified http.c by reusing the new non-blocking writes in IO.
300 - Reworked the capi API so cache is only accessable from capi.
301 - Rewrote the CCC's OpAbort handling.
302 - Rewrote the DNS API and the Dpid start code inside Dillo.
303 - Implemented Stop button to not only stop rendering but also networking.
304 - Fixed the problem of scrolling position (remember position in a page).
305 - Implemented a new scheme of scroll-position remembering. This is one per
306 visited page intead of one per url (this is more standard).
307 - Fixed a subtle bug in klist that was affecting IO.
308 - Fixed the position of the Bug Meter popup menu.
309 - Hooked vertical scrolling to the mouse wheel.
310 - Reimplemented plain.cc using a class, and hooked memory-release.
311 - Reimplemented html.cc using a class, removed the linkblock,
312 and hooked memory-release to dw destruction.
313 - Switched UI shortcuts from a global event handler to UI::handle.
314 - Bound Ctrl+Space to toggle fullscreen mode.
315 - Switched dillo to push a URL with fragment (anchor) into the stack.
316 - Added a workaround for a CCC reentrancy segfault.
317 - Bound FltkMultiLineTextResource to the html parser (TEXTAREA).
318 - Added code to ignore the first <P> after <LI>.
319 - Added a http_referer preference. See details in dillorc.
320 - Added a text placeholder: "[IMG]" for img_off mode.
321 - Fixed a SEGFAULT bug in http.c (handling of web->url).
322 - Fixed handling of #anchors with repush, and other operations.
323 - Implemented a_Dialog_choice5(). May be used by dpis and dillo.
324 - Improved parsing of collapsing white space.
325 - FTP dpi: Fixed algorithm bugs and improved the mime-type detector.
326 - CCC: added reentrancy control to the OpEnd and OpAbort operations.
327 - CCC: enhanced the debug function and implemented OpAbort for dpi.
328 - Hooked a decoder for text/plain with charset.
329 - Forbid dpi GET and POST from non dpi-generated urls.
330 - Cleaned up a_Url_new().
331 - Implemented tabbed browsing.
332 Patches: Jorge Arellano Cid
333 +- Connected signals to <li> elements (fixes links within lists).
334 - Enabled text, background-color, panel_size, geometry, fullscreen,
335 start_page, geometry offset, proxy_user and limit_text_width in preferences.
336 - Enabled clicking over image links.
337 - Improved notification upon leaving links.
338 - Implemented image-link URL showing in status bar.
339 - Added missing size-parsing for the <hr> element.
340 - Hooked "Activate" to the form_receiver.
341 - Connected the plain page context menu.
342 - Added code for the image menu and hooked it to dw2 signals.
343 - Hooked the page and link menus.
344 - Added a image-loading toggle button to the UI.
345 - Enabled hiding widgets of the control panel from dillorc.
346 - Added a save-directory preference (save_dir in dillorc).
347 - Fixed page-popup-menu to use the stack's top URL instead of base_url.
348 - Added the "static" qualifier where missing.
349 - Bound "Copy link location".
350 - Bound preliminar find text support.
351 - Added line numbers and enabled wrapping in the "View Source" window.
352 - Added HTTP-1.1's chunked transfer support!
353 - Made the stop button sensitive when loading an image.
354 - Added more statics in dpi, const in pixmaps, and removed redundant includes.
355 - Made cleanups in prefs (hiding local data/defs/symbols).
356 - Fixed a segfault in cookies.c when no .dillo directory exists.
357 - Added a MSG_HTTP for HTTP/1.1's warning headers.
358 - Added support for multi-line header fields.
359 - Added support for "charset" in the HTTP header field for Content-Type.
360 - Added support for progressive display of progressive jpegs.
361 - Fixed progressive display of interlaced pngs.
362 - Enabled colspan=0 in tables parsing.
363 - Fixed a memory leak in cookies.c
364 - Added "standard_widget_colors" preference. It allows a more stylish look.
365 - Fixed the return value of Cache_parse_multiple_field.
366 - Added the multipart/form-data encoding method to form submission.
367 - Fixed a bug in Html_parse_entity.
368 - Fixed a bug in a_Url_cmp.
369 - Fixed a bug in Cookies_parse_one. Set it to a single return point too!
370 - Added dStr_memmem() and dStr_printable() to dlib.
371 - Split Html_append_input() into smaller functions.
372 - Implemented ISINDEX.
373 - Added input image for FORMS.
374 - Added button for FORMS.
375 - Added nesting checks for BUTTON, SELECT and FORM.
376 - Fix: shape=default is the background in a client-side image map.
377 - Enabled client and server-side image maps.
378 - Switched Window::destroy to Window::delete, fixing side effects.
379 - Made zlib a configure requirement, and cleaned up configure.in.
380 - Fixed a segfault bug in Nav.c.
381 - Switched from charset to content-type for handling data.
382 - Moved charset decoding into cache.
383 - Implemented OBJECT as link (similar to FRAME).
384 - Enabled the file dpi to look inside gzipped files.
385 - Allowed form inputs outside the FORM element (it's in the standard).
386 - Fixed a segfault bug in VERBATIM mode.
387 - Made image inputs less of a special case by using x,y in ComplexButton.
388 - Made forms show their action URL upon enter/leave mouse events (safety).
389 - Fixed a memory leak in plain.cc.
390 - Switched from DEBUG_MSG to MSG.
391 Patches: place (AKA corvid)
392 +- Fixed a problem with locally-installed dpis.
393 - Added code for optional image loading (nice interface) very advanced!
394 - Added an experimental gzip decoder!
395 - Implemented "Load Images" in the page menu and cleaned up html.hh.
396 - Added shortcuts: PgDn=Spc, PgUp=b, Back=BackSpace, Forw=Shift+Backspace.
397 - Made a cleanup in cache's parse header code.
398 - Added support for "charset" in the META element.
399 - Added a_Capi_get_flags(). It requests a cache entry's status as flags.
400 - Switched URL_DATA type from char* to a dStr.
401 - Implemented the file input control for forms.
402 - Fixed data guesser to detect ASCII, LATIN1, UTF8, KOI8-R, CP-1251 as text.
403 Patch: place, Jorge Arellano Cid
404 +- Fixed a cookies-related dillo freeze bug happening at:
405 http://www.fltk.org/newsgroups.php?gfltk.general+v:24912
406 Patch: Andreas Kemnade, Jorge Arellano Cid
407 +- Fixed a va_list-related SEGFAULT on 64bit-arch in dStr_vsprintfa().
408 Added const declarations in html parser.
409 Patch: Vincent Thomasset
410 +- Fixed void to int conversions for 64bit-arch.
411 Patch: Jorge Arellano Cid, higuita
412 +- Set the url resolver to escape illegal chars instead of stripping.
413 Patch: Jorge Arellano Cid, Jeremy Henty
414 +- Added suport for old iconv() (const char** as 2nd arg).
415 Patch: Jorge Arellano Cid, Christian Kellermann
416 +- Added a strndup() replacement in dw2
417 Patch: Alexander Becher, Johannes Hofmann, Jorge Arellano Cid
418 +- Fixed calcHashValue() to only return non-negative numbers (was SEGFAULT).
419 - Improved scrolling performance on large pages by copying screen data
420 instead of rendering.
421 - Updated configure.in to check only for fltk2-config.
422 - Implemented drag-scrolling with the mouse's middle button.
423 - Disabled double buffering (good for debugging redraws).
424 - Switched dns.c from gethostbyname* to getaddrinfo (& removed libc5 code).
425 - Made "New browser window" inherit the panel style of its parent.
426 - Made TopGroup a PackedGroup, simplifying UI code and removing workarounds.
427 - Added a redraw(DAMAGE_HIGHLIGHT) call to Back, Forw and Stop buttons.
428 - Fixed a segfault bug when closing a bw under active networking.
429 - Removed the unused SPCBuf variable.
430 - Fixed a freeze-bug in IO.c where the IOwatch for reading was not removed.
431 Patches: Johannes Hofmann
432 +- Made progress bars resize automatically.
433 Patches: Johannes Hofmann, Jorge Arellano Cid
434 +- Improved FLTK library detection at configure time.
435 Patch: Frank Gevaerts
436 +- Bound Ctrl-R to reload.
437 - Made dialogs use font_factor (e.g. view source).
438 - Implemented the SELECT element in FORMS!
439 - Implemented MULTIPLE SELECT in FORMS.
440 - Fixed a memory leak in nav.c
441 !- html.cc cleanup (in progress). New classes, form API, source split.
442 - Fixed a bug in style caching.
443 Patches: Jeremy Henty
444 +- Added int32_t, EAI_NODATA and iconv tests for FreeBSD.
445 Patch: Thomas-Martin Seck
446 +- Made CTRL-l focus the location bar instead of popping up a dialog.
447 - Set key bindings with modifiers to work when alone only.
448 - Replaced the findtext dialog with an in-window widget!
449 Patches: Justus Winter
450
451
452 -----------------------------------------------------------------------------
453 dw
454
455 0.0.43
456 - Fixed bug in dw::core::ExtIterator (wrong mask, see also Jorge's
457 patch "createvar.diff" from Nov 08).
458 - Applied Jorge's patch for dw::core::AlignedTextblock
459 ("lists.diff" in mail from Nov 08).
460 - Applied Jorge's patch for dw::core::Textblock ("links.diff" in
461 mail from Nov 08).
462 - Applied Jorge's patch for configure.in ("conf.diff" in mail from
463 Nov 08).
464 - Renamed ExtIterator to DeepIterator.
465 - Implemented CharIterator (as an alternative to word iterators).
466 - Implemented text search (simple KMP based on CharIterator).
467 - Completed scrolling.
468 Patches: Sebastian Geerken
469
470 + Implemented drag scrolling with mouse's middle button.
471 - Enabled commented out partial image redraw, adding some checks.
472 - Enabled clipped redraws (avoids some flickering).
473 - Improvement: avoid complete redraws for child widget updates.
474 - Added code to really delete fltk2 widgets embedded in dw2.
475 - Fixed partial redraws and scrolling interference.
476 - Added combination of drawing rectangles into a larger one.
477 - Bug fix: a newly added rectangle may contain others.
478 - Made draw() check whether a rectangle is visible at drawing time.
479 - The background is now cleared properly on partial redraws.
480 - Made getWidgetAtPoint() a virtual method of widget and implemented a
481 custom one for TextBlock, reducing CPU usage on pages full of links.
482 - Added a style reference and an initialization to mustQueueResize.
483 - Replaced prepareCreateFltkWidget with an explicit call to add().
484 - Fixed an assertion-exit bug in DeepIterator.
485 - Fixed two viewport bugs: in drawing and scrolling.
486 - Made scrollbars really children of FltkViewport.
487 - Avoided multiple redraws when Layout::resizeIdle() queues itself.
488 - Set FltkViewBase::draw to intersect with view area for expose.
489 - Set cursor shape to CURSOR_MOVE on drag. Disabled drag over links.
490 - Added Layout::queueDrawExcept(), it reduces flickering by avoiding
491 a redraw when another rectangle is added.
492 - Fixed a scrollIdleId test to properly compare against -1.
493 - Set FltkPlatform::removeIdle to use removeRef() instead of remove().
494 - Cleaned up scroll code and moved updateCanvasWidgets() out of draw().
495 - Switched begin-end pairs with add() calls (fixes side-effect bugs!).
496 - Fixed checks in adjustScrollPos() to not allow wild values.
497 - Added double buffering for partial redraws!
498 - Implemented ComplexButton.
499 - Fixed find text so it works for phrases and PRE-wrapped text.
500 - Fixed a bug in DeepIterator::prev.
501 - Added the "lout" namespace.
502 - Reduced memory usage in 30% by reusing styles, reducing the size
503 of struct Content, and not preallocating in SimpleVector. !
504 - Made fontsTable and colorsTable static members of Font and Color.
505 - Moved highlighting information from struct Word into Textblock
506 to save memory.
507 - Reduced memory usage 10% with a custom memory handler in Textblock.
508 - Fixed a segfault when searching for single characters.
509 - Fixed memory leaks by s/delete/delete[]/ where necessary.
510 - Fixed three iterator memory leaks in Iterator::scrollTo().
511 - Changed DeepIterator to always clone its parameter (segfault bug).
512 - Implemented selection of multibyte glyphs (UTF-8).
513 - Removed the canvasWidgets list (fltk's children seem enough).
514 - Switched misc:assert() to the standard assert() call.
515 Patches: Johannes Hofmann
516 + Fixed a segfault-on-empty-strings bug in ConstString::hashValue.
517 - Fixed a segfault in reallocChildren (colspan/rowspan related).
518 - Fixed another assertion-exit bug in DeepIterator.
519 - Added the dw::fltk::ui::FltkMultiLineTextResource class.
520 - Implemented TEXTAREA using fltk::TextEditor.
521 - Bugfix: added the missing fltk::setfont calls before ::getwidth.
522 - Bugfix: initialized scrollIdleNotInterrupted variable.
523 - Commented out obsolete DEBUG_MSG lines in widget.cc.
524 - Fixed rowspan apportion when no single rowspan=1 row is found.
525 - Fixed allocateFltkWidget to handle and display FltkListResource.
526 - Fixed a slithery BUG in lout::misc::Stringbuffer.
527 - Implemented multiple item selection in FltkSelectionResource.
528 Patches: Jeremy Henty
529 + Added an extra argument in the link signals
530 (I recommended that instead of an array of image handlers --jcid)
531 - Aded an x_img camp to style (an image array index, like x_link).
532 - Added the same workaround in ui.cc for WHEN_ENTER_KEY_ALWAYS.
533 - Fixed shading (style.cc) and implemented FltkViewBase::drawPolygon().
534 - Implemented Circle and Disk bullet drawing.
535 - Fixed a bug in FltkViewBase::getClippingView.
536 - Made FltkColor::FltkColor use ::fltk::BLACK (bugfix).
537 - Fixed a bug with dissappearing widgets when scrolling with low CPU.
538 - Fixed a bug with the canvas offset of scrolling bars.
539 - Fixed a typo bug in scrollIdle() and a typo in processMouseEvent().
540 - Fixed an offset arithmetic bug with widgets inside textblock.
541 - Fixed RTFL debugging messages.
542 - Switched ComplexButton to use "Activate" instead of "Clicked" signal.
543 - Made the ComplexButton resource remember its click x,y.
544 - Added "enter" and "leave" signals into class Resource.
545 Patches: place
546 + Enabled mouse wheel scrolling.
547 FltkViewport::setScrollStep() sets how many points at a time.
548 - Added setDeleteCallback(DW_Callback_t func, void *data) to widget.
549 This allows to hook a callback when the widget is destroyed.
550 - Implemented a weighted apportionment algorithm for table rowspan.
551 - Implemented a weighted apportionment algorithm for table colspan.
552 - Implemented percentage widths in tables (better rendering!).
553 - Fixed an initialization bug in hruler.
554 - Fixed a bug in the textblock's wrapping algorithm.
555 - Fixed a bug in table cellpadding.
556 - Fixed a bug in getContentHeight().
557 - Changed the table-apportion algorithms + bug fixes. Big work!
558 - Fixed a mistake in the CSS-box-model PNG image (style-box-model.png).
559 - Added initialization for scrollX and scrollY.
560 - Fixed a typo bug in adjustScrollPos().
561 - Fixed two typo bugs in Textblock::drawLine().
562 - Changed Textblock::addText() to internally allocate its text string,
563 making the memory handling opaque to the caller.
564 Patches: Jorge Arellano Cid
565 + Added actual text selection.
566 Patch: Sebastian Geerken, place
567 + Implemented dw::fltk::ui::FltkOptionMenuResource::isSelected(),
568 added Item::createNewGroupWidget(), Item::createNewWidget().
569 Patch: Jeremy Henty, Johannes Hofmann
570 + Implemented the necessary base for image maps.
571 Patch: Johannes Hofmann, place
572
573 0.0.42
574 - Fixed event handling in FLTK views. (Fixes links and several
575 problems with UI resources.)
576 - Implemented clipping views. (dw::Image used this already in
577 version 0.0.41.)
578 - Added "activated" signals to UI resources.
579 Patches: Sebastian Geerken
580
581 -----------------------------------------------------------------------------
582
583
584 0.8.5-pre-dw-redesign-1 [internal]
585 - Prototype
586
587 dillo-0.8.3-pre-dw-redesign-3 [Aug 30, 2004]
588 - * Fixed bug GtkDwViewport, which caused some redraws to be ignored.
589 * Added GdkDwPreview.
590 Patches: Sebastian Geerken
591
592
593 dillo-0.8.3-pre-dw-redesign-2 [Aug 28, 2004]
594 - * Added images to the current state of the redesign.
595 - New module Imgbuf, see doc/Imgbuf.txt for details.
596 Patch: Sebastian Geerken
597
598
599 dillo-0.8.3-pre-dw-redesign-1 [Aug 25, 2004]
600 - * Introduced an abstraction layer between Dw and Gtk+. See README-port and
601 doc/DwRender.txt for more details.
602 Patch: Sebastian Geerken
603
604
0605 =============================================================================
1606 Dillo project
2607 =============================================================================
30635 * Made the parser aware of buggy pages with multiple BODY and HTML elements.
31636 * Fixed a bug in MIME content/type detection.
32637 * Check HTTP Content-Type against real data (a security procedure).
33 Patches: Jorge Arellano
638 Patches: Jorge Arellano Cid
34639 - * Added a datauri dpi to handle "data:" URIs (RFC-2397).
35640 Patch: Jorge Arellano, Ben Wiley Sittler
36641 - * Moved the cookies management into a dpi server: cookies.dpi.
49654 - * Fixed a file descriptor leak in the dpi protocol library.
50655 * Fixed a subtle segfault bug with malformed URLs in cookies.c.
51656 Patch: Francis Daly
657 - * Improved the dpi framework. Now dpi-programs can be specified in dpidrc,
658 and there's no need to touch dillo's sources to add new dpi services.
659 Just make your dpi program, add a dpidrc line and play with it!
660 Patch: Diego Sáenz, Jorge Arellano
52661
53662
54663 dillo-0.8.5 [Jun 15, 2005]
13491958 Patches: Jorge Arellano Cid
13501959
13511960
1961
+0
-89
ChangeLog.old less more
0
1 This is the Changelog file that existed before Dillo began.
2
3
4 ===========================================================================
5 Gzilla project
6
7 Wed 28 Oct 1999 Christopher Reid Palmer <chrisp@innerfireworks.com>
8 Rota Luca
9 * support for arrow keys
10 * proper tool bar
11 * cursor changing
12
13 Randy Maas
14 * documentation
15 * URL sub-tree cleanups and speed-ups
16
17 Sammy Mannaert
18 * bookmarks.c fix
19 * menu hotkeys, menu pulloffs and Location pull-off
20 * file:/ URL bugfix, for larger than 12 char names
21
22 David Press
23 * fix to cursor change
24
25 Fri Jul 23 22:12:14 1999 ObiTuarY <obituary@freshmeat.net>
26
27 * *.*: Upgraded to autoconf 2.13 and automake 1.4. Reorganization
28 of the source.
29 * src/interface.{c,h}: Added pixmaps for the buttons. Removed the
30 open button. Doesn't it feel redundant with the location text entry
31 just under it ? ;-)
32 * src/pixmaps.h: New file holding the pixmaps.
33
34 0.2.0 17-18 Jul 1999 Christopher Reid Palmer <chrisp@innerFireWorks.com>
35 * Restructured menus, beginning to implement functionality of new menu items.
36 * Closed hole for when ~/.gzilla exists but is not a dir.
37 * Poked at bookmarks.
38
39
40
41 ===========================================================================
42 Raph Levien project
43
44
45 Mon Dec 21 00:07:36 1998 Raph Levien <raph@gimp.org>
46
47 * gzilladns.c: fixed a bug (adding the input handler more
48 than once) that got triggered in Gtk 1.1.x.
49 * gzillageturl.c: added an "about:" mechanism with simple
50 redirects. This is 0.1.7.
51
52 Sun Dec 20 22:25:17 1998 Raph Levien <raph@gimp.org>
53
54 * Accelerator group code so that it now compiles with both
55 Gtk 1.0.x and 1.1.x
56 * Added directory scanning support to gzilla_file (thanks to Jim
57 McBeath!)
58
59 0.1.5 (2 Jan 1998 RLL)
60 * Finished moving junk out of bytesink (mostly into linkblock).
61 Now, the former abstraction is lean and mean, and the latter
62 is a junkheap :).
63 * Tried to hook up most of the abort/destroy logic, so it should
64 leak a lot less memory now.
65 * I'm moving images from gtk_preview to gzw_image as well.
66
67 0.1.4 (26 Nov 1997 RLL)
68 * Pages > 32kpixels actually scroll now.
69 * Pretty significant rework of the whole abort architecture (see
70 abort.html for more details). Also separated out some of the
71 web-specific stuff in bytesink to linkblock.
72 * It seems relatively stable now, but it's nowhere near freeing
73 all of the memory it allocates.
74
75 0.1.3
76 * Switched page widget from gtk to gzw.
77 * Significant enhancements in functionality and stability.
78
79 0.0.10
80 * Major change is reorganization of network code - all fetches now
81 go through gzilla_url_get.
82
83 * Aborts (i.e. the implementation of the Stop button) are now
84 handled by a new bytesink signal ("abort"). The abort logic has
85 been partially upgraded to handle multiple windows - now,
86 gzilla_bw_abort_all aborts all bytesinks in a window.
87
88
0 # Doxyfile 1.5.8
1
2 # This file describes the settings to be used by the documentation system
3 # doxygen (www.doxygen.org) for a project
4 #
5 # All text after a hash (#) is considered a comment and will be ignored
6 # The format is:
7 # TAG = value [value, ...]
8 # For lists items can also be appended using:
9 # TAG += value [value, ...]
10 # Values that contain spaces should be placed between quotes (" ")
11
12 #---------------------------------------------------------------------------
13 # Project related configuration options
14 #---------------------------------------------------------------------------
15
16 # This tag specifies the encoding used for all characters in the config file
17 # that follow. The default is UTF-8 which is also the encoding used for all
18 # text before the first occurrence of this tag. Doxygen uses libiconv (or the
19 # iconv built into libc) for the transcoding. See
20 # http://www.gnu.org/software/libiconv for the list of possible encodings.
21
22 DOXYFILE_ENCODING = UTF-8
23
24 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded
25 # by quotes) that should identify the project.
26
27 PROJECT_NAME = Dillo
28
29 # The PROJECT_NUMBER tag can be used to enter a project or revision number.
30 # This could be handy for archiving the generated documentation or
31 # if some version control system is used.
32
33 PROJECT_NUMBER =
34
35 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
36 # base path where the generated documentation will be put.
37 # If a relative path is entered, it will be relative to the location
38 # where doxygen was started. If left blank the current directory will be used.
39
40 OUTPUT_DIRECTORY =
41
42 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
43 # 4096 sub-directories (in 2 levels) under the output directory of each output
44 # format and will distribute the generated files over these directories.
45 # Enabling this option can be useful when feeding doxygen a huge amount of
46 # source files, where putting all generated files in the same directory would
47 # otherwise cause performance problems for the file system.
48
49 CREATE_SUBDIRS = NO
50
51 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
52 # documentation generated by doxygen is written. Doxygen will use this
53 # information to generate all constant output in the proper language.
54 # The default language is English, other supported languages are:
55 # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
56 # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
57 # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
58 # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
59 # Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
60 # Spanish, Swedish, and Ukrainian.
61
62 OUTPUT_LANGUAGE = English
63
64 # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
65 # include brief member descriptions after the members that are listed in
66 # the file and class documentation (similar to JavaDoc).
67 # Set to NO to disable this.
68
69 BRIEF_MEMBER_DESC = YES
70
71 # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
72 # the brief description of a member or function before the detailed description.
73 # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
74 # brief descriptions will be completely suppressed.
75
76 REPEAT_BRIEF = YES
77
78 # This tag implements a quasi-intelligent brief description abbreviator
79 # that is used to form the text in various listings. Each string
80 # in this list, if found as the leading text of the brief description, will be
81 # stripped from the text and the result after processing the whole list, is
82 # used as the annotated text. Otherwise, the brief description is used as-is.
83 # If left blank, the following values are used ("$name" is automatically
84 # replaced with the name of the entity): "The $name class" "The $name widget"
85 # "The $name file" "is" "provides" "specifies" "contains"
86 # "represents" "a" "an" "the"
87
88 ABBREVIATE_BRIEF =
89
90 # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
91 # Doxygen will generate a detailed section even if there is only a brief
92 # description.
93
94 ALWAYS_DETAILED_SEC = NO
95
96 # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
97 # inherited members of a class in the documentation of that class as if those
98 # members were ordinary class members. Constructors, destructors and assignment
99 # operators of the base classes will not be shown.
100
101 INLINE_INHERITED_MEMB = NO
102
103 # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
104 # path before files name in the file list and in the header files. If set
105 # to NO the shortest path that makes the file name unique will be used.
106
107 FULL_PATH_NAMES = YES
108
109 # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
110 # can be used to strip a user-defined part of the path. Stripping is
111 # only done if one of the specified strings matches the left-hand part of
112 # the path. The tag can be used to show relative paths in the file list.
113 # If left blank the directory from which doxygen is run is used as the
114 # path to strip.
115
116 STRIP_FROM_PATH =
117
118 # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
119 # the path mentioned in the documentation of a class, which tells
120 # the reader which header file to include in order to use a class.
121 # If left blank only the name of the header file containing the class
122 # definition is used. Otherwise one should specify the include paths that
123 # are normally passed to the compiler using the -I flag.
124
125 STRIP_FROM_INC_PATH =
126
127 # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
128 # (but less readable) file names. This can be useful is your file systems
129 # doesn't support long names like on DOS, Mac, or CD-ROM.
130
131 SHORT_NAMES = NO
132
133 # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
134 # will interpret the first line (until the first dot) of a JavaDoc-style
135 # comment as the brief description. If set to NO, the JavaDoc
136 # comments will behave just like regular Qt-style comments
137 # (thus requiring an explicit @brief command for a brief description.)
138
139 JAVADOC_AUTOBRIEF = NO
140
141 # If the QT_AUTOBRIEF tag is set to YES then Doxygen will
142 # interpret the first line (until the first dot) of a Qt-style
143 # comment as the brief description. If set to NO, the comments
144 # will behave just like regular Qt-style comments (thus requiring
145 # an explicit \brief command for a brief description.)
146
147 QT_AUTOBRIEF = NO
148
149 # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
150 # treat a multi-line C++ special comment block (i.e. a block of //! or ///
151 # comments) as a brief description. This used to be the default behaviour.
152 # The new default is to treat a multi-line C++ comment block as a detailed
153 # description. Set this tag to YES if you prefer the old behaviour instead.
154
155 MULTILINE_CPP_IS_BRIEF = NO
156
157 # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
158 # member inherits the documentation from any documented member that it
159 # re-implements.
160
161 INHERIT_DOCS = YES
162
163 # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
164 # a new page for each member. If set to NO, the documentation of a member will
165 # be part of the file/class/namespace that contains it.
166
167 SEPARATE_MEMBER_PAGES = NO
168
169 # The TAB_SIZE tag can be used to set the number of spaces in a tab.
170 # Doxygen uses this value to replace tabs by spaces in code fragments.
171
172 TAB_SIZE = 8
173
174 # This tag can be used to specify a number of aliases that acts
175 # as commands in the documentation. An alias has the form "name=value".
176 # For example adding "sideeffect=\par Side Effects:\n" will allow you to
177 # put the command \sideeffect (or @sideeffect) in the documentation, which
178 # will result in a user-defined paragraph with heading "Side Effects:".
179 # You can put \n's in the value part of an alias to insert newlines.
180
181 ALIASES =
182
183 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
184 # sources only. Doxygen will then generate output that is more tailored for C.
185 # For instance, some of the names that are used will be different. The list
186 # of all members will be omitted, etc.
187
188 OPTIMIZE_OUTPUT_FOR_C = NO
189
190 # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
191 # sources only. Doxygen will then generate output that is more tailored for
192 # Java. For instance, namespaces will be presented as packages, qualified
193 # scopes will look different, etc.
194
195 OPTIMIZE_OUTPUT_JAVA = NO
196
197 # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
198 # sources only. Doxygen will then generate output that is more tailored for
199 # Fortran.
200
201 OPTIMIZE_FOR_FORTRAN = NO
202
203 # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
204 # sources. Doxygen will then generate output that is tailored for
205 # VHDL.
206
207 OPTIMIZE_OUTPUT_VHDL = NO
208
209 # Doxygen selects the parser to use depending on the extension of the files it parses.
210 # With this tag you can assign which parser to use for a given extension.
211 # Doxygen has a built-in mapping, but you can override or extend it using this tag.
212 # The format is ext=language, where ext is a file extension, and language is one of
213 # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
214 # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
215 # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
216 # use: inc=Fortran f=C
217
218 EXTENSION_MAPPING =
219
220 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
221 # to include (a tag file for) the STL sources as input, then you should
222 # set this tag to YES in order to let doxygen match functions declarations and
223 # definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
224 # func(std::string) {}). This also make the inheritance and collaboration
225 # diagrams that involve STL classes more complete and accurate.
226
227 BUILTIN_STL_SUPPORT = NO
228
229 # If you use Microsoft's C++/CLI language, you should set this option to YES to
230 # enable parsing support.
231
232 CPP_CLI_SUPPORT = NO
233
234 # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
235 # Doxygen will parse them like normal C++ but will assume all classes use public
236 # instead of private inheritance when no explicit protection keyword is present.
237
238 SIP_SUPPORT = NO
239
240 # For Microsoft's IDL there are propget and propput attributes to indicate getter
241 # and setter methods for a property. Setting this option to YES (the default)
242 # will make doxygen to replace the get and set methods by a property in the
243 # documentation. This will only work if the methods are indeed getting or
244 # setting a simple type. If this is not the case, or you want to show the
245 # methods anyway, you should set this option to NO.
246
247 IDL_PROPERTY_SUPPORT = YES
248
249 # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
250 # tag is set to YES, then doxygen will reuse the documentation of the first
251 # member in the group (if any) for the other members of the group. By default
252 # all members of a group must be documented explicitly.
253
254 DISTRIBUTE_GROUP_DOC = NO
255
256 # Set the SUBGROUPING tag to YES (the default) to allow class member groups of
257 # the same type (for instance a group of public functions) to be put as a
258 # subgroup of that type (e.g. under the Public Functions section). Set it to
259 # NO to prevent subgrouping. Alternatively, this can be done per class using
260 # the \nosubgrouping command.
261
262 SUBGROUPING = YES
263
264 # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
265 # is documented as struct, union, or enum with the name of the typedef. So
266 # typedef struct TypeS {} TypeT, will appear in the documentation as a struct
267 # with name TypeT. When disabled the typedef will appear as a member of a file,
268 # namespace, or class. And the struct will be named TypeS. This can typically
269 # be useful for C code in case the coding convention dictates that all compound
270 # types are typedef'ed and only the typedef is referenced, never the tag name.
271
272 TYPEDEF_HIDES_STRUCT = NO
273
274 # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
275 # determine which symbols to keep in memory and which to flush to disk.
276 # When the cache is full, less often used symbols will be written to disk.
277 # For small to medium size projects (<1000 input files) the default value is
278 # probably good enough. For larger projects a too small cache size can cause
279 # doxygen to be busy swapping symbols to and from disk most of the time
280 # causing a significant performance penality.
281 # If the system has enough physical memory increasing the cache will improve the
282 # performance by keeping more symbols in memory. Note that the value works on
283 # a logarithmic scale so increasing the size by one will rougly double the
284 # memory usage. The cache size is given by this formula:
285 # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
286 # corresponding to a cache size of 2^16 = 65536 symbols
287
288 SYMBOL_CACHE_SIZE = 0
289
290 #---------------------------------------------------------------------------
291 # Build related configuration options
292 #---------------------------------------------------------------------------
293
294 # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
295 # documentation are documented, even if no documentation was available.
296 # Private class members and static file members will be hidden unless
297 # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
298
299 EXTRACT_ALL = YES
300
301 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class
302 # will be included in the documentation.
303
304 EXTRACT_PRIVATE = YES
305
306 # If the EXTRACT_STATIC tag is set to YES all static members of a file
307 # will be included in the documentation.
308
309 EXTRACT_STATIC = YES
310
311 # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
312 # defined locally in source files will be included in the documentation.
313 # If set to NO only classes defined in header files are included.
314
315 EXTRACT_LOCAL_CLASSES = NO
316
317 # This flag is only useful for Objective-C code. When set to YES local
318 # methods, which are defined in the implementation section but not in
319 # the interface are included in the documentation.
320 # If set to NO (the default) only methods in the interface are included.
321
322 EXTRACT_LOCAL_METHODS = NO
323
324 # If this flag is set to YES, the members of anonymous namespaces will be
325 # extracted and appear in the documentation as a namespace called
326 # 'anonymous_namespace{file}', where file will be replaced with the base
327 # name of the file that contains the anonymous namespace. By default
328 # anonymous namespace are hidden.
329
330 EXTRACT_ANON_NSPACES = NO
331
332 # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
333 # undocumented members of documented classes, files or namespaces.
334 # If set to NO (the default) these members will be included in the
335 # various overviews, but no documentation section is generated.
336 # This option has no effect if EXTRACT_ALL is enabled.
337
338 HIDE_UNDOC_MEMBERS = NO
339
340 # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
341 # undocumented classes that are normally visible in the class hierarchy.
342 # If set to NO (the default) these classes will be included in the various
343 # overviews. This option has no effect if EXTRACT_ALL is enabled.
344
345 HIDE_UNDOC_CLASSES = NO
346
347 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
348 # friend (class|struct|union) declarations.
349 # If set to NO (the default) these declarations will be included in the
350 # documentation.
351
352 HIDE_FRIEND_COMPOUNDS = NO
353
354 # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
355 # documentation blocks found inside the body of a function.
356 # If set to NO (the default) these blocks will be appended to the
357 # function's detailed documentation block.
358
359 HIDE_IN_BODY_DOCS = NO
360
361 # The INTERNAL_DOCS tag determines if documentation
362 # that is typed after a \internal command is included. If the tag is set
363 # to NO (the default) then the documentation will be excluded.
364 # Set it to YES to include the internal documentation.
365
366 INTERNAL_DOCS = NO
367
368 # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
369 # file names in lower-case letters. If set to YES upper-case letters are also
370 # allowed. This is useful if you have classes or files whose names only differ
371 # in case and if your file system supports case sensitive file names. Windows
372 # and Mac users are advised to set this option to NO.
373
374 CASE_SENSE_NAMES = YES
375
376 # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
377 # will show members with their full class and namespace scopes in the
378 # documentation. If set to YES the scope will be hidden.
379
380 HIDE_SCOPE_NAMES = NO
381
382 # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
383 # will put a list of the files that are included by a file in the documentation
384 # of that file.
385
386 SHOW_INCLUDE_FILES = YES
387
388 # If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
389 # is inserted in the documentation for inline members.
390
391 INLINE_INFO = YES
392
393 # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
394 # will sort the (detailed) documentation of file and class members
395 # alphabetically by member name. If set to NO the members will appear in
396 # declaration order.
397
398 SORT_MEMBER_DOCS = YES
399
400 # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
401 # brief documentation of file, namespace and class members alphabetically
402 # by member name. If set to NO (the default) the members will appear in
403 # declaration order.
404
405 SORT_BRIEF_DOCS = NO
406
407 # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
408 # hierarchy of group names into alphabetical order. If set to NO (the default)
409 # the group names will appear in their defined order.
410
411 SORT_GROUP_NAMES = NO
412
413 # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
414 # sorted by fully-qualified names, including namespaces. If set to
415 # NO (the default), the class list will be sorted only by class name,
416 # not including the namespace part.
417 # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
418 # Note: This option applies only to the class list, not to the
419 # alphabetical list.
420
421 SORT_BY_SCOPE_NAME = NO
422
423 # The GENERATE_TODOLIST tag can be used to enable (YES) or
424 # disable (NO) the todo list. This list is created by putting \todo
425 # commands in the documentation.
426
427 GENERATE_TODOLIST = YES
428
429 # The GENERATE_TESTLIST tag can be used to enable (YES) or
430 # disable (NO) the test list. This list is created by putting \test
431 # commands in the documentation.
432
433 GENERATE_TESTLIST = YES
434
435 # The GENERATE_BUGLIST tag can be used to enable (YES) or
436 # disable (NO) the bug list. This list is created by putting \bug
437 # commands in the documentation.
438
439 GENERATE_BUGLIST = YES
440
441 # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
442 # disable (NO) the deprecated list. This list is created by putting
443 # \deprecated commands in the documentation.
444
445 GENERATE_DEPRECATEDLIST= YES
446
447 # The ENABLED_SECTIONS tag can be used to enable conditional
448 # documentation sections, marked by \if sectionname ... \endif.
449
450 ENABLED_SECTIONS =
451
452 # The MAX_INITIALIZER_LINES tag determines the maximum number of lines
453 # the initial value of a variable or define consists of for it to appear in
454 # the documentation. If the initializer consists of more lines than specified
455 # here it will be hidden. Use a value of 0 to hide initializers completely.
456 # The appearance of the initializer of individual variables and defines in the
457 # documentation can be controlled using \showinitializer or \hideinitializer
458 # command in the documentation regardless of this setting.
459
460 MAX_INITIALIZER_LINES = 30
461
462 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated
463 # at the bottom of the documentation of classes and structs. If set to YES the
464 # list will mention the files that were used to generate the documentation.
465
466 SHOW_USED_FILES = YES
467
468 # If the sources in your project are distributed over multiple directories
469 # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
470 # in the documentation. The default is NO.
471
472 SHOW_DIRECTORIES = NO
473
474 # Set the SHOW_FILES tag to NO to disable the generation of the Files page.
475 # This will remove the Files entry from the Quick Index and from the
476 # Folder Tree View (if specified). The default is YES.
477
478 SHOW_FILES = YES
479
480 # Set the SHOW_NAMESPACES tag to NO to disable the generation of the
481 # Namespaces page.
482 # This will remove the Namespaces entry from the Quick Index
483 # and from the Folder Tree View (if specified). The default is YES.
484
485 SHOW_NAMESPACES = YES
486
487 # The FILE_VERSION_FILTER tag can be used to specify a program or script that
488 # doxygen should invoke to get the current version for each file (typically from
489 # the version control system). Doxygen will invoke the program by executing (via
490 # popen()) the command <command> <input-file>, where <command> is the value of
491 # the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
492 # provided by doxygen. Whatever the program writes to standard output
493 # is used as the file version. See the manual for examples.
494
495 FILE_VERSION_FILTER =
496
497 # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
498 # doxygen. The layout file controls the global structure of the generated output files
499 # in an output format independent way. The create the layout file that represents
500 # doxygen's defaults, run doxygen with the -l option. You can optionally specify a
501 # file name after the option, if omitted DoxygenLayout.xml will be used as the name
502 # of the layout file.
503
504 LAYOUT_FILE =
505
506 #---------------------------------------------------------------------------
507 # configuration options related to warning and progress messages
508 #---------------------------------------------------------------------------
509
510 # The QUIET tag can be used to turn on/off the messages that are generated
511 # by doxygen. Possible values are YES and NO. If left blank NO is used.
512
513 QUIET = NO
514
515 # The WARNINGS tag can be used to turn on/off the warning messages that are
516 # generated by doxygen. Possible values are YES and NO. If left blank
517 # NO is used.
518
519 WARNINGS = YES
520
521 # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
522 # for undocumented members. If EXTRACT_ALL is set to YES then this flag will
523 # automatically be disabled.
524
525 WARN_IF_UNDOCUMENTED = YES
526
527 # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
528 # potential errors in the documentation, such as not documenting some
529 # parameters in a documented function, or documenting parameters that
530 # don't exist or using markup commands wrongly.
531
532 WARN_IF_DOC_ERROR = YES
533
534 # This WARN_NO_PARAMDOC option can be abled to get warnings for
535 # functions that are documented, but have no documentation for their parameters
536 # or return value. If set to NO (the default) doxygen will only warn about
537 # wrong or incomplete parameter documentation, but not about the absence of
538 # documentation.
539
540 WARN_NO_PARAMDOC = NO
541
542 # The WARN_FORMAT tag determines the format of the warning messages that
543 # doxygen can produce. The string should contain the $file, $line, and $text
544 # tags, which will be replaced by the file and line number from which the
545 # warning originated and the warning text. Optionally the format may contain
546 # $version, which will be replaced by the version of the file (if it could
547 # be obtained via FILE_VERSION_FILTER)
548
549 WARN_FORMAT = "$file:$line: $text"
550
551 # The WARN_LOGFILE tag can be used to specify a file to which warning
552 # and error messages should be written. If left blank the output is written
553 # to stderr.
554
555 WARN_LOGFILE =
556
557 #---------------------------------------------------------------------------
558 # configuration options related to the input files
559 #---------------------------------------------------------------------------
560
561 # The INPUT tag can be used to specify the files and/or directories that contain
562 # documented source files. You may enter file names like "myfile.cpp" or
563 # directories like "/usr/src/myproject". Separate the files or directories
564 # with spaces.
565
566 INPUT =
567
568 # This tag can be used to specify the character encoding of the source files
569 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
570 # also the default input encoding. Doxygen uses libiconv (or the iconv built
571 # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
572 # the list of possible encodings.
573
574 INPUT_ENCODING = UTF-8
575
576 # If the value of the INPUT tag contains directories, you can use the
577 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
578 # and *.h) to filter out the source-files in the directories. If left
579 # blank the following patterns are tested:
580 # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
581 # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
582
583 FILE_PATTERNS = *.cc \
584 *.hh \
585 *.doc
586
587 # The RECURSIVE tag can be used to turn specify whether or not subdirectories
588 # should be searched for input files as well. Possible values are YES and NO.
589 # If left blank NO is used.
590
591 RECURSIVE = YES
592
593 # The EXCLUDE tag can be used to specify files and/or directories that should
594 # excluded from the INPUT source files. This way you can easily exclude a
595 # subdirectory from a directory tree whose root is specified with the INPUT tag.
596
597 EXCLUDE = dlib \
598 dpi \
599 dpid \
600 dpip \
601 src \
602 test
603
604 # The EXCLUDE_SYMLINKS tag can be used select whether or not files or
605 # directories that are symbolic links (a Unix filesystem feature) are excluded
606 # from the input.
607
608 EXCLUDE_SYMLINKS = NO
609
610 # If the value of the INPUT tag contains directories, you can use the
611 # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
612 # certain files from those directories. Note that the wildcards are matched
613 # against the file with absolute path, so to exclude all test directories
614 # for example use the pattern */test/*
615
616 EXCLUDE_PATTERNS =
617
618 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
619 # (namespaces, classes, functions, etc.) that should be excluded from the
620 # output. The symbol name can be a fully qualified name, a word, or if the
621 # wildcard * is used, a substring. Examples: ANamespace, AClass,
622 # AClass::ANamespace, ANamespace::*Test
623
624 EXCLUDE_SYMBOLS =
625
626 # The EXAMPLE_PATH tag can be used to specify one or more files or
627 # directories that contain example code fragments that are included (see
628 # the \include command).
629
630 EXAMPLE_PATH =
631
632 # If the value of the EXAMPLE_PATH tag contains directories, you can use the
633 # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
634 # and *.h) to filter out the source-files in the directories. If left
635 # blank all files are included.
636
637 EXAMPLE_PATTERNS =
638
639 # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
640 # searched for input files to be used with the \include or \dontinclude
641 # commands irrespective of the value of the RECURSIVE tag.
642 # Possible values are YES and NO. If left blank NO is used.
643
644 EXAMPLE_RECURSIVE = NO
645
646 # The IMAGE_PATH tag can be used to specify one or more files or
647 # directories that contain image that are included in the documentation (see
648 # the \image command).
649
650 IMAGE_PATH = doc
651
652 # The INPUT_FILTER tag can be used to specify a program that doxygen should
653 # invoke to filter for each input file. Doxygen will invoke the filter program
654 # by executing (via popen()) the command <filter> <input-file>, where <filter>
655 # is the value of the INPUT_FILTER tag, and <input-file> is the name of an
656 # input file. Doxygen will then use the output that the filter program writes
657 # to standard output.
658 # If FILTER_PATTERNS is specified, this tag will be
659 # ignored.
660
661 INPUT_FILTER =
662
663 # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
664 # basis.
665 # Doxygen will compare the file name with each pattern and apply the
666 # filter if there is a match.
667 # The filters are a list of the form:
668 # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
669 # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
670 # is applied to all files.
671
672 FILTER_PATTERNS =
673
674 # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
675 # INPUT_FILTER) will be used to filter the input files when producing source
676 # files to browse (i.e. when SOURCE_BROWSER is set to YES).
677
678 FILTER_SOURCE_FILES = NO
679
680 #---------------------------------------------------------------------------
681 # configuration options related to source browsing
682 #---------------------------------------------------------------------------
683
684 # If the SOURCE_BROWSER tag is set to YES then a list of source files will
685 # be generated. Documented entities will be cross-referenced with these sources.
686 # Note: To get rid of all source code in the generated output, make sure also
687 # VERBATIM_HEADERS is set to NO.
688
689 SOURCE_BROWSER = NO
690
691 # Setting the INLINE_SOURCES tag to YES will include the body
692 # of functions and classes directly in the documentation.
693
694 INLINE_SOURCES = NO
695
696 # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
697 # doxygen to hide any special comment blocks from generated source code
698 # fragments. Normal C and C++ comments will always remain visible.
699
700 STRIP_CODE_COMMENTS = YES
701
702 # If the REFERENCED_BY_RELATION tag is set to YES
703 # then for each documented function all documented
704 # functions referencing it will be listed.
705
706 REFERENCED_BY_RELATION = YES
707
708 # If the REFERENCES_RELATION tag is set to YES
709 # then for each documented function all documented entities
710 # called/used by that function will be listed.
711
712 REFERENCES_RELATION = YES
713
714 # If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
715 # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
716 # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
717 # link to the source code.
718 # Otherwise they will link to the documentation.
719
720 REFERENCES_LINK_SOURCE = YES
721
722 # If the USE_HTAGS tag is set to YES then the references to source code
723 # will point to the HTML generated by the htags(1) tool instead of doxygen
724 # built-in source browser. The htags tool is part of GNU's global source
725 # tagging system (see http://www.gnu.org/software/global/global.html). You
726 # will need version 4.8.6 or higher.
727
728 USE_HTAGS = NO
729
730 # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
731 # will generate a verbatim copy of the header file for each class for
732 # which an include is specified. Set to NO to disable this.
733
734 VERBATIM_HEADERS = YES
735
736 #---------------------------------------------------------------------------
737 # configuration options related to the alphabetical class index
738 #---------------------------------------------------------------------------
739
740 # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
741 # of all compounds will be generated. Enable this if the project
742 # contains a lot of classes, structs, unions or interfaces.
743
744 ALPHABETICAL_INDEX = YES
745
746 # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
747 # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
748 # in which this list will be split (can be a number in the range [1..20])
749
750 COLS_IN_ALPHA_INDEX = 3
751
752 # In case all classes in a project start with a common prefix, all
753 # classes will be put under the same header in the alphabetical index.
754 # The IGNORE_PREFIX tag can be used to specify one or more prefixes that
755 # should be ignored while generating the index headers.
756
757 IGNORE_PREFIX =
758
759 #---------------------------------------------------------------------------
760 # configuration options related to the HTML output
761 #---------------------------------------------------------------------------
762
763 # If the GENERATE_HTML tag is set to YES (the default) Doxygen will
764 # generate HTML output.
765
766 GENERATE_HTML = YES
767
768 # The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
769 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
770 # put in front of it. If left blank `html' will be used as the default path.
771
772 HTML_OUTPUT = html
773
774 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for
775 # each generated HTML page (for example: .htm,.php,.asp). If it is left blank
776 # doxygen will generate files with .html extension.
777
778 HTML_FILE_EXTENSION = .html
779
780 # The HTML_HEADER tag can be used to specify a personal HTML header for
781 # each generated HTML page. If it is left blank doxygen will generate a
782 # standard header.
783
784 HTML_HEADER =
785
786 # The HTML_FOOTER tag can be used to specify a personal HTML footer for
787 # each generated HTML page. If it is left blank doxygen will generate a
788 # standard footer.
789
790 HTML_FOOTER =
791
792 # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
793 # style sheet that is used by each HTML page. It can be used to
794 # fine-tune the look of the HTML output. If the tag is left blank doxygen
795 # will generate a default style sheet. Note that doxygen will try to copy
796 # the style sheet file to the HTML output directory, so don't put your own
797 # stylesheet in the HTML output directory as well, or it will be erased!
798
799 HTML_STYLESHEET =
800
801 # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
802 # files or namespaces will be aligned in HTML using tables. If set to
803 # NO a bullet list will be used.
804
805 HTML_ALIGN_MEMBERS = YES
806
807 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
808 # documentation will contain sections that can be hidden and shown after the
809 # page has loaded. For this to work a browser that supports
810 # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
811 # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
812
813 HTML_DYNAMIC_SECTIONS = NO
814
815 # If the GENERATE_DOCSET tag is set to YES, additional index files
816 # will be generated that can be used as input for Apple's Xcode 3
817 # integrated development environment, introduced with OSX 10.5 (Leopard).
818 # To create a documentation set, doxygen will generate a Makefile in the
819 # HTML output directory. Running make will produce the docset in that
820 # directory and running "make install" will install the docset in
821 # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
822 # it at startup.
823 # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
824
825 GENERATE_DOCSET = NO
826
827 # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
828 # feed. A documentation feed provides an umbrella under which multiple
829 # documentation sets from a single provider (such as a company or product suite)
830 # can be grouped.
831
832 DOCSET_FEEDNAME = "Doxygen generated docs"
833
834 # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
835 # should uniquely identify the documentation set bundle. This should be a
836 # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
837 # will append .docset to the name.
838
839 DOCSET_BUNDLE_ID = org.doxygen.Project
840
841 # If the GENERATE_HTMLHELP tag is set to YES, additional index files
842 # will be generated that can be used as input for tools like the
843 # Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
844 # of the generated HTML documentation.
845
846 GENERATE_HTMLHELP = NO
847
848 # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
849 # be used to specify the file name of the resulting .chm file. You
850 # can add a path in front of the file if the result should not be
851 # written to the html output directory.
852
853 CHM_FILE =
854
855 # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
856 # be used to specify the location (absolute path including file name) of
857 # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
858 # the HTML help compiler on the generated index.hhp.
859
860 HHC_LOCATION =
861
862 # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
863 # controls if a separate .chi index file is generated (YES) or that
864 # it should be included in the master .chm file (NO).
865
866 GENERATE_CHI = NO
867
868 # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
869 # is used to encode HtmlHelp index (hhk), content (hhc) and project file
870 # content.
871
872 CHM_INDEX_ENCODING =
873
874 # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
875 # controls whether a binary table of contents is generated (YES) or a
876 # normal table of contents (NO) in the .chm file.
877
878 BINARY_TOC = NO
879
880 # The TOC_EXPAND flag can be set to YES to add extra items for group members
881 # to the contents of the HTML help documentation and to the tree view.
882
883 TOC_EXPAND = NO
884
885 # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
886 # are set, an additional index file will be generated that can be used as input for
887 # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
888 # HTML documentation.
889
890 GENERATE_QHP = NO
891
892 # If the QHG_LOCATION tag is specified, the QCH_FILE tag can
893 # be used to specify the file name of the resulting .qch file.
894 # The path specified is relative to the HTML output folder.
895
896 QCH_FILE =
897
898 # The QHP_NAMESPACE tag specifies the namespace to use when generating
899 # Qt Help Project output. For more information please see
900 # http://doc.trolltech.com/qthelpproject.html#namespace
901
902 QHP_NAMESPACE =
903
904 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
905 # Qt Help Project output. For more information please see
906 # http://doc.trolltech.com/qthelpproject.html#virtual-folders
907
908 QHP_VIRTUAL_FOLDER = doc
909
910 # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
911 # For more information please see
912 # http://doc.trolltech.com/qthelpproject.html#custom-filters
913
914 QHP_CUST_FILTER_NAME =
915
916 # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
917 # <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
918
919 QHP_CUST_FILTER_ATTRS =
920
921 # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
922 # filter section matches.
923 # <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
924
925 QHP_SECT_FILTER_ATTRS =
926
927 # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
928 # be used to specify the location of Qt's qhelpgenerator.
929 # If non-empty doxygen will try to run qhelpgenerator on the generated
930 # .qhp file.
931
932 QHG_LOCATION =
933
934 # The DISABLE_INDEX tag can be used to turn on/off the condensed index at
935 # top of each HTML page. The value NO (the default) enables the index and
936 # the value YES disables it.
937
938 DISABLE_INDEX = NO
939
940 # This tag can be used to set the number of enum values (range [1..20])
941 # that doxygen will group on one line in the generated HTML documentation.
942
943 ENUM_VALUES_PER_LINE = 4
944
945 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
946 # structure should be generated to display hierarchical information.
947 # If the tag value is set to FRAME, a side panel will be generated
948 # containing a tree-like index structure (just like the one that
949 # is generated for HTML Help). For this to work a browser that supports
950 # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
951 # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
952 # probably better off using the HTML help feature. Other possible values
953 # for this tag are: HIERARCHIES, which will generate the Groups, Directories,
954 # and Class Hierarchy pages using a tree view instead of an ordered list;
955 # ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
956 # disables this behavior completely. For backwards compatibility with previous
957 # releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
958 # respectively.
959
960 GENERATE_TREEVIEW = NO
961
962 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
963 # used to set the initial width (in pixels) of the frame in which the tree
964 # is shown.
965
966 TREEVIEW_WIDTH = 250
967
968 # Use this tag to change the font size of Latex formulas included
969 # as images in the HTML documentation. The default is 10. Note that
970 # when you change the font size after a successful doxygen run you need
971 # to manually remove any form_*.png images from the HTML output directory
972 # to force them to be regenerated.
973
974 FORMULA_FONTSIZE = 10
975
976 #---------------------------------------------------------------------------
977 # configuration options related to the LaTeX output
978 #---------------------------------------------------------------------------
979
980 # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
981 # generate Latex output.
982
983 GENERATE_LATEX = NO
984
985 # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
986 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
987 # put in front of it. If left blank `latex' will be used as the default path.
988
989 LATEX_OUTPUT = latex
990
991 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
992 # invoked. If left blank `latex' will be used as the default command name.
993
994 LATEX_CMD_NAME = latex
995
996 # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
997 # generate index for LaTeX. If left blank `makeindex' will be used as the
998 # default command name.
999
1000 MAKEINDEX_CMD_NAME = makeindex
1001
1002 # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
1003 # LaTeX documents. This may be useful for small projects and may help to
1004 # save some trees in general.
1005
1006 COMPACT_LATEX = NO
1007
1008 # The PAPER_TYPE tag can be used to set the paper type that is used
1009 # by the printer. Possible values are: a4, a4wide, letter, legal and
1010 # executive. If left blank a4wide will be used.
1011
1012 PAPER_TYPE = a4wide
1013
1014 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
1015 # packages that should be included in the LaTeX output.
1016
1017 EXTRA_PACKAGES =
1018
1019 # The LATEX_HEADER tag can be used to specify a personal LaTeX header for
1020 # the generated latex document. The header should contain everything until
1021 # the first chapter. If it is left blank doxygen will generate a
1022 # standard header. Notice: only use this tag if you know what you are doing!
1023
1024 LATEX_HEADER =
1025
1026 # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
1027 # is prepared for conversion to pdf (using ps2pdf). The pdf file will
1028 # contain links (just like the HTML output) instead of page references
1029 # This makes the output suitable for online browsing using a pdf viewer.
1030
1031 PDF_HYPERLINKS = NO
1032
1033 # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
1034 # plain latex in the generated Makefile. Set this option to YES to get a
1035 # higher quality PDF documentation.
1036
1037 USE_PDFLATEX = NO
1038
1039 # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
1040 # command to the generated LaTeX files. This will instruct LaTeX to keep
1041 # running if errors occur, instead of asking the user for help.
1042 # This option is also used when generating formulas in HTML.
1043
1044 LATEX_BATCHMODE = NO
1045
1046 # If LATEX_HIDE_INDICES is set to YES then doxygen will not
1047 # include the index chapters (such as File Index, Compound Index, etc.)
1048 # in the output.
1049
1050 LATEX_HIDE_INDICES = NO
1051
1052 #---------------------------------------------------------------------------
1053 # configuration options related to the RTF output
1054 #---------------------------------------------------------------------------
1055
1056 # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
1057 # The RTF output is optimized for Word 97 and may not look very pretty with
1058 # other RTF readers or editors.
1059
1060 GENERATE_RTF = NO
1061
1062 # The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
1063 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1064 # put in front of it. If left blank `rtf' will be used as the default path.
1065
1066 RTF_OUTPUT = rtf
1067
1068 # If the COMPACT_RTF tag is set to YES Doxygen generates more compact
1069 # RTF documents. This may be useful for small projects and may help to
1070 # save some trees in general.
1071
1072 COMPACT_RTF = NO
1073
1074 # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
1075 # will contain hyperlink fields. The RTF file will
1076 # contain links (just like the HTML output) instead of page references.
1077 # This makes the output suitable for online browsing using WORD or other
1078 # programs which support those fields.
1079 # Note: wordpad (write) and others do not support links.
1080
1081 RTF_HYPERLINKS = NO
1082
1083 # Load stylesheet definitions from file. Syntax is similar to doxygen's
1084 # config file, i.e. a series of assignments. You only have to provide
1085 # replacements, missing definitions are set to their default value.
1086
1087 RTF_STYLESHEET_FILE =
1088
1089 # Set optional variables used in the generation of an rtf document.
1090 # Syntax is similar to doxygen's config file.
1091
1092 RTF_EXTENSIONS_FILE =
1093
1094 #---------------------------------------------------------------------------
1095 # configuration options related to the man page output
1096 #---------------------------------------------------------------------------
1097
1098 # If the GENERATE_MAN tag is set to YES (the default) Doxygen will
1099 # generate man pages
1100
1101 GENERATE_MAN = NO
1102
1103 # The MAN_OUTPUT tag is used to specify where the man pages will be put.
1104 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1105 # put in front of it. If left blank `man' will be used as the default path.
1106
1107 MAN_OUTPUT = man
1108
1109 # The MAN_EXTENSION tag determines the extension that is added to
1110 # the generated man pages (default is the subroutine's section .3)
1111
1112 MAN_EXTENSION = .3
1113
1114 # If the MAN_LINKS tag is set to YES and Doxygen generates man output,
1115 # then it will generate one additional man file for each entity
1116 # documented in the real man page(s). These additional files
1117 # only source the real man page, but without them the man command
1118 # would be unable to find the correct page. The default is NO.
1119
1120 MAN_LINKS = NO
1121
1122 #---------------------------------------------------------------------------
1123 # configuration options related to the XML output
1124 #---------------------------------------------------------------------------
1125
1126 # If the GENERATE_XML tag is set to YES Doxygen will
1127 # generate an XML file that captures the structure of
1128 # the code including all documentation.
1129
1130 GENERATE_XML = NO
1131
1132 # The XML_OUTPUT tag is used to specify where the XML pages will be put.
1133 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
1134 # put in front of it. If left blank `xml' will be used as the default path.
1135
1136 XML_OUTPUT = xml
1137
1138 # The XML_SCHEMA tag can be used to specify an XML schema,
1139 # which can be used by a validating XML parser to check the
1140 # syntax of the XML files.
1141
1142 XML_SCHEMA =
1143
1144 # The XML_DTD tag can be used to specify an XML DTD,
1145 # which can be used by a validating XML parser to check the
1146 # syntax of the XML files.
1147
1148 XML_DTD =
1149
1150 # If the XML_PROGRAMLISTING tag is set to YES Doxygen will
1151 # dump the program listings (including syntax highlighting
1152 # and cross-referencing information) to the XML output. Note that
1153 # enabling this will significantly increase the size of the XML output.
1154
1155 XML_PROGRAMLISTING = YES
1156
1157 #---------------------------------------------------------------------------
1158 # configuration options for the AutoGen Definitions output
1159 #---------------------------------------------------------------------------
1160
1161 # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
1162 # generate an AutoGen Definitions (see autogen.sf.net) file
1163 # that captures the structure of the code including all
1164 # documentation. Note that this feature is still experimental
1165 # and incomplete at the moment.
1166
1167 GENERATE_AUTOGEN_DEF = NO
1168
1169 #---------------------------------------------------------------------------
1170 # configuration options related to the Perl module output
1171 #---------------------------------------------------------------------------
1172
1173 # If the GENERATE_PERLMOD tag is set to YES Doxygen will
1174 # generate a Perl module file that captures the structure of
1175 # the code including all documentation. Note that this
1176 # feature is still experimental and incomplete at the
1177 # moment.
1178
1179 GENERATE_PERLMOD = NO
1180
1181 # If the PERLMOD_LATEX tag is set to YES Doxygen will generate
1182 # the necessary Makefile rules, Perl scripts and LaTeX code to be able
1183 # to generate PDF and DVI output from the Perl module output.
1184
1185 PERLMOD_LATEX = NO
1186
1187 # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
1188 # nicely formatted so it can be parsed by a human reader.
1189 # This is useful
1190 # if you want to understand what is going on.
1191 # On the other hand, if this
1192 # tag is set to NO the size of the Perl module output will be much smaller
1193 # and Perl will parse it just the same.
1194
1195 PERLMOD_PRETTY = YES
1196
1197 # The names of the make variables in the generated doxyrules.make file
1198 # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
1199 # This is useful so different doxyrules.make files included by the same
1200 # Makefile don't overwrite each other's variables.
1201
1202 PERLMOD_MAKEVAR_PREFIX =
1203
1204 #---------------------------------------------------------------------------
1205 # Configuration options related to the preprocessor
1206 #---------------------------------------------------------------------------
1207
1208 # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
1209 # evaluate all C-preprocessor directives found in the sources and include
1210 # files.
1211
1212 ENABLE_PREPROCESSING = YES
1213
1214 # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
1215 # names in the source code. If set to NO (the default) only conditional
1216 # compilation will be performed. Macro expansion can be done in a controlled
1217 # way by setting EXPAND_ONLY_PREDEF to YES.
1218
1219 MACRO_EXPANSION = NO
1220
1221 # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
1222 # then the macro expansion is limited to the macros specified with the
1223 # PREDEFINED and EXPAND_AS_DEFINED tags.
1224
1225 EXPAND_ONLY_PREDEF = NO
1226
1227 # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
1228 # in the INCLUDE_PATH (see below) will be search if a #include is found.
1229
1230 SEARCH_INCLUDES = YES
1231
1232 # The INCLUDE_PATH tag can be used to specify one or more directories that
1233 # contain include files that are not input files but should be processed by
1234 # the preprocessor.
1235
1236 INCLUDE_PATH =
1237
1238 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
1239 # patterns (like *.h and *.hpp) to filter out the header-files in the
1240 # directories. If left blank, the patterns specified with FILE_PATTERNS will
1241 # be used.
1242
1243 INCLUDE_FILE_PATTERNS =
1244
1245 # The PREDEFINED tag can be used to specify one or more macro names that
1246 # are defined before the preprocessor is started (similar to the -D option of
1247 # gcc). The argument of the tag is a list of macros of the form: name
1248 # or name=definition (no spaces). If the definition and the = are
1249 # omitted =1 is assumed. To prevent a macro definition from being
1250 # undefined via #undef or recursively expanded use the := operator
1251 # instead of the = operator.
1252
1253 PREDEFINED =
1254
1255 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
1256 # this tag can be used to specify a list of macro names that should be expanded.
1257 # The macro definition that is found in the sources will be used.
1258 # Use the PREDEFINED tag if you want to use a different macro definition.
1259
1260 EXPAND_AS_DEFINED =
1261
1262 # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
1263 # doxygen's preprocessor will remove all function-like macros that are alone
1264 # on a line, have an all uppercase name, and do not end with a semicolon. Such
1265 # function macros are typically used for boiler-plate code, and will confuse
1266 # the parser if not removed.
1267
1268 SKIP_FUNCTION_MACROS = YES
1269
1270 #---------------------------------------------------------------------------
1271 # Configuration::additions related to external references
1272 #---------------------------------------------------------------------------
1273
1274 # The TAGFILES option can be used to specify one or more tagfiles.
1275 # Optionally an initial location of the external documentation
1276 # can be added for each tagfile. The format of a tag file without
1277 # this location is as follows:
1278 #
1279 # TAGFILES = file1 file2 ...
1280 # Adding location for the tag files is done as follows:
1281 #
1282 # TAGFILES = file1=loc1 "file2 = loc2" ...
1283 # where "loc1" and "loc2" can be relative or absolute paths or
1284 # URLs. If a location is present for each tag, the installdox tool
1285 # does not have to be run to correct the links.
1286 # Note that each tag file must have a unique name
1287 # (where the name does NOT include the path)
1288 # If a tag file is not located in the directory in which doxygen
1289 # is run, you must also specify the path to the tagfile here.
1290
1291 TAGFILES =
1292
1293 # When a file name is specified after GENERATE_TAGFILE, doxygen will create
1294 # a tag file that is based on the input files it reads.
1295
1296 GENERATE_TAGFILE =
1297
1298 # If the ALLEXTERNALS tag is set to YES all external classes will be listed
1299 # in the class index. If set to NO only the inherited external classes
1300 # will be listed.
1301
1302 ALLEXTERNALS = NO
1303
1304 # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
1305 # in the modules index. If set to NO, only the current project's groups will
1306 # be listed.
1307
1308 EXTERNAL_GROUPS = YES
1309
1310 # The PERL_PATH should be the absolute path and name of the perl script
1311 # interpreter (i.e. the result of `which perl').
1312
1313 PERL_PATH = /usr/bin/perl
1314
1315 #---------------------------------------------------------------------------
1316 # Configuration options related to the dot tool
1317 #---------------------------------------------------------------------------
1318
1319 # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
1320 # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
1321 # or super classes. Setting the tag to NO turns the diagrams off. Note that
1322 # this option is superseded by the HAVE_DOT option below. This is only a
1323 # fallback. It is recommended to install and use dot, since it yields more
1324 # powerful graphs.
1325
1326 CLASS_DIAGRAMS = YES
1327
1328 # You can define message sequence charts within doxygen comments using the \msc
1329 # command. Doxygen will then run the mscgen tool (see
1330 # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
1331 # documentation. The MSCGEN_PATH tag allows you to specify the directory where
1332 # the mscgen tool resides. If left empty the tool is assumed to be found in the
1333 # default search path.
1334
1335 MSCGEN_PATH =
1336
1337 # If set to YES, the inheritance and collaboration graphs will hide
1338 # inheritance and usage relations if the target is undocumented
1339 # or is not a class.
1340
1341 HIDE_UNDOC_RELATIONS = YES
1342
1343 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
1344 # available from the path. This tool is part of Graphviz, a graph visualization
1345 # toolkit from AT&T and Lucent Bell Labs. The other options in this section
1346 # have no effect if this option is set to NO (the default)
1347
1348 HAVE_DOT = YES
1349
1350 # By default doxygen will write a font called FreeSans.ttf to the output
1351 # directory and reference it in all dot files that doxygen generates. This
1352 # font does not include all possible unicode characters however, so when you need
1353 # these (or just want a differently looking font) you can specify the font name
1354 # using DOT_FONTNAME. You need need to make sure dot is able to find the font,
1355 # which can be done by putting it in a standard location or by setting the
1356 # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
1357 # containing the font.
1358
1359 DOT_FONTNAME = FreeSans
1360
1361 # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
1362 # The default size is 10pt.
1363
1364 DOT_FONTSIZE = 10
1365
1366 # By default doxygen will tell dot to use the output directory to look for the
1367 # FreeSans.ttf font (which doxygen will put there itself). If you specify a
1368 # different font using DOT_FONTNAME you can set the path where dot
1369 # can find it using this tag.
1370
1371 DOT_FONTPATH =
1372
1373 # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
1374 # will generate a graph for each documented class showing the direct and
1375 # indirect inheritance relations. Setting this tag to YES will force the
1376 # the CLASS_DIAGRAMS tag to NO.
1377
1378 CLASS_GRAPH = NO
1379
1380 # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
1381 # will generate a graph for each documented class showing the direct and
1382 # indirect implementation dependencies (inheritance, containment, and
1383 # class references variables) of the class with other documented classes.
1384
1385 COLLABORATION_GRAPH = NO
1386
1387 # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
1388 # will generate a graph for groups, showing the direct groups dependencies
1389
1390 GROUP_GRAPHS = YES
1391
1392 # If the UML_LOOK tag is set to YES doxygen will generate inheritance and
1393 # collaboration diagrams in a style similar to the OMG's Unified Modeling
1394 # Language.
1395
1396 UML_LOOK = YES
1397
1398 # If set to YES, the inheritance and collaboration graphs will show the
1399 # relations between templates and their instances.
1400
1401 TEMPLATE_RELATIONS = NO
1402
1403 # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
1404 # tags are set to YES then doxygen will generate a graph for each documented
1405 # file showing the direct and indirect include dependencies of the file with
1406 # other documented files.
1407
1408 INCLUDE_GRAPH = YES
1409
1410 # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
1411 # HAVE_DOT tags are set to YES then doxygen will generate a graph for each
1412 # documented header file showing the documented files that directly or
1413 # indirectly include this file.
1414
1415 INCLUDED_BY_GRAPH = YES
1416
1417 # If the CALL_GRAPH and HAVE_DOT options are set to YES then
1418 # doxygen will generate a call dependency graph for every global function
1419 # or class method. Note that enabling this option will significantly increase
1420 # the time of a run. So in most cases it will be better to enable call graphs
1421 # for selected functions only using the \callgraph command.
1422
1423 CALL_GRAPH = NO
1424
1425 # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
1426 # doxygen will generate a caller dependency graph for every global function
1427 # or class method. Note that enabling this option will significantly increase
1428 # the time of a run. So in most cases it will be better to enable caller
1429 # graphs for selected functions only using the \callergraph command.
1430
1431 CALLER_GRAPH = NO
1432
1433 # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
1434 # will graphical hierarchy of all classes instead of a textual one.
1435
1436 GRAPHICAL_HIERARCHY = NO
1437
1438 # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
1439 # then doxygen will show the dependencies a directory has on other directories
1440 # in a graphical way. The dependency relations are determined by the #include
1441 # relations between the files in the directories.
1442
1443 DIRECTORY_GRAPH = YES
1444
1445 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
1446 # generated by dot. Possible values are png, jpg, or gif
1447 # If left blank png will be used.
1448
1449 DOT_IMAGE_FORMAT = png
1450
1451 # The tag DOT_PATH can be used to specify the path where the dot tool can be
1452 # found. If left blank, it is assumed the dot tool can be found in the path.
1453
1454 DOT_PATH =
1455
1456 # The DOTFILE_DIRS tag can be used to specify one or more directories that
1457 # contain dot files that are included in the documentation (see the
1458 # \dotfile command).
1459
1460 DOTFILE_DIRS =
1461
1462 # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
1463 # nodes that will be shown in the graph. If the number of nodes in a graph
1464 # becomes larger than this value, doxygen will truncate the graph, which is
1465 # visualized by representing a node as a red box. Note that doxygen if the
1466 # number of direct children of the root node in a graph is already larger than
1467 # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
1468 # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
1469
1470 DOT_GRAPH_MAX_NODES = 50
1471
1472 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
1473 # graphs generated by dot. A depth value of 3 means that only nodes reachable
1474 # from the root by following a path via at most 3 edges will be shown. Nodes
1475 # that lay further from the root node will be omitted. Note that setting this
1476 # option to 1 or 2 may greatly reduce the computation time needed for large
1477 # code bases. Also note that the size of a graph can be further restricted by
1478 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
1479
1480 MAX_DOT_GRAPH_DEPTH = 0
1481
1482 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
1483 # background. This is disabled by default, because dot on Windows does not
1484 # seem to support this out of the box. Warning: Depending on the platform used,
1485 # enabling this option may lead to badly anti-aliased labels on the edges of
1486 # a graph (i.e. they become hard to read).
1487
1488 DOT_TRANSPARENT = NO
1489
1490 # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
1491 # files in one run (i.e. multiple -o and -T options on the command line). This
1492 # makes dot run faster, but since only newer versions of dot (>1.8.10)
1493 # support this, this feature is disabled by default.
1494
1495 DOT_MULTI_TARGETS = NO
1496
1497 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
1498 # generate a legend page explaining the meaning of the various boxes and
1499 # arrows in the dot generated graphs.
1500
1501 GENERATE_LEGEND = YES
1502
1503 # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
1504 # remove the intermediate dot files that are used to generate
1505 # the various graphs.
1506
1507 DOT_CLEANUP = YES
1508
1509 #---------------------------------------------------------------------------
1510 # Options related to the search engine
1511 #---------------------------------------------------------------------------
1512
1513 # The SEARCHENGINE tag specifies whether or not a search engine should be
1514 # used. If set to NO the values of all tags below this one will be ignored.
1515
1516 SEARCHENGINE = NO
0 SUBDIRS = doc dpip src dpid dpi
0 SUBDIRS = lout dw dlib dpip src doc dpid dpi test
11
2 EXTRA_DIST = ChangeLog.old dillorc install-dpi-local
2 EXTRA_DIST = Doxyfile dillorc install-dpi-local d_size.h
33
44 sysconf_DATA = dillorc
+0
-644
Makefile.in less more
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 srcdir = @srcdir@
17 top_srcdir = @top_srcdir@
18 VPATH = @srcdir@
19 pkgdatadir = $(datadir)/@PACKAGE@
20 pkglibdir = $(libdir)/@PACKAGE@
21 pkgincludedir = $(includedir)/@PACKAGE@
22 top_builddir = .
23 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
24 INSTALL = @INSTALL@
25 install_sh_DATA = $(install_sh) -c -m 644
26 install_sh_PROGRAM = $(install_sh) -c
27 install_sh_SCRIPT = $(install_sh) -c
28 INSTALL_HEADER = $(INSTALL_DATA)
29 transform = $(program_transform_name)
30 NORMAL_INSTALL = :
31 PRE_INSTALL = :
32 POST_INSTALL = :
33 NORMAL_UNINSTALL = :
34 PRE_UNINSTALL = :
35 POST_UNINSTALL = :
36 build_triplet = @build@
37 host_triplet = @host@
38 target_triplet = @target@
39 DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
40 $(srcdir)/Makefile.in $(srcdir)/config.h.in \
41 $(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
42 config.guess config.sub depcomp install-sh missing
43 subdir = .
44 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
45 am__aclocal_m4_deps = $(top_srcdir)/configure.in
46 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
47 $(ACLOCAL_M4)
48 am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
49 configure.lineno configure.status.lineno
50 mkinstalldirs = $(install_sh) -d
51 CONFIG_HEADER = config.h
52 CONFIG_CLEAN_FILES =
53 SOURCES =
54 DIST_SOURCES =
55 RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
56 html-recursive info-recursive install-data-recursive \
57 install-exec-recursive install-info-recursive \
58 install-recursive installcheck-recursive installdirs-recursive \
59 pdf-recursive ps-recursive uninstall-info-recursive \
60 uninstall-recursive
61 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
62 am__vpath_adj = case $$p in \
63 $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
64 *) f=$$p;; \
65 esac;
66 am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
67 am__installdirs = "$(DESTDIR)$(sysconfdir)"
68 sysconfDATA_INSTALL = $(INSTALL_DATA)
69 DATA = $(sysconf_DATA)
70 ETAGS = etags
71 CTAGS = ctags
72 DIST_SUBDIRS = $(SUBDIRS)
73 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
74 distdir = $(PACKAGE)-$(VERSION)
75 top_distdir = $(distdir)
76 am__remove_distdir = \
77 { test ! -d $(distdir) \
78 || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
79 && rm -fr $(distdir); }; }
80 DIST_ARCHIVES = $(distdir).tar.gz
81 GZIP_ENV = --best
82 distuninstallcheck_listfiles = find . -type f -print
83 distcleancheck_listfiles = find . -type f -print
84 ACLOCAL = @ACLOCAL@
85 AMDEP_FALSE = @AMDEP_FALSE@
86 AMDEP_TRUE = @AMDEP_TRUE@
87 AMTAR = @AMTAR@
88 AUTOCONF = @AUTOCONF@
89 AUTOHEADER = @AUTOHEADER@
90 AUTOMAKE = @AUTOMAKE@
91 AWK = @AWK@
92 CC = @CC@
93 CCDEPMODE = @CCDEPMODE@
94 CFLAGS = @CFLAGS@
95 CPP = @CPP@
96 CPPFLAGS = @CPPFLAGS@
97 CXX = @CXX@
98 CXXDEPMODE = @CXXDEPMODE@
99 CXXFLAGS = @CXXFLAGS@
100 CYGPATH_W = @CYGPATH_W@
101 DEFS = @DEFS@
102 DEPDIR = @DEPDIR@
103 DLGUI_FALSE = @DLGUI_FALSE@
104 DLGUI_TRUE = @DLGUI_TRUE@
105 ECHO_C = @ECHO_C@
106 ECHO_N = @ECHO_N@
107 ECHO_T = @ECHO_T@
108 EGREP = @EGREP@
109 EXEEXT = @EXEEXT@
110 GLIB_CFLAGS = @GLIB_CFLAGS@
111 GLIB_CONFIG = @GLIB_CONFIG@
112 GLIB_LIBS = @GLIB_LIBS@
113 GTK_CFLAGS = @GTK_CFLAGS@
114 GTK_CONFIG = @GTK_CONFIG@
115 GTK_LIBS = @GTK_LIBS@
116 INSTALL_DATA = @INSTALL_DATA@
117 INSTALL_PROGRAM = @INSTALL_PROGRAM@
118 INSTALL_SCRIPT = @INSTALL_SCRIPT@
119 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
120 LDFLAGS = @LDFLAGS@
121 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
122 LIBFLTK_LIBS = @LIBFLTK_LIBS@
123 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
124 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
125 LIBJPEG_LIBS = @LIBJPEG_LIBS@
126 LIBOBJS = @LIBOBJS@
127 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
128 LIBPNG_LIBS = @LIBPNG_LIBS@
129 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
130 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
131 LIBS = @LIBS@
132 LIBSSL_LIBS = @LIBSSL_LIBS@
133 LIBZ_LIBS = @LIBZ_LIBS@
134 LTLIBOBJS = @LTLIBOBJS@
135 MAKEINFO = @MAKEINFO@
136 OBJEXT = @OBJEXT@
137 PACKAGE = @PACKAGE@
138 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
139 PACKAGE_NAME = @PACKAGE_NAME@
140 PACKAGE_STRING = @PACKAGE_STRING@
141 PACKAGE_TARNAME = @PACKAGE_TARNAME@
142 PACKAGE_VERSION = @PACKAGE_VERSION@
143 PATH_SEPARATOR = @PATH_SEPARATOR@
144 RANLIB = @RANLIB@
145 SET_MAKE = @SET_MAKE@
146 SHELL = @SHELL@
147 STRIP = @STRIP@
148 VERSION = @VERSION@
149 ac_ct_CC = @ac_ct_CC@
150 ac_ct_CXX = @ac_ct_CXX@
151 ac_ct_RANLIB = @ac_ct_RANLIB@
152 ac_ct_STRIP = @ac_ct_STRIP@
153 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
154 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
155 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
156 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
157 am__include = @am__include@
158 am__leading_dot = @am__leading_dot@
159 am__quote = @am__quote@
160 am__tar = @am__tar@
161 am__untar = @am__untar@
162 bindir = @bindir@
163 build = @build@
164 build_alias = @build_alias@
165 build_cpu = @build_cpu@
166 build_os = @build_os@
167 build_vendor = @build_vendor@
168 datadir = @datadir@
169 exec_prefix = @exec_prefix@
170 host = @host@
171 host_alias = @host_alias@
172 host_cpu = @host_cpu@
173 host_os = @host_os@
174 host_vendor = @host_vendor@
175 includedir = @includedir@
176 infodir = @infodir@
177 install_sh = @install_sh@
178 libdir = @libdir@
179 libexecdir = @libexecdir@
180 localstatedir = @localstatedir@
181 mandir = @mandir@
182 mkdir_p = @mkdir_p@
183 oldincludedir = @oldincludedir@
184 prefix = @prefix@
185 program_transform_name = @program_transform_name@
186 sbindir = @sbindir@
187 sharedstatedir = @sharedstatedir@
188 sysconfdir = @sysconfdir@
189 target = @target@
190 target_alias = @target_alias@
191 target_cpu = @target_cpu@
192 target_os = @target_os@
193 target_vendor = @target_vendor@
194 SUBDIRS = doc dpip src dpid dpi
195 EXTRA_DIST = ChangeLog.old dillorc install-dpi-local
196 sysconf_DATA = dillorc
197 all: config.h
198 $(MAKE) $(AM_MAKEFLAGS) all-recursive
199
200 .SUFFIXES:
201 am--refresh:
202 @:
203 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
204 @for dep in $?; do \
205 case '$(am__configure_deps)' in \
206 *$$dep*) \
207 echo ' cd $(srcdir) && $(AUTOMAKE) --gnu '; \
208 cd $(srcdir) && $(AUTOMAKE) --gnu \
209 && exit 0; \
210 exit 1;; \
211 esac; \
212 done; \
213 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \
214 cd $(top_srcdir) && \
215 $(AUTOMAKE) --gnu Makefile
216 .PRECIOUS: Makefile
217 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
218 @case '$?' in \
219 *config.status*) \
220 echo ' $(SHELL) ./config.status'; \
221 $(SHELL) ./config.status;; \
222 *) \
223 echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
224 cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
225 esac;
226
227 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
228 $(SHELL) ./config.status --recheck
229
230 $(top_srcdir)/configure: $(am__configure_deps)
231 cd $(srcdir) && $(AUTOCONF)
232 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
233 cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
234
235 config.h: stamp-h1
236 @if test ! -f $@; then \
237 rm -f stamp-h1; \
238 $(MAKE) stamp-h1; \
239 else :; fi
240
241 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
242 @rm -f stamp-h1
243 cd $(top_builddir) && $(SHELL) ./config.status config.h
244 $(srcdir)/config.h.in: $(am__configure_deps)
245 cd $(top_srcdir) && $(AUTOHEADER)
246 rm -f stamp-h1
247 touch $@
248
249 distclean-hdr:
250 -rm -f config.h stamp-h1
251 uninstall-info-am:
252 install-sysconfDATA: $(sysconf_DATA)
253 @$(NORMAL_INSTALL)
254 test -z "$(sysconfdir)" || $(mkdir_p) "$(DESTDIR)$(sysconfdir)"
255 @list='$(sysconf_DATA)'; for p in $$list; do \
256 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
257 f=$(am__strip_dir) \
258 echo " $(sysconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(sysconfdir)/$$f'"; \
259 $(sysconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(sysconfdir)/$$f"; \
260 done
261
262 uninstall-sysconfDATA:
263 @$(NORMAL_UNINSTALL)
264 @list='$(sysconf_DATA)'; for p in $$list; do \
265 f=$(am__strip_dir) \
266 echo " rm -f '$(DESTDIR)$(sysconfdir)/$$f'"; \
267 rm -f "$(DESTDIR)$(sysconfdir)/$$f"; \
268 done
269
270 # This directory's subdirectories are mostly independent; you can cd
271 # into them and run `make' without going through this Makefile.
272 # To change the values of `make' variables: instead of editing Makefiles,
273 # (1) if the variable is set in `config.status', edit `config.status'
274 # (which will cause the Makefiles to be regenerated when you run `make');
275 # (2) otherwise, pass the desired values on the `make' command line.
276 $(RECURSIVE_TARGETS):
277 @failcom='exit 1'; \
278 for f in x $$MAKEFLAGS; do \
279 case $$f in \
280 *=* | --[!k]*);; \
281 *k*) failcom='fail=yes';; \
282 esac; \
283 done; \
284 dot_seen=no; \
285 target=`echo $@ | sed s/-recursive//`; \
286 list='$(SUBDIRS)'; for subdir in $$list; do \
287 echo "Making $$target in $$subdir"; \
288 if test "$$subdir" = "."; then \
289 dot_seen=yes; \
290 local_target="$$target-am"; \
291 else \
292 local_target="$$target"; \
293 fi; \
294 (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
295 || eval $$failcom; \
296 done; \
297 if test "$$dot_seen" = "no"; then \
298 $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
299 fi; test -z "$$fail"
300
301 mostlyclean-recursive clean-recursive distclean-recursive \
302 maintainer-clean-recursive:
303 @failcom='exit 1'; \
304 for f in x $$MAKEFLAGS; do \
305 case $$f in \
306 *=* | --[!k]*);; \
307 *k*) failcom='fail=yes';; \
308 esac; \
309 done; \
310 dot_seen=no; \
311 case "$@" in \
312 distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
313 *) list='$(SUBDIRS)' ;; \
314 esac; \
315 rev=''; for subdir in $$list; do \
316 if test "$$subdir" = "."; then :; else \
317 rev="$$subdir $$rev"; \
318 fi; \
319 done; \
320 rev="$$rev ."; \
321 target=`echo $@ | sed s/-recursive//`; \
322 for subdir in $$rev; do \
323 echo "Making $$target in $$subdir"; \
324 if test "$$subdir" = "."; then \
325 local_target="$$target-am"; \
326 else \
327 local_target="$$target"; \
328 fi; \
329 (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
330 || eval $$failcom; \
331 done && test -z "$$fail"
332 tags-recursive:
333 list='$(SUBDIRS)'; for subdir in $$list; do \
334 test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
335 done
336 ctags-recursive:
337 list='$(SUBDIRS)'; for subdir in $$list; do \
338 test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
339 done
340
341 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
342 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
343 unique=`for i in $$list; do \
344 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
345 done | \
346 $(AWK) ' { files[$$0] = 1; } \
347 END { for (i in files) print i; }'`; \
348 mkid -fID $$unique
349 tags: TAGS
350
351 TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
352 $(TAGS_FILES) $(LISP)
353 tags=; \
354 here=`pwd`; \
355 if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
356 include_option=--etags-include; \
357 empty_fix=.; \
358 else \
359 include_option=--include; \
360 empty_fix=; \
361 fi; \
362 list='$(SUBDIRS)'; for subdir in $$list; do \
363 if test "$$subdir" = .; then :; else \
364 test ! -f $$subdir/TAGS || \
365 tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
366 fi; \
367 done; \
368 list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
369 unique=`for i in $$list; do \
370 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
371 done | \
372 $(AWK) ' { files[$$0] = 1; } \
373 END { for (i in files) print i; }'`; \
374 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
375 test -n "$$unique" || unique=$$empty_fix; \
376 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
377 $$tags $$unique; \
378 fi
379 ctags: CTAGS
380 CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
381 $(TAGS_FILES) $(LISP)
382 tags=; \
383 here=`pwd`; \
384 list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
385 unique=`for i in $$list; do \
386 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
387 done | \
388 $(AWK) ' { files[$$0] = 1; } \
389 END { for (i in files) print i; }'`; \
390 test -z "$(CTAGS_ARGS)$$tags$$unique" \
391 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
392 $$tags $$unique
393
394 GTAGS:
395 here=`$(am__cd) $(top_builddir) && pwd` \
396 && cd $(top_srcdir) \
397 && gtags -i $(GTAGS_ARGS) $$here
398
399 distclean-tags:
400 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
401
402 distdir: $(DISTFILES)
403 $(am__remove_distdir)
404 mkdir $(distdir)
405 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
406 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
407 list='$(DISTFILES)'; for file in $$list; do \
408 case $$file in \
409 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
410 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
411 esac; \
412 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
413 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
414 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
415 dir="/$$dir"; \
416 $(mkdir_p) "$(distdir)$$dir"; \
417 else \
418 dir=''; \
419 fi; \
420 if test -d $$d/$$file; then \
421 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
422 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
423 fi; \
424 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
425 else \
426 test -f $(distdir)/$$file \
427 || cp -p $$d/$$file $(distdir)/$$file \
428 || exit 1; \
429 fi; \
430 done
431 list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
432 if test "$$subdir" = .; then :; else \
433 test -d "$(distdir)/$$subdir" \
434 || $(mkdir_p) "$(distdir)/$$subdir" \
435 || exit 1; \
436 distdir=`$(am__cd) $(distdir) && pwd`; \
437 top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
438 (cd $$subdir && \
439 $(MAKE) $(AM_MAKEFLAGS) \
440 top_distdir="$$top_distdir" \
441 distdir="$$distdir/$$subdir" \
442 distdir) \
443 || exit 1; \
444 fi; \
445 done
446 -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
447 ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
448 ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
449 ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
450 || chmod -R a+r $(distdir)
451 dist-gzip: distdir
452 tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
453 $(am__remove_distdir)
454
455 dist-bzip2: distdir
456 tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
457 $(am__remove_distdir)
458
459 dist-tarZ: distdir
460 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
461 $(am__remove_distdir)
462
463 dist-shar: distdir
464 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
465 $(am__remove_distdir)
466
467 dist-zip: distdir
468 -rm -f $(distdir).zip
469 zip -rq $(distdir).zip $(distdir)
470 $(am__remove_distdir)
471
472 dist dist-all: distdir
473 tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
474 $(am__remove_distdir)
475
476 # This target untars the dist file and tries a VPATH configuration. Then
477 # it guarantees that the distribution is self-contained by making another
478 # tarfile.
479 distcheck: dist
480 case '$(DIST_ARCHIVES)' in \
481 *.tar.gz*) \
482 GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
483 *.tar.bz2*) \
484 bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
485 *.tar.Z*) \
486 uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
487 *.shar.gz*) \
488 GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
489 *.zip*) \
490 unzip $(distdir).zip ;;\
491 esac
492 chmod -R a-w $(distdir); chmod a+w $(distdir)
493 mkdir $(distdir)/_build
494 mkdir $(distdir)/_inst
495 chmod a-w $(distdir)
496 dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
497 && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
498 && cd $(distdir)/_build \
499 && ../configure --srcdir=.. --prefix="$$dc_install_base" \
500 $(DISTCHECK_CONFIGURE_FLAGS) \
501 && $(MAKE) $(AM_MAKEFLAGS) \
502 && $(MAKE) $(AM_MAKEFLAGS) dvi \
503 && $(MAKE) $(AM_MAKEFLAGS) check \
504 && $(MAKE) $(AM_MAKEFLAGS) install \
505 && $(MAKE) $(AM_MAKEFLAGS) installcheck \
506 && $(MAKE) $(AM_MAKEFLAGS) uninstall \
507 && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
508 distuninstallcheck \
509 && chmod -R a-w "$$dc_install_base" \
510 && ({ \
511 (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
512 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
513 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
514 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
515 distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
516 } || { rm -rf "$$dc_destdir"; exit 1; }) \
517 && rm -rf "$$dc_destdir" \
518 && $(MAKE) $(AM_MAKEFLAGS) dist \
519 && rm -rf $(DIST_ARCHIVES) \
520 && $(MAKE) $(AM_MAKEFLAGS) distcleancheck
521 $(am__remove_distdir)
522 @(echo "$(distdir) archives ready for distribution: "; \
523 list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
524 sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
525 distuninstallcheck:
526 @cd $(distuninstallcheck_dir) \
527 && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
528 || { echo "ERROR: files left after uninstall:" ; \
529 if test -n "$(DESTDIR)"; then \
530 echo " (check DESTDIR support)"; \
531 fi ; \
532 $(distuninstallcheck_listfiles) ; \
533 exit 1; } >&2
534 distcleancheck: distclean
535 @if test '$(srcdir)' = . ; then \
536 echo "ERROR: distcleancheck can only run from a VPATH build" ; \
537 exit 1 ; \
538 fi
539 @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
540 || { echo "ERROR: files left in build directory after distclean:" ; \
541 $(distcleancheck_listfiles) ; \
542 exit 1; } >&2
543 check-am: all-am
544 check: check-recursive
545 all-am: Makefile $(DATA) config.h
546 installdirs: installdirs-recursive
547 installdirs-am:
548 for dir in "$(DESTDIR)$(sysconfdir)"; do \
549 test -z "$$dir" || $(mkdir_p) "$$dir"; \
550 done
551 install: install-recursive
552 install-exec: install-exec-recursive
553 install-data: install-data-recursive
554 uninstall: uninstall-recursive
555
556 install-am: all-am
557 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
558
559 installcheck: installcheck-recursive
560 install-strip:
561 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
562 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
563 `test -z '$(STRIP)' || \
564 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
565 mostlyclean-generic:
566
567 clean-generic:
568
569 distclean-generic:
570 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
571
572 maintainer-clean-generic:
573 @echo "This command is intended for maintainers to use"
574 @echo "it deletes files that may require special tools to rebuild."
575 clean: clean-recursive
576
577 clean-am: clean-generic mostlyclean-am
578
579 distclean: distclean-recursive
580 -rm -f $(am__CONFIG_DISTCLEAN_FILES)
581 -rm -f Makefile
582 distclean-am: clean-am distclean-generic distclean-hdr distclean-tags
583
584 dvi: dvi-recursive
585
586 dvi-am:
587
588 html: html-recursive
589
590 info: info-recursive
591
592 info-am:
593
594 install-data-am:
595
596 install-exec-am: install-sysconfDATA
597
598 install-info: install-info-recursive
599
600 install-man:
601
602 installcheck-am:
603
604 maintainer-clean: maintainer-clean-recursive
605 -rm -f $(am__CONFIG_DISTCLEAN_FILES)
606 -rm -rf $(top_srcdir)/autom4te.cache
607 -rm -f Makefile
608 maintainer-clean-am: distclean-am maintainer-clean-generic
609
610 mostlyclean: mostlyclean-recursive
611
612 mostlyclean-am: mostlyclean-generic
613
614 pdf: pdf-recursive
615
616 pdf-am:
617
618 ps: ps-recursive
619
620 ps-am:
621
622 uninstall-am: uninstall-info-am uninstall-sysconfDATA
623
624 uninstall-info: uninstall-info-recursive
625
626 .PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \
627 check-am clean clean-generic clean-recursive ctags \
628 ctags-recursive dist dist-all dist-bzip2 dist-gzip dist-shar \
629 dist-tarZ dist-zip distcheck distclean distclean-generic \
630 distclean-hdr distclean-recursive distclean-tags \
631 distcleancheck distdir distuninstallcheck dvi dvi-am html \
632 html-am info info-am install install-am install-data \
633 install-data-am install-exec install-exec-am install-info \
634 install-info-am install-man install-strip install-sysconfDATA \
635 installcheck installcheck-am installdirs installdirs-am \
636 maintainer-clean maintainer-clean-generic \
637 maintainer-clean-recursive mostlyclean mostlyclean-generic \
638 mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \
639 uninstall uninstall-am uninstall-info-am uninstall-sysconfDATA
640
641 # Tell versions [3.59,3.63) of GNU make to not export all variables.
642 # Otherwise a system limit (for SysV at least) may be exceeded.
643 .NOEXPORT:
00 ----
11 NEWS
22 ----
3
4 Nov 2000:
5
6 Introduced a new design layer between the IO and the Dw.
37
48 March 2001:
59
610 Finally the new dillo widget is ready! (dillo >= 0.4.0).
711 0.4.0 is able to cope with low resolution depths.
812
9
10 Nov 2000:
11
12 Introduced a new design layer between the IO and the Dw.
13
14 We'll try to focus on table rendering.
15
16
1713 Apr 2002:
1814
1915 We moved to: http://dillo.cipsga.org.br/
20
2116
2217 Dec 2002
2318
2722
2823 http://www.dillo.org/ (hosted at the wearlab!)
2924
25 Sep 2007
26
27 The new FLTK2-based dillo code is released! (under GPL3)
3028
3129 Jorge.-
32 jcid@inf.utfsm.cl
30 jcid@dillo.org
3331 Project maintainer, core developer, patcher, you name it! :-)
3432
0 =======
1 Dillo
2 =======
0 ===================
1 Dillo web browser
2 ===================
33
4 Dillo 2.2 features a major overhaul of the cookies subsystem,
5 a reimplementation of the Dillo Plugin (DPI) API, a configurable
6 connection limit, and various CSS improvements.
47
5 If you're using libc5, don't worry, I used to use libc5! If
6 you're brave, edit 'dns.c' and uncomment line 46, pray and
7 compile. If it doesn't work, email me (most probably a header
8 include problem); if you succeed, report it to me anyway!
8 This release is part of the Dillo2 series. In Dillo2, significant
9 parts of the codebase were ported to C++, and the rendering engine was
10 modified to use the FLTK2 library rather than GTK1.
911
10 Dillo needs the following packages:
11 glib
12 GTK+-1.2.x (2.0 will not compile).
13 jpeg, png (for displaying images).
14 wget (for getting files via ftp; wget is not compiled into
15 the dillo binaries)
16
17 If you get an error that dillo can't load the shared library,
18 try "setenv LD_LIBRARY_PATH /usr/local/lib", or wherever you have
19 your gtk+ libs.
12 Here's a list of some well-known problems:
2013
21 Dillo can be invoked with command line options. Just type
22 'dillo --help' to know about them.
14 * no FRAMES rendering
15 * no https (there's a barebones prototype).
2316
24 You may experience some font handling problems derived from
25 locale handling (for instance if you use LC_ALL=sv_SE). Just try
26 LC_ALL=POSIX and it will be fine.
17 -----
18 FLTK2
19 -----
2720
28 Once you have dillo running for the first time, the next step
29 is to read the help! (there's a link from the splash screen).
21 The FLTK2 library is statically linked into Dillo2.
22 You can get it from fltk.org.
23 The recommended version is >= r6916. e.g. in:
3024
31 This is release should be regarded as "very stable beta".
32
33
34 Here's a list of some well known problems:
35
36 * no FRAMES rendering
37 * no http authentication
38 * no https -- Read the online FAQ to enable a protoype.
25 http://fltk.org/software.php?VERSION=2.0.x-r6916
3926
4027
4128 ------------
4734 and install the dpis by running "./install-dpi-local" from the
4835 top directory (they will be installed under ~/.dillo).
4936
50 -----
51 Linux
52 -----
53
54 There's a small chance of experiencing dillo "freezes" with
55 certain linux kernels. This is a _kernel bug_, inside the 2.2.x
56 series up to 2.4.8. From 2.4.9 it has been corrected. The bug is
57 inside poll() and the best that you can do is to upgrade.
58 If you can't upgrade the kernel though, this workaround patch
59 for dillo may help you:
60 http://www.ime.usp.br/~livio/dillo/patches/poll-fix-fast.diff
6137
6238 ----
6339 *BSD
6440 ----
6541
66 Dillo compiles on *BSD systems; Starting from dillo-0.6.5, we
67 included special detection code (at ./configure time) that should
68 arrange everything for a clean compile, but please note that
69 you'll need GNU make.
42 Dillo compiles on *BSD systems; please report on this anyway,
43 and note that you'll need GNU make.
7044
71 From OpenBSD >= 3.3, gethost* calls are not thread safe. If
72 your dillo crashes or locks at times, just use:
45 If your dillo crashes or locks at times, just use:
7346
7447 ./configure --disable-threaded-dns
7548
8053 Solaris
8154 -------
8255
83 Dillo compiles and runs OK on Solaris but:
56 Dillo may compile and run OK on Solaris but (please report):
8457
8558 * use gmake (a symbolic link make -> gmake works OK)
86 * If you have trouble with GTK and GLIB from the Freeware
87 Companion CD, get the ones at www.sunfreeware.com instead.
88
89 These environment vars may help setting yours:
90
91 PATH=/usr/bin:/usr/sbin:/usr/dt/bin:/usr/openwin/bin:/usr/local/bin:/opt/sfw/bi:/usr/ccs/bin:/usr/ucb
92 LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib:/usr/local/lib/glib:/opt/sfw/lib:/usr/ucblib
9359
9460 Solaris is very inconsistent so you may need to add/remove:
9561
9864 at link time.
9965
10066
101 ---
102 gcc
103 ---
104
105 gcc 2.95.2 has problems with -O2:
106 "-O2 -mcpu=pentium" works fine.
107 "-O0 -mcpu=k6 -march=pentium -g3" works fine also.
108 "-O -mcpu=k6 -march=pentium -g3" works fine as well, but
109 "-O2 -mcpu=k6 -march=pentium -g3" doesn't work. Then again
110 "-O6 -mcpu=pentium -march=pentium -g3" DOES work.
111
112
113
114
11567 Jorge.-
11668 (jcid@dillo.org)
117 Mar 24, 2006
69 June, 2009
+0
-1277
aclocal.m4 less more
0 # generated automatically by aclocal 1.9.5 -*- Autoconf -*-
1
2 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
3 # 2005 Free Software Foundation, Inc.
4 # This file is free software; the Free Software Foundation
5 # gives unlimited permission to copy and/or distribute it,
6 # with or without modifications, as long as this notice is preserved.
7
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
10 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11 # PARTICULAR PURPOSE.
12
13 # Configure paths for GLIB
14 # Owen Taylor 97-11-3
15
16 dnl AM_PATH_GLIB([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
17 dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if "gmodule" or
18 dnl gthread is specified in MODULES, pass to glib-config
19 dnl
20 AC_DEFUN(AM_PATH_GLIB,
21 [dnl
22 dnl Get the cflags and libraries from the glib-config script
23 dnl
24 AC_ARG_WITH(glib-prefix,[ --with-glib-prefix=PFX Prefix where GLIB is installed (optional)],
25 glib_config_prefix="$withval", glib_config_prefix="")
26 AC_ARG_WITH(glib-exec-prefix,[ --with-glib-exec-prefix=PFX Exec prefix where GLIB is installed (optional)],
27 glib_config_exec_prefix="$withval", glib_config_exec_prefix="")
28 AC_ARG_ENABLE(glibtest, [ --disable-glibtest Do not try to compile and run a test GLIB program],
29 , enable_glibtest=yes)
30
31 if test x$glib_config_exec_prefix != x ; then
32 glib_config_args="$glib_config_args --exec-prefix=$glib_config_exec_prefix"
33 if test x${GLIB_CONFIG+set} != xset ; then
34 GLIB_CONFIG=$glib_config_exec_prefix/bin/glib-config
35 fi
36 fi
37 if test x$glib_config_prefix != x ; then
38 glib_config_args="$glib_config_args --prefix=$glib_config_prefix"
39 if test x${GLIB_CONFIG+set} != xset ; then
40 GLIB_CONFIG=$glib_config_prefix/bin/glib-config
41 fi
42 fi
43
44 for module in . $4
45 do
46 case "$module" in
47 gmodule)
48 glib_config_args="$glib_config_args gmodule"
49 ;;
50 gthread)
51 glib_config_args="$glib_config_args gthread"
52 ;;
53 esac
54 done
55
56 AC_PATH_PROG(GLIB_CONFIG, glib-config, no)
57 min_glib_version=ifelse([$1], ,0.99.7,$1)
58 AC_MSG_CHECKING(for GLIB - version >= $min_glib_version)
59 no_glib=""
60 if test "$GLIB_CONFIG" = "no" ; then
61 no_glib=yes
62 else
63 GLIB_CFLAGS=`$GLIB_CONFIG $glib_config_args --cflags`
64 GLIB_LIBS=`$GLIB_CONFIG $glib_config_args --libs`
65 glib_config_major_version=`$GLIB_CONFIG $glib_config_args --version | \
66 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
67 glib_config_minor_version=`$GLIB_CONFIG $glib_config_args --version | \
68 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
69 glib_config_micro_version=`$GLIB_CONFIG $glib_config_args --version | \
70 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
71 if test "x$enable_glibtest" = "xyes" ; then
72 ac_save_CFLAGS="$CFLAGS"
73 ac_save_LIBS="$LIBS"
74 CFLAGS="$CFLAGS $GLIB_CFLAGS"
75 LIBS="$GLIB_LIBS $LIBS"
76 dnl
77 dnl Now check if the installed GLIB is sufficiently new. (Also sanity
78 dnl checks the results of glib-config to some extent
79 dnl
80 rm -f conf.glibtest
81 AC_TRY_RUN([
82 #include <glib.h>
83 #include <stdio.h>
84 #include <stdlib.h>
85
86 int
87 main ()
88 {
89 int major, minor, micro;
90 char *tmp_version;
91
92 system ("touch conf.glibtest");
93
94 /* HP/UX 9 (%@#!) writes to sscanf strings */
95 tmp_version = g_strdup("$min_glib_version");
96 if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
97 printf("%s, bad version string\n", "$min_glib_version");
98 exit(1);
99 }
100
101 if ((glib_major_version != $glib_config_major_version) ||
102 (glib_minor_version != $glib_config_minor_version) ||
103 (glib_micro_version != $glib_config_micro_version))
104 {
105 printf("\n*** 'glib-config --version' returned %d.%d.%d, but GLIB (%d.%d.%d)\n",
106 $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version,
107 glib_major_version, glib_minor_version, glib_micro_version);
108 printf ("*** was found! If glib-config was correct, then it is best\n");
109 printf ("*** to remove the old version of GLIB. You may also be able to fix the error\n");
110 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
111 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
112 printf("*** required on your system.\n");
113 printf("*** If glib-config was wrong, set the environment variable GLIB_CONFIG\n");
114 printf("*** to point to the correct copy of glib-config, and remove the file config.cache\n");
115 printf("*** before re-running configure\n");
116 }
117 else if ((glib_major_version != GLIB_MAJOR_VERSION) ||
118 (glib_minor_version != GLIB_MINOR_VERSION) ||
119 (glib_micro_version != GLIB_MICRO_VERSION))
120 {
121 printf("*** GLIB header files (version %d.%d.%d) do not match\n",
122 GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
123 printf("*** library (version %d.%d.%d)\n",
124 glib_major_version, glib_minor_version, glib_micro_version);
125 }
126 else
127 {
128 if ((glib_major_version > major) ||
129 ((glib_major_version == major) && (glib_minor_version > minor)) ||
130 ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro)))
131 {
132 return 0;
133 }
134 else
135 {
136 printf("\n*** An old version of GLIB (%d.%d.%d) was found.\n",
137 glib_major_version, glib_minor_version, glib_micro_version);
138 printf("*** You need a version of GLIB newer than %d.%d.%d. The latest version of\n",
139 major, minor, micro);
140 printf("*** GLIB is always available from ftp://ftp.gtk.org.\n");
141 printf("***\n");
142 printf("*** If you have already installed a sufficiently new version, this error\n");
143 printf("*** probably means that the wrong copy of the glib-config shell script is\n");
144 printf("*** being found. The easiest way to fix this is to remove the old version\n");
145 printf("*** of GLIB, but you can also set the GLIB_CONFIG environment to point to the\n");
146 printf("*** correct copy of glib-config. (In this case, you will have to\n");
147 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
148 printf("*** so that the correct libraries are found at run-time))\n");
149 }
150 }
151 return 1;
152 }
153 ],, no_glib=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
154 CFLAGS="$ac_save_CFLAGS"
155 LIBS="$ac_save_LIBS"
156 fi
157 fi
158 if test "x$no_glib" = x ; then
159 AC_MSG_RESULT(yes)
160 ifelse([$2], , :, [$2])
161 else
162 AC_MSG_RESULT(no)
163 if test "$GLIB_CONFIG" = "no" ; then
164 echo "*** The glib-config script installed by GLIB could not be found"
165 echo "*** If GLIB was installed in PREFIX, make sure PREFIX/bin is in"
166 echo "*** your path, or set the GLIB_CONFIG environment variable to the"
167 echo "*** full path to glib-config."
168 else
169 if test -f conf.glibtest ; then
170 :
171 else
172 echo "*** Could not run GLIB test program, checking why..."
173 CFLAGS="$CFLAGS $GLIB_CFLAGS"
174 LIBS="$LIBS $GLIB_LIBS"
175 AC_TRY_LINK([
176 #include <glib.h>
177 #include <stdio.h>
178 ], [ return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ],
179 [ echo "*** The test program compiled, but did not run. This usually means"
180 echo "*** that the run-time linker is not finding GLIB or finding the wrong"
181 echo "*** version of GLIB. If it is not finding GLIB, you'll need to set your"
182 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
183 echo "*** to the installed location Also, make sure you have run ldconfig if that"
184 echo "*** is required on your system"
185 echo "***"
186 echo "*** If you have an old version installed, it is best to remove it, although"
187 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
188 echo "***"
189 echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
190 echo "*** came with the system with the command"
191 echo "***"
192 echo "*** rpm --erase --nodeps gtk gtk-devel" ],
193 [ echo "*** The test program failed to compile or link. See the file config.log for the"
194 echo "*** exact error that occured. This usually means GLIB was incorrectly installed"
195 echo "*** or that you have moved GLIB since it was installed. In the latter case, you"
196 echo "*** may want to edit the glib-config script: $GLIB_CONFIG" ])
197 CFLAGS="$ac_save_CFLAGS"
198 LIBS="$ac_save_LIBS"
199 fi
200 fi
201 GLIB_CFLAGS=""
202 GLIB_LIBS=""
203 ifelse([$3], , :, [$3])
204 fi
205 AC_SUBST(GLIB_CFLAGS)
206 AC_SUBST(GLIB_LIBS)
207 rm -f conf.glibtest
208 ])
209
210 # Configure paths for GTK+
211 # Owen Taylor 97-11-3
212
213 dnl AM_PATH_GTK([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
214 dnl Test for GTK, and define GTK_CFLAGS and GTK_LIBS
215 dnl
216 AC_DEFUN(AM_PATH_GTK,
217 [dnl
218 dnl Get the cflags and libraries from the gtk-config script
219 dnl
220 AC_ARG_WITH(gtk-prefix,[ --with-gtk-prefix=PFX Prefix where GTK is installed (optional)],
221 gtk_config_prefix="$withval", gtk_config_prefix="")
222 AC_ARG_WITH(gtk-exec-prefix,[ --with-gtk-exec-prefix=PFX Exec prefix where GTK is installed (optional)],
223 gtk_config_exec_prefix="$withval", gtk_config_exec_prefix="")
224 AC_ARG_ENABLE(gtktest, [ --disable-gtktest Do not try to compile and run a test GTK program],
225 , enable_gtktest=yes)
226
227 for module in . $4
228 do
229 case "$module" in
230 gthread)
231 gtk_config_args="$gtk_config_args gthread"
232 ;;
233 esac
234 done
235
236 if test x$gtk_config_exec_prefix != x ; then
237 gtk_config_args="$gtk_config_args --exec-prefix=$gtk_config_exec_prefix"
238 if test x${GTK_CONFIG+set} != xset ; then
239 GTK_CONFIG=$gtk_config_exec_prefix/bin/gtk-config
240 fi
241 fi
242 if test x$gtk_config_prefix != x ; then
243 gtk_config_args="$gtk_config_args --prefix=$gtk_config_prefix"
244 if test x${GTK_CONFIG+set} != xset ; then
245 GTK_CONFIG=$gtk_config_prefix/bin/gtk-config
246 fi
247 fi
248
249 AC_PATH_PROG(GTK_CONFIG, gtk-config, no)
250 min_gtk_version=ifelse([$1], ,0.99.7,$1)
251 AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
252 no_gtk=""
253 if test "$GTK_CONFIG" = "no" ; then
254 no_gtk=yes
255 else
256 GTK_CFLAGS=`$GTK_CONFIG $gtk_config_args --cflags`
257 GTK_LIBS=`$GTK_CONFIG $gtk_config_args --libs`
258 gtk_config_major_version=`$GTK_CONFIG $gtk_config_args --version | \
259 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
260 gtk_config_minor_version=`$GTK_CONFIG $gtk_config_args --version | \
261 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
262 gtk_config_micro_version=`$GTK_CONFIG $gtk_config_args --version | \
263 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
264 if test "x$enable_gtktest" = "xyes" ; then
265 ac_save_CFLAGS="$CFLAGS"
266 ac_save_LIBS="$LIBS"
267 CFLAGS="$CFLAGS $GTK_CFLAGS"
268 LIBS="$GTK_LIBS $LIBS"
269 dnl
270 dnl Now check if the installed GTK is sufficiently new. (Also sanity
271 dnl checks the results of gtk-config to some extent
272 dnl
273 rm -f conf.gtktest
274 AC_TRY_RUN([
275 #include <gtk/gtk.h>
276 #include <stdio.h>
277 #include <stdlib.h>
278
279 int
280 main ()
281 {
282 int major, minor, micro;
283 char *tmp_version;
284
285 system ("touch conf.gtktest");
286
287 /* HP/UX 9 (%@#!) writes to sscanf strings */
288 tmp_version = g_strdup("$min_gtk_version");
289 if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
290 printf("%s, bad version string\n", "$min_gtk_version");
291 exit(1);
292 }
293
294 if ((gtk_major_version != $gtk_config_major_version) ||
295 (gtk_minor_version != $gtk_config_minor_version) ||
296 (gtk_micro_version != $gtk_config_micro_version))
297 {
298 printf("\n*** 'gtk-config --version' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n",
299 $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
300 gtk_major_version, gtk_minor_version, gtk_micro_version);
301 printf ("*** was found! If gtk-config was correct, then it is best\n");
302 printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
303 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
304 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
305 printf("*** required on your system.\n");
306 printf("*** If gtk-config was wrong, set the environment variable GTK_CONFIG\n");
307 printf("*** to point to the correct copy of gtk-config, and remove the file config.cache\n");
308 printf("*** before re-running configure\n");
309 }
310 #if defined (GTK_MAJOR_VERSION) && defined (GTK_MINOR_VERSION) && defined (GTK_MICRO_VERSION)
311 else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
312 (gtk_minor_version != GTK_MINOR_VERSION) ||
313 (gtk_micro_version != GTK_MICRO_VERSION))
314 {
315 printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
316 GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
317 printf("*** library (version %d.%d.%d)\n",
318 gtk_major_version, gtk_minor_version, gtk_micro_version);
319 }
320 #endif /* defined (GTK_MAJOR_VERSION) ... */
321 else
322 {
323 if ((gtk_major_version > major) ||
324 ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
325 ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
326 {
327 return 0;
328 }
329 else
330 {
331 printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n",
332 gtk_major_version, gtk_minor_version, gtk_micro_version);
333 printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n",
334 major, minor, micro);
335 printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
336 printf("***\n");
337 printf("*** If you have already installed a sufficiently new version, this error\n");
338 printf("*** probably means that the wrong copy of the gtk-config shell script is\n");
339 printf("*** being found. The easiest way to fix this is to remove the old version\n");
340 printf("*** of GTK+, but you can also set the GTK_CONFIG environment to point to the\n");
341 printf("*** correct copy of gtk-config. (In this case, you will have to\n");
342 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
343 printf("*** so that the correct libraries are found at run-time))\n");
344 }
345 }
346 return 1;
347 }
348 ],, no_gtk=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
349 CFLAGS="$ac_save_CFLAGS"
350 LIBS="$ac_save_LIBS"
351 fi
352 fi
353 if test "x$no_gtk" = x ; then
354 AC_MSG_RESULT(yes)
355 ifelse([$2], , :, [$2])
356 else
357 AC_MSG_RESULT(no)
358 if test "$GTK_CONFIG" = "no" ; then
359 echo "*** The gtk-config script installed by GTK could not be found"
360 echo "*** If GTK was installed in PREFIX, make sure PREFIX/bin is in"
361 echo "*** your path, or set the GTK_CONFIG environment variable to the"
362 echo "*** full path to gtk-config."
363 else
364 if test -f conf.gtktest ; then
365 :
366 else
367 echo "*** Could not run GTK test program, checking why..."
368 CFLAGS="$CFLAGS $GTK_CFLAGS"
369 LIBS="$LIBS $GTK_LIBS"
370 AC_TRY_LINK([
371 #include <gtk/gtk.h>
372 #include <stdio.h>
373 ], [ return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ],
374 [ echo "*** The test program compiled, but did not run. This usually means"
375 echo "*** that the run-time linker is not finding GTK or finding the wrong"
376 echo "*** version of GTK. If it is not finding GTK, you'll need to set your"
377 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
378 echo "*** to the installed location Also, make sure you have run ldconfig if that"
379 echo "*** is required on your system"
380 echo "***"
381 echo "*** If you have an old version installed, it is best to remove it, although"
382 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
383 echo "***"
384 echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
385 echo "*** came with the system with the command"
386 echo "***"
387 echo "*** rpm --erase --nodeps gtk gtk-devel" ],
388 [ echo "*** The test program failed to compile or link. See the file config.log for the"
389 echo "*** exact error that occured. This usually means GTK was incorrectly installed"
390 echo "*** or that you have moved GTK since it was installed. In the latter case, you"
391 echo "*** may want to edit the gtk-config script: $GTK_CONFIG" ])
392 CFLAGS="$ac_save_CFLAGS"
393 LIBS="$ac_save_LIBS"
394 fi
395 fi
396 GTK_CFLAGS=""
397 GTK_LIBS=""
398 ifelse([$3], , :, [$3])
399 fi
400 AC_SUBST(GTK_CFLAGS)
401 AC_SUBST(GTK_LIBS)
402 rm -f conf.gtktest
403 ])
404
405 # Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
406 #
407 # This file is free software; the Free Software Foundation
408 # gives unlimited permission to copy and/or distribute it,
409 # with or without modifications, as long as this notice is preserved.
410
411 # AM_AUTOMAKE_VERSION(VERSION)
412 # ----------------------------
413 # Automake X.Y traces this macro to ensure aclocal.m4 has been
414 # generated from the m4 files accompanying Automake X.Y.
415 AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
416
417 # AM_SET_CURRENT_AUTOMAKE_VERSION
418 # -------------------------------
419 # Call AM_AUTOMAKE_VERSION so it can be traced.
420 # This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
421 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
422 [AM_AUTOMAKE_VERSION([1.9.5])])
423
424 # AM_AUX_DIR_EXPAND -*- Autoconf -*-
425
426 # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
427 #
428 # This file is free software; the Free Software Foundation
429 # gives unlimited permission to copy and/or distribute it,
430 # with or without modifications, as long as this notice is preserved.
431
432 # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
433 # $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
434 # `$srcdir', `$srcdir/..', or `$srcdir/../..'.
435 #
436 # Of course, Automake must honor this variable whenever it calls a
437 # tool from the auxiliary directory. The problem is that $srcdir (and
438 # therefore $ac_aux_dir as well) can be either absolute or relative,
439 # depending on how configure is run. This is pretty annoying, since
440 # it makes $ac_aux_dir quite unusable in subdirectories: in the top
441 # source directory, any form will work fine, but in subdirectories a
442 # relative path needs to be adjusted first.
443 #
444 # $ac_aux_dir/missing
445 # fails when called from a subdirectory if $ac_aux_dir is relative
446 # $top_srcdir/$ac_aux_dir/missing
447 # fails if $ac_aux_dir is absolute,
448 # fails when called from a subdirectory in a VPATH build with
449 # a relative $ac_aux_dir
450 #
451 # The reason of the latter failure is that $top_srcdir and $ac_aux_dir
452 # are both prefixed by $srcdir. In an in-source build this is usually
453 # harmless because $srcdir is `.', but things will broke when you
454 # start a VPATH build or use an absolute $srcdir.
455 #
456 # So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
457 # iff we strip the leading $srcdir from $ac_aux_dir. That would be:
458 # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
459 # and then we would define $MISSING as
460 # MISSING="\${SHELL} $am_aux_dir/missing"
461 # This will work as long as MISSING is not called from configure, because
462 # unfortunately $(top_srcdir) has no meaning in configure.
463 # However there are other variables, like CC, which are often used in
464 # configure, and could therefore not use this "fixed" $ac_aux_dir.
465 #
466 # Another solution, used here, is to always expand $ac_aux_dir to an
467 # absolute PATH. The drawback is that using absolute paths prevent a
468 # configured tree to be moved without reconfiguration.
469
470 AC_DEFUN([AM_AUX_DIR_EXPAND],
471 [dnl Rely on autoconf to set up CDPATH properly.
472 AC_PREREQ([2.50])dnl
473 # expand $ac_aux_dir to an absolute path
474 am_aux_dir=`cd $ac_aux_dir && pwd`
475 ])
476
477
478 # Copyright (C) 1996, 1997, 1999, 2000, 2001, 2002, 2003, 2005
479 # Free Software Foundation, Inc.
480 #
481 # This file is free software; the Free Software Foundation
482 # gives unlimited permission to copy and/or distribute it,
483 # with or without modifications, as long as this notice is preserved.
484
485 # serial 4
486
487 # This was merged into AC_PROG_CC in Autoconf.
488
489 AU_DEFUN([AM_PROG_CC_STDC],
490 [AC_PROG_CC
491 AC_DIAGNOSE([obsolete], [$0:
492 your code should no longer depend upon `am_cv_prog_cc_stdc', but upon
493 `ac_cv_prog_cc_stdc'. Remove this warning and the assignment when
494 you adjust the code. You can also remove the above call to
495 AC_PROG_CC if you already called it elsewhere.])
496 am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc
497 ])
498 AU_DEFUN([fp_PROG_CC_STDC])
499
500 # AM_CONDITIONAL -*- Autoconf -*-
501
502 # Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005
503 # Free Software Foundation, Inc.
504 #
505 # This file is free software; the Free Software Foundation
506 # gives unlimited permission to copy and/or distribute it,
507 # with or without modifications, as long as this notice is preserved.
508
509 # serial 7
510
511 # AM_CONDITIONAL(NAME, SHELL-CONDITION)
512 # -------------------------------------
513 # Define a conditional.
514 AC_DEFUN([AM_CONDITIONAL],
515 [AC_PREREQ(2.52)dnl
516 ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
517 [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
518 AC_SUBST([$1_TRUE])
519 AC_SUBST([$1_FALSE])
520 if $2; then
521 $1_TRUE=
522 $1_FALSE='#'
523 else
524 $1_TRUE='#'
525 $1_FALSE=
526 fi
527 AC_CONFIG_COMMANDS_PRE(
528 [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
529 AC_MSG_ERROR([[conditional "$1" was never defined.
530 Usually this means the macro was only invoked conditionally.]])
531 fi])])
532
533
534 # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
535 # Free Software Foundation, Inc.
536 #
537 # This file is free software; the Free Software Foundation
538 # gives unlimited permission to copy and/or distribute it,
539 # with or without modifications, as long as this notice is preserved.
540
541 # serial 8
542
543 # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
544 # written in clear, in which case automake, when reading aclocal.m4,
545 # will think it sees a *use*, and therefore will trigger all it's
546 # C support machinery. Also note that it means that autoscan, seeing
547 # CC etc. in the Makefile, will ask for an AC_PROG_CC use...
548
549
550 # _AM_DEPENDENCIES(NAME)
551 # ----------------------
552 # See how the compiler implements dependency checking.
553 # NAME is "CC", "CXX", "GCJ", or "OBJC".
554 # We try a few techniques and use that to set a single cache variable.
555 #
556 # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
557 # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
558 # dependency, and given that the user is not expected to run this macro,
559 # just rely on AC_PROG_CC.
560 AC_DEFUN([_AM_DEPENDENCIES],
561 [AC_REQUIRE([AM_SET_DEPDIR])dnl
562 AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
563 AC_REQUIRE([AM_MAKE_INCLUDE])dnl
564 AC_REQUIRE([AM_DEP_TRACK])dnl
565
566 ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
567 [$1], CXX, [depcc="$CXX" am_compiler_list=],
568 [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
569 [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
570 [depcc="$$1" am_compiler_list=])
571
572 AC_CACHE_CHECK([dependency style of $depcc],
573 [am_cv_$1_dependencies_compiler_type],
574 [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
575 # We make a subdir and do the tests there. Otherwise we can end up
576 # making bogus files that we don't know about and never remove. For
577 # instance it was reported that on HP-UX the gcc test will end up
578 # making a dummy file named `D' -- because `-MD' means `put the output
579 # in D'.
580 mkdir conftest.dir
581 # Copy depcomp to subdir because otherwise we won't find it if we're
582 # using a relative directory.
583 cp "$am_depcomp" conftest.dir
584 cd conftest.dir
585 # We will build objects and dependencies in a subdirectory because
586 # it helps to detect inapplicable dependency modes. For instance
587 # both Tru64's cc and ICC support -MD to output dependencies as a
588 # side effect of compilation, but ICC will put the dependencies in
589 # the current directory while Tru64 will put them in the object
590 # directory.
591 mkdir sub
592
593 am_cv_$1_dependencies_compiler_type=none
594 if test "$am_compiler_list" = ""; then
595 am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
596 fi
597 for depmode in $am_compiler_list; do
598 # Setup a source with many dependencies, because some compilers
599 # like to wrap large dependency lists on column 80 (with \), and
600 # we should not choose a depcomp mode which is confused by this.
601 #
602 # We need to recreate these files for each test, as the compiler may
603 # overwrite some of them when testing with obscure command lines.
604 # This happens at least with the AIX C compiler.
605 : > sub/conftest.c
606 for i in 1 2 3 4 5 6; do
607 echo '#include "conftst'$i'.h"' >> sub/conftest.c
608 # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
609 # Solaris 8's {/usr,}/bin/sh.
610 touch sub/conftst$i.h
611 done
612 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
613
614 case $depmode in
615 nosideeffect)
616 # after this tag, mechanisms are not by side-effect, so they'll
617 # only be used when explicitly requested
618 if test "x$enable_dependency_tracking" = xyes; then
619 continue
620 else
621 break
622 fi
623 ;;
624 none) break ;;
625 esac
626 # We check with `-c' and `-o' for the sake of the "dashmstdout"
627 # mode. It turns out that the SunPro C++ compiler does not properly
628 # handle `-M -o', and we need to detect this.
629 if depmode=$depmode \
630 source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
631 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
632 $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
633 >/dev/null 2>conftest.err &&
634 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
635 grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
636 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
637 # icc doesn't choke on unknown options, it will just issue warnings
638 # or remarks (even with -Werror). So we grep stderr for any message
639 # that says an option was ignored or not supported.
640 # When given -MP, icc 7.0 and 7.1 complain thusly:
641 # icc: Command line warning: ignoring option '-M'; no argument required
642 # The diagnosis changed in icc 8.0:
643 # icc: Command line remark: option '-MP' not supported
644 if (grep 'ignoring option' conftest.err ||
645 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
646 am_cv_$1_dependencies_compiler_type=$depmode
647 break
648 fi
649 fi
650 done
651
652 cd ..
653 rm -rf conftest.dir
654 else
655 am_cv_$1_dependencies_compiler_type=none
656 fi
657 ])
658 AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
659 AM_CONDITIONAL([am__fastdep$1], [
660 test "x$enable_dependency_tracking" != xno \
661 && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
662 ])
663
664
665 # AM_SET_DEPDIR
666 # -------------
667 # Choose a directory name for dependency files.
668 # This macro is AC_REQUIREd in _AM_DEPENDENCIES
669 AC_DEFUN([AM_SET_DEPDIR],
670 [AC_REQUIRE([AM_SET_LEADING_DOT])dnl
671 AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
672 ])
673
674
675 # AM_DEP_TRACK
676 # ------------
677 AC_DEFUN([AM_DEP_TRACK],
678 [AC_ARG_ENABLE(dependency-tracking,
679 [ --disable-dependency-tracking speeds up one-time build
680 --enable-dependency-tracking do not reject slow dependency extractors])
681 if test "x$enable_dependency_tracking" != xno; then
682 am_depcomp="$ac_aux_dir/depcomp"
683 AMDEPBACKSLASH='\'
684 fi
685 AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
686 AC_SUBST([AMDEPBACKSLASH])
687 ])
688
689 # Generate code to set up dependency tracking. -*- Autoconf -*-
690
691 # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
692 # Free Software Foundation, Inc.
693 #
694 # This file is free software; the Free Software Foundation
695 # gives unlimited permission to copy and/or distribute it,
696 # with or without modifications, as long as this notice is preserved.
697
698 #serial 3
699
700 # _AM_OUTPUT_DEPENDENCY_COMMANDS
701 # ------------------------------
702 AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
703 [for mf in $CONFIG_FILES; do
704 # Strip MF so we end up with the name of the file.
705 mf=`echo "$mf" | sed -e 's/:.*$//'`
706 # Check whether this is an Automake generated Makefile or not.
707 # We used to match only the files named `Makefile.in', but
708 # some people rename them; so instead we look at the file content.
709 # Grep'ing the first line is not enough: some people post-process
710 # each Makefile.in and add a new line on top of each file to say so.
711 # So let's grep whole file.
712 if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
713 dirpart=`AS_DIRNAME("$mf")`
714 else
715 continue
716 fi
717 # Extract the definition of DEPDIR, am__include, and am__quote
718 # from the Makefile without running `make'.
719 DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
720 test -z "$DEPDIR" && continue
721 am__include=`sed -n 's/^am__include = //p' < "$mf"`
722 test -z "am__include" && continue
723 am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
724 # When using ansi2knr, U may be empty or an underscore; expand it
725 U=`sed -n 's/^U = //p' < "$mf"`
726 # Find all dependency output files, they are included files with
727 # $(DEPDIR) in their names. We invoke sed twice because it is the
728 # simplest approach to changing $(DEPDIR) to its actual value in the
729 # expansion.
730 for file in `sed -n "
731 s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
732 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
733 # Make sure the directory exists.
734 test -f "$dirpart/$file" && continue
735 fdir=`AS_DIRNAME(["$file"])`
736 AS_MKDIR_P([$dirpart/$fdir])
737 # echo "creating $dirpart/$file"
738 echo '# dummy' > "$dirpart/$file"
739 done
740 done
741 ])# _AM_OUTPUT_DEPENDENCY_COMMANDS
742
743
744 # AM_OUTPUT_DEPENDENCY_COMMANDS
745 # -----------------------------
746 # This macro should only be invoked once -- use via AC_REQUIRE.
747 #
748 # This code is only required when automatic dependency tracking
749 # is enabled. FIXME. This creates each `.P' file that we will
750 # need in order to bootstrap the dependency handling code.
751 AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
752 [AC_CONFIG_COMMANDS([depfiles],
753 [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
754 [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
755 ])
756
757 # Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
758 # Free Software Foundation, Inc.
759 #
760 # This file is free software; the Free Software Foundation
761 # gives unlimited permission to copy and/or distribute it,
762 # with or without modifications, as long as this notice is preserved.
763
764 # serial 8
765
766 # AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS.
767 AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)])
768
769 # Do all the work for Automake. -*- Autoconf -*-
770
771 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
772 # Free Software Foundation, Inc.
773 #
774 # This file is free software; the Free Software Foundation
775 # gives unlimited permission to copy and/or distribute it,
776 # with or without modifications, as long as this notice is preserved.
777
778 # serial 12
779
780 # This macro actually does too much. Some checks are only needed if
781 # your package does certain things. But this isn't really a big deal.
782
783 # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
784 # AM_INIT_AUTOMAKE([OPTIONS])
785 # -----------------------------------------------
786 # The call with PACKAGE and VERSION arguments is the old style
787 # call (pre autoconf-2.50), which is being phased out. PACKAGE
788 # and VERSION should now be passed to AC_INIT and removed from
789 # the call to AM_INIT_AUTOMAKE.
790 # We support both call styles for the transition. After
791 # the next Automake release, Autoconf can make the AC_INIT
792 # arguments mandatory, and then we can depend on a new Autoconf
793 # release and drop the old call support.
794 AC_DEFUN([AM_INIT_AUTOMAKE],
795 [AC_PREREQ([2.58])dnl
796 dnl Autoconf wants to disallow AM_ names. We explicitly allow
797 dnl the ones we care about.
798 m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
799 AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
800 AC_REQUIRE([AC_PROG_INSTALL])dnl
801 # test to see if srcdir already configured
802 if test "`cd $srcdir && pwd`" != "`pwd`" &&
803 test -f $srcdir/config.status; then
804 AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
805 fi
806
807 # test whether we have cygpath
808 if test -z "$CYGPATH_W"; then
809 if (cygpath --version) >/dev/null 2>/dev/null; then
810 CYGPATH_W='cygpath -w'
811 else
812 CYGPATH_W=echo
813 fi
814 fi
815 AC_SUBST([CYGPATH_W])
816
817 # Define the identity of the package.
818 dnl Distinguish between old-style and new-style calls.
819 m4_ifval([$2],
820 [m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
821 AC_SUBST([PACKAGE], [$1])dnl
822 AC_SUBST([VERSION], [$2])],
823 [_AM_SET_OPTIONS([$1])dnl
824 AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
825 AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
826
827 _AM_IF_OPTION([no-define],,
828 [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
829 AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
830
831 # Some tools Automake needs.
832 AC_REQUIRE([AM_SANITY_CHECK])dnl
833 AC_REQUIRE([AC_ARG_PROGRAM])dnl
834 AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
835 AM_MISSING_PROG(AUTOCONF, autoconf)
836 AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
837 AM_MISSING_PROG(AUTOHEADER, autoheader)
838 AM_MISSING_PROG(MAKEINFO, makeinfo)
839 AM_PROG_INSTALL_SH
840 AM_PROG_INSTALL_STRIP
841 AC_REQUIRE([AM_PROG_MKDIR_P])dnl
842 # We need awk for the "check" target. The system "awk" is bad on
843 # some platforms.
844 AC_REQUIRE([AC_PROG_AWK])dnl
845 AC_REQUIRE([AC_PROG_MAKE_SET])dnl
846 AC_REQUIRE([AM_SET_LEADING_DOT])dnl
847 _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
848 [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
849 [_AM_PROG_TAR([v7])])])
850 _AM_IF_OPTION([no-dependencies],,
851 [AC_PROVIDE_IFELSE([AC_PROG_CC],
852 [_AM_DEPENDENCIES(CC)],
853 [define([AC_PROG_CC],
854 defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
855 AC_PROVIDE_IFELSE([AC_PROG_CXX],
856 [_AM_DEPENDENCIES(CXX)],
857 [define([AC_PROG_CXX],
858 defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
859 ])
860 ])
861
862
863 # When config.status generates a header, we must update the stamp-h file.
864 # This file resides in the same directory as the config header
865 # that is generated. The stamp files are numbered to have different names.
866
867 # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
868 # loop where config.status creates the headers, so we can generate
869 # our stamp files there.
870 AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
871 [# Compute $1's index in $config_headers.
872 _am_stamp_count=1
873 for _am_header in $config_headers :; do
874 case $_am_header in
875 $1 | $1:* )
876 break ;;
877 * )
878 _am_stamp_count=`expr $_am_stamp_count + 1` ;;
879 esac
880 done
881 echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
882
883 # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
884 #
885 # This file is free software; the Free Software Foundation
886 # gives unlimited permission to copy and/or distribute it,
887 # with or without modifications, as long as this notice is preserved.
888
889 # AM_PROG_INSTALL_SH
890 # ------------------
891 # Define $install_sh.
892 AC_DEFUN([AM_PROG_INSTALL_SH],
893 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
894 install_sh=${install_sh-"$am_aux_dir/install-sh"}
895 AC_SUBST(install_sh)])
896
897 # Copyright (C) 2003, 2005 Free Software Foundation, Inc.
898 #
899 # This file is free software; the Free Software Foundation
900 # gives unlimited permission to copy and/or distribute it,
901 # with or without modifications, as long as this notice is preserved.
902
903 # serial 2
904
905 # Check whether the underlying file-system supports filenames
906 # with a leading dot. For instance MS-DOS doesn't.
907 AC_DEFUN([AM_SET_LEADING_DOT],
908 [rm -rf .tst 2>/dev/null
909 mkdir .tst 2>/dev/null
910 if test -d .tst; then
911 am__leading_dot=.
912 else
913 am__leading_dot=_
914 fi
915 rmdir .tst 2>/dev/null
916 AC_SUBST([am__leading_dot])])
917
918 # Check to see how 'make' treats includes. -*- Autoconf -*-
919
920 # Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
921 #
922 # This file is free software; the Free Software Foundation
923 # gives unlimited permission to copy and/or distribute it,
924 # with or without modifications, as long as this notice is preserved.
925
926 # serial 3
927
928 # AM_MAKE_INCLUDE()
929 # -----------------
930 # Check to see how make treats includes.
931 AC_DEFUN([AM_MAKE_INCLUDE],
932 [am_make=${MAKE-make}
933 cat > confinc << 'END'
934 am__doit:
935 @echo done
936 .PHONY: am__doit
937 END
938 # If we don't find an include directive, just comment out the code.
939 AC_MSG_CHECKING([for style of include used by $am_make])
940 am__include="#"
941 am__quote=
942 _am_result=none
943 # First try GNU make style include.
944 echo "include confinc" > confmf
945 # We grep out `Entering directory' and `Leaving directory'
946 # messages which can occur if `w' ends up in MAKEFLAGS.
947 # In particular we don't look at `^make:' because GNU make might
948 # be invoked under some other name (usually "gmake"), in which
949 # case it prints its new name instead of `make'.
950 if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
951 am__include=include
952 am__quote=
953 _am_result=GNU
954 fi
955 # Now try BSD make style include.
956 if test "$am__include" = "#"; then
957 echo '.include "confinc"' > confmf
958 if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
959 am__include=.include
960 am__quote="\""
961 _am_result=BSD
962 fi
963 fi
964 AC_SUBST([am__include])
965 AC_SUBST([am__quote])
966 AC_MSG_RESULT([$_am_result])
967 rm -f confinc confmf
968 ])
969
970 # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
971
972 # Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005
973 # Free Software Foundation, Inc.
974 #
975 # This file is free software; the Free Software Foundation
976 # gives unlimited permission to copy and/or distribute it,
977 # with or without modifications, as long as this notice is preserved.
978
979 # serial 4
980
981 # AM_MISSING_PROG(NAME, PROGRAM)
982 # ------------------------------
983 AC_DEFUN([AM_MISSING_PROG],
984 [AC_REQUIRE([AM_MISSING_HAS_RUN])
985 $1=${$1-"${am_missing_run}$2"}
986 AC_SUBST($1)])
987
988
989 # AM_MISSING_HAS_RUN
990 # ------------------
991 # Define MISSING if not defined so far and test if it supports --run.
992 # If it does, set am_missing_run to use it, otherwise, to nothing.
993 AC_DEFUN([AM_MISSING_HAS_RUN],
994 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
995 test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
996 # Use eval to expand $SHELL
997 if eval "$MISSING --run true"; then
998 am_missing_run="$MISSING --run "
999 else
1000 am_missing_run=
1001 AC_MSG_WARN([`missing' script is too old or missing])
1002 fi
1003 ])
1004
1005 # Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
1006 #
1007 # This file is free software; the Free Software Foundation
1008 # gives unlimited permission to copy and/or distribute it,
1009 # with or without modifications, as long as this notice is preserved.
1010
1011 # AM_PROG_MKDIR_P
1012 # ---------------
1013 # Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
1014 #
1015 # Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
1016 # created by `make install' are always world readable, even if the
1017 # installer happens to have an overly restrictive umask (e.g. 077).
1018 # This was a mistake. There are at least two reasons why we must not
1019 # use `-m 0755':
1020 # - it causes special bits like SGID to be ignored,
1021 # - it may be too restrictive (some setups expect 775 directories).
1022 #
1023 # Do not use -m 0755 and let people choose whatever they expect by
1024 # setting umask.
1025 #
1026 # We cannot accept any implementation of `mkdir' that recognizes `-p'.
1027 # Some implementations (such as Solaris 8's) are not thread-safe: if a
1028 # parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
1029 # concurrently, both version can detect that a/ is missing, but only
1030 # one can create it and the other will error out. Consequently we
1031 # restrict ourselves to GNU make (using the --version option ensures
1032 # this.)
1033 AC_DEFUN([AM_PROG_MKDIR_P],
1034 [if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
1035 # We used to keeping the `.' as first argument, in order to
1036 # allow $(mkdir_p) to be used without argument. As in
1037 # $(mkdir_p) $(somedir)
1038 # where $(somedir) is conditionally defined. However this is wrong
1039 # for two reasons:
1040 # 1. if the package is installed by a user who cannot write `.'
1041 # make install will fail,
1042 # 2. the above comment should most certainly read
1043 # $(mkdir_p) $(DESTDIR)$(somedir)
1044 # so it does not work when $(somedir) is undefined and
1045 # $(DESTDIR) is not.
1046 # To support the latter case, we have to write
1047 # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
1048 # so the `.' trick is pointless.
1049 mkdir_p='mkdir -p --'
1050 else
1051 # On NextStep and OpenStep, the `mkdir' command does not
1052 # recognize any option. It will interpret all options as
1053 # directories to create, and then abort because `.' already
1054 # exists.
1055 for d in ./-p ./--version;
1056 do
1057 test -d $d && rmdir $d
1058 done
1059 # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
1060 if test -f "$ac_aux_dir/mkinstalldirs"; then
1061 mkdir_p='$(mkinstalldirs)'
1062 else
1063 mkdir_p='$(install_sh) -d'
1064 fi
1065 fi
1066 AC_SUBST([mkdir_p])])
1067
1068 # Helper functions for option handling. -*- Autoconf -*-
1069
1070 # Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
1071 #
1072 # This file is free software; the Free Software Foundation
1073 # gives unlimited permission to copy and/or distribute it,
1074 # with or without modifications, as long as this notice is preserved.
1075
1076 # serial 3
1077
1078 # _AM_MANGLE_OPTION(NAME)
1079 # -----------------------
1080 AC_DEFUN([_AM_MANGLE_OPTION],
1081 [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
1082
1083 # _AM_SET_OPTION(NAME)
1084 # ------------------------------
1085 # Set option NAME. Presently that only means defining a flag for this option.
1086 AC_DEFUN([_AM_SET_OPTION],
1087 [m4_define(_AM_MANGLE_OPTION([$1]), 1)])
1088
1089 # _AM_SET_OPTIONS(OPTIONS)
1090 # ----------------------------------
1091 # OPTIONS is a space-separated list of Automake options.
1092 AC_DEFUN([_AM_SET_OPTIONS],
1093 [AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
1094
1095 # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
1096 # -------------------------------------------
1097 # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
1098 AC_DEFUN([_AM_IF_OPTION],
1099 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
1100
1101 # Check to make sure that the build environment is sane. -*- Autoconf -*-
1102
1103 # Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
1104 # Free Software Foundation, Inc.
1105 #
1106 # This file is free software; the Free Software Foundation
1107 # gives unlimited permission to copy and/or distribute it,
1108 # with or without modifications, as long as this notice is preserved.
1109
1110 # serial 4
1111
1112 # AM_SANITY_CHECK
1113 # ---------------
1114 AC_DEFUN([AM_SANITY_CHECK],
1115 [AC_MSG_CHECKING([whether build environment is sane])
1116 # Just in case
1117 sleep 1
1118 echo timestamp > conftest.file
1119 # Do `set' in a subshell so we don't clobber the current shell's
1120 # arguments. Must try -L first in case configure is actually a
1121 # symlink; some systems play weird games with the mod time of symlinks
1122 # (eg FreeBSD returns the mod time of the symlink's containing
1123 # directory).
1124 if (
1125 set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
1126 if test "$[*]" = "X"; then
1127 # -L didn't work.
1128 set X `ls -t $srcdir/configure conftest.file`
1129 fi
1130 rm -f conftest.file
1131 if test "$[*]" != "X $srcdir/configure conftest.file" \
1132 && test "$[*]" != "X conftest.file $srcdir/configure"; then
1133
1134 # If neither matched, then we have a broken ls. This can happen
1135 # if, for instance, CONFIG_SHELL is bash and it inherits a
1136 # broken ls alias from the environment. This has actually
1137 # happened. Such a system could not be considered "sane".
1138 AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
1139 alias in your environment])
1140 fi
1141
1142 test "$[2]" = conftest.file
1143 )
1144 then
1145 # Ok.
1146 :
1147 else
1148 AC_MSG_ERROR([newly created file is older than distributed files!
1149 Check your system clock])
1150 fi
1151 AC_MSG_RESULT(yes)])
1152
1153 # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
1154 #
1155 # This file is free software; the Free Software Foundation
1156 # gives unlimited permission to copy and/or distribute it,
1157 # with or without modifications, as long as this notice is preserved.
1158
1159 # AM_PROG_INSTALL_STRIP
1160 # ---------------------
1161 # One issue with vendor `install' (even GNU) is that you can't
1162 # specify the program used to strip binaries. This is especially
1163 # annoying in cross-compiling environments, where the build's strip
1164 # is unlikely to handle the host's binaries.
1165 # Fortunately install-sh will honor a STRIPPROG variable, so we
1166 # always use install-sh in `make install-strip', and initialize
1167 # STRIPPROG with the value of the STRIP variable (set by the user).
1168 AC_DEFUN([AM_PROG_INSTALL_STRIP],
1169 [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
1170 # Installed binaries are usually stripped using `strip' when the user
1171 # run `make install-strip'. However `strip' might not be the right
1172 # tool to use in cross-compilation environments, therefore Automake
1173 # will honor the `STRIP' environment variable to overrule this program.
1174 dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
1175 if test "$cross_compiling" != no; then
1176 AC_CHECK_TOOL([STRIP], [strip], :)
1177 fi
1178 INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
1179 AC_SUBST([INSTALL_STRIP_PROGRAM])])
1180
1181 # Check how to create a tarball. -*- Autoconf -*-
1182
1183 # Copyright (C) 2004, 2005 Free Software Foundation, Inc.
1184 #
1185 # This file is free software; the Free Software Foundation
1186 # gives unlimited permission to copy and/or distribute it,
1187 # with or without modifications, as long as this notice is preserved.
1188
1189 # serial 2
1190
1191 # _AM_PROG_TAR(FORMAT)
1192 # --------------------
1193 # Check how to create a tarball in format FORMAT.
1194 # FORMAT should be one of `v7', `ustar', or `pax'.
1195 #
1196 # Substitute a variable $(am__tar) that is a command
1197 # writing to stdout a FORMAT-tarball containing the directory
1198 # $tardir.
1199 # tardir=directory && $(am__tar) > result.tar
1200 #
1201 # Substitute a variable $(am__untar) that extract such
1202 # a tarball read from stdin.
1203 # $(am__untar) < result.tar
1204 AC_DEFUN([_AM_PROG_TAR],
1205 [# Always define AMTAR for backward compatibility.
1206 AM_MISSING_PROG([AMTAR], [tar])
1207 m4_if([$1], [v7],
1208 [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
1209 [m4_case([$1], [ustar],, [pax],,
1210 [m4_fatal([Unknown tar format])])
1211 AC_MSG_CHECKING([how to create a $1 tar archive])
1212 # Loop over all known methods to create a tar archive until one works.
1213 _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
1214 _am_tools=${am_cv_prog_tar_$1-$_am_tools}
1215 # Do not fold the above two line into one, because Tru64 sh and
1216 # Solaris sh will not grok spaces in the rhs of `-'.
1217 for _am_tool in $_am_tools
1218 do
1219 case $_am_tool in
1220 gnutar)
1221 for _am_tar in tar gnutar gtar;
1222 do
1223 AM_RUN_LOG([$_am_tar --version]) && break
1224 done
1225 am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
1226 am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
1227 am__untar="$_am_tar -xf -"
1228 ;;
1229 plaintar)
1230 # Must skip GNU tar: if it does not support --format= it doesn't create
1231 # ustar tarball either.
1232 (tar --version) >/dev/null 2>&1 && continue
1233 am__tar='tar chf - "$$tardir"'
1234 am__tar_='tar chf - "$tardir"'
1235 am__untar='tar xf -'
1236 ;;
1237 pax)
1238 am__tar='pax -L -x $1 -w "$$tardir"'
1239 am__tar_='pax -L -x $1 -w "$tardir"'
1240 am__untar='pax -r'
1241 ;;
1242 cpio)
1243 am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
1244 am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
1245 am__untar='cpio -i -H $1 -d'
1246 ;;
1247 none)
1248 am__tar=false
1249 am__tar_=false
1250 am__untar=false
1251 ;;
1252 esac
1253
1254 # If the value was cached, stop now. We just wanted to have am__tar
1255 # and am__untar set.
1256 test -n "${am_cv_prog_tar_$1}" && break
1257
1258 # tar/untar a dummy directory, and stop if the command works
1259 rm -rf conftest.dir
1260 mkdir conftest.dir
1261 echo GrepMe > conftest.dir/file
1262 AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
1263 rm -rf conftest.dir
1264 if test -s conftest.tar; then
1265 AM_RUN_LOG([$am__untar <conftest.tar])
1266 grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
1267 fi
1268 done
1269 rm -rf conftest.dir
1270
1271 AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
1272 AC_MSG_RESULT([$am_cv_prog_tar_$1])])
1273 AC_SUBST([am__tar])
1274 AC_SUBST([am__untar])
1275 ]) # _AM_PROG_TAR
1276
0 #!/bin/sh
1 #
2 # Script to generate configure&make stuff
3 #
4
5 #-----------------------------------------------------
6 # If defined, get these programs from the environment
7 #
8 : ${ACLOCAL:=aclocal}
9 : ${AUTOHEADER:=autoheader}
10 : ${AUTOCONF:=autoconf}
11 : ${AUTOMAKE:=automake}
12
13 #-------------------------
14 # Required binaries check
15 #
16 check_bin_file(){
17 which $1 > /dev/null 2>&1
18 if [ $? = 0 ]; then
19 return 0
20 else
21 return 1
22 fi
23 }
24
25 #------
26 # Main
27 #
28
29 #clear
30 ERR="no"
31 for cmd in "$ACLOCAL" "$AUTOHEADER" "$AUTOCONF" "$AUTOMAKE"
32 do
33 if check_bin_file "$cmd"
34 then
35 echo -e "$cmd \tfound"
36 else
37 echo -e "$cmd \tNOT found"
38 ERR="yes"
39 fi
40 done
41
42 if test $ERR = "yes"
43 then
44 echo
45 echo "ERROR: to run this program you need the following installed"
46 echo " $ACLOCAL $AUTOHEADER $AUTOCONF $AUTOMAKE"
47 echo
48 exit 1
49 fi
50
51 echo "[Checks passed]"
52 echo "Generating..."
53
54 "$ACLOCAL"
55 "$AUTOHEADER"
56 "$AUTOCONF"
57 "$AUTOMAKE" -a
58
+0
-1459
config.guess less more
0 #! /bin/sh
1 # Attempt to guess a canonical system name.
2 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
3 # 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4
5 timestamp='2005-02-10'
6
7 # This file is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #
21 # As a special exception to the GNU General Public License, if you
22 # distribute this file as part of a program that contains a
23 # configuration script generated by Autoconf, you may include it under
24 # the same distribution terms that you use for the rest of that program.
25
26 # Originally written by Per Bothner <per@bothner.com>.
27 # Please send patches to <config-patches@gnu.org>. Submit a context
28 # diff and a properly formatted ChangeLog entry.
29 #
30 # This script attempts to guess a canonical system name similar to
31 # config.sub. If it succeeds, it prints the system name on stdout, and
32 # exits with 0. Otherwise, it exits with 1.
33 #
34 # The plan is that this can be called by configure scripts if you
35 # don't specify an explicit build system type.
36
37 me=`echo "$0" | sed -e 's,.*/,,'`
38
39 usage="\
40 Usage: $0 [OPTION]
41
42 Output the configuration name of the system \`$me' is run on.
43
44 Operation modes:
45 -h, --help print this help, then exit
46 -t, --time-stamp print date of last modification, then exit
47 -v, --version print version number, then exit
48
49 Report bugs and patches to <config-patches@gnu.org>."
50
51 version="\
52 GNU config.guess ($timestamp)
53
54 Originally written by Per Bothner.
55 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
56 Free Software Foundation, Inc.
57
58 This is free software; see the source for copying conditions. There is NO
59 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
60
61 help="
62 Try \`$me --help' for more information."
63
64 # Parse command line
65 while test $# -gt 0 ; do
66 case $1 in
67 --time-stamp | --time* | -t )
68 echo "$timestamp" ; exit 0 ;;
69 --version | -v )
70 echo "$version" ; exit 0 ;;
71 --help | --h* | -h )
72 echo "$usage"; exit 0 ;;
73 -- ) # Stop option processing
74 shift; break ;;
75 - ) # Use stdin as input.
76 break ;;
77 -* )
78 echo "$me: invalid option $1$help" >&2
79 exit 1 ;;
80 * )
81 break ;;
82 esac
83 done
84
85 if test $# != 0; then
86 echo "$me: too many arguments$help" >&2
87 exit 1
88 fi
89
90 trap 'exit 1' 1 2 15
91
92 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a
93 # compiler to aid in system detection is discouraged as it requires
94 # temporary files to be created and, as you can see below, it is a
95 # headache to deal with in a portable fashion.
96
97 # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
98 # use `HOST_CC' if defined, but it is deprecated.
99
100 # Portable tmp directory creation inspired by the Autoconf team.
101
102 set_cc_for_build='
103 trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
104 trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
105 : ${TMPDIR=/tmp} ;
106 { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
107 { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
108 { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
109 { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
110 dummy=$tmp/dummy ;
111 tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
112 case $CC_FOR_BUILD,$HOST_CC,$CC in
113 ,,) echo "int x;" > $dummy.c ;
114 for c in cc gcc c89 c99 ; do
115 if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
116 CC_FOR_BUILD="$c"; break ;
117 fi ;
118 done ;
119 if test x"$CC_FOR_BUILD" = x ; then
120 CC_FOR_BUILD=no_compiler_found ;
121 fi
122 ;;
123 ,,*) CC_FOR_BUILD=$CC ;;
124 ,*,*) CC_FOR_BUILD=$HOST_CC ;;
125 esac ;'
126
127 # This is needed to find uname on a Pyramid OSx when run in the BSD universe.
128 # (ghazi@noc.rutgers.edu 1994-08-24)
129 if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
130 PATH=$PATH:/.attbin ; export PATH
131 fi
132
133 UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
134 UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
135 UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
136 UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
137
138 # Note: order is significant - the case branches are not exclusive.
139
140 case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
141 *:NetBSD:*:*)
142 # NetBSD (nbsd) targets should (where applicable) match one or
143 # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
144 # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
145 # switched to ELF, *-*-netbsd* would select the old
146 # object file format. This provides both forward
147 # compatibility and a consistent mechanism for selecting the
148 # object file format.
149 #
150 # Note: NetBSD doesn't particularly care about the vendor
151 # portion of the name. We always set it to "unknown".
152 sysctl="sysctl -n hw.machine_arch"
153 UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
154 /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
155 case "${UNAME_MACHINE_ARCH}" in
156 armeb) machine=armeb-unknown ;;
157 arm*) machine=arm-unknown ;;
158 sh3el) machine=shl-unknown ;;
159 sh3eb) machine=sh-unknown ;;
160 *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
161 esac
162 # The Operating System including object format, if it has switched
163 # to ELF recently, or will in the future.
164 case "${UNAME_MACHINE_ARCH}" in
165 arm*|i386|m68k|ns32k|sh3*|sparc|vax)
166 eval $set_cc_for_build
167 if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
168 | grep __ELF__ >/dev/null
169 then
170 # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
171 # Return netbsd for either. FIX?
172 os=netbsd
173 else
174 os=netbsdelf
175 fi
176 ;;
177 *)
178 os=netbsd
179 ;;
180 esac
181 # The OS release
182 # Debian GNU/NetBSD machines have a different userland, and
183 # thus, need a distinct triplet. However, they do not need
184 # kernel version information, so it can be replaced with a
185 # suitable tag, in the style of linux-gnu.
186 case "${UNAME_VERSION}" in
187 Debian*)
188 release='-gnu'
189 ;;
190 *)
191 release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
192 ;;
193 esac
194 # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
195 # contains redundant information, the shorter form:
196 # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
197 echo "${machine}-${os}${release}"
198 exit 0 ;;
199 amd64:OpenBSD:*:*)
200 echo x86_64-unknown-openbsd${UNAME_RELEASE}
201 exit 0 ;;
202 amiga:OpenBSD:*:*)
203 echo m68k-unknown-openbsd${UNAME_RELEASE}
204 exit 0 ;;
205 cats:OpenBSD:*:*)
206 echo arm-unknown-openbsd${UNAME_RELEASE}
207 exit 0 ;;
208 hp300:OpenBSD:*:*)
209 echo m68k-unknown-openbsd${UNAME_RELEASE}
210 exit 0 ;;
211 luna88k:OpenBSD:*:*)
212 echo m88k-unknown-openbsd${UNAME_RELEASE}
213 exit 0 ;;
214 mac68k:OpenBSD:*:*)
215 echo m68k-unknown-openbsd${UNAME_RELEASE}
216 exit 0 ;;
217 macppc:OpenBSD:*:*)
218 echo powerpc-unknown-openbsd${UNAME_RELEASE}
219 exit 0 ;;
220 mvme68k:OpenBSD:*:*)
221 echo m68k-unknown-openbsd${UNAME_RELEASE}
222 exit 0 ;;
223 mvme88k:OpenBSD:*:*)
224 echo m88k-unknown-openbsd${UNAME_RELEASE}
225 exit 0 ;;
226 mvmeppc:OpenBSD:*:*)
227 echo powerpc-unknown-openbsd${UNAME_RELEASE}
228 exit 0 ;;
229 sgi:OpenBSD:*:*)
230 echo mips64-unknown-openbsd${UNAME_RELEASE}
231 exit 0 ;;
232 sun3:OpenBSD:*:*)
233 echo m68k-unknown-openbsd${UNAME_RELEASE}
234 exit 0 ;;
235 *:OpenBSD:*:*)
236 echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
237 exit 0 ;;
238 *:ekkoBSD:*:*)
239 echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
240 exit 0 ;;
241 macppc:MirBSD:*:*)
242 echo powerppc-unknown-mirbsd${UNAME_RELEASE}
243 exit 0 ;;
244 *:MirBSD:*:*)
245 echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
246 exit 0 ;;
247 alpha:OSF1:*:*)
248 case $UNAME_RELEASE in
249 *4.0)
250 UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
251 ;;
252 *5.*)
253 UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
254 ;;
255 esac
256 # According to Compaq, /usr/sbin/psrinfo has been available on
257 # OSF/1 and Tru64 systems produced since 1995. I hope that
258 # covers most systems running today. This code pipes the CPU
259 # types through head -n 1, so we only detect the type of CPU 0.
260 ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
261 case "$ALPHA_CPU_TYPE" in
262 "EV4 (21064)")
263 UNAME_MACHINE="alpha" ;;
264 "EV4.5 (21064)")
265 UNAME_MACHINE="alpha" ;;
266 "LCA4 (21066/21068)")
267 UNAME_MACHINE="alpha" ;;
268 "EV5 (21164)")
269 UNAME_MACHINE="alphaev5" ;;
270 "EV5.6 (21164A)")
271 UNAME_MACHINE="alphaev56" ;;
272 "EV5.6 (21164PC)")
273 UNAME_MACHINE="alphapca56" ;;
274 "EV5.7 (21164PC)")
275 UNAME_MACHINE="alphapca57" ;;
276 "EV6 (21264)")
277 UNAME_MACHINE="alphaev6" ;;
278 "EV6.7 (21264A)")
279 UNAME_MACHINE="alphaev67" ;;
280 "EV6.8CB (21264C)")
281 UNAME_MACHINE="alphaev68" ;;
282 "EV6.8AL (21264B)")
283 UNAME_MACHINE="alphaev68" ;;
284 "EV6.8CX (21264D)")
285 UNAME_MACHINE="alphaev68" ;;
286 "EV6.9A (21264/EV69A)")
287 UNAME_MACHINE="alphaev69" ;;
288 "EV7 (21364)")
289 UNAME_MACHINE="alphaev7" ;;
290 "EV7.9 (21364A)")
291 UNAME_MACHINE="alphaev79" ;;
292 esac
293 # A Pn.n version is a patched version.
294 # A Vn.n version is a released version.
295 # A Tn.n version is a released field test version.
296 # A Xn.n version is an unreleased experimental baselevel.
297 # 1.2 uses "1.2" for uname -r.
298 echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
299 exit 0 ;;
300 Alpha\ *:Windows_NT*:*)
301 # How do we know it's Interix rather than the generic POSIX subsystem?
302 # Should we change UNAME_MACHINE based on the output of uname instead
303 # of the specific Alpha model?
304 echo alpha-pc-interix
305 exit 0 ;;
306 21064:Windows_NT:50:3)
307 echo alpha-dec-winnt3.5
308 exit 0 ;;
309 Amiga*:UNIX_System_V:4.0:*)
310 echo m68k-unknown-sysv4
311 exit 0;;
312 *:[Aa]miga[Oo][Ss]:*:*)
313 echo ${UNAME_MACHINE}-unknown-amigaos
314 exit 0 ;;
315 *:[Mm]orph[Oo][Ss]:*:*)
316 echo ${UNAME_MACHINE}-unknown-morphos
317 exit 0 ;;
318 *:OS/390:*:*)
319 echo i370-ibm-openedition
320 exit 0 ;;
321 *:z/VM:*:*)
322 echo s390-ibm-zvmoe
323 exit 0 ;;
324 *:OS400:*:*)
325 echo powerpc-ibm-os400
326 exit 0 ;;
327 arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
328 echo arm-acorn-riscix${UNAME_RELEASE}
329 exit 0;;
330 SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
331 echo hppa1.1-hitachi-hiuxmpp
332 exit 0;;
333 Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
334 # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
335 if test "`(/bin/universe) 2>/dev/null`" = att ; then
336 echo pyramid-pyramid-sysv3
337 else
338 echo pyramid-pyramid-bsd
339 fi
340 exit 0 ;;
341 NILE*:*:*:dcosx)
342 echo pyramid-pyramid-svr4
343 exit 0 ;;
344 DRS?6000:unix:4.0:6*)
345 echo sparc-icl-nx6
346 exit 0 ;;
347 DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
348 case `/usr/bin/uname -p` in
349 sparc) echo sparc-icl-nx7 && exit 0 ;;
350 esac ;;
351 sun4H:SunOS:5.*:*)
352 echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
353 exit 0 ;;
354 sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
355 echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
356 exit 0 ;;
357 i86pc:SunOS:5.*:*)
358 echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
359 exit 0 ;;
360 sun4*:SunOS:6*:*)
361 # According to config.sub, this is the proper way to canonicalize
362 # SunOS6. Hard to guess exactly what SunOS6 will be like, but
363 # it's likely to be more like Solaris than SunOS4.
364 echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
365 exit 0 ;;
366 sun4*:SunOS:*:*)
367 case "`/usr/bin/arch -k`" in
368 Series*|S4*)
369 UNAME_RELEASE=`uname -v`
370 ;;
371 esac
372 # Japanese Language versions have a version number like `4.1.3-JL'.
373 echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
374 exit 0 ;;
375 sun3*:SunOS:*:*)
376 echo m68k-sun-sunos${UNAME_RELEASE}
377 exit 0 ;;
378 sun*:*:4.2BSD:*)
379 UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
380 test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
381 case "`/bin/arch`" in
382 sun3)
383 echo m68k-sun-sunos${UNAME_RELEASE}
384 ;;
385 sun4)
386 echo sparc-sun-sunos${UNAME_RELEASE}
387 ;;
388 esac
389 exit 0 ;;
390 aushp:SunOS:*:*)
391 echo sparc-auspex-sunos${UNAME_RELEASE}
392 exit 0 ;;
393 # The situation for MiNT is a little confusing. The machine name
394 # can be virtually everything (everything which is not
395 # "atarist" or "atariste" at least should have a processor
396 # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
397 # to the lowercase version "mint" (or "freemint"). Finally
398 # the system name "TOS" denotes a system which is actually not
399 # MiNT. But MiNT is downward compatible to TOS, so this should
400 # be no problem.
401 atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
402 echo m68k-atari-mint${UNAME_RELEASE}
403 exit 0 ;;
404 atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
405 echo m68k-atari-mint${UNAME_RELEASE}
406 exit 0 ;;
407 *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
408 echo m68k-atari-mint${UNAME_RELEASE}
409 exit 0 ;;
410 milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
411 echo m68k-milan-mint${UNAME_RELEASE}
412 exit 0 ;;
413 hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
414 echo m68k-hades-mint${UNAME_RELEASE}
415 exit 0 ;;
416 *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
417 echo m68k-unknown-mint${UNAME_RELEASE}
418 exit 0 ;;
419 m68k:machten:*:*)
420 echo m68k-apple-machten${UNAME_RELEASE}
421 exit 0 ;;
422 powerpc:machten:*:*)
423 echo powerpc-apple-machten${UNAME_RELEASE}
424 exit 0 ;;
425 RISC*:Mach:*:*)
426 echo mips-dec-mach_bsd4.3
427 exit 0 ;;
428 RISC*:ULTRIX:*:*)
429 echo mips-dec-ultrix${UNAME_RELEASE}
430 exit 0 ;;
431 VAX*:ULTRIX*:*:*)
432 echo vax-dec-ultrix${UNAME_RELEASE}
433 exit 0 ;;
434 2020:CLIX:*:* | 2430:CLIX:*:*)
435 echo clipper-intergraph-clix${UNAME_RELEASE}
436 exit 0 ;;
437 mips:*:*:UMIPS | mips:*:*:RISCos)
438 eval $set_cc_for_build
439 sed 's/^ //' << EOF >$dummy.c
440 #ifdef __cplusplus
441 #include <stdio.h> /* for printf() prototype */
442 int main (int argc, char *argv[]) {
443 #else
444 int main (argc, argv) int argc; char *argv[]; {
445 #endif
446 #if defined (host_mips) && defined (MIPSEB)
447 #if defined (SYSTYPE_SYSV)
448 printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
449 #endif
450 #if defined (SYSTYPE_SVR4)
451 printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
452 #endif
453 #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
454 printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
455 #endif
456 #endif
457 exit (-1);
458 }
459 EOF
460 $CC_FOR_BUILD -o $dummy $dummy.c \
461 && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
462 && exit 0
463 echo mips-mips-riscos${UNAME_RELEASE}
464 exit 0 ;;
465 Motorola:PowerMAX_OS:*:*)
466 echo powerpc-motorola-powermax
467 exit 0 ;;
468 Motorola:*:4.3:PL8-*)
469 echo powerpc-harris-powermax
470 exit 0 ;;
471 Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
472 echo powerpc-harris-powermax
473 exit 0 ;;
474 Night_Hawk:Power_UNIX:*:*)
475 echo powerpc-harris-powerunix
476 exit 0 ;;
477 m88k:CX/UX:7*:*)
478 echo m88k-harris-cxux7
479 exit 0 ;;
480 m88k:*:4*:R4*)
481 echo m88k-motorola-sysv4
482 exit 0 ;;
483 m88k:*:3*:R3*)
484 echo m88k-motorola-sysv3
485 exit 0 ;;
486 AViiON:dgux:*:*)
487 # DG/UX returns AViiON for all architectures
488 UNAME_PROCESSOR=`/usr/bin/uname -p`
489 if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
490 then
491 if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
492 [ ${TARGET_BINARY_INTERFACE}x = x ]
493 then
494 echo m88k-dg-dgux${UNAME_RELEASE}
495 else
496 echo m88k-dg-dguxbcs${UNAME_RELEASE}
497 fi
498 else
499 echo i586-dg-dgux${UNAME_RELEASE}
500 fi
501 exit 0 ;;
502 M88*:DolphinOS:*:*) # DolphinOS (SVR3)
503 echo m88k-dolphin-sysv3
504 exit 0 ;;
505 M88*:*:R3*:*)
506 # Delta 88k system running SVR3
507 echo m88k-motorola-sysv3
508 exit 0 ;;
509 XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
510 echo m88k-tektronix-sysv3
511 exit 0 ;;
512 Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
513 echo m68k-tektronix-bsd
514 exit 0 ;;
515 *:IRIX*:*:*)
516 echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
517 exit 0 ;;
518 ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
519 echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
520 exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
521 i*86:AIX:*:*)
522 echo i386-ibm-aix
523 exit 0 ;;
524 ia64:AIX:*:*)
525 if [ -x /usr/bin/oslevel ] ; then
526 IBM_REV=`/usr/bin/oslevel`
527 else
528 IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
529 fi
530 echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
531 exit 0 ;;
532 *:AIX:2:3)
533 if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
534 eval $set_cc_for_build
535 sed 's/^ //' << EOF >$dummy.c
536 #include <sys/systemcfg.h>
537
538 main()
539 {
540 if (!__power_pc())
541 exit(1);
542 puts("powerpc-ibm-aix3.2.5");
543 exit(0);
544 }
545 EOF
546 $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
547 echo rs6000-ibm-aix3.2.5
548 elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
549 echo rs6000-ibm-aix3.2.4
550 else
551 echo rs6000-ibm-aix3.2
552 fi
553 exit 0 ;;
554 *:AIX:*:[45])
555 IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
556 if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
557 IBM_ARCH=rs6000
558 else
559 IBM_ARCH=powerpc
560 fi
561 if [ -x /usr/bin/oslevel ] ; then
562 IBM_REV=`/usr/bin/oslevel`
563 else
564 IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
565 fi
566 echo ${IBM_ARCH}-ibm-aix${IBM_REV}
567 exit 0 ;;
568 *:AIX:*:*)
569 echo rs6000-ibm-aix
570 exit 0 ;;
571 ibmrt:4.4BSD:*|romp-ibm:BSD:*)
572 echo romp-ibm-bsd4.4
573 exit 0 ;;
574 ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
575 echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
576 exit 0 ;; # report: romp-ibm BSD 4.3
577 *:BOSX:*:*)
578 echo rs6000-bull-bosx
579 exit 0 ;;
580 DPX/2?00:B.O.S.:*:*)
581 echo m68k-bull-sysv3
582 exit 0 ;;
583 9000/[34]??:4.3bsd:1.*:*)
584 echo m68k-hp-bsd
585 exit 0 ;;
586 hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
587 echo m68k-hp-bsd4.4
588 exit 0 ;;
589 9000/[34678]??:HP-UX:*:*)
590 HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
591 case "${UNAME_MACHINE}" in
592 9000/31? ) HP_ARCH=m68000 ;;
593 9000/[34]?? ) HP_ARCH=m68k ;;
594 9000/[678][0-9][0-9])
595 if [ -x /usr/bin/getconf ]; then
596 sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
597 sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
598 case "${sc_cpu_version}" in
599 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
600 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
601 532) # CPU_PA_RISC2_0
602 case "${sc_kernel_bits}" in
603 32) HP_ARCH="hppa2.0n" ;;
604 64) HP_ARCH="hppa2.0w" ;;
605 '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
606 esac ;;
607 esac
608 fi
609 if [ "${HP_ARCH}" = "" ]; then
610 eval $set_cc_for_build
611 sed 's/^ //' << EOF >$dummy.c
612
613 #define _HPUX_SOURCE
614 #include <stdlib.h>
615 #include <unistd.h>
616
617 int main ()
618 {
619 #if defined(_SC_KERNEL_BITS)
620 long bits = sysconf(_SC_KERNEL_BITS);
621 #endif
622 long cpu = sysconf (_SC_CPU_VERSION);
623
624 switch (cpu)
625 {
626 case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
627 case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
628 case CPU_PA_RISC2_0:
629 #if defined(_SC_KERNEL_BITS)
630 switch (bits)
631 {
632 case 64: puts ("hppa2.0w"); break;
633 case 32: puts ("hppa2.0n"); break;
634 default: puts ("hppa2.0"); break;
635 } break;
636 #else /* !defined(_SC_KERNEL_BITS) */
637 puts ("hppa2.0"); break;
638 #endif
639 default: puts ("hppa1.0"); break;
640 }
641 exit (0);
642 }
643 EOF
644 (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
645 test -z "$HP_ARCH" && HP_ARCH=hppa
646 fi ;;
647 esac
648 if [ ${HP_ARCH} = "hppa2.0w" ]
649 then
650 # avoid double evaluation of $set_cc_for_build
651 test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
652 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
653 then
654 HP_ARCH="hppa2.0w"
655 else
656 HP_ARCH="hppa64"
657 fi
658 fi
659 echo ${HP_ARCH}-hp-hpux${HPUX_REV}
660 exit 0 ;;
661 ia64:HP-UX:*:*)
662 HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
663 echo ia64-hp-hpux${HPUX_REV}
664 exit 0 ;;
665 3050*:HI-UX:*:*)
666 eval $set_cc_for_build
667 sed 's/^ //' << EOF >$dummy.c
668 #include <unistd.h>
669 int
670 main ()
671 {
672 long cpu = sysconf (_SC_CPU_VERSION);
673 /* The order matters, because CPU_IS_HP_MC68K erroneously returns
674 true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
675 results, however. */
676 if (CPU_IS_PA_RISC (cpu))
677 {
678 switch (cpu)
679 {
680 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
681 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
682 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
683 default: puts ("hppa-hitachi-hiuxwe2"); break;
684 }
685 }
686 else if (CPU_IS_HP_MC68K (cpu))
687 puts ("m68k-hitachi-hiuxwe2");
688 else puts ("unknown-hitachi-hiuxwe2");
689 exit (0);
690 }
691 EOF
692 $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
693 echo unknown-hitachi-hiuxwe2
694 exit 0 ;;
695 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
696 echo hppa1.1-hp-bsd
697 exit 0 ;;
698 9000/8??:4.3bsd:*:*)
699 echo hppa1.0-hp-bsd
700 exit 0 ;;
701 *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
702 echo hppa1.0-hp-mpeix
703 exit 0 ;;
704 hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
705 echo hppa1.1-hp-osf
706 exit 0 ;;
707 hp8??:OSF1:*:*)
708 echo hppa1.0-hp-osf
709 exit 0 ;;
710 i*86:OSF1:*:*)
711 if [ -x /usr/sbin/sysversion ] ; then
712 echo ${UNAME_MACHINE}-unknown-osf1mk
713 else
714 echo ${UNAME_MACHINE}-unknown-osf1
715 fi
716 exit 0 ;;
717 parisc*:Lites*:*:*)
718 echo hppa1.1-hp-lites
719 exit 0 ;;
720 C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
721 echo c1-convex-bsd
722 exit 0 ;;
723 C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
724 if getsysinfo -f scalar_acc
725 then echo c32-convex-bsd
726 else echo c2-convex-bsd
727 fi
728 exit 0 ;;
729 C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
730 echo c34-convex-bsd
731 exit 0 ;;
732 C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
733 echo c38-convex-bsd
734 exit 0 ;;
735 C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
736 echo c4-convex-bsd
737 exit 0 ;;
738 CRAY*Y-MP:*:*:*)
739 echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
740 exit 0 ;;
741 CRAY*[A-Z]90:*:*:*)
742 echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
743 | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
744 -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
745 -e 's/\.[^.]*$/.X/'
746 exit 0 ;;
747 CRAY*TS:*:*:*)
748 echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
749 exit 0 ;;
750 CRAY*T3E:*:*:*)
751 echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
752 exit 0 ;;
753 CRAY*SV1:*:*:*)
754 echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
755 exit 0 ;;
756 *:UNICOS/mp:*:*)
757 echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
758 exit 0 ;;
759 F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
760 FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
761 FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
762 FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
763 echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
764 exit 0 ;;
765 5000:UNIX_System_V:4.*:*)
766 FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
767 FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
768 echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
769 exit 0 ;;
770 i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
771 echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
772 exit 0 ;;
773 sparc*:BSD/OS:*:*)
774 echo sparc-unknown-bsdi${UNAME_RELEASE}
775 exit 0 ;;
776 *:BSD/OS:*:*)
777 echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
778 exit 0 ;;
779 *:FreeBSD:*:*)
780 echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
781 exit 0 ;;
782 i*:CYGWIN*:*)
783 echo ${UNAME_MACHINE}-pc-cygwin
784 exit 0 ;;
785 i*:MINGW*:*)
786 echo ${UNAME_MACHINE}-pc-mingw32
787 exit 0 ;;
788 i*:PW*:*)
789 echo ${UNAME_MACHINE}-pc-pw32
790 exit 0 ;;
791 x86:Interix*:[34]*)
792 echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
793 exit 0 ;;
794 [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
795 echo i${UNAME_MACHINE}-pc-mks
796 exit 0 ;;
797 i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
798 # How do we know it's Interix rather than the generic POSIX subsystem?
799 # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
800 # UNAME_MACHINE based on the output of uname instead of i386?
801 echo i586-pc-interix
802 exit 0 ;;
803 i*:UWIN*:*)
804 echo ${UNAME_MACHINE}-pc-uwin
805 exit 0 ;;
806 amd64:CYGWIN*:*:*)
807 echo x86_64-unknown-cygwin
808 exit 0 ;;
809 p*:CYGWIN*:*)
810 echo powerpcle-unknown-cygwin
811 exit 0 ;;
812 prep*:SunOS:5.*:*)
813 echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
814 exit 0 ;;
815 *:GNU:*:*)
816 # the GNU system
817 echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
818 exit 0 ;;
819 *:GNU/*:*:*)
820 # other systems with GNU libc and userland
821 echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
822 exit 0 ;;
823 i*86:Minix:*:*)
824 echo ${UNAME_MACHINE}-pc-minix
825 exit 0 ;;
826 arm*:Linux:*:*)
827 echo ${UNAME_MACHINE}-unknown-linux-gnu
828 exit 0 ;;
829 cris:Linux:*:*)
830 echo cris-axis-linux-gnu
831 exit 0 ;;
832 crisv32:Linux:*:*)
833 echo crisv32-axis-linux-gnu
834 exit 0 ;;
835 frv:Linux:*:*)
836 echo frv-unknown-linux-gnu
837 exit 0 ;;
838 ia64:Linux:*:*)
839 echo ${UNAME_MACHINE}-unknown-linux-gnu
840 exit 0 ;;
841 m32r*:Linux:*:*)
842 echo ${UNAME_MACHINE}-unknown-linux-gnu
843 exit 0 ;;
844 m68*:Linux:*:*)
845 echo ${UNAME_MACHINE}-unknown-linux-gnu
846 exit 0 ;;
847 mips:Linux:*:*)
848 eval $set_cc_for_build
849 sed 's/^ //' << EOF >$dummy.c
850 #undef CPU
851 #undef mips
852 #undef mipsel
853 #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
854 CPU=mipsel
855 #else
856 #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
857 CPU=mips
858 #else
859 CPU=
860 #endif
861 #endif
862 EOF
863 eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
864 test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
865 ;;
866 mips64:Linux:*:*)
867 eval $set_cc_for_build
868 sed 's/^ //' << EOF >$dummy.c
869 #undef CPU
870 #undef mips64
871 #undef mips64el
872 #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
873 CPU=mips64el
874 #else
875 #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
876 CPU=mips64
877 #else
878 CPU=
879 #endif
880 #endif
881 EOF
882 eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
883 test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
884 ;;
885 ppc:Linux:*:*)
886 echo powerpc-unknown-linux-gnu
887 exit 0 ;;
888 ppc64:Linux:*:*)
889 echo powerpc64-unknown-linux-gnu
890 exit 0 ;;
891 alpha:Linux:*:*)
892 case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
893 EV5) UNAME_MACHINE=alphaev5 ;;
894 EV56) UNAME_MACHINE=alphaev56 ;;
895 PCA56) UNAME_MACHINE=alphapca56 ;;
896 PCA57) UNAME_MACHINE=alphapca56 ;;
897 EV6) UNAME_MACHINE=alphaev6 ;;
898 EV67) UNAME_MACHINE=alphaev67 ;;
899 EV68*) UNAME_MACHINE=alphaev68 ;;
900 esac
901 objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
902 if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
903 echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
904 exit 0 ;;
905 parisc:Linux:*:* | hppa:Linux:*:*)
906 # Look for CPU level
907 case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
908 PA7*) echo hppa1.1-unknown-linux-gnu ;;
909 PA8*) echo hppa2.0-unknown-linux-gnu ;;
910 *) echo hppa-unknown-linux-gnu ;;
911 esac
912 exit 0 ;;
913 parisc64:Linux:*:* | hppa64:Linux:*:*)
914 echo hppa64-unknown-linux-gnu
915 exit 0 ;;
916 s390:Linux:*:* | s390x:Linux:*:*)
917 echo ${UNAME_MACHINE}-ibm-linux
918 exit 0 ;;
919 sh64*:Linux:*:*)
920 echo ${UNAME_MACHINE}-unknown-linux-gnu
921 exit 0 ;;
922 sh*:Linux:*:*)
923 echo ${UNAME_MACHINE}-unknown-linux-gnu
924 exit 0 ;;
925 sparc:Linux:*:* | sparc64:Linux:*:*)
926 echo ${UNAME_MACHINE}-unknown-linux-gnu
927 exit 0 ;;
928 x86_64:Linux:*:*)
929 echo x86_64-unknown-linux-gnu
930 exit 0 ;;
931 i*86:Linux:*:*)
932 # The BFD linker knows what the default object file format is, so
933 # first see if it will tell us. cd to the root directory to prevent
934 # problems with other programs or directories called `ld' in the path.
935 # Set LC_ALL=C to ensure ld outputs messages in English.
936 ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
937 | sed -ne '/supported targets:/!d
938 s/[ ][ ]*/ /g
939 s/.*supported targets: *//
940 s/ .*//
941 p'`
942 case "$ld_supported_targets" in
943 elf32-i386)
944 TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
945 ;;
946 a.out-i386-linux)
947 echo "${UNAME_MACHINE}-pc-linux-gnuaout"
948 exit 0 ;;
949 coff-i386)
950 echo "${UNAME_MACHINE}-pc-linux-gnucoff"
951 exit 0 ;;
952 "")
953 # Either a pre-BFD a.out linker (linux-gnuoldld) or
954 # one that does not give us useful --help.
955 echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
956 exit 0 ;;
957 esac
958 # Determine whether the default compiler is a.out or elf
959 eval $set_cc_for_build
960 sed 's/^ //' << EOF >$dummy.c
961 #include <features.h>
962 #ifdef __ELF__
963 # ifdef __GLIBC__
964 # if __GLIBC__ >= 2
965 LIBC=gnu
966 # else
967 LIBC=gnulibc1
968 # endif
969 # else
970 LIBC=gnulibc1
971 # endif
972 #else
973 #ifdef __INTEL_COMPILER
974 LIBC=gnu
975 #else
976 LIBC=gnuaout
977 #endif
978 #endif
979 #ifdef __dietlibc__
980 LIBC=dietlibc
981 #endif
982 EOF
983 eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
984 test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
985 test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
986 ;;
987 i*86:DYNIX/ptx:4*:*)
988 # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
989 # earlier versions are messed up and put the nodename in both
990 # sysname and nodename.
991 echo i386-sequent-sysv4
992 exit 0 ;;
993 i*86:UNIX_SV:4.2MP:2.*)
994 # Unixware is an offshoot of SVR4, but it has its own version
995 # number series starting with 2...
996 # I am not positive that other SVR4 systems won't match this,
997 # I just have to hope. -- rms.
998 # Use sysv4.2uw... so that sysv4* matches it.
999 echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
1000 exit 0 ;;
1001 i*86:OS/2:*:*)
1002 # If we were able to find `uname', then EMX Unix compatibility
1003 # is probably installed.
1004 echo ${UNAME_MACHINE}-pc-os2-emx
1005 exit 0 ;;
1006 i*86:XTS-300:*:STOP)
1007 echo ${UNAME_MACHINE}-unknown-stop
1008 exit 0 ;;
1009 i*86:atheos:*:*)
1010 echo ${UNAME_MACHINE}-unknown-atheos
1011 exit 0 ;;
1012 i*86:syllable:*:*)
1013 echo ${UNAME_MACHINE}-pc-syllable
1014 exit 0 ;;
1015 i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
1016 echo i386-unknown-lynxos${UNAME_RELEASE}
1017 exit 0 ;;
1018 i*86:*DOS:*:*)
1019 echo ${UNAME_MACHINE}-pc-msdosdjgpp
1020 exit 0 ;;
1021 i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
1022 UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
1023 if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
1024 echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
1025 else
1026 echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
1027 fi
1028 exit 0 ;;
1029 i*86:*:5:[78]*)
1030 case `/bin/uname -X | grep "^Machine"` in
1031 *486*) UNAME_MACHINE=i486 ;;
1032 *Pentium) UNAME_MACHINE=i586 ;;
1033 *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
1034 esac
1035 echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
1036 exit 0 ;;
1037 i*86:*:3.2:*)
1038 if test -f /usr/options/cb.name; then
1039 UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
1040 echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
1041 elif /bin/uname -X 2>/dev/null >/dev/null ; then
1042 UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
1043 (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
1044 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
1045 && UNAME_MACHINE=i586
1046 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
1047 && UNAME_MACHINE=i686
1048 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
1049 && UNAME_MACHINE=i686
1050 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
1051 else
1052 echo ${UNAME_MACHINE}-pc-sysv32
1053 fi
1054 exit 0 ;;
1055 pc:*:*:*)
1056 # Left here for compatibility:
1057 # uname -m prints for DJGPP always 'pc', but it prints nothing about
1058 # the processor, so we play safe by assuming i386.
1059 echo i386-pc-msdosdjgpp
1060 exit 0 ;;
1061 Intel:Mach:3*:*)
1062 echo i386-pc-mach3
1063 exit 0 ;;
1064 paragon:*:*:*)
1065 echo i860-intel-osf1
1066 exit 0 ;;
1067 i860:*:4.*:*) # i860-SVR4
1068 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
1069 echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
1070 else # Add other i860-SVR4 vendors below as they are discovered.
1071 echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
1072 fi
1073 exit 0 ;;
1074 mini*:CTIX:SYS*5:*)
1075 # "miniframe"
1076 echo m68010-convergent-sysv
1077 exit 0 ;;
1078 mc68k:UNIX:SYSTEM5:3.51m)
1079 echo m68k-convergent-sysv
1080 exit 0 ;;
1081 M680?0:D-NIX:5.3:*)
1082 echo m68k-diab-dnix
1083 exit 0 ;;
1084 M68*:*:R3V[5678]*:*)
1085 test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
1086 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
1087 OS_REL=''
1088 test -r /etc/.relid \
1089 && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
1090 /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
1091 && echo i486-ncr-sysv4.3${OS_REL} && exit 0
1092 /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
1093 && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
1094 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
1095 /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
1096 && echo i486-ncr-sysv4 && exit 0 ;;
1097 m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
1098 echo m68k-unknown-lynxos${UNAME_RELEASE}
1099 exit 0 ;;
1100 mc68030:UNIX_System_V:4.*:*)
1101 echo m68k-atari-sysv4
1102 exit 0 ;;
1103 TSUNAMI:LynxOS:2.*:*)
1104 echo sparc-unknown-lynxos${UNAME_RELEASE}
1105 exit 0 ;;
1106 rs6000:LynxOS:2.*:*)
1107 echo rs6000-unknown-lynxos${UNAME_RELEASE}
1108 exit 0 ;;
1109 PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
1110 echo powerpc-unknown-lynxos${UNAME_RELEASE}
1111 exit 0 ;;
1112 SM[BE]S:UNIX_SV:*:*)
1113 echo mips-dde-sysv${UNAME_RELEASE}
1114 exit 0 ;;
1115 RM*:ReliantUNIX-*:*:*)
1116 echo mips-sni-sysv4
1117 exit 0 ;;
1118 RM*:SINIX-*:*:*)
1119 echo mips-sni-sysv4
1120 exit 0 ;;
1121 *:SINIX-*:*:*)
1122 if uname -p 2>/dev/null >/dev/null ; then
1123 UNAME_MACHINE=`(uname -p) 2>/dev/null`
1124 echo ${UNAME_MACHINE}-sni-sysv4
1125 else
1126 echo ns32k-sni-sysv
1127 fi
1128 exit 0 ;;
1129 PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
1130 # says <Richard.M.Bartel@ccMail.Census.GOV>
1131 echo i586-unisys-sysv4
1132 exit 0 ;;
1133 *:UNIX_System_V:4*:FTX*)
1134 # From Gerald Hewes <hewes@openmarket.com>.
1135 # How about differentiating between stratus architectures? -djm
1136 echo hppa1.1-stratus-sysv4
1137 exit 0 ;;
1138 *:*:*:FTX*)
1139 # From seanf@swdc.stratus.com.
1140 echo i860-stratus-sysv4
1141 exit 0 ;;
1142 *:VOS:*:*)
1143 # From Paul.Green@stratus.com.
1144 echo hppa1.1-stratus-vos
1145 exit 0 ;;
1146 mc68*:A/UX:*:*)
1147 echo m68k-apple-aux${UNAME_RELEASE}
1148 exit 0 ;;
1149 news*:NEWS-OS:6*:*)
1150 echo mips-sony-newsos6
1151 exit 0 ;;
1152 R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
1153 if [ -d /usr/nec ]; then
1154 echo mips-nec-sysv${UNAME_RELEASE}
1155 else
1156 echo mips-unknown-sysv${UNAME_RELEASE}
1157 fi
1158 exit 0 ;;
1159 BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
1160 echo powerpc-be-beos
1161 exit 0 ;;
1162 BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
1163 echo powerpc-apple-beos
1164 exit 0 ;;
1165 BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
1166 echo i586-pc-beos
1167 exit 0 ;;
1168 SX-4:SUPER-UX:*:*)
1169 echo sx4-nec-superux${UNAME_RELEASE}
1170 exit 0 ;;
1171 SX-5:SUPER-UX:*:*)
1172 echo sx5-nec-superux${UNAME_RELEASE}
1173 exit 0 ;;
1174 SX-6:SUPER-UX:*:*)
1175 echo sx6-nec-superux${UNAME_RELEASE}
1176 exit 0 ;;
1177 Power*:Rhapsody:*:*)
1178 echo powerpc-apple-rhapsody${UNAME_RELEASE}
1179 exit 0 ;;
1180 *:Rhapsody:*:*)
1181 echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
1182 exit 0 ;;
1183 *:Darwin:*:*)
1184 UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
1185 case $UNAME_PROCESSOR in
1186 *86) UNAME_PROCESSOR=i686 ;;
1187 unknown) UNAME_PROCESSOR=powerpc ;;
1188 esac
1189 echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
1190 exit 0 ;;
1191 *:procnto*:*:* | *:QNX:[0123456789]*:*)
1192 UNAME_PROCESSOR=`uname -p`
1193 if test "$UNAME_PROCESSOR" = "x86"; then
1194 UNAME_PROCESSOR=i386
1195 UNAME_MACHINE=pc
1196 fi
1197 echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
1198 exit 0 ;;
1199 *:QNX:*:4*)
1200 echo i386-pc-qnx
1201 exit 0 ;;
1202 NSE-?:NONSTOP_KERNEL:*:*)
1203 echo nse-tandem-nsk${UNAME_RELEASE}
1204 exit 0 ;;
1205 NSR-?:NONSTOP_KERNEL:*:*)
1206 echo nsr-tandem-nsk${UNAME_RELEASE}
1207 exit 0 ;;
1208 *:NonStop-UX:*:*)
1209 echo mips-compaq-nonstopux
1210 exit 0 ;;
1211 BS2000:POSIX*:*:*)
1212 echo bs2000-siemens-sysv
1213 exit 0 ;;
1214 DS/*:UNIX_System_V:*:*)
1215 echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
1216 exit 0 ;;
1217 *:Plan9:*:*)
1218 # "uname -m" is not consistent, so use $cputype instead. 386
1219 # is converted to i386 for consistency with other x86
1220 # operating systems.
1221 if test "$cputype" = "386"; then
1222 UNAME_MACHINE=i386
1223 else
1224 UNAME_MACHINE="$cputype"
1225 fi
1226 echo ${UNAME_MACHINE}-unknown-plan9
1227 exit 0 ;;
1228 *:TOPS-10:*:*)
1229 echo pdp10-unknown-tops10
1230 exit 0 ;;
1231 *:TENEX:*:*)
1232 echo pdp10-unknown-tenex
1233 exit 0 ;;
1234 KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
1235 echo pdp10-dec-tops20
1236 exit 0 ;;
1237 XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
1238 echo pdp10-xkl-tops20
1239 exit 0 ;;
1240 *:TOPS-20:*:*)
1241 echo pdp10-unknown-tops20
1242 exit 0 ;;
1243 *:ITS:*:*)
1244 echo pdp10-unknown-its
1245 exit 0 ;;
1246 SEI:*:*:SEIUX)
1247 echo mips-sei-seiux${UNAME_RELEASE}
1248 exit 0 ;;
1249 *:DragonFly:*:*)
1250 echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
1251 exit 0 ;;
1252 *:*VMS:*:*)
1253 UNAME_MACHINE=`(uname -p) 2>/dev/null`
1254 case "${UNAME_MACHINE}" in
1255 A*) echo alpha-dec-vms && exit 0 ;;
1256 I*) echo ia64-dec-vms && exit 0 ;;
1257 V*) echo vax-dec-vms && exit 0 ;;
1258 esac ;;
1259 *:XENIX:*:SysV)
1260 echo i386-pc-xenix
1261 exit 0 ;;
1262 esac
1263
1264 #echo '(No uname command or uname output not recognized.)' 1>&2
1265 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
1266
1267 eval $set_cc_for_build
1268 cat >$dummy.c <<EOF
1269 #ifdef _SEQUENT_
1270 # include <sys/types.h>
1271 # include <sys/utsname.h>
1272 #endif
1273 main ()
1274 {
1275 #if defined (sony)
1276 #if defined (MIPSEB)
1277 /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
1278 I don't know.... */
1279 printf ("mips-sony-bsd\n"); exit (0);
1280 #else
1281 #include <sys/param.h>
1282 printf ("m68k-sony-newsos%s\n",
1283 #ifdef NEWSOS4
1284 "4"
1285 #else
1286 ""
1287 #endif
1288 ); exit (0);
1289 #endif
1290 #endif
1291
1292 #if defined (__arm) && defined (__acorn) && defined (__unix)
1293 printf ("arm-acorn-riscix"); exit (0);
1294 #endif
1295
1296 #if defined (hp300) && !defined (hpux)
1297 printf ("m68k-hp-bsd\n"); exit (0);
1298 #endif
1299
1300 #if defined (NeXT)
1301 #if !defined (__ARCHITECTURE__)
1302 #define __ARCHITECTURE__ "m68k"
1303 #endif
1304 int version;
1305 version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
1306 if (version < 4)
1307 printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
1308 else
1309 printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
1310 exit (0);
1311 #endif
1312
1313 #if defined (MULTIMAX) || defined (n16)
1314 #if defined (UMAXV)
1315 printf ("ns32k-encore-sysv\n"); exit (0);
1316 #else
1317 #if defined (CMU)
1318 printf ("ns32k-encore-mach\n"); exit (0);
1319 #else
1320 printf ("ns32k-encore-bsd\n"); exit (0);
1321 #endif
1322 #endif
1323 #endif
1324
1325 #if defined (__386BSD__)
1326 printf ("i386-pc-bsd\n"); exit (0);
1327 #endif
1328
1329 #if defined (sequent)
1330 #if defined (i386)
1331 printf ("i386-sequent-dynix\n"); exit (0);
1332 #endif
1333 #if defined (ns32000)
1334 printf ("ns32k-sequent-dynix\n"); exit (0);
1335 #endif
1336 #endif
1337
1338 #if defined (_SEQUENT_)
1339 struct utsname un;
1340
1341 uname(&un);
1342
1343 if (strncmp(un.version, "V2", 2) == 0) {
1344 printf ("i386-sequent-ptx2\n"); exit (0);
1345 }
1346 if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
1347 printf ("i386-sequent-ptx1\n"); exit (0);
1348 }
1349 printf ("i386-sequent-ptx\n"); exit (0);
1350
1351 #endif
1352
1353 #if defined (vax)
1354 # if !defined (ultrix)
1355 # include <sys/param.h>
1356 # if defined (BSD)
1357 # if BSD == 43
1358 printf ("vax-dec-bsd4.3\n"); exit (0);
1359 # else
1360 # if BSD == 199006
1361 printf ("vax-dec-bsd4.3reno\n"); exit (0);
1362 # else
1363 printf ("vax-dec-bsd\n"); exit (0);
1364 # endif
1365 # endif
1366 # else
1367 printf ("vax-dec-bsd\n"); exit (0);
1368 # endif
1369 # else
1370 printf ("vax-dec-ultrix\n"); exit (0);
1371 # endif
1372 #endif
1373
1374 #if defined (alliant) && defined (i860)
1375 printf ("i860-alliant-bsd\n"); exit (0);
1376 #endif
1377
1378 exit (1);
1379 }
1380 EOF
1381
1382 $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
1383
1384 # Apollos put the system type in the environment.
1385
1386 test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
1387
1388 # Convex versions that predate uname can use getsysinfo(1)
1389
1390 if [ -x /usr/convex/getsysinfo ]
1391 then
1392 case `getsysinfo -f cpu_type` in
1393 c1*)
1394 echo c1-convex-bsd
1395 exit 0 ;;
1396 c2*)
1397 if getsysinfo -f scalar_acc
1398 then echo c32-convex-bsd
1399 else echo c2-convex-bsd
1400 fi
1401 exit 0 ;;
1402 c34*)
1403 echo c34-convex-bsd
1404 exit 0 ;;
1405 c38*)
1406 echo c38-convex-bsd
1407 exit 0 ;;
1408 c4*)
1409 echo c4-convex-bsd
1410 exit 0 ;;
1411 esac
1412 fi
1413
1414 cat >&2 <<EOF
1415 $0: unable to guess system type
1416
1417 This script, last modified $timestamp, has failed to recognize
1418 the operating system you are using. It is advised that you
1419 download the most up to date version of the config scripts from
1420
1421 ftp://ftp.gnu.org/pub/gnu/config/
1422
1423 If the version you run ($0) is already up to date, please
1424 send the following data and any information you think might be
1425 pertinent to <config-patches@gnu.org> in order to provide the needed
1426 information to handle your system.
1427
1428 config.guess timestamp = $timestamp
1429
1430 uname -m = `(uname -m) 2>/dev/null || echo unknown`
1431 uname -r = `(uname -r) 2>/dev/null || echo unknown`
1432 uname -s = `(uname -s) 2>/dev/null || echo unknown`
1433 uname -v = `(uname -v) 2>/dev/null || echo unknown`
1434
1435 /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
1436 /bin/uname -X = `(/bin/uname -X) 2>/dev/null`
1437
1438 hostinfo = `(hostinfo) 2>/dev/null`
1439 /bin/universe = `(/bin/universe) 2>/dev/null`
1440 /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
1441 /bin/arch = `(/bin/arch) 2>/dev/null`
1442 /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
1443 /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
1444
1445 UNAME_MACHINE = ${UNAME_MACHINE}
1446 UNAME_RELEASE = ${UNAME_RELEASE}
1447 UNAME_SYSTEM = ${UNAME_SYSTEM}
1448 UNAME_VERSION = ${UNAME_VERSION}
1449 EOF
1450
1451 exit 1
1452
1453 # Local variables:
1454 # eval: (add-hook 'write-file-hooks 'time-stamp)
1455 # time-stamp-start: "timestamp='"
1456 # time-stamp-format: "%:y-%02m-%02d"
1457 # time-stamp-end: "'"
1458 # End:
8080 /* Define to the version of this package. */
8181 #undef PACKAGE_VERSION
8282
83 /* The size of `char', as computed by sizeof. */
84 #undef SIZEOF_CHAR
85
86 /* The size of `int', as computed by sizeof. */
87 #undef SIZEOF_INT
88
89 /* The size of `long', as computed by sizeof. */
90 #undef SIZEOF_LONG
91
92 /* The size of `short', as computed by sizeof. */
93 #undef SIZEOF_SHORT
94
95 /* The size of `void *', as computed by sizeof. */
96 #undef SIZEOF_VOID_P
97
8398 /* Define to 1 if you have the ANSI C header files. */
8499 #undef STDC_HEADERS
85100
86101 /* Version number of package */
87102 #undef VERSION
88103
104 /* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
105 <pthread.h>, or <semaphore.h> is not used. If the typedef was allowed, the
106 #define below would cause a syntax error. */
107 #undef _UINT32_T
108
109 /* Use char pointers for newer libiconv */
110 #undef inbuf_t
111
112 /* Define to the type of a signed integer type of width exactly 16 bits if
113 such a type exists and the standard includes do not define it. */
114 #undef int16_t
115
116 /* Define to the type of a signed integer type of width exactly 32 bits if
117 such a type exists and the standard includes do not define it. */
118 #undef int32_t
119
89120 /* Define the real type of socklen_t */
90121 #undef socklen_t
122
123 /* Define to the type of an unsigned integer type of width exactly 16 bits if
124 such a type exists and the standard includes do not define it. */
125 #undef uint16_t
126
127 /* Define to the type of an unsigned integer type of width exactly 32 bits if
128 such a type exists and the standard includes do not define it. */
129 #undef uint32_t
+0
-1566
config.sub less more
0 #! /bin/sh
1 # Configuration validation subroutine script.
2 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
3 # 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4
5 timestamp='2005-02-10'
6
7 # This file is (in principle) common to ALL GNU software.
8 # The presence of a machine in this file suggests that SOME GNU software
9 # can handle that machine. It does not imply ALL GNU software can.
10 #
11 # This file is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place - Suite 330,
24 # Boston, MA 02111-1307, USA.
25
26 # As a special exception to the GNU General Public License, if you
27 # distribute this file as part of a program that contains a
28 # configuration script generated by Autoconf, you may include it under
29 # the same distribution terms that you use for the rest of that program.
30
31 # Please send patches to <config-patches@gnu.org>. Submit a context
32 # diff and a properly formatted ChangeLog entry.
33 #
34 # Configuration subroutine to validate and canonicalize a configuration type.
35 # Supply the specified configuration type as an argument.
36 # If it is invalid, we print an error message on stderr and exit with code 1.
37 # Otherwise, we print the canonical config type on stdout and succeed.
38
39 # This file is supposed to be the same for all GNU packages
40 # and recognize all the CPU types, system types and aliases
41 # that are meaningful with *any* GNU software.
42 # Each package is responsible for reporting which valid configurations
43 # it does not support. The user should be able to distinguish
44 # a failure to support a valid configuration from a meaningless
45 # configuration.
46
47 # The goal of this file is to map all the various variations of a given
48 # machine specification into a single specification in the form:
49 # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
50 # or in some cases, the newer four-part form:
51 # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
52 # It is wrong to echo any other type of specification.
53
54 me=`echo "$0" | sed -e 's,.*/,,'`
55
56 usage="\
57 Usage: $0 [OPTION] CPU-MFR-OPSYS
58 $0 [OPTION] ALIAS
59
60 Canonicalize a configuration name.
61
62 Operation modes:
63 -h, --help print this help, then exit
64 -t, --time-stamp print date of last modification, then exit
65 -v, --version print version number, then exit
66
67 Report bugs and patches to <config-patches@gnu.org>."
68
69 version="\
70 GNU config.sub ($timestamp)
71
72 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
73 Free Software Foundation, Inc.
74
75 This is free software; see the source for copying conditions. There is NO
76 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
77
78 help="
79 Try \`$me --help' for more information."
80
81 # Parse command line
82 while test $# -gt 0 ; do
83 case $1 in
84 --time-stamp | --time* | -t )
85 echo "$timestamp" ; exit 0 ;;
86 --version | -v )
87 echo "$version" ; exit 0 ;;
88 --help | --h* | -h )
89 echo "$usage"; exit 0 ;;
90 -- ) # Stop option processing
91 shift; break ;;
92 - ) # Use stdin as input.
93 break ;;
94 -* )
95 echo "$me: invalid option $1$help"
96 exit 1 ;;
97
98 *local*)
99 # First pass through any local machine types.
100 echo $1
101 exit 0;;
102
103 * )
104 break ;;
105 esac
106 done
107
108 case $# in
109 0) echo "$me: missing argument$help" >&2
110 exit 1;;
111 1) ;;
112 *) echo "$me: too many arguments$help" >&2
113 exit 1;;
114 esac
115
116 # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
117 # Here we must recognize all the valid KERNEL-OS combinations.
118 maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
119 case $maybe_os in
120 nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
121 kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
122 os=-$maybe_os
123 basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
124 ;;
125 *)
126 basic_machine=`echo $1 | sed 's/-[^-]*$//'`
127 if [ $basic_machine != $1 ]
128 then os=`echo $1 | sed 's/.*-/-/'`
129 else os=; fi
130 ;;
131 esac
132
133 ### Let's recognize common machines as not being operating systems so
134 ### that things like config.sub decstation-3100 work. We also
135 ### recognize some manufacturers as not being operating systems, so we
136 ### can provide default operating systems below.
137 case $os in
138 -sun*os*)
139 # Prevent following clause from handling this invalid input.
140 ;;
141 -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
142 -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
143 -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
144 -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
145 -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
146 -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
147 -apple | -axis | -knuth | -cray)
148 os=
149 basic_machine=$1
150 ;;
151 -sim | -cisco | -oki | -wec | -winbond)
152 os=
153 basic_machine=$1
154 ;;
155 -scout)
156 ;;
157 -wrs)
158 os=-vxworks
159 basic_machine=$1
160 ;;
161 -chorusos*)
162 os=-chorusos
163 basic_machine=$1
164 ;;
165 -chorusrdb)
166 os=-chorusrdb
167 basic_machine=$1
168 ;;
169 -hiux*)
170 os=-hiuxwe2
171 ;;
172 -sco5)
173 os=-sco3.2v5
174 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
175 ;;
176 -sco4)
177 os=-sco3.2v4
178 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
179 ;;
180 -sco3.2.[4-9]*)
181 os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
182 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
183 ;;
184 -sco3.2v[4-9]*)
185 # Don't forget version if it is 3.2v4 or newer.
186 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
187 ;;
188 -sco*)
189 os=-sco3.2v2
190 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
191 ;;
192 -udk*)
193 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
194 ;;
195 -isc)
196 os=-isc2.2
197 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
198 ;;
199 -clix*)
200 basic_machine=clipper-intergraph
201 ;;
202 -isc*)
203 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
204 ;;
205 -lynx*)
206 os=-lynxos
207 ;;
208 -ptx*)
209 basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
210 ;;
211 -windowsnt*)
212 os=`echo $os | sed -e 's/windowsnt/winnt/'`
213 ;;
214 -psos*)
215 os=-psos
216 ;;
217 -mint | -mint[0-9]*)
218 basic_machine=m68k-atari
219 os=-mint
220 ;;
221 esac
222
223 # Decode aliases for certain CPU-COMPANY combinations.
224 case $basic_machine in
225 # Recognize the basic CPU types without company name.
226 # Some are omitted here because they have special meanings below.
227 1750a | 580 \
228 | a29k \
229 | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
230 | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
231 | am33_2.0 \
232 | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
233 | c4x | clipper \
234 | d10v | d30v | dlx | dsp16xx \
235 | fr30 | frv \
236 | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
237 | i370 | i860 | i960 | ia64 \
238 | ip2k | iq2000 \
239 | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \
240 | mips | mipsbe | mipseb | mipsel | mipsle \
241 | mips16 \
242 | mips64 | mips64el \
243 | mips64vr | mips64vrel \
244 | mips64orion | mips64orionel \
245 | mips64vr4100 | mips64vr4100el \
246 | mips64vr4300 | mips64vr4300el \
247 | mips64vr5000 | mips64vr5000el \
248 | mipsisa32 | mipsisa32el \
249 | mipsisa32r2 | mipsisa32r2el \
250 | mipsisa64 | mipsisa64el \
251 | mipsisa64r2 | mipsisa64r2el \
252 | mipsisa64sb1 | mipsisa64sb1el \
253 | mipsisa64sr71k | mipsisa64sr71kel \
254 | mipstx39 | mipstx39el \
255 | mn10200 | mn10300 \
256 | msp430 \
257 | ns16k | ns32k \
258 | openrisc | or32 \
259 | pdp10 | pdp11 | pj | pjl \
260 | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
261 | pyramid \
262 | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
263 | sh64 | sh64le \
264 | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \
265 | strongarm \
266 | tahoe | thumb | tic4x | tic80 | tron \
267 | v850 | v850e \
268 | we32k \
269 | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \
270 | z8k)
271 basic_machine=$basic_machine-unknown
272 ;;
273 m6811 | m68hc11 | m6812 | m68hc12)
274 # Motorola 68HC11/12.
275 basic_machine=$basic_machine-unknown
276 os=-none
277 ;;
278 m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
279 ;;
280
281 # We use `pc' rather than `unknown'
282 # because (1) that's what they normally are, and
283 # (2) the word "unknown" tends to confuse beginning users.
284 i*86 | x86_64)
285 basic_machine=$basic_machine-pc
286 ;;
287 # Object if more than one company name word.
288 *-*-*)
289 echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
290 exit 1
291 ;;
292 # Recognize the basic CPU types with company name.
293 580-* \
294 | a29k-* \
295 | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
296 | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
297 | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
298 | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
299 | avr-* \
300 | bs2000-* \
301 | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
302 | clipper-* | craynv-* | cydra-* \
303 | d10v-* | d30v-* | dlx-* \
304 | elxsi-* \
305 | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
306 | h8300-* | h8500-* \
307 | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
308 | i*86-* | i860-* | i960-* | ia64-* \
309 | ip2k-* | iq2000-* \
310 | m32r-* | m32rle-* \
311 | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
312 | m88110-* | m88k-* | maxq-* | mcore-* \
313 | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
314 | mips16-* \
315 | mips64-* | mips64el-* \
316 | mips64vr-* | mips64vrel-* \
317 | mips64orion-* | mips64orionel-* \
318 | mips64vr4100-* | mips64vr4100el-* \
319 | mips64vr4300-* | mips64vr4300el-* \
320 | mips64vr5000-* | mips64vr5000el-* \
321 | mipsisa32-* | mipsisa32el-* \
322 | mipsisa32r2-* | mipsisa32r2el-* \
323 | mipsisa64-* | mipsisa64el-* \
324 | mipsisa64r2-* | mipsisa64r2el-* \
325 | mipsisa64sb1-* | mipsisa64sb1el-* \
326 | mipsisa64sr71k-* | mipsisa64sr71kel-* \
327 | mipstx39-* | mipstx39el-* \
328 | mmix-* \
329 | msp430-* \
330 | none-* | np1-* | ns16k-* | ns32k-* \
331 | orion-* \
332 | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
333 | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
334 | pyramid-* \
335 | romp-* | rs6000-* \
336 | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
337 | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
338 | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
339 | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
340 | tahoe-* | thumb-* \
341 | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
342 | tron-* \
343 | v850-* | v850e-* | vax-* \
344 | we32k-* \
345 | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \
346 | xstormy16-* | xtensa-* \
347 | ymp-* \
348 | z8k-*)
349 ;;
350 # Recognize the various machine names and aliases which stand
351 # for a CPU type and a company and sometimes even an OS.
352 386bsd)
353 basic_machine=i386-unknown
354 os=-bsd
355 ;;
356 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
357 basic_machine=m68000-att
358 ;;
359 3b*)
360 basic_machine=we32k-att
361 ;;
362 a29khif)
363 basic_machine=a29k-amd
364 os=-udi
365 ;;
366 abacus)
367 basic_machine=abacus-unknown
368 ;;
369 adobe68k)
370 basic_machine=m68010-adobe
371 os=-scout
372 ;;
373 alliant | fx80)
374 basic_machine=fx80-alliant
375 ;;
376 altos | altos3068)
377 basic_machine=m68k-altos
378 ;;
379 am29k)
380 basic_machine=a29k-none
381 os=-bsd
382 ;;
383 amd64)
384 basic_machine=x86_64-pc
385 ;;
386 amd64-*)
387 basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
388 ;;
389 amdahl)
390 basic_machine=580-amdahl
391 os=-sysv
392 ;;
393 amiga | amiga-*)
394 basic_machine=m68k-unknown
395 ;;
396 amigaos | amigados)
397 basic_machine=m68k-unknown
398 os=-amigaos
399 ;;
400 amigaunix | amix)
401 basic_machine=m68k-unknown
402 os=-sysv4
403 ;;
404 apollo68)
405 basic_machine=m68k-apollo
406 os=-sysv
407 ;;
408 apollo68bsd)
409 basic_machine=m68k-apollo
410 os=-bsd
411 ;;
412 aux)
413 basic_machine=m68k-apple
414 os=-aux
415 ;;
416 balance)
417 basic_machine=ns32k-sequent
418 os=-dynix
419 ;;
420 c90)
421 basic_machine=c90-cray
422 os=-unicos
423 ;;
424 convex-c1)
425 basic_machine=c1-convex
426 os=-bsd
427 ;;
428 convex-c2)
429 basic_machine=c2-convex
430 os=-bsd
431 ;;
432 convex-c32)
433 basic_machine=c32-convex
434 os=-bsd
435 ;;
436 convex-c34)
437 basic_machine=c34-convex
438 os=-bsd
439 ;;
440 convex-c38)
441 basic_machine=c38-convex
442 os=-bsd
443 ;;
444 cray | j90)
445 basic_machine=j90-cray
446 os=-unicos
447 ;;
448 craynv)
449 basic_machine=craynv-cray
450 os=-unicosmp
451 ;;
452 cr16c)
453 basic_machine=cr16c-unknown
454 os=-elf
455 ;;
456 crds | unos)
457 basic_machine=m68k-crds
458 ;;
459 crisv32 | crisv32-* | etraxfs*)
460 basic_machine=crisv32-axis
461 ;;
462 cris | cris-* | etrax*)
463 basic_machine=cris-axis
464 ;;
465 crx)
466 basic_machine=crx-unknown
467 os=-elf
468 ;;
469 da30 | da30-*)
470 basic_machine=m68k-da30
471 ;;
472 decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
473 basic_machine=mips-dec
474 ;;
475 decsystem10* | dec10*)
476 basic_machine=pdp10-dec
477 os=-tops10
478 ;;
479 decsystem20* | dec20*)
480 basic_machine=pdp10-dec
481 os=-tops20
482 ;;
483 delta | 3300 | motorola-3300 | motorola-delta \
484 | 3300-motorola | delta-motorola)
485 basic_machine=m68k-motorola
486 ;;
487 delta88)
488 basic_machine=m88k-motorola
489 os=-sysv3
490 ;;
491 djgpp)
492 basic_machine=i586-pc
493 os=-msdosdjgpp
494 ;;
495 dpx20 | dpx20-*)
496 basic_machine=rs6000-bull
497 os=-bosx
498 ;;
499 dpx2* | dpx2*-bull)
500 basic_machine=m68k-bull
501 os=-sysv3
502 ;;
503 ebmon29k)
504 basic_machine=a29k-amd
505 os=-ebmon
506 ;;
507 elxsi)
508 basic_machine=elxsi-elxsi
509 os=-bsd
510 ;;
511 encore | umax | mmax)
512 basic_machine=ns32k-encore
513 ;;
514 es1800 | OSE68k | ose68k | ose | OSE)
515 basic_machine=m68k-ericsson
516 os=-ose
517 ;;
518 fx2800)
519 basic_machine=i860-alliant
520 ;;
521 genix)
522 basic_machine=ns32k-ns
523 ;;
524 gmicro)
525 basic_machine=tron-gmicro
526 os=-sysv
527 ;;
528 go32)
529 basic_machine=i386-pc
530 os=-go32
531 ;;
532 h3050r* | hiux*)
533 basic_machine=hppa1.1-hitachi
534 os=-hiuxwe2
535 ;;
536 h8300hms)
537 basic_machine=h8300-hitachi
538 os=-hms
539 ;;
540 h8300xray)
541 basic_machine=h8300-hitachi
542 os=-xray
543 ;;
544 h8500hms)
545 basic_machine=h8500-hitachi
546 os=-hms
547 ;;
548 harris)
549 basic_machine=m88k-harris
550 os=-sysv3
551 ;;
552 hp300-*)
553 basic_machine=m68k-hp
554 ;;
555 hp300bsd)
556 basic_machine=m68k-hp
557 os=-bsd
558 ;;
559 hp300hpux)
560 basic_machine=m68k-hp
561 os=-hpux
562 ;;
563 hp3k9[0-9][0-9] | hp9[0-9][0-9])
564 basic_machine=hppa1.0-hp
565 ;;
566 hp9k2[0-9][0-9] | hp9k31[0-9])
567 basic_machine=m68000-hp
568 ;;
569 hp9k3[2-9][0-9])
570 basic_machine=m68k-hp
571 ;;
572 hp9k6[0-9][0-9] | hp6[0-9][0-9])
573 basic_machine=hppa1.0-hp
574 ;;
575 hp9k7[0-79][0-9] | hp7[0-79][0-9])
576 basic_machine=hppa1.1-hp
577 ;;
578 hp9k78[0-9] | hp78[0-9])
579 # FIXME: really hppa2.0-hp
580 basic_machine=hppa1.1-hp
581 ;;
582 hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
583 # FIXME: really hppa2.0-hp
584 basic_machine=hppa1.1-hp
585 ;;
586 hp9k8[0-9][13679] | hp8[0-9][13679])
587 basic_machine=hppa1.1-hp
588 ;;
589 hp9k8[0-9][0-9] | hp8[0-9][0-9])
590 basic_machine=hppa1.0-hp
591 ;;
592 hppa-next)
593 os=-nextstep3
594 ;;
595 hppaosf)
596 basic_machine=hppa1.1-hp
597 os=-osf
598 ;;
599 hppro)
600 basic_machine=hppa1.1-hp
601 os=-proelf
602 ;;
603 i370-ibm* | ibm*)
604 basic_machine=i370-ibm
605 ;;
606 # I'm not sure what "Sysv32" means. Should this be sysv3.2?
607 i*86v32)
608 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
609 os=-sysv32
610 ;;
611 i*86v4*)
612 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
613 os=-sysv4
614 ;;
615 i*86v)
616 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
617 os=-sysv
618 ;;
619 i*86sol2)
620 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
621 os=-solaris2
622 ;;
623 i386mach)
624 basic_machine=i386-mach
625 os=-mach
626 ;;
627 i386-vsta | vsta)
628 basic_machine=i386-unknown
629 os=-vsta
630 ;;
631 iris | iris4d)
632 basic_machine=mips-sgi
633 case $os in
634 -irix*)
635 ;;
636 *)
637 os=-irix4
638 ;;
639 esac
640 ;;
641 isi68 | isi)
642 basic_machine=m68k-isi
643 os=-sysv
644 ;;
645 m88k-omron*)
646 basic_machine=m88k-omron
647 ;;
648 magnum | m3230)
649 basic_machine=mips-mips
650 os=-sysv
651 ;;
652 merlin)
653 basic_machine=ns32k-utek
654 os=-sysv
655 ;;
656 mingw32)
657 basic_machine=i386-pc
658 os=-mingw32
659 ;;
660 miniframe)
661 basic_machine=m68000-convergent
662 ;;
663 *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
664 basic_machine=m68k-atari
665 os=-mint
666 ;;
667 mips3*-*)
668 basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
669 ;;
670 mips3*)
671 basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
672 ;;
673 monitor)
674 basic_machine=m68k-rom68k
675 os=-coff
676 ;;
677 morphos)
678 basic_machine=powerpc-unknown
679 os=-morphos
680 ;;
681 msdos)
682 basic_machine=i386-pc
683 os=-msdos
684 ;;
685 mvs)
686 basic_machine=i370-ibm
687 os=-mvs
688 ;;
689 ncr3000)
690 basic_machine=i486-ncr
691 os=-sysv4
692 ;;
693 netbsd386)
694 basic_machine=i386-unknown
695 os=-netbsd
696 ;;
697 netwinder)
698 basic_machine=armv4l-rebel
699 os=-linux
700 ;;
701 news | news700 | news800 | news900)
702 basic_machine=m68k-sony
703 os=-newsos
704 ;;
705 news1000)
706 basic_machine=m68030-sony
707 os=-newsos
708 ;;
709 news-3600 | risc-news)
710 basic_machine=mips-sony
711 os=-newsos
712 ;;
713 necv70)
714 basic_machine=v70-nec
715 os=-sysv
716 ;;
717 next | m*-next )
718 basic_machine=m68k-next
719 case $os in
720 -nextstep* )
721 ;;
722 -ns2*)
723 os=-nextstep2
724 ;;
725 *)
726 os=-nextstep3
727 ;;
728 esac
729 ;;
730 nh3000)
731 basic_machine=m68k-harris
732 os=-cxux
733 ;;
734 nh[45]000)
735 basic_machine=m88k-harris
736 os=-cxux
737 ;;
738 nindy960)
739 basic_machine=i960-intel
740 os=-nindy
741 ;;
742 mon960)
743 basic_machine=i960-intel
744 os=-mon960
745 ;;
746 nonstopux)
747 basic_machine=mips-compaq
748 os=-nonstopux
749 ;;
750 np1)
751 basic_machine=np1-gould
752 ;;
753 nsr-tandem)
754 basic_machine=nsr-tandem
755 ;;
756 op50n-* | op60c-*)
757 basic_machine=hppa1.1-oki
758 os=-proelf
759 ;;
760 or32 | or32-*)
761 basic_machine=or32-unknown
762 os=-coff
763 ;;
764 os400)
765 basic_machine=powerpc-ibm
766 os=-os400
767 ;;
768 OSE68000 | ose68000)
769 basic_machine=m68000-ericsson
770 os=-ose
771 ;;
772 os68k)
773 basic_machine=m68k-none
774 os=-os68k
775 ;;
776 pa-hitachi)
777 basic_machine=hppa1.1-hitachi
778 os=-hiuxwe2
779 ;;
780 paragon)
781 basic_machine=i860-intel
782 os=-osf
783 ;;
784 pbd)
785 basic_machine=sparc-tti
786 ;;
787 pbb)
788 basic_machine=m68k-tti
789 ;;
790 pc532 | pc532-*)
791 basic_machine=ns32k-pc532
792 ;;
793 pentium | p5 | k5 | k6 | nexgen | viac3)
794 basic_machine=i586-pc
795 ;;
796 pentiumpro | p6 | 6x86 | athlon | athlon_*)
797 basic_machine=i686-pc
798 ;;
799 pentiumii | pentium2 | pentiumiii | pentium3)
800 basic_machine=i686-pc
801 ;;
802 pentium4)
803 basic_machine=i786-pc
804 ;;
805 pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
806 basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
807 ;;
808 pentiumpro-* | p6-* | 6x86-* | athlon-*)
809 basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
810 ;;
811 pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
812 basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
813 ;;
814 pentium4-*)
815 basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
816 ;;
817 pn)
818 basic_machine=pn-gould
819 ;;
820 power) basic_machine=power-ibm
821 ;;
822 ppc) basic_machine=powerpc-unknown
823 ;;
824 ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
825 ;;
826 ppcle | powerpclittle | ppc-le | powerpc-little)
827 basic_machine=powerpcle-unknown
828 ;;
829 ppcle-* | powerpclittle-*)
830 basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
831 ;;
832 ppc64) basic_machine=powerpc64-unknown
833 ;;
834 ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
835 ;;
836 ppc64le | powerpc64little | ppc64-le | powerpc64-little)
837 basic_machine=powerpc64le-unknown
838 ;;
839 ppc64le-* | powerpc64little-*)
840 basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
841 ;;
842 ps2)
843 basic_machine=i386-ibm
844 ;;
845 pw32)
846 basic_machine=i586-unknown
847 os=-pw32
848 ;;
849 rom68k)
850 basic_machine=m68k-rom68k
851 os=-coff
852 ;;
853 rm[46]00)
854 basic_machine=mips-siemens
855 ;;
856 rtpc | rtpc-*)
857 basic_machine=romp-ibm
858 ;;
859 s390 | s390-*)
860 basic_machine=s390-ibm
861 ;;
862 s390x | s390x-*)
863 basic_machine=s390x-ibm
864 ;;
865 sa29200)
866 basic_machine=a29k-amd
867 os=-udi
868 ;;
869 sb1)
870 basic_machine=mipsisa64sb1-unknown
871 ;;
872 sb1el)
873 basic_machine=mipsisa64sb1el-unknown
874 ;;
875 sei)
876 basic_machine=mips-sei
877 os=-seiux
878 ;;
879 sequent)
880 basic_machine=i386-sequent
881 ;;
882 sh)
883 basic_machine=sh-hitachi
884 os=-hms
885 ;;
886 sh64)
887 basic_machine=sh64-unknown
888 ;;
889 sparclite-wrs | simso-wrs)
890 basic_machine=sparclite-wrs
891 os=-vxworks
892 ;;
893 sps7)
894 basic_machine=m68k-bull
895 os=-sysv2
896 ;;
897 spur)
898 basic_machine=spur-unknown
899 ;;
900 st2000)
901 basic_machine=m68k-tandem
902 ;;
903 stratus)
904 basic_machine=i860-stratus
905 os=-sysv4
906 ;;
907 sun2)
908 basic_machine=m68000-sun
909 ;;
910 sun2os3)
911 basic_machine=m68000-sun
912 os=-sunos3
913 ;;
914 sun2os4)
915 basic_machine=m68000-sun
916 os=-sunos4
917 ;;
918 sun3os3)
919 basic_machine=m68k-sun
920 os=-sunos3
921 ;;
922 sun3os4)
923 basic_machine=m68k-sun
924 os=-sunos4
925 ;;
926 sun4os3)
927 basic_machine=sparc-sun
928 os=-sunos3
929 ;;
930 sun4os4)
931 basic_machine=sparc-sun
932 os=-sunos4
933 ;;
934 sun4sol2)
935 basic_machine=sparc-sun
936 os=-solaris2
937 ;;
938 sun3 | sun3-*)
939 basic_machine=m68k-sun
940 ;;
941 sun4)
942 basic_machine=sparc-sun
943 ;;
944 sun386 | sun386i | roadrunner)
945 basic_machine=i386-sun
946 ;;
947 sv1)
948 basic_machine=sv1-cray
949 os=-unicos
950 ;;
951 symmetry)
952 basic_machine=i386-sequent
953 os=-dynix
954 ;;
955 t3e)
956 basic_machine=alphaev5-cray
957 os=-unicos
958 ;;
959 t90)
960 basic_machine=t90-cray
961 os=-unicos
962 ;;
963 tic54x | c54x*)
964 basic_machine=tic54x-unknown
965 os=-coff
966 ;;
967 tic55x | c55x*)
968 basic_machine=tic55x-unknown
969 os=-coff
970 ;;
971 tic6x | c6x*)
972 basic_machine=tic6x-unknown
973 os=-coff
974 ;;
975 tx39)
976 basic_machine=mipstx39-unknown
977 ;;
978 tx39el)
979 basic_machine=mipstx39el-unknown
980 ;;
981 toad1)
982 basic_machine=pdp10-xkl
983 os=-tops20
984 ;;
985 tower | tower-32)
986 basic_machine=m68k-ncr
987 ;;
988 tpf)
989 basic_machine=s390x-ibm
990 os=-tpf
991 ;;
992 udi29k)
993 basic_machine=a29k-amd
994 os=-udi
995 ;;
996 ultra3)
997 basic_machine=a29k-nyu
998 os=-sym1
999 ;;
1000 v810 | necv810)
1001 basic_machine=v810-nec
1002 os=-none
1003 ;;
1004 vaxv)
1005 basic_machine=vax-dec
1006 os=-sysv
1007 ;;
1008 vms)
1009 basic_machine=vax-dec
1010 os=-vms
1011 ;;
1012 vpp*|vx|vx-*)
1013 basic_machine=f301-fujitsu
1014 ;;
1015 vxworks960)
1016 basic_machine=i960-wrs
1017 os=-vxworks
1018 ;;
1019 vxworks68)
1020 basic_machine=m68k-wrs
1021 os=-vxworks
1022 ;;
1023 vxworks29k)
1024 basic_machine=a29k-wrs
1025 os=-vxworks
1026 ;;
1027 w65*)
1028 basic_machine=w65-wdc
1029 os=-none
1030 ;;
1031 w89k-*)
1032 basic_machine=hppa1.1-winbond
1033 os=-proelf
1034 ;;
1035 xbox)
1036 basic_machine=i686-pc
1037 os=-mingw32
1038 ;;
1039 xps | xps100)
1040 basic_machine=xps100-honeywell
1041 ;;
1042 ymp)
1043 basic_machine=ymp-cray
1044 os=-unicos
1045 ;;
1046 z8k-*-coff)
1047 basic_machine=z8k-unknown
1048 os=-sim
1049 ;;
1050 none)
1051 basic_machine=none-none
1052 os=-none
1053 ;;
1054
1055 # Here we handle the default manufacturer of certain CPU types. It is in
1056 # some cases the only manufacturer, in others, it is the most popular.
1057 w89k)
1058 basic_machine=hppa1.1-winbond
1059 ;;
1060 op50n)
1061 basic_machine=hppa1.1-oki
1062 ;;
1063 op60c)
1064 basic_machine=hppa1.1-oki
1065 ;;
1066 romp)
1067 basic_machine=romp-ibm
1068 ;;
1069 mmix)
1070 basic_machine=mmix-knuth
1071 ;;
1072 rs6000)
1073 basic_machine=rs6000-ibm
1074 ;;
1075 vax)
1076 basic_machine=vax-dec
1077 ;;
1078 pdp10)
1079 # there are many clones, so DEC is not a safe bet
1080 basic_machine=pdp10-unknown
1081 ;;
1082 pdp11)
1083 basic_machine=pdp11-dec
1084 ;;
1085 we32k)
1086 basic_machine=we32k-att
1087 ;;
1088 sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
1089 basic_machine=sh-unknown
1090 ;;
1091 sh64)
1092 basic_machine=sh64-unknown
1093 ;;
1094 sparc | sparcv8 | sparcv9 | sparcv9b)
1095 basic_machine=sparc-sun
1096 ;;
1097 cydra)
1098 basic_machine=cydra-cydrome
1099 ;;
1100 orion)
1101 basic_machine=orion-highlevel
1102 ;;
1103 orion105)
1104 basic_machine=clipper-highlevel
1105 ;;
1106 mac | mpw | mac-mpw)
1107 basic_machine=m68k-apple
1108 ;;
1109 pmac | pmac-mpw)
1110 basic_machine=powerpc-apple
1111 ;;
1112 *-unknown)
1113 # Make sure to match an already-canonicalized machine name.
1114 ;;
1115 *)
1116 echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
1117 exit 1
1118 ;;
1119 esac
1120
1121 # Here we canonicalize certain aliases for manufacturers.
1122 case $basic_machine in
1123 *-digital*)
1124 basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
1125 ;;
1126 *-commodore*)
1127 basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
1128 ;;
1129 *)
1130 ;;
1131 esac
1132
1133 # Decode manufacturer-specific aliases for certain operating systems.
1134
1135 if [ x"$os" != x"" ]
1136 then
1137 case $os in
1138 # First match some system type aliases
1139 # that might get confused with valid system types.
1140 # -solaris* is a basic system type, with this one exception.
1141 -solaris1 | -solaris1.*)
1142 os=`echo $os | sed -e 's|solaris1|sunos4|'`
1143 ;;
1144 -solaris)
1145 os=-solaris2
1146 ;;
1147 -svr4*)
1148 os=-sysv4
1149 ;;
1150 -unixware*)
1151 os=-sysv4.2uw
1152 ;;
1153 -gnu/linux*)
1154 os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
1155 ;;
1156 # First accept the basic system types.
1157 # The portable systems comes first.
1158 # Each alternative MUST END IN A *, to match a version number.
1159 # -sysv* is not here because it comes later, after sysvr4.
1160 -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
1161 | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
1162 | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
1163 | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
1164 | -aos* \
1165 | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
1166 | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
1167 | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
1168 | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
1169 | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
1170 | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
1171 | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
1172 | -chorusos* | -chorusrdb* \
1173 | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
1174 | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
1175 | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
1176 | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
1177 | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
1178 | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
1179 | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
1180 | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*)
1181 # Remember, each alternative MUST END IN *, to match a version number.
1182 ;;
1183 -qnx*)
1184 case $basic_machine in
1185 x86-* | i*86-*)
1186 ;;
1187 *)
1188 os=-nto$os
1189 ;;
1190 esac
1191 ;;
1192 -nto-qnx*)
1193 ;;
1194 -nto*)
1195 os=`echo $os | sed -e 's|nto|nto-qnx|'`
1196 ;;
1197 -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
1198 | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
1199 | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
1200 ;;
1201 -mac*)
1202 os=`echo $os | sed -e 's|mac|macos|'`
1203 ;;
1204 -linux-dietlibc)
1205 os=-linux-dietlibc
1206 ;;
1207 -linux*)
1208 os=`echo $os | sed -e 's|linux|linux-gnu|'`
1209 ;;
1210 -sunos5*)
1211 os=`echo $os | sed -e 's|sunos5|solaris2|'`
1212 ;;
1213 -sunos6*)
1214 os=`echo $os | sed -e 's|sunos6|solaris3|'`
1215 ;;
1216 -opened*)
1217 os=-openedition
1218 ;;
1219 -os400*)
1220 os=-os400
1221 ;;
1222 -wince*)
1223 os=-wince
1224 ;;
1225 -osfrose*)
1226 os=-osfrose
1227 ;;
1228 -osf*)
1229 os=-osf
1230 ;;
1231 -utek*)
1232 os=-bsd
1233 ;;
1234 -dynix*)
1235 os=-bsd
1236 ;;
1237 -acis*)
1238 os=-aos
1239 ;;
1240 -atheos*)
1241 os=-atheos
1242 ;;
1243 -syllable*)
1244 os=-syllable
1245 ;;
1246 -386bsd)
1247 os=-bsd
1248 ;;
1249 -ctix* | -uts*)
1250 os=-sysv
1251 ;;
1252 -nova*)
1253 os=-rtmk-nova
1254 ;;
1255 -ns2 )
1256 os=-nextstep2
1257 ;;
1258 -nsk*)
1259 os=-nsk
1260 ;;
1261 # Preserve the version number of sinix5.
1262 -sinix5.*)
1263 os=`echo $os | sed -e 's|sinix|sysv|'`
1264 ;;
1265 -sinix*)
1266 os=-sysv4
1267 ;;
1268 -tpf*)
1269 os=-tpf
1270 ;;
1271 -triton*)
1272 os=-sysv3
1273 ;;
1274 -oss*)
1275 os=-sysv3
1276 ;;
1277 -svr4)
1278 os=-sysv4
1279 ;;
1280 -svr3)
1281 os=-sysv3
1282 ;;
1283 -sysvr4)
1284 os=-sysv4
1285 ;;
1286 # This must come after -sysvr4.
1287 -sysv*)
1288 ;;
1289 -ose*)
1290 os=-ose
1291 ;;
1292 -es1800*)
1293 os=-ose
1294 ;;
1295 -xenix)
1296 os=-xenix
1297 ;;
1298 -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
1299 os=-mint
1300 ;;
1301 -aros*)
1302 os=-aros
1303 ;;
1304 -kaos*)
1305 os=-kaos
1306 ;;
1307 -zvmoe)
1308 os=-zvmoe
1309 ;;
1310 -none)
1311 ;;
1312 *)
1313 # Get rid of the `-' at the beginning of $os.
1314 os=`echo $os | sed 's/[^-]*-//'`
1315 echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
1316 exit 1
1317 ;;
1318 esac
1319 else
1320
1321 # Here we handle the default operating systems that come with various machines.
1322 # The value should be what the vendor currently ships out the door with their
1323 # machine or put another way, the most popular os provided with the machine.
1324
1325 # Note that if you're going to try to match "-MANUFACTURER" here (say,
1326 # "-sun"), then you have to tell the case statement up towards the top
1327 # that MANUFACTURER isn't an operating system. Otherwise, code above
1328 # will signal an error saying that MANUFACTURER isn't an operating
1329 # system, and we'll never get to this point.
1330
1331 case $basic_machine in
1332 *-acorn)
1333 os=-riscix1.2
1334 ;;
1335 arm*-rebel)
1336 os=-linux
1337 ;;
1338 arm*-semi)
1339 os=-aout
1340 ;;
1341 c4x-* | tic4x-*)
1342 os=-coff
1343 ;;
1344 # This must come before the *-dec entry.
1345 pdp10-*)
1346 os=-tops20
1347 ;;
1348 pdp11-*)
1349 os=-none
1350 ;;
1351 *-dec | vax-*)
1352 os=-ultrix4.2
1353 ;;
1354 m68*-apollo)
1355 os=-domain
1356 ;;
1357 i386-sun)
1358 os=-sunos4.0.2
1359 ;;
1360 m68000-sun)
1361 os=-sunos3
1362 # This also exists in the configure program, but was not the
1363 # default.
1364 # os=-sunos4
1365 ;;
1366 m68*-cisco)
1367 os=-aout
1368 ;;
1369 mips*-cisco)
1370 os=-elf
1371 ;;
1372 mips*-*)
1373 os=-elf
1374 ;;
1375 or32-*)
1376 os=-coff
1377 ;;
1378 *-tti) # must be before sparc entry or we get the wrong os.
1379 os=-sysv3
1380 ;;
1381 sparc-* | *-sun)
1382 os=-sunos4.1.1
1383 ;;
1384 *-be)
1385 os=-beos
1386 ;;
1387 *-ibm)
1388 os=-aix
1389 ;;
1390 *-knuth)
1391 os=-mmixware
1392 ;;
1393 *-wec)
1394 os=-proelf
1395 ;;
1396 *-winbond)
1397 os=-proelf
1398 ;;
1399 *-oki)
1400 os=-proelf
1401 ;;
1402 *-hp)
1403 os=-hpux
1404 ;;
1405 *-hitachi)
1406 os=-hiux
1407 ;;
1408 i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
1409 os=-sysv
1410 ;;
1411 *-cbm)
1412 os=-amigaos
1413 ;;
1414 *-dg)
1415 os=-dgux
1416 ;;
1417 *-dolphin)
1418 os=-sysv3
1419 ;;
1420 m68k-ccur)
1421 os=-rtu
1422 ;;
1423 m88k-omron*)
1424 os=-luna
1425 ;;
1426 *-next )
1427 os=-nextstep
1428 ;;
1429 *-sequent)
1430 os=-ptx
1431 ;;
1432 *-crds)
1433 os=-unos
1434 ;;
1435 *-ns)
1436 os=-genix
1437 ;;
1438 i370-*)
1439 os=-mvs
1440 ;;
1441 *-next)
1442 os=-nextstep3
1443 ;;
1444 *-gould)
1445 os=-sysv
1446 ;;
1447 *-highlevel)
1448 os=-bsd
1449 ;;
1450 *-encore)
1451 os=-bsd
1452 ;;
1453 *-sgi)
1454 os=-irix
1455 ;;
1456 *-siemens)
1457 os=-sysv4
1458 ;;
1459 *-masscomp)
1460 os=-rtu
1461 ;;
1462 f30[01]-fujitsu | f700-fujitsu)
1463 os=-uxpv
1464 ;;
1465 *-rom68k)
1466 os=-coff
1467 ;;
1468 *-*bug)
1469 os=-coff
1470 ;;
1471 *-apple)
1472 os=-macos
1473 ;;
1474 *-atari*)
1475 os=-mint
1476 ;;
1477 *)
1478 os=-none
1479 ;;
1480 esac
1481 fi
1482
1483 # Here we handle the case where we know the os, and the CPU type, but not the
1484 # manufacturer. We pick the logical manufacturer.
1485 vendor=unknown
1486 case $basic_machine in
1487 *-unknown)
1488 case $os in
1489 -riscix*)
1490 vendor=acorn
1491 ;;
1492 -sunos*)
1493 vendor=sun
1494 ;;
1495 -aix*)
1496 vendor=ibm
1497 ;;
1498 -beos*)
1499 vendor=be
1500 ;;
1501 -hpux*)
1502 vendor=hp
1503 ;;
1504 -mpeix*)
1505 vendor=hp
1506 ;;
1507 -hiux*)
1508 vendor=hitachi
1509 ;;
1510 -unos*)
1511 vendor=crds
1512 ;;
1513 -dgux*)
1514 vendor=dg
1515 ;;
1516 -luna*)
1517 vendor=omron
1518 ;;
1519 -genix*)
1520 vendor=ns
1521 ;;
1522 -mvs* | -opened*)
1523 vendor=ibm
1524 ;;
1525 -os400*)
1526 vendor=ibm
1527 ;;
1528 -ptx*)
1529 vendor=sequent
1530 ;;
1531 -tpf*)
1532 vendor=ibm
1533 ;;
1534 -vxsim* | -vxworks* | -windiss*)
1535 vendor=wrs
1536 ;;
1537 -aux*)
1538 vendor=apple
1539 ;;
1540 -hms*)
1541 vendor=hitachi
1542 ;;
1543 -mpw* | -macos*)
1544 vendor=apple
1545 ;;
1546 -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
1547 vendor=atari
1548 ;;
1549 -vos*)
1550 vendor=stratus
1551 ;;
1552 esac
1553 basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
1554 ;;
1555 esac
1556
1557 echo $basic_machine$os
1558 exit 0
1559
1560 # Local variables:
1561 # eval: (add-hook 'write-file-hooks 'time-stamp)
1562 # time-stamp-start: "timestamp='"
1563 # time-stamp-format: "%:y-%02m-%02d"
1564 # time-stamp-end: "'"
1565 # End:
+0
-9694
configure less more
0 #! /bin/sh
1 # Guess values for system-dependent variables and create Makefiles.
2 # Generated by GNU Autoconf 2.59.
3 #
4 # Copyright (C) 2003 Free Software Foundation, Inc.
5 # This configure script is free software; the Free Software Foundation
6 # gives unlimited permission to copy, distribute and modify it.
7 ## --------------------- ##
8 ## M4sh Initialization. ##
9 ## --------------------- ##
10
11 # Be Bourne compatible
12 if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
13 emulate sh
14 NULLCMD=:
15 # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
16 # is contrary to our usage. Disable this feature.
17 alias -g '${1+"$@"}'='"$@"'
18 elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
19 set -o posix
20 fi
21 DUALCASE=1; export DUALCASE # for MKS sh
22
23 # Support unset when possible.
24 if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
25 as_unset=unset
26 else
27 as_unset=false
28 fi
29
30
31 # Work around bugs in pre-3.0 UWIN ksh.
32 $as_unset ENV MAIL MAILPATH
33 PS1='$ '
34 PS2='> '
35 PS4='+ '
36
37 # NLS nuisances.
38 for as_var in \
39 LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
40 LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
41 LC_TELEPHONE LC_TIME
42 do
43 if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
44 eval $as_var=C; export $as_var
45 else
46 $as_unset $as_var
47 fi
48 done
49
50 # Required to use basename.
51 if expr a : '\(a\)' >/dev/null 2>&1; then
52 as_expr=expr
53 else
54 as_expr=false
55 fi
56
57 if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
58 as_basename=basename
59 else
60 as_basename=false
61 fi
62
63
64 # Name of the executable.
65 as_me=`$as_basename "$0" ||
66 $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
67 X"$0" : 'X\(//\)$' \| \
68 X"$0" : 'X\(/\)$' \| \
69 . : '\(.\)' 2>/dev/null ||
70 echo X/"$0" |
71 sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
72 /^X\/\(\/\/\)$/{ s//\1/; q; }
73 /^X\/\(\/\).*/{ s//\1/; q; }
74 s/.*/./; q'`
75
76
77 # PATH needs CR, and LINENO needs CR and PATH.
78 # Avoid depending upon Character Ranges.
79 as_cr_letters='abcdefghijklmnopqrstuvwxyz'
80 as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
81 as_cr_Letters=$as_cr_letters$as_cr_LETTERS
82 as_cr_digits='0123456789'
83 as_cr_alnum=$as_cr_Letters$as_cr_digits
84
85 # The user is always right.
86 if test "${PATH_SEPARATOR+set}" != set; then
87 echo "#! /bin/sh" >conf$$.sh
88 echo "exit 0" >>conf$$.sh
89 chmod +x conf$$.sh
90 if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
91 PATH_SEPARATOR=';'
92 else
93 PATH_SEPARATOR=:
94 fi
95 rm -f conf$$.sh
96 fi
97
98
99 as_lineno_1=$LINENO
100 as_lineno_2=$LINENO
101 as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
102 test "x$as_lineno_1" != "x$as_lineno_2" &&
103 test "x$as_lineno_3" = "x$as_lineno_2" || {
104 # Find who we are. Look in the path if we contain no path at all
105 # relative or not.
106 case $0 in
107 *[\\/]* ) as_myself=$0 ;;
108 *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
109 for as_dir in $PATH
110 do
111 IFS=$as_save_IFS
112 test -z "$as_dir" && as_dir=.
113 test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
114 done
115
116 ;;
117 esac
118 # We did not find ourselves, most probably we were run as `sh COMMAND'
119 # in which case we are not to be found in the path.
120 if test "x$as_myself" = x; then
121 as_myself=$0
122 fi
123 if test ! -f "$as_myself"; then
124 { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
125 { (exit 1); exit 1; }; }
126 fi
127 case $CONFIG_SHELL in
128 '')
129 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
130 for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
131 do
132 IFS=$as_save_IFS
133 test -z "$as_dir" && as_dir=.
134 for as_base in sh bash ksh sh5; do
135 case $as_dir in
136 /*)
137 if ("$as_dir/$as_base" -c '
138 as_lineno_1=$LINENO
139 as_lineno_2=$LINENO
140 as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
141 test "x$as_lineno_1" != "x$as_lineno_2" &&
142 test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
143 $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
144 $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
145 CONFIG_SHELL=$as_dir/$as_base
146 export CONFIG_SHELL
147 exec "$CONFIG_SHELL" "$0" ${1+"$@"}
148 fi;;
149 esac
150 done
151 done
152 ;;
153 esac
154
155 # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
156 # uniformly replaced by the line number. The first 'sed' inserts a
157 # line-number line before each line; the second 'sed' does the real
158 # work. The second script uses 'N' to pair each line-number line
159 # with the numbered line, and appends trailing '-' during
160 # substitution so that $LINENO is not a special case at line end.
161 # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
162 # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
163 sed '=' <$as_myself |
164 sed '
165 N
166 s,$,-,
167 : loop
168 s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
169 t loop
170 s,-$,,
171 s,^['$as_cr_digits']*\n,,
172 ' >$as_me.lineno &&
173 chmod +x $as_me.lineno ||
174 { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
175 { (exit 1); exit 1; }; }
176
177 # Don't try to exec as it changes $[0], causing all sort of problems
178 # (the dirname of $[0] is not the place where we might find the
179 # original and so on. Autoconf is especially sensible to this).
180 . ./$as_me.lineno
181 # Exit status is that of the last command.
182 exit
183 }
184
185
186 case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
187 *c*,-n*) ECHO_N= ECHO_C='
188 ' ECHO_T=' ' ;;
189 *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
190 *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
191 esac
192
193 if expr a : '\(a\)' >/dev/null 2>&1; then
194 as_expr=expr
195 else
196 as_expr=false
197 fi
198
199 rm -f conf$$ conf$$.exe conf$$.file
200 echo >conf$$.file
201 if ln -s conf$$.file conf$$ 2>/dev/null; then
202 # We could just check for DJGPP; but this test a) works b) is more generic
203 # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
204 if test -f conf$$.exe; then
205 # Don't use ln at all; we don't have any links
206 as_ln_s='cp -p'
207 else
208 as_ln_s='ln -s'
209 fi
210 elif ln conf$$.file conf$$ 2>/dev/null; then
211 as_ln_s=ln
212 else
213 as_ln_s='cp -p'
214 fi
215 rm -f conf$$ conf$$.exe conf$$.file
216
217 if mkdir -p . 2>/dev/null; then
218 as_mkdir_p=:
219 else
220 test -d ./-p && rmdir ./-p
221 as_mkdir_p=false
222 fi
223
224 as_executable_p="test -f"
225
226 # Sed expression to map a string onto a valid CPP name.
227 as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
228
229 # Sed expression to map a string onto a valid variable name.
230 as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
231
232
233 # IFS
234 # We need space, tab and new line, in precisely that order.
235 as_nl='
236 '
237 IFS=" $as_nl"
238
239 # CDPATH.
240 $as_unset CDPATH
241
242
243 # Name of the host.
244 # hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
245 # so uname gets run too.
246 ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
247
248 exec 6>&1
249
250 #
251 # Initializations.
252 #
253 ac_default_prefix=/usr/local
254 ac_config_libobj_dir=.
255 cross_compiling=no
256 subdirs=
257 MFLAGS=
258 MAKEFLAGS=
259 SHELL=${CONFIG_SHELL-/bin/sh}
260
261 # Maximum number of lines to put in a shell here document.
262 # This variable seems obsolete. It should probably be removed, and
263 # only ac_max_sed_lines should be used.
264 : ${ac_max_here_lines=38}
265
266 # Identity of this package.
267 PACKAGE_NAME=
268 PACKAGE_TARNAME=
269 PACKAGE_VERSION=
270 PACKAGE_STRING=
271 PACKAGE_BUGREPORT=
272
273 ac_unique_file="src/dillo.c"
274 # Factoring default headers for most tests.
275 ac_includes_default="\
276 #include <stdio.h>
277 #if HAVE_SYS_TYPES_H
278 # include <sys/types.h>
279 #endif
280 #if HAVE_SYS_STAT_H
281 # include <sys/stat.h>
282 #endif
283 #if STDC_HEADERS
284 # include <stdlib.h>
285 # include <stddef.h>
286 #else
287 # if HAVE_STDLIB_H
288 # include <stdlib.h>
289 # endif
290 #endif
291 #if HAVE_STRING_H
292 # if !STDC_HEADERS && HAVE_MEMORY_H
293 # include <memory.h>
294 # endif
295 # include <string.h>
296 #endif
297 #if HAVE_STRINGS_H
298 # include <strings.h>
299 #endif
300 #if HAVE_INTTYPES_H
301 # include <inttypes.h>
302 #else
303 # if HAVE_STDINT_H
304 # include <stdint.h>
305 # endif
306 #endif
307 #if HAVE_UNISTD_H
308 # include <unistd.h>
309 #endif"
310
311 ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar DLGUI_TRUE DLGUI_FALSE CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE RANLIB ac_ct_RANLIB CPP CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE GLIB_CONFIG GLIB_CFLAGS GLIB_LIBS GTK_CONFIG GTK_CFLAGS GTK_LIBS EGREP LIBJPEG_LIBS LIBJPEG_LDFLAGS LIBJPEG_CPPFLAGS LIBPNG_LIBS LIBPNG_CFLAGS LIBZ_LIBS LIBSSL_LIBS LIBPTHREAD_LIBS LIBPTHREAD_LDFLAGS LIBFLTK_CXXFLAGS LIBFLTK_LIBS src doc bin util lib LIBOBJS LTLIBOBJS'
312 ac_subst_files=''
313
314 # Initialize some variables set by options.
315 ac_init_help=
316 ac_init_version=false
317 # The variables have the same names as the options, with
318 # dashes changed to underlines.
319 cache_file=/dev/null
320 exec_prefix=NONE
321 no_create=
322 no_recursion=
323 prefix=NONE
324 program_prefix=NONE
325 program_suffix=NONE
326 program_transform_name=s,x,x,
327 silent=
328 site=
329 srcdir=
330 verbose=
331 x_includes=NONE
332 x_libraries=NONE
333
334 # Installation directory options.
335 # These are left unexpanded so users can "make install exec_prefix=/foo"
336 # and all the variables that are supposed to be based on exec_prefix
337 # by default will actually change.
338 # Use braces instead of parens because sh, perl, etc. also accept them.
339 bindir='${exec_prefix}/bin'
340 sbindir='${exec_prefix}/sbin'
341 libexecdir='${exec_prefix}/libexec'
342 datadir='${prefix}/share'
343 sysconfdir='${prefix}/etc'
344 sharedstatedir='${prefix}/com'
345 localstatedir='${prefix}/var'
346 libdir='${exec_prefix}/lib'
347 includedir='${prefix}/include'
348 oldincludedir='/usr/include'
349 infodir='${prefix}/info'
350 mandir='${prefix}/man'
351
352 ac_prev=
353 for ac_option
354 do
355 # If the previous option needs an argument, assign it.
356 if test -n "$ac_prev"; then
357 eval "$ac_prev=\$ac_option"
358 ac_prev=
359 continue
360 fi
361
362 ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
363
364 # Accept the important Cygnus configure options, so we can diagnose typos.
365
366 case $ac_option in
367
368 -bindir | --bindir | --bindi | --bind | --bin | --bi)
369 ac_prev=bindir ;;
370 -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
371 bindir=$ac_optarg ;;
372
373 -build | --build | --buil | --bui | --bu)
374 ac_prev=build_alias ;;
375 -build=* | --build=* | --buil=* | --bui=* | --bu=*)
376 build_alias=$ac_optarg ;;
377
378 -cache-file | --cache-file | --cache-fil | --cache-fi \
379 | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
380 ac_prev=cache_file ;;
381 -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
382 | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
383 cache_file=$ac_optarg ;;
384
385 --config-cache | -C)
386 cache_file=config.cache ;;
387
388 -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
389 ac_prev=datadir ;;
390 -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
391 | --da=*)
392 datadir=$ac_optarg ;;
393
394 -disable-* | --disable-*)
395 ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
396 # Reject names that are not valid shell variable names.
397 expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
398 { echo "$as_me: error: invalid feature name: $ac_feature" >&2
399 { (exit 1); exit 1; }; }
400 ac_feature=`echo $ac_feature | sed 's/-/_/g'`
401 eval "enable_$ac_feature=no" ;;
402
403 -enable-* | --enable-*)
404 ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
405 # Reject names that are not valid shell variable names.
406 expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
407 { echo "$as_me: error: invalid feature name: $ac_feature" >&2
408 { (exit 1); exit 1; }; }
409 ac_feature=`echo $ac_feature | sed 's/-/_/g'`
410 case $ac_option in
411 *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
412 *) ac_optarg=yes ;;
413 esac
414 eval "enable_$ac_feature='$ac_optarg'" ;;
415
416 -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
417 | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
418 | --exec | --exe | --ex)
419 ac_prev=exec_prefix ;;
420 -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
421 | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
422 | --exec=* | --exe=* | --ex=*)
423 exec_prefix=$ac_optarg ;;
424
425 -gas | --gas | --ga | --g)
426 # Obsolete; use --with-gas.
427 with_gas=yes ;;
428
429 -help | --help | --hel | --he | -h)
430 ac_init_help=long ;;
431 -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
432 ac_init_help=recursive ;;
433 -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
434 ac_init_help=short ;;
435
436 -host | --host | --hos | --ho)
437 ac_prev=host_alias ;;
438 -host=* | --host=* | --hos=* | --ho=*)
439 host_alias=$ac_optarg ;;
440
441 -includedir | --includedir | --includedi | --included | --include \
442 | --includ | --inclu | --incl | --inc)
443 ac_prev=includedir ;;
444 -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
445 | --includ=* | --inclu=* | --incl=* | --inc=*)
446 includedir=$ac_optarg ;;
447
448 -infodir | --infodir | --infodi | --infod | --info | --inf)
449 ac_prev=infodir ;;
450 -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
451 infodir=$ac_optarg ;;
452
453 -libdir | --libdir | --libdi | --libd)
454 ac_prev=libdir ;;
455 -libdir=* | --libdir=* | --libdi=* | --libd=*)
456 libdir=$ac_optarg ;;
457
458 -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
459 | --libexe | --libex | --libe)
460 ac_prev=libexecdir ;;
461 -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
462 | --libexe=* | --libex=* | --libe=*)
463 libexecdir=$ac_optarg ;;
464
465 -localstatedir | --localstatedir | --localstatedi | --localstated \
466 | --localstate | --localstat | --localsta | --localst \
467 | --locals | --local | --loca | --loc | --lo)
468 ac_prev=localstatedir ;;
469 -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
470 | --localstate=* | --localstat=* | --localsta=* | --localst=* \
471 | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
472 localstatedir=$ac_optarg ;;
473
474 -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
475 ac_prev=mandir ;;
476 -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
477 mandir=$ac_optarg ;;
478
479 -nfp | --nfp | --nf)
480 # Obsolete; use --without-fp.
481 with_fp=no ;;
482
483 -no-create | --no-create | --no-creat | --no-crea | --no-cre \
484 | --no-cr | --no-c | -n)
485 no_create=yes ;;
486
487 -no-recursion | --no-recursion | --no-recursio | --no-recursi \
488 | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
489 no_recursion=yes ;;
490
491 -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
492 | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
493 | --oldin | --oldi | --old | --ol | --o)
494 ac_prev=oldincludedir ;;
495 -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
496 | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
497 | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
498 oldincludedir=$ac_optarg ;;
499
500 -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
501 ac_prev=prefix ;;
502 -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
503 prefix=$ac_optarg ;;
504
505 -program-prefix | --program-prefix | --program-prefi | --program-pref \
506 | --program-pre | --program-pr | --program-p)
507 ac_prev=program_prefix ;;
508 -program-prefix=* | --program-prefix=* | --program-prefi=* \
509 | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
510 program_prefix=$ac_optarg ;;
511
512 -program-suffix | --program-suffix | --program-suffi | --program-suff \
513 | --program-suf | --program-su | --program-s)
514 ac_prev=program_suffix ;;
515 -program-suffix=* | --program-suffix=* | --program-suffi=* \
516 | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
517 program_suffix=$ac_optarg ;;
518
519 -program-transform-name | --program-transform-name \
520 | --program-transform-nam | --program-transform-na \
521 | --program-transform-n | --program-transform- \
522 | --program-transform | --program-transfor \
523 | --program-transfo | --program-transf \
524 | --program-trans | --program-tran \
525 | --progr-tra | --program-tr | --program-t)
526 ac_prev=program_transform_name ;;
527 -program-transform-name=* | --program-transform-name=* \
528 | --program-transform-nam=* | --program-transform-na=* \
529 | --program-transform-n=* | --program-transform-=* \
530 | --program-transform=* | --program-transfor=* \
531 | --program-transfo=* | --program-transf=* \
532 | --program-trans=* | --program-tran=* \
533 | --progr-tra=* | --program-tr=* | --program-t=*)
534 program_transform_name=$ac_optarg ;;
535
536 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
537 | -silent | --silent | --silen | --sile | --sil)
538 silent=yes ;;
539
540 -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
541 ac_prev=sbindir ;;
542 -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
543 | --sbi=* | --sb=*)
544 sbindir=$ac_optarg ;;
545
546 -sharedstatedir | --sharedstatedir | --sharedstatedi \
547 | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
548 | --sharedst | --shareds | --shared | --share | --shar \
549 | --sha | --sh)
550 ac_prev=sharedstatedir ;;
551 -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
552 | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
553 | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
554 | --sha=* | --sh=*)
555 sharedstatedir=$ac_optarg ;;
556
557 -site | --site | --sit)
558 ac_prev=site ;;
559 -site=* | --site=* | --sit=*)
560 site=$ac_optarg ;;
561
562 -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
563 ac_prev=srcdir ;;
564 -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
565 srcdir=$ac_optarg ;;
566
567 -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
568 | --syscon | --sysco | --sysc | --sys | --sy)
569 ac_prev=sysconfdir ;;
570 -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
571 | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
572 sysconfdir=$ac_optarg ;;
573
574 -target | --target | --targe | --targ | --tar | --ta | --t)
575 ac_prev=target_alias ;;
576 -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
577 target_alias=$ac_optarg ;;
578
579 -v | -verbose | --verbose | --verbos | --verbo | --verb)
580 verbose=yes ;;
581
582 -version | --version | --versio | --versi | --vers | -V)
583 ac_init_version=: ;;
584
585 -with-* | --with-*)
586 ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
587 # Reject names that are not valid shell variable names.
588 expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
589 { echo "$as_me: error: invalid package name: $ac_package" >&2
590 { (exit 1); exit 1; }; }
591 ac_package=`echo $ac_package| sed 's/-/_/g'`
592 case $ac_option in
593 *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
594 *) ac_optarg=yes ;;
595 esac
596 eval "with_$ac_package='$ac_optarg'" ;;
597
598 -without-* | --without-*)
599 ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
600 # Reject names that are not valid shell variable names.
601 expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
602 { echo "$as_me: error: invalid package name: $ac_package" >&2
603 { (exit 1); exit 1; }; }
604 ac_package=`echo $ac_package | sed 's/-/_/g'`
605 eval "with_$ac_package=no" ;;
606
607 --x)
608 # Obsolete; use --with-x.
609 with_x=yes ;;
610
611 -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
612 | --x-incl | --x-inc | --x-in | --x-i)
613 ac_prev=x_includes ;;
614 -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
615 | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
616 x_includes=$ac_optarg ;;
617
618 -x-libraries | --x-libraries | --x-librarie | --x-librari \
619 | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
620 ac_prev=x_libraries ;;
621 -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
622 | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
623 x_libraries=$ac_optarg ;;
624
625 -*) { echo "$as_me: error: unrecognized option: $ac_option
626 Try \`$0 --help' for more information." >&2
627 { (exit 1); exit 1; }; }
628 ;;
629
630 *=*)
631 ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
632 # Reject names that are not valid shell variable names.
633 expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
634 { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
635 { (exit 1); exit 1; }; }
636 ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
637 eval "$ac_envvar='$ac_optarg'"
638 export $ac_envvar ;;
639
640 *)
641 # FIXME: should be removed in autoconf 3.0.
642 echo "$as_me: WARNING: you should use --build, --host, --target" >&2
643 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
644 echo "$as_me: WARNING: invalid host type: $ac_option" >&2
645 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
646 ;;
647
648 esac
649 done
650
651 if test -n "$ac_prev"; then
652 ac_option=--`echo $ac_prev | sed 's/_/-/g'`
653 { echo "$as_me: error: missing argument to $ac_option" >&2
654 { (exit 1); exit 1; }; }
655 fi
656
657 # Be sure to have absolute paths.
658 for ac_var in exec_prefix prefix
659 do
660 eval ac_val=$`echo $ac_var`
661 case $ac_val in
662 [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
663 *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
664 { (exit 1); exit 1; }; };;
665 esac
666 done
667
668 # Be sure to have absolute paths.
669 for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
670 localstatedir libdir includedir oldincludedir infodir mandir
671 do
672 eval ac_val=$`echo $ac_var`
673 case $ac_val in
674 [\\/$]* | ?:[\\/]* ) ;;
675 *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
676 { (exit 1); exit 1; }; };;
677 esac
678 done
679
680 # There might be people who depend on the old broken behavior: `$host'
681 # used to hold the argument of --host etc.
682 # FIXME: To remove some day.
683 build=$build_alias
684 host=$host_alias
685 target=$target_alias
686
687 # FIXME: To remove some day.
688 if test "x$host_alias" != x; then
689 if test "x$build_alias" = x; then
690 cross_compiling=maybe
691 echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
692 If a cross compiler is detected then cross compile mode will be used." >&2
693 elif test "x$build_alias" != "x$host_alias"; then
694 cross_compiling=yes
695 fi
696 fi
697
698 ac_tool_prefix=
699 test -n "$host_alias" && ac_tool_prefix=$host_alias-
700
701 test "$silent" = yes && exec 6>/dev/null
702
703
704 # Find the source files, if location was not specified.
705 if test -z "$srcdir"; then
706 ac_srcdir_defaulted=yes
707 # Try the directory containing this script, then its parent.
708 ac_confdir=`(dirname "$0") 2>/dev/null ||
709 $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
710 X"$0" : 'X\(//\)[^/]' \| \
711 X"$0" : 'X\(//\)$' \| \
712 X"$0" : 'X\(/\)' \| \
713 . : '\(.\)' 2>/dev/null ||
714 echo X"$0" |
715 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
716 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
717 /^X\(\/\/\)$/{ s//\1/; q; }
718 /^X\(\/\).*/{ s//\1/; q; }
719 s/.*/./; q'`
720 srcdir=$ac_confdir
721 if test ! -r $srcdir/$ac_unique_file; then
722 srcdir=..
723 fi
724 else
725 ac_srcdir_defaulted=no
726 fi
727 if test ! -r $srcdir/$ac_unique_file; then
728 if test "$ac_srcdir_defaulted" = yes; then
729 { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
730 { (exit 1); exit 1; }; }
731 else
732 { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
733 { (exit 1); exit 1; }; }
734 fi
735 fi
736 (cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
737 { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
738 { (exit 1); exit 1; }; }
739 srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
740 ac_env_build_alias_set=${build_alias+set}
741 ac_env_build_alias_value=$build_alias
742 ac_cv_env_build_alias_set=${build_alias+set}
743 ac_cv_env_build_alias_value=$build_alias
744 ac_env_host_alias_set=${host_alias+set}
745 ac_env_host_alias_value=$host_alias
746 ac_cv_env_host_alias_set=${host_alias+set}
747 ac_cv_env_host_alias_value=$host_alias
748 ac_env_target_alias_set=${target_alias+set}
749 ac_env_target_alias_value=$target_alias
750 ac_cv_env_target_alias_set=${target_alias+set}
751 ac_cv_env_target_alias_value=$target_alias
752 ac_env_CC_set=${CC+set}
753 ac_env_CC_value=$CC
754 ac_cv_env_CC_set=${CC+set}
755 ac_cv_env_CC_value=$CC
756 ac_env_CFLAGS_set=${CFLAGS+set}
757 ac_env_CFLAGS_value=$CFLAGS
758 ac_cv_env_CFLAGS_set=${CFLAGS+set}
759 ac_cv_env_CFLAGS_value=$CFLAGS
760 ac_env_LDFLAGS_set=${LDFLAGS+set}
761 ac_env_LDFLAGS_value=$LDFLAGS
762 ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
763 ac_cv_env_LDFLAGS_value=$LDFLAGS
764 ac_env_CPPFLAGS_set=${CPPFLAGS+set}
765 ac_env_CPPFLAGS_value=$CPPFLAGS
766 ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
767 ac_cv_env_CPPFLAGS_value=$CPPFLAGS
768 ac_env_CPP_set=${CPP+set}
769 ac_env_CPP_value=$CPP
770 ac_cv_env_CPP_set=${CPP+set}
771 ac_cv_env_CPP_value=$CPP
772 ac_env_CXX_set=${CXX+set}
773 ac_env_CXX_value=$CXX
774 ac_cv_env_CXX_set=${CXX+set}
775 ac_cv_env_CXX_value=$CXX
776 ac_env_CXXFLAGS_set=${CXXFLAGS+set}
777 ac_env_CXXFLAGS_value=$CXXFLAGS
778 ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set}
779 ac_cv_env_CXXFLAGS_value=$CXXFLAGS
780
781 #
782 # Report the --help message.
783 #
784 if test "$ac_init_help" = "long"; then
785 # Omit some internal or obsolete options to make the list less imposing.
786 # This message is too long to be a string in the A/UX 3.1 sh.
787 cat <<_ACEOF
788 \`configure' configures this package to adapt to many kinds of systems.
789
790 Usage: $0 [OPTION]... [VAR=VALUE]...
791
792 To assign environment variables (e.g., CC, CFLAGS...), specify them as
793 VAR=VALUE. See below for descriptions of some of the useful variables.
794
795 Defaults for the options are specified in brackets.
796
797 Configuration:
798 -h, --help display this help and exit
799 --help=short display options specific to this package
800 --help=recursive display the short help of all the included packages
801 -V, --version display version information and exit
802 -q, --quiet, --silent do not print \`checking...' messages
803 --cache-file=FILE cache test results in FILE [disabled]
804 -C, --config-cache alias for \`--cache-file=config.cache'
805 -n, --no-create do not create output files
806 --srcdir=DIR find the sources in DIR [configure dir or \`..']
807
808 _ACEOF
809
810 cat <<_ACEOF
811 Installation directories:
812 --prefix=PREFIX install architecture-independent files in PREFIX
813 [$ac_default_prefix]
814 --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
815 [PREFIX]
816
817 By default, \`make install' will install all the files in
818 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
819 an installation prefix other than \`$ac_default_prefix' using \`--prefix',
820 for instance \`--prefix=\$HOME'.
821
822 For better control, use the options below.
823
824 Fine tuning of the installation directories:
825 --bindir=DIR user executables [EPREFIX/bin]
826 --sbindir=DIR system admin executables [EPREFIX/sbin]
827 --libexecdir=DIR program executables [EPREFIX/libexec]
828 --datadir=DIR read-only architecture-independent data [PREFIX/share]
829 --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
830 --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
831 --localstatedir=DIR modifiable single-machine data [PREFIX/var]
832 --libdir=DIR object code libraries [EPREFIX/lib]
833 --includedir=DIR C header files [PREFIX/include]
834 --oldincludedir=DIR C header files for non-gcc [/usr/include]
835 --infodir=DIR info documentation [PREFIX/info]
836 --mandir=DIR man documentation [PREFIX/man]
837 _ACEOF
838
839 cat <<\_ACEOF
840
841 Program names:
842 --program-prefix=PREFIX prepend PREFIX to installed program names
843 --program-suffix=SUFFIX append SUFFIX to installed program names
844 --program-transform-name=PROGRAM run sed PROGRAM on installed program names
845
846 System types:
847 --build=BUILD configure for building on BUILD [guessed]
848 --host=HOST cross-compile to build programs to run on HOST [BUILD]
849 --target=TARGET configure for building compilers for TARGET [HOST]
850 _ACEOF
851 fi
852
853 if test -n "$ac_init_help"; then
854
855 cat <<\_ACEOF
856
857 Optional Features:
858 --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
859 --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
860 --enable-efence Try to compile and run with Electric Fence
861 --enable-gprof Try to compile and run with profiling enabled
862 --enable-insure Try to compile and run with Insure++
863 --enable-ansi Try to compile and run with ANSI flags
864 --enable-ipv6 Build with support for IPv6
865 --enable-rtfl Build with rtfl messages
866 --disable-cookies Don't compile support for cookies
867 --disable-png Disable support for PNG images
868 --disable-jpeg Disable support for JPEG images
869 --disable-gif Disable support for GIF images
870 --disable-ssl Disable ssl features (eg. https)
871 --disable-dlgui Disable FLTK2 GUI for downloads
872 --disable-threaded-dns Disable the advantage of a reentrant resolver library
873 --disable-dependency-tracking speeds up one-time build
874 --enable-dependency-tracking do not reject slow dependency extractors
875 --disable-glibtest Do not try to compile and run a test GLIB program
876 --disable-gtktest Do not try to compile and run a test GTK program
877
878 Optional Packages:
879 --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
880 --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
881 --with-jpeg-lib=DIR Specify where to find libjpeg
882 --with-jpeg-inc=DIR Specify where to find libjpeg's headers
883 --with-glib-prefix=PFX Prefix where GLIB is installed (optional)
884 --with-glib-exec-prefix=PFX Exec prefix where GLIB is installed (optional)
885 --with-gtk-prefix=PFX Prefix where GTK is installed (optional)
886 --with-gtk-exec-prefix=PFX Exec prefix where GTK is installed (optional)
887
888 Some influential environment variables:
889 CC C compiler command
890 CFLAGS C compiler flags
891 LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
892 nonstandard directory <lib dir>
893 CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have
894 headers in a nonstandard directory <include dir>
895 CPP C preprocessor
896 CXX C++ compiler command
897 CXXFLAGS C++ compiler flags
898
899 Use these variables to override the choices made by `configure' or to help
900 it to find libraries and programs with nonstandard names/locations.
901
902 _ACEOF
903 fi
904
905 if test "$ac_init_help" = "recursive"; then
906 # If there are subdirs, report their specific --help.
907 ac_popdir=`pwd`
908 for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
909 test -d $ac_dir || continue
910 ac_builddir=.
911
912 if test "$ac_dir" != .; then
913 ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
914 # A "../" for each directory in $ac_dir_suffix.
915 ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
916 else
917 ac_dir_suffix= ac_top_builddir=
918 fi
919
920 case $srcdir in
921 .) # No --srcdir option. We are building in place.
922 ac_srcdir=.
923 if test -z "$ac_top_builddir"; then
924 ac_top_srcdir=.
925 else
926 ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
927 fi ;;
928 [\\/]* | ?:[\\/]* ) # Absolute path.
929 ac_srcdir=$srcdir$ac_dir_suffix;
930 ac_top_srcdir=$srcdir ;;
931 *) # Relative path.
932 ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
933 ac_top_srcdir=$ac_top_builddir$srcdir ;;
934 esac
935
936 # Do not use `cd foo && pwd` to compute absolute paths, because
937 # the directories may not exist.
938 case `pwd` in
939 .) ac_abs_builddir="$ac_dir";;
940 *)
941 case "$ac_dir" in
942 .) ac_abs_builddir=`pwd`;;
943 [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
944 *) ac_abs_builddir=`pwd`/"$ac_dir";;
945 esac;;
946 esac
947 case $ac_abs_builddir in
948 .) ac_abs_top_builddir=${ac_top_builddir}.;;
949 *)
950 case ${ac_top_builddir}. in
951 .) ac_abs_top_builddir=$ac_abs_builddir;;
952 [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
953 *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
954 esac;;
955 esac
956 case $ac_abs_builddir in
957 .) ac_abs_srcdir=$ac_srcdir;;
958 *)
959 case $ac_srcdir in
960 .) ac_abs_srcdir=$ac_abs_builddir;;
961 [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
962 *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
963 esac;;
964 esac
965 case $ac_abs_builddir in
966 .) ac_abs_top_srcdir=$ac_top_srcdir;;
967 *)
968 case $ac_top_srcdir in
969 .) ac_abs_top_srcdir=$ac_abs_builddir;;
970 [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
971 *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
972 esac;;
973 esac
974
975 cd $ac_dir
976 # Check for guested configure; otherwise get Cygnus style configure.
977 if test -f $ac_srcdir/configure.gnu; then
978 echo
979 $SHELL $ac_srcdir/configure.gnu --help=recursive
980 elif test -f $ac_srcdir/configure; then
981 echo
982 $SHELL $ac_srcdir/configure --help=recursive
983 elif test -f $ac_srcdir/configure.ac ||
984 test -f $ac_srcdir/configure.in; then
985 echo
986 $ac_configure --help
987 else
988 echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
989 fi
990 cd $ac_popdir
991 done
992 fi
993
994 test -n "$ac_init_help" && exit 0
995 if $ac_init_version; then
996 cat <<\_ACEOF
997
998 Copyright (C) 2003 Free Software Foundation, Inc.
999 This configure script is free software; the Free Software Foundation
1000 gives unlimited permission to copy, distribute and modify it.
1001 _ACEOF
1002 exit 0
1003 fi
1004 exec 5>config.log
1005 cat >&5 <<_ACEOF
1006 This file contains any messages produced by compilers while
1007 running configure, to aid debugging if configure makes a mistake.
1008
1009 It was created by $as_me, which was
1010 generated by GNU Autoconf 2.59. Invocation command line was
1011
1012 $ $0 $@
1013
1014 _ACEOF
1015 {
1016 cat <<_ASUNAME
1017 ## --------- ##
1018 ## Platform. ##
1019 ## --------- ##
1020
1021 hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
1022 uname -m = `(uname -m) 2>/dev/null || echo unknown`
1023 uname -r = `(uname -r) 2>/dev/null || echo unknown`
1024 uname -s = `(uname -s) 2>/dev/null || echo unknown`
1025 uname -v = `(uname -v) 2>/dev/null || echo unknown`
1026
1027 /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
1028 /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
1029
1030 /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
1031 /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
1032 /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
1033 hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
1034 /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
1035 /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
1036 /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
1037
1038 _ASUNAME
1039
1040 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1041 for as_dir in $PATH
1042 do
1043 IFS=$as_save_IFS
1044 test -z "$as_dir" && as_dir=.
1045 echo "PATH: $as_dir"
1046 done
1047
1048 } >&5
1049
1050 cat >&5 <<_ACEOF
1051
1052
1053 ## ----------- ##
1054 ## Core tests. ##
1055 ## ----------- ##
1056
1057 _ACEOF
1058
1059
1060 # Keep a trace of the command line.
1061 # Strip out --no-create and --no-recursion so they do not pile up.
1062 # Strip out --silent because we don't want to record it for future runs.
1063 # Also quote any args containing shell meta-characters.
1064 # Make two passes to allow for proper duplicate-argument suppression.
1065 ac_configure_args=
1066 ac_configure_args0=
1067 ac_configure_args1=
1068 ac_sep=
1069 ac_must_keep_next=false
1070 for ac_pass in 1 2
1071 do
1072 for ac_arg
1073 do
1074 case $ac_arg in
1075 -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
1076 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
1077 | -silent | --silent | --silen | --sile | --sil)
1078 continue ;;
1079 *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
1080 ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
1081 esac
1082 case $ac_pass in
1083 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
1084 2)
1085 ac_configure_args1="$ac_configure_args1 '$ac_arg'"
1086 if test $ac_must_keep_next = true; then
1087 ac_must_keep_next=false # Got value, back to normal.
1088 else
1089 case $ac_arg in
1090 *=* | --config-cache | -C | -disable-* | --disable-* \
1091 | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
1092 | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
1093 | -with-* | --with-* | -without-* | --without-* | --x)
1094 case "$ac_configure_args0 " in
1095 "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
1096 esac
1097 ;;
1098 -* ) ac_must_keep_next=true ;;
1099 esac
1100 fi
1101 ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
1102 # Get rid of the leading space.
1103 ac_sep=" "
1104 ;;
1105 esac
1106 done
1107 done
1108 $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
1109 $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
1110
1111 # When interrupted or exit'd, cleanup temporary files, and complete
1112 # config.log. We remove comments because anyway the quotes in there
1113 # would cause problems or look ugly.
1114 # WARNING: Be sure not to use single quotes in there, as some shells,
1115 # such as our DU 5.0 friend, will then `close' the trap.
1116 trap 'exit_status=$?
1117 # Save into config.log some information that might help in debugging.
1118 {
1119 echo
1120
1121 cat <<\_ASBOX
1122 ## ---------------- ##
1123 ## Cache variables. ##
1124 ## ---------------- ##
1125 _ASBOX
1126 echo
1127 # The following way of writing the cache mishandles newlines in values,
1128 {
1129 (set) 2>&1 |
1130 case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
1131 *ac_space=\ *)
1132 sed -n \
1133 "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
1134 s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
1135 ;;
1136 *)
1137 sed -n \
1138 "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
1139 ;;
1140 esac;
1141 }
1142 echo
1143
1144 cat <<\_ASBOX
1145 ## ----------------- ##
1146 ## Output variables. ##
1147 ## ----------------- ##
1148 _ASBOX
1149 echo
1150 for ac_var in $ac_subst_vars
1151 do
1152 eval ac_val=$`echo $ac_var`
1153 echo "$ac_var='"'"'$ac_val'"'"'"
1154 done | sort
1155 echo
1156
1157 if test -n "$ac_subst_files"; then
1158 cat <<\_ASBOX
1159 ## ------------- ##
1160 ## Output files. ##
1161 ## ------------- ##
1162 _ASBOX
1163 echo
1164 for ac_var in $ac_subst_files
1165 do
1166 eval ac_val=$`echo $ac_var`
1167 echo "$ac_var='"'"'$ac_val'"'"'"
1168 done | sort
1169 echo
1170 fi
1171
1172 if test -s confdefs.h; then
1173 cat <<\_ASBOX
1174 ## ----------- ##
1175 ## confdefs.h. ##
1176 ## ----------- ##
1177 _ASBOX
1178 echo
1179 sed "/^$/d" confdefs.h | sort
1180 echo
1181 fi
1182 test "$ac_signal" != 0 &&
1183 echo "$as_me: caught signal $ac_signal"
1184 echo "$as_me: exit $exit_status"
1185 } >&5
1186 rm -f core *.core &&
1187 rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
1188 exit $exit_status
1189 ' 0
1190 for ac_signal in 1 2 13 15; do
1191 trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
1192 done
1193 ac_signal=0
1194
1195 # confdefs.h avoids OS command line length limits that DEFS can exceed.
1196 rm -rf conftest* confdefs.h
1197 # AIX cpp loses on an empty file, so make sure it contains at least a newline.
1198 echo >confdefs.h
1199
1200 # Predefined preprocessor variables.
1201
1202 cat >>confdefs.h <<_ACEOF
1203 #define PACKAGE_NAME "$PACKAGE_NAME"
1204 _ACEOF
1205
1206
1207 cat >>confdefs.h <<_ACEOF
1208 #define PACKAGE_TARNAME "$PACKAGE_TARNAME"
1209 _ACEOF
1210
1211
1212 cat >>confdefs.h <<_ACEOF
1213 #define PACKAGE_VERSION "$PACKAGE_VERSION"
1214 _ACEOF
1215
1216
1217 cat >>confdefs.h <<_ACEOF
1218 #define PACKAGE_STRING "$PACKAGE_STRING"
1219 _ACEOF
1220
1221
1222 cat >>confdefs.h <<_ACEOF
1223 #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
1224 _ACEOF
1225
1226
1227 # Let the site file select an alternate cache file if it wants to.
1228 # Prefer explicitly selected file to automatically selected ones.
1229 if test -z "$CONFIG_SITE"; then
1230 if test "x$prefix" != xNONE; then
1231 CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
1232 else
1233 CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
1234 fi
1235 fi
1236 for ac_site_file in $CONFIG_SITE; do
1237 if test -r "$ac_site_file"; then
1238 { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
1239 echo "$as_me: loading site script $ac_site_file" >&6;}
1240 sed 's/^/| /' "$ac_site_file" >&5
1241 . "$ac_site_file"
1242 fi
1243 done
1244
1245 if test -r "$cache_file"; then
1246 # Some versions of bash will fail to source /dev/null (special
1247 # files actually), so we avoid doing that.
1248 if test -f "$cache_file"; then
1249 { echo "$as_me:$LINENO: loading cache $cache_file" >&5
1250 echo "$as_me: loading cache $cache_file" >&6;}
1251 case $cache_file in
1252 [\\/]* | ?:[\\/]* ) . $cache_file;;
1253 *) . ./$cache_file;;
1254 esac
1255 fi
1256 else
1257 { echo "$as_me:$LINENO: creating cache $cache_file" >&5
1258 echo "$as_me: creating cache $cache_file" >&6;}
1259 >$cache_file
1260 fi
1261
1262 # Check that the precious variables saved in the cache have kept the same
1263 # value.
1264 ac_cache_corrupted=false
1265 for ac_var in `(set) 2>&1 |
1266 sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
1267 eval ac_old_set=\$ac_cv_env_${ac_var}_set
1268 eval ac_new_set=\$ac_env_${ac_var}_set
1269 eval ac_old_val="\$ac_cv_env_${ac_var}_value"
1270 eval ac_new_val="\$ac_env_${ac_var}_value"
1271 case $ac_old_set,$ac_new_set in
1272 set,)
1273 { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
1274 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
1275 ac_cache_corrupted=: ;;
1276 ,set)
1277 { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
1278 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
1279 ac_cache_corrupted=: ;;
1280 ,);;
1281 *)
1282 if test "x$ac_old_val" != "x$ac_new_val"; then
1283 { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
1284 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
1285 { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
1286 echo "$as_me: former value: $ac_old_val" >&2;}
1287 { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
1288 echo "$as_me: current value: $ac_new_val" >&2;}
1289 ac_cache_corrupted=:
1290 fi;;
1291 esac
1292 # Pass precious variables to config.status.
1293 if test "$ac_new_set" = set; then
1294 case $ac_new_val in
1295 *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
1296 ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
1297 *) ac_arg=$ac_var=$ac_new_val ;;
1298 esac
1299 case " $ac_configure_args " in
1300 *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
1301 *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
1302 esac
1303 fi
1304 done
1305 if $ac_cache_corrupted; then
1306 { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
1307 echo "$as_me: error: changes in the environment can compromise the build" >&2;}
1308 { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
1309 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
1310 { (exit 1); exit 1; }; }
1311 fi
1312
1313 ac_ext=c
1314 ac_cpp='$CPP $CPPFLAGS'
1315 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
1316 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
1317 ac_compiler_gnu=$ac_cv_c_compiler_gnu
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338 ac_aux_dir=
1339 for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
1340 if test -f $ac_dir/install-sh; then
1341 ac_aux_dir=$ac_dir
1342 ac_install_sh="$ac_aux_dir/install-sh -c"
1343 break
1344 elif test -f $ac_dir/install.sh; then
1345 ac_aux_dir=$ac_dir
1346 ac_install_sh="$ac_aux_dir/install.sh -c"
1347 break
1348 elif test -f $ac_dir/shtool; then
1349 ac_aux_dir=$ac_dir
1350 ac_install_sh="$ac_aux_dir/shtool install -c"
1351 break
1352 fi
1353 done
1354 if test -z "$ac_aux_dir"; then
1355 { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
1356 echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
1357 { (exit 1); exit 1; }; }
1358 fi
1359 ac_config_guess="$SHELL $ac_aux_dir/config.guess"
1360 ac_config_sub="$SHELL $ac_aux_dir/config.sub"
1361 ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
1362
1363 # Make sure we can run config.sub.
1364 $ac_config_sub sun4 >/dev/null 2>&1 ||
1365 { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5
1366 echo "$as_me: error: cannot run $ac_config_sub" >&2;}
1367 { (exit 1); exit 1; }; }
1368
1369 echo "$as_me:$LINENO: checking build system type" >&5
1370 echo $ECHO_N "checking build system type... $ECHO_C" >&6
1371 if test "${ac_cv_build+set}" = set; then
1372 echo $ECHO_N "(cached) $ECHO_C" >&6
1373 else
1374 ac_cv_build_alias=$build_alias
1375 test -z "$ac_cv_build_alias" &&
1376 ac_cv_build_alias=`$ac_config_guess`
1377 test -z "$ac_cv_build_alias" &&
1378 { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
1379 echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
1380 { (exit 1); exit 1; }; }
1381 ac_cv_build=`$ac_config_sub $ac_cv_build_alias` ||
1382 { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5
1383 echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;}
1384 { (exit 1); exit 1; }; }
1385
1386 fi
1387 echo "$as_me:$LINENO: result: $ac_cv_build" >&5
1388 echo "${ECHO_T}$ac_cv_build" >&6
1389 build=$ac_cv_build
1390 build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
1391 build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
1392 build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
1393
1394
1395 echo "$as_me:$LINENO: checking host system type" >&5
1396 echo $ECHO_N "checking host system type... $ECHO_C" >&6
1397 if test "${ac_cv_host+set}" = set; then
1398 echo $ECHO_N "(cached) $ECHO_C" >&6
1399 else
1400 ac_cv_host_alias=$host_alias
1401 test -z "$ac_cv_host_alias" &&
1402 ac_cv_host_alias=$ac_cv_build_alias
1403 ac_cv_host=`$ac_config_sub $ac_cv_host_alias` ||
1404 { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5
1405 echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;}
1406 { (exit 1); exit 1; }; }
1407
1408 fi
1409 echo "$as_me:$LINENO: result: $ac_cv_host" >&5
1410 echo "${ECHO_T}$ac_cv_host" >&6
1411 host=$ac_cv_host
1412 host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
1413 host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
1414 host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
1415
1416
1417 echo "$as_me:$LINENO: checking target system type" >&5
1418 echo $ECHO_N "checking target system type... $ECHO_C" >&6
1419 if test "${ac_cv_target+set}" = set; then
1420 echo $ECHO_N "(cached) $ECHO_C" >&6
1421 else
1422 ac_cv_target_alias=$target_alias
1423 test "x$ac_cv_target_alias" = "x" &&
1424 ac_cv_target_alias=$ac_cv_host_alias
1425 ac_cv_target=`$ac_config_sub $ac_cv_target_alias` ||
1426 { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5
1427 echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;}
1428 { (exit 1); exit 1; }; }
1429
1430 fi
1431 echo "$as_me:$LINENO: result: $ac_cv_target" >&5
1432 echo "${ECHO_T}$ac_cv_target" >&6
1433 target=$ac_cv_target
1434 target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
1435 target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
1436 target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
1437
1438
1439 # The aliases save the names the user supplied, while $host etc.
1440 # will get canonicalized.
1441 test -n "$target_alias" &&
1442 test "$program_prefix$program_suffix$program_transform_name" = \
1443 NONENONEs,x,x, &&
1444 program_prefix=${target_alias}-
1445
1446 am__api_version="1.9"
1447 # Find a good install program. We prefer a C program (faster),
1448 # so one script is as good as another. But avoid the broken or
1449 # incompatible versions:
1450 # SysV /etc/install, /usr/sbin/install
1451 # SunOS /usr/etc/install
1452 # IRIX /sbin/install
1453 # AIX /bin/install
1454 # AmigaOS /C/install, which installs bootblocks on floppy discs
1455 # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
1456 # AFS /usr/afsws/bin/install, which mishandles nonexistent args
1457 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
1458 # OS/2's system install, which has a completely different semantic
1459 # ./install, which can be erroneously created by make from ./install.sh.
1460 echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
1461 echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
1462 if test -z "$INSTALL"; then
1463 if test "${ac_cv_path_install+set}" = set; then
1464 echo $ECHO_N "(cached) $ECHO_C" >&6
1465 else
1466 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1467 for as_dir in $PATH
1468 do
1469 IFS=$as_save_IFS
1470 test -z "$as_dir" && as_dir=.
1471 # Account for people who put trailing slashes in PATH elements.
1472 case $as_dir/ in
1473 ./ | .// | /cC/* | \
1474 /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
1475 ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
1476 /usr/ucb/* ) ;;
1477 *)
1478 # OSF1 and SCO ODT 3.0 have their own names for install.
1479 # Don't use installbsd from OSF since it installs stuff as root
1480 # by default.
1481 for ac_prog in ginstall scoinst install; do
1482 for ac_exec_ext in '' $ac_executable_extensions; do
1483 if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
1484 if test $ac_prog = install &&
1485 grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
1486 # AIX install. It has an incompatible calling convention.
1487 :
1488 elif test $ac_prog = install &&
1489 grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
1490 # program-specific install script used by HP pwplus--don't use.
1491 :
1492 else
1493 ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
1494 break 3
1495 fi
1496 fi
1497 done
1498 done
1499 ;;
1500 esac
1501 done
1502
1503
1504 fi
1505 if test "${ac_cv_path_install+set}" = set; then
1506 INSTALL=$ac_cv_path_install
1507 else
1508 # As a last resort, use the slow shell script. We don't cache a
1509 # path for INSTALL within a source directory, because that will
1510 # break other packages using the cache if that directory is
1511 # removed, or if the path is relative.
1512 INSTALL=$ac_install_sh
1513 fi
1514 fi
1515 echo "$as_me:$LINENO: result: $INSTALL" >&5
1516 echo "${ECHO_T}$INSTALL" >&6
1517
1518 # Use test -z because SunOS4 sh mishandles braces in ${var-val}.
1519 # It thinks the first close brace ends the variable substitution.
1520 test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
1521
1522 test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
1523
1524 test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
1525
1526 echo "$as_me:$LINENO: checking whether build environment is sane" >&5
1527 echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6
1528 # Just in case
1529 sleep 1
1530 echo timestamp > conftest.file
1531 # Do `set' in a subshell so we don't clobber the current shell's
1532 # arguments. Must try -L first in case configure is actually a
1533 # symlink; some systems play weird games with the mod time of symlinks
1534 # (eg FreeBSD returns the mod time of the symlink's containing
1535 # directory).
1536 if (
1537 set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
1538 if test "$*" = "X"; then
1539 # -L didn't work.
1540 set X `ls -t $srcdir/configure conftest.file`
1541 fi
1542 rm -f conftest.file
1543 if test "$*" != "X $srcdir/configure conftest.file" \
1544 && test "$*" != "X conftest.file $srcdir/configure"; then
1545
1546 # If neither matched, then we have a broken ls. This can happen
1547 # if, for instance, CONFIG_SHELL is bash and it inherits a
1548 # broken ls alias from the environment. This has actually
1549 # happened. Such a system could not be considered "sane".
1550 { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken
1551 alias in your environment" >&5
1552 echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken
1553 alias in your environment" >&2;}
1554 { (exit 1); exit 1; }; }
1555 fi
1556
1557 test "$2" = conftest.file
1558 )
1559 then
1560 # Ok.
1561 :
1562 else
1563 { { echo "$as_me:$LINENO: error: newly created file is older than distributed files!
1564 Check your system clock" >&5
1565 echo "$as_me: error: newly created file is older than distributed files!
1566 Check your system clock" >&2;}
1567 { (exit 1); exit 1; }; }
1568 fi
1569 echo "$as_me:$LINENO: result: yes" >&5
1570 echo "${ECHO_T}yes" >&6
1571 test "$program_prefix" != NONE &&
1572 program_transform_name="s,^,$program_prefix,;$program_transform_name"
1573 # Use a double $ so make ignores it.
1574 test "$program_suffix" != NONE &&
1575 program_transform_name="s,\$,$program_suffix,;$program_transform_name"
1576 # Double any \ or $. echo might interpret backslashes.
1577 # By default was `s,x,x', remove it if useless.
1578 cat <<\_ACEOF >conftest.sed
1579 s/[\\$]/&&/g;s/;s,x,x,$//
1580 _ACEOF
1581 program_transform_name=`echo $program_transform_name | sed -f conftest.sed`
1582 rm conftest.sed
1583
1584 # expand $ac_aux_dir to an absolute path
1585 am_aux_dir=`cd $ac_aux_dir && pwd`
1586
1587 test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
1588 # Use eval to expand $SHELL
1589 if eval "$MISSING --run true"; then
1590 am_missing_run="$MISSING --run "
1591 else
1592 am_missing_run=
1593 { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5
1594 echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
1595 fi
1596
1597 if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
1598 # We used to keeping the `.' as first argument, in order to
1599 # allow $(mkdir_p) to be used without argument. As in
1600 # $(mkdir_p) $(somedir)
1601 # where $(somedir) is conditionally defined. However this is wrong
1602 # for two reasons:
1603 # 1. if the package is installed by a user who cannot write `.'
1604 # make install will fail,
1605 # 2. the above comment should most certainly read
1606 # $(mkdir_p) $(DESTDIR)$(somedir)
1607 # so it does not work when $(somedir) is undefined and
1608 # $(DESTDIR) is not.
1609 # To support the latter case, we have to write
1610 # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
1611 # so the `.' trick is pointless.
1612 mkdir_p='mkdir -p --'
1613 else
1614 # On NextStep and OpenStep, the `mkdir' command does not
1615 # recognize any option. It will interpret all options as
1616 # directories to create, and then abort because `.' already
1617 # exists.
1618 for d in ./-p ./--version;
1619 do
1620 test -d $d && rmdir $d
1621 done
1622 # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
1623 if test -f "$ac_aux_dir/mkinstalldirs"; then
1624 mkdir_p='$(mkinstalldirs)'
1625 else
1626 mkdir_p='$(install_sh) -d'
1627 fi
1628 fi
1629
1630 for ac_prog in gawk mawk nawk awk
1631 do
1632 # Extract the first word of "$ac_prog", so it can be a program name with args.
1633 set dummy $ac_prog; ac_word=$2
1634 echo "$as_me:$LINENO: checking for $ac_word" >&5
1635 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
1636 if test "${ac_cv_prog_AWK+set}" = set; then
1637 echo $ECHO_N "(cached) $ECHO_C" >&6
1638 else
1639 if test -n "$AWK"; then
1640 ac_cv_prog_AWK="$AWK" # Let the user override the test.
1641 else
1642 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1643 for as_dir in $PATH
1644 do
1645 IFS=$as_save_IFS
1646 test -z "$as_dir" && as_dir=.
1647 for ac_exec_ext in '' $ac_executable_extensions; do
1648 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
1649 ac_cv_prog_AWK="$ac_prog"
1650 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
1651 break 2
1652 fi
1653 done
1654 done
1655
1656 fi
1657 fi
1658 AWK=$ac_cv_prog_AWK
1659 if test -n "$AWK"; then
1660 echo "$as_me:$LINENO: result: $AWK" >&5
1661 echo "${ECHO_T}$AWK" >&6
1662 else
1663 echo "$as_me:$LINENO: result: no" >&5
1664 echo "${ECHO_T}no" >&6
1665 fi
1666
1667 test -n "$AWK" && break
1668 done
1669
1670 echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
1671 echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
1672 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'`
1673 if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
1674 echo $ECHO_N "(cached) $ECHO_C" >&6
1675 else
1676 cat >conftest.make <<\_ACEOF
1677 all:
1678 @echo 'ac_maketemp="$(MAKE)"'
1679 _ACEOF
1680 # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
1681 eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
1682 if test -n "$ac_maketemp"; then
1683 eval ac_cv_prog_make_${ac_make}_set=yes
1684 else
1685 eval ac_cv_prog_make_${ac_make}_set=no
1686 fi
1687 rm -f conftest.make
1688 fi
1689 if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
1690 echo "$as_me:$LINENO: result: yes" >&5
1691 echo "${ECHO_T}yes" >&6
1692 SET_MAKE=
1693 else
1694 echo "$as_me:$LINENO: result: no" >&5
1695 echo "${ECHO_T}no" >&6
1696 SET_MAKE="MAKE=${MAKE-make}"
1697 fi
1698
1699 rm -rf .tst 2>/dev/null
1700 mkdir .tst 2>/dev/null
1701 if test -d .tst; then
1702 am__leading_dot=.
1703 else
1704 am__leading_dot=_
1705 fi
1706 rmdir .tst 2>/dev/null
1707
1708 # test to see if srcdir already configured
1709 if test "`cd $srcdir && pwd`" != "`pwd`" &&
1710 test -f $srcdir/config.status; then
1711 { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5
1712 echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;}
1713 { (exit 1); exit 1; }; }
1714 fi
1715
1716 # test whether we have cygpath
1717 if test -z "$CYGPATH_W"; then
1718 if (cygpath --version) >/dev/null 2>/dev/null; then
1719 CYGPATH_W='cygpath -w'
1720 else
1721 CYGPATH_W=echo
1722 fi
1723 fi
1724
1725
1726 # Define the identity of the package.
1727 PACKAGE=dillo
1728 VERSION=0.8.6
1729
1730
1731 cat >>confdefs.h <<_ACEOF
1732 #define PACKAGE "$PACKAGE"
1733 _ACEOF
1734
1735
1736 cat >>confdefs.h <<_ACEOF
1737 #define VERSION "$VERSION"
1738 _ACEOF
1739
1740 # Some tools Automake needs.
1741
1742 ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
1743
1744
1745 AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
1746
1747
1748 AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
1749
1750
1751 AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
1752
1753
1754 MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
1755
1756 install_sh=${install_sh-"$am_aux_dir/install-sh"}
1757
1758 # Installed binaries are usually stripped using `strip' when the user
1759 # run `make install-strip'. However `strip' might not be the right
1760 # tool to use in cross-compilation environments, therefore Automake
1761 # will honor the `STRIP' environment variable to overrule this program.
1762 if test "$cross_compiling" != no; then
1763 if test -n "$ac_tool_prefix"; then
1764 # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
1765 set dummy ${ac_tool_prefix}strip; ac_word=$2
1766 echo "$as_me:$LINENO: checking for $ac_word" >&5
1767 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
1768 if test "${ac_cv_prog_STRIP+set}" = set; then
1769 echo $ECHO_N "(cached) $ECHO_C" >&6
1770 else
1771 if test -n "$STRIP"; then
1772 ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
1773 else
1774 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1775 for as_dir in $PATH
1776 do
1777 IFS=$as_save_IFS
1778 test -z "$as_dir" && as_dir=.
1779 for ac_exec_ext in '' $ac_executable_extensions; do
1780 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
1781 ac_cv_prog_STRIP="${ac_tool_prefix}strip"
1782 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
1783 break 2
1784 fi
1785 done
1786 done
1787
1788 fi
1789 fi
1790 STRIP=$ac_cv_prog_STRIP
1791 if test -n "$STRIP"; then
1792 echo "$as_me:$LINENO: result: $STRIP" >&5
1793 echo "${ECHO_T}$STRIP" >&6
1794 else
1795 echo "$as_me:$LINENO: result: no" >&5
1796 echo "${ECHO_T}no" >&6
1797 fi
1798
1799 fi
1800 if test -z "$ac_cv_prog_STRIP"; then
1801 ac_ct_STRIP=$STRIP
1802 # Extract the first word of "strip", so it can be a program name with args.
1803 set dummy strip; ac_word=$2
1804 echo "$as_me:$LINENO: checking for $ac_word" >&5
1805 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
1806 if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
1807 echo $ECHO_N "(cached) $ECHO_C" >&6
1808 else
1809 if test -n "$ac_ct_STRIP"; then
1810 ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
1811 else
1812 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1813 for as_dir in $PATH
1814 do
1815 IFS=$as_save_IFS
1816 test -z "$as_dir" && as_dir=.
1817 for ac_exec_ext in '' $ac_executable_extensions; do
1818 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
1819 ac_cv_prog_ac_ct_STRIP="strip"
1820 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
1821 break 2
1822 fi
1823 done
1824 done
1825
1826 test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":"
1827 fi
1828 fi
1829 ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
1830 if test -n "$ac_ct_STRIP"; then
1831 echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
1832 echo "${ECHO_T}$ac_ct_STRIP" >&6
1833 else
1834 echo "$as_me:$LINENO: result: no" >&5
1835 echo "${ECHO_T}no" >&6
1836 fi
1837
1838 STRIP=$ac_ct_STRIP
1839 else
1840 STRIP="$ac_cv_prog_STRIP"
1841 fi
1842
1843 fi
1844 INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
1845
1846 # We need awk for the "check" target. The system "awk" is bad on
1847 # some platforms.
1848 # Always define AMTAR for backward compatibility.
1849
1850 AMTAR=${AMTAR-"${am_missing_run}tar"}
1851
1852 am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
1853
1854
1855
1856
1857
1858 ac_config_headers="$ac_config_headers config.h"
1859
1860
1861
1862
1863 # Check whether --with-jpeg-lib or --without-jpeg-lib was given.
1864 if test "${with_jpeg_lib+set}" = set; then
1865 withval="$with_jpeg_lib"
1866 LIBJPEG_LIBDIR=$withval
1867 fi;
1868
1869 # Check whether --with-jpeg-inc or --without-jpeg-inc was given.
1870 if test "${with_jpeg_inc+set}" = set; then
1871 withval="$with_jpeg_inc"
1872 LIBJPEG_INCDIR=$withval
1873 fi;
1874
1875 # Check whether --enable-efence or --disable-efence was given.
1876 if test "${enable_efence+set}" = set; then
1877 enableval="$enable_efence"
1878
1879 else
1880 enable_efence=no
1881 fi;
1882 # Check whether --enable-gprof or --disable-gprof was given.
1883 if test "${enable_gprof+set}" = set; then
1884 enableval="$enable_gprof"
1885
1886 else
1887 enable_gprof=no
1888 fi;
1889 # Check whether --enable-insure or --disable-insure was given.
1890 if test "${enable_insure+set}" = set; then
1891 enableval="$enable_insure"
1892
1893 else
1894 enable_insure=no
1895 fi;
1896 # Check whether --enable-ansi or --disable-ansi was given.
1897 if test "${enable_ansi+set}" = set; then
1898 enableval="$enable_ansi"
1899
1900 else
1901 enable_ansi=no
1902 fi;
1903 # Check whether --enable-ipv6 or --disable-ipv6 was given.
1904 if test "${enable_ipv6+set}" = set; then
1905 enableval="$enable_ipv6"
1906
1907 fi;
1908 # Check whether --enable-rtfl or --disable-rtfl was given.
1909 if test "${enable_rtfl+set}" = set; then
1910 enableval="$enable_rtfl"
1911 enable_rtfl=yes
1912 fi;
1913 # Check whether --enable-cookies or --disable-cookies was given.
1914 if test "${enable_cookies+set}" = set; then
1915 enableval="$enable_cookies"
1916
1917 else
1918 enable_cookies=yes
1919 fi;
1920 # Check whether --enable-png or --disable-png was given.
1921 if test "${enable_png+set}" = set; then
1922 enableval="$enable_png"
1923 enable_png=$enableval
1924 else
1925 enable_png=yes
1926 fi;
1927 # Check whether --enable-jpeg or --disable-jpeg was given.
1928 if test "${enable_jpeg+set}" = set; then
1929 enableval="$enable_jpeg"
1930 enable_jpeg=$enableval
1931 else
1932 enable_jpeg=yes
1933 fi;
1934 # Check whether --enable-gif or --disable-gif was given.
1935 if test "${enable_gif+set}" = set; then
1936 enableval="$enable_gif"
1937 enable_gif=$enableval
1938 else
1939 enable_gif=yes
1940 fi;
1941 # Check whether --enable-ssl or --disable-ssl was given.
1942 if test "${enable_ssl+set}" = set; then
1943 enableval="$enable_ssl"
1944 enable_ssl=$enableval
1945 else
1946 enable_ssl=yes
1947 fi;
1948 # Check whether --enable-dlgui or --disable-dlgui was given.
1949 if test "${enable_dlgui+set}" = set; then
1950 enableval="$enable_dlgui"
1951 enable_dlgui=$enableval
1952 else
1953 enable_dlgui=yes
1954 fi;
1955 # Check whether --enable-threaded-dns or --disable-threaded-dns was given.
1956 if test "${enable_threaded_dns+set}" = set; then
1957 enableval="$enable_threaded_dns"
1958 enable_threaded_dns=$enableval
1959 else
1960 enable_threaded_dns=yes
1961 fi;
1962
1963
1964 if test x$enable_dlgui = xyes; then
1965 DLGUI_TRUE=
1966 DLGUI_FALSE='#'
1967 else
1968 DLGUI_TRUE='#'
1969 DLGUI_FALSE=
1970 fi
1971
1972
1973 ac_ext=c
1974 ac_cpp='$CPP $CPPFLAGS'
1975 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
1976 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
1977 ac_compiler_gnu=$ac_cv_c_compiler_gnu
1978 if test -n "$ac_tool_prefix"; then
1979 # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
1980 set dummy ${ac_tool_prefix}gcc; ac_word=$2
1981 echo "$as_me:$LINENO: checking for $ac_word" >&5
1982 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
1983 if test "${ac_cv_prog_CC+set}" = set; then
1984 echo $ECHO_N "(cached) $ECHO_C" >&6
1985 else
1986 if test -n "$CC"; then
1987 ac_cv_prog_CC="$CC" # Let the user override the test.
1988 else
1989 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1990 for as_dir in $PATH
1991 do
1992 IFS=$as_save_IFS
1993 test -z "$as_dir" && as_dir=.
1994 for ac_exec_ext in '' $ac_executable_extensions; do
1995 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
1996 ac_cv_prog_CC="${ac_tool_prefix}gcc"
1997 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
1998 break 2
1999 fi
2000 done
2001 done
2002
2003 fi
2004 fi
2005 CC=$ac_cv_prog_CC
2006 if test -n "$CC"; then
2007 echo "$as_me:$LINENO: result: $CC" >&5
2008 echo "${ECHO_T}$CC" >&6
2009 else
2010 echo "$as_me:$LINENO: result: no" >&5
2011 echo "${ECHO_T}no" >&6
2012 fi
2013
2014 fi
2015 if test -z "$ac_cv_prog_CC"; then
2016 ac_ct_CC=$CC
2017 # Extract the first word of "gcc", so it can be a program name with args.
2018 set dummy gcc; ac_word=$2
2019 echo "$as_me:$LINENO: checking for $ac_word" >&5
2020 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2021 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
2022 echo $ECHO_N "(cached) $ECHO_C" >&6
2023 else
2024 if test -n "$ac_ct_CC"; then
2025 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
2026 else
2027 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2028 for as_dir in $PATH
2029 do
2030 IFS=$as_save_IFS
2031 test -z "$as_dir" && as_dir=.
2032 for ac_exec_ext in '' $ac_executable_extensions; do
2033 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2034 ac_cv_prog_ac_ct_CC="gcc"
2035 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2036 break 2
2037 fi
2038 done
2039 done
2040
2041 fi
2042 fi
2043 ac_ct_CC=$ac_cv_prog_ac_ct_CC
2044 if test -n "$ac_ct_CC"; then
2045 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
2046 echo "${ECHO_T}$ac_ct_CC" >&6
2047 else
2048 echo "$as_me:$LINENO: result: no" >&5
2049 echo "${ECHO_T}no" >&6
2050 fi
2051
2052 CC=$ac_ct_CC
2053 else
2054 CC="$ac_cv_prog_CC"
2055 fi
2056
2057 if test -z "$CC"; then
2058 if test -n "$ac_tool_prefix"; then
2059 # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
2060 set dummy ${ac_tool_prefix}cc; ac_word=$2
2061 echo "$as_me:$LINENO: checking for $ac_word" >&5
2062 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2063 if test "${ac_cv_prog_CC+set}" = set; then
2064 echo $ECHO_N "(cached) $ECHO_C" >&6
2065 else
2066 if test -n "$CC"; then
2067 ac_cv_prog_CC="$CC" # Let the user override the test.
2068 else
2069 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2070 for as_dir in $PATH
2071 do
2072 IFS=$as_save_IFS
2073 test -z "$as_dir" && as_dir=.
2074 for ac_exec_ext in '' $ac_executable_extensions; do
2075 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2076 ac_cv_prog_CC="${ac_tool_prefix}cc"
2077 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2078 break 2
2079 fi
2080 done
2081 done
2082
2083 fi
2084 fi
2085 CC=$ac_cv_prog_CC
2086 if test -n "$CC"; then
2087 echo "$as_me:$LINENO: result: $CC" >&5
2088 echo "${ECHO_T}$CC" >&6
2089 else
2090 echo "$as_me:$LINENO: result: no" >&5
2091 echo "${ECHO_T}no" >&6
2092 fi
2093
2094 fi
2095 if test -z "$ac_cv_prog_CC"; then
2096 ac_ct_CC=$CC
2097 # Extract the first word of "cc", so it can be a program name with args.
2098 set dummy cc; ac_word=$2
2099 echo "$as_me:$LINENO: checking for $ac_word" >&5
2100 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2101 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
2102 echo $ECHO_N "(cached) $ECHO_C" >&6
2103 else
2104 if test -n "$ac_ct_CC"; then
2105 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
2106 else
2107 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2108 for as_dir in $PATH
2109 do
2110 IFS=$as_save_IFS
2111 test -z "$as_dir" && as_dir=.
2112 for ac_exec_ext in '' $ac_executable_extensions; do
2113 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2114 ac_cv_prog_ac_ct_CC="cc"
2115 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2116 break 2
2117 fi
2118 done
2119 done
2120
2121 fi
2122 fi
2123 ac_ct_CC=$ac_cv_prog_ac_ct_CC
2124 if test -n "$ac_ct_CC"; then
2125 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
2126 echo "${ECHO_T}$ac_ct_CC" >&6
2127 else
2128 echo "$as_me:$LINENO: result: no" >&5
2129 echo "${ECHO_T}no" >&6
2130 fi
2131
2132 CC=$ac_ct_CC
2133 else
2134 CC="$ac_cv_prog_CC"
2135 fi
2136
2137 fi
2138 if test -z "$CC"; then
2139 # Extract the first word of "cc", so it can be a program name with args.
2140 set dummy cc; ac_word=$2
2141 echo "$as_me:$LINENO: checking for $ac_word" >&5
2142 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2143 if test "${ac_cv_prog_CC+set}" = set; then
2144 echo $ECHO_N "(cached) $ECHO_C" >&6
2145 else
2146 if test -n "$CC"; then
2147 ac_cv_prog_CC="$CC" # Let the user override the test.
2148 else
2149 ac_prog_rejected=no
2150 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2151 for as_dir in $PATH
2152 do
2153 IFS=$as_save_IFS
2154 test -z "$as_dir" && as_dir=.
2155 for ac_exec_ext in '' $ac_executable_extensions; do
2156 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2157 if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
2158 ac_prog_rejected=yes
2159 continue
2160 fi
2161 ac_cv_prog_CC="cc"
2162 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2163 break 2
2164 fi
2165 done
2166 done
2167
2168 if test $ac_prog_rejected = yes; then
2169 # We found a bogon in the path, so make sure we never use it.
2170 set dummy $ac_cv_prog_CC
2171 shift
2172 if test $# != 0; then
2173 # We chose a different compiler from the bogus one.
2174 # However, it has the same basename, so the bogon will be chosen
2175 # first if we set CC to just the basename; use the full file name.
2176 shift
2177 ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
2178 fi
2179 fi
2180 fi
2181 fi
2182 CC=$ac_cv_prog_CC
2183 if test -n "$CC"; then
2184 echo "$as_me:$LINENO: result: $CC" >&5
2185 echo "${ECHO_T}$CC" >&6
2186 else
2187 echo "$as_me:$LINENO: result: no" >&5
2188 echo "${ECHO_T}no" >&6
2189 fi
2190
2191 fi
2192 if test -z "$CC"; then
2193 if test -n "$ac_tool_prefix"; then
2194 for ac_prog in cl
2195 do
2196 # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
2197 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
2198 echo "$as_me:$LINENO: checking for $ac_word" >&5
2199 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2200 if test "${ac_cv_prog_CC+set}" = set; then
2201 echo $ECHO_N "(cached) $ECHO_C" >&6
2202 else
2203 if test -n "$CC"; then
2204 ac_cv_prog_CC="$CC" # Let the user override the test.
2205 else
2206 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2207 for as_dir in $PATH
2208 do
2209 IFS=$as_save_IFS
2210 test -z "$as_dir" && as_dir=.
2211 for ac_exec_ext in '' $ac_executable_extensions; do
2212 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2213 ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
2214 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2215 break 2
2216 fi
2217 done
2218 done
2219
2220 fi
2221 fi
2222 CC=$ac_cv_prog_CC
2223 if test -n "$CC"; then
2224 echo "$as_me:$LINENO: result: $CC" >&5
2225 echo "${ECHO_T}$CC" >&6
2226 else
2227 echo "$as_me:$LINENO: result: no" >&5
2228 echo "${ECHO_T}no" >&6
2229 fi
2230
2231 test -n "$CC" && break
2232 done
2233 fi
2234 if test -z "$CC"; then
2235 ac_ct_CC=$CC
2236 for ac_prog in cl
2237 do
2238 # Extract the first word of "$ac_prog", so it can be a program name with args.
2239 set dummy $ac_prog; ac_word=$2
2240 echo "$as_me:$LINENO: checking for $ac_word" >&5
2241 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
2242 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
2243 echo $ECHO_N "(cached) $ECHO_C" >&6
2244 else
2245 if test -n "$ac_ct_CC"; then
2246 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
2247 else
2248 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2249 for as_dir in $PATH
2250 do
2251 IFS=$as_save_IFS
2252 test -z "$as_dir" && as_dir=.
2253 for ac_exec_ext in '' $ac_executable_extensions; do
2254 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2255 ac_cv_prog_ac_ct_CC="$ac_prog"
2256 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
2257 break 2
2258 fi
2259 done
2260 done
2261
2262 fi
2263 fi
2264 ac_ct_CC=$ac_cv_prog_ac_ct_CC
2265 if test -n "$ac_ct_CC"; then
2266 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
2267 echo "${ECHO_T}$ac_ct_CC" >&6
2268 else
2269 echo "$as_me:$LINENO: result: no" >&5
2270 echo "${ECHO_T}no" >&6
2271 fi
2272
2273 test -n "$ac_ct_CC" && break
2274 done
2275
2276 CC=$ac_ct_CC
2277 fi
2278
2279 fi
2280
2281
2282 test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
2283 See \`config.log' for more details." >&5
2284 echo "$as_me: error: no acceptable C compiler found in \$PATH
2285 See \`config.log' for more details." >&2;}
2286 { (exit 1); exit 1; }; }
2287
2288 # Provide some information about the compiler.
2289 echo "$as_me:$LINENO:" \
2290 "checking for C compiler version" >&5
2291 ac_compiler=`set X $ac_compile; echo $2`
2292 { (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
2293 (eval $ac_compiler --version </dev/null >&5) 2>&5
2294 ac_status=$?
2295 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2296 (exit $ac_status); }
2297 { (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
2298 (eval $ac_compiler -v </dev/null >&5) 2>&5
2299 ac_status=$?
2300 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2301 (exit $ac_status); }
2302 { (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
2303 (eval $ac_compiler -V </dev/null >&5) 2>&5
2304 ac_status=$?
2305 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2306 (exit $ac_status); }
2307
2308 cat >conftest.$ac_ext <<_ACEOF
2309 /* confdefs.h. */
2310 _ACEOF
2311 cat confdefs.h >>conftest.$ac_ext
2312 cat >>conftest.$ac_ext <<_ACEOF
2313 /* end confdefs.h. */
2314
2315 int
2316 main ()
2317 {
2318
2319 ;
2320 return 0;
2321 }
2322 _ACEOF
2323 ac_clean_files_save=$ac_clean_files
2324 ac_clean_files="$ac_clean_files a.out a.exe b.out"
2325 # Try to create an executable without -o first, disregard a.out.
2326 # It will help us diagnose broken compilers, and finding out an intuition
2327 # of exeext.
2328 echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
2329 echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
2330 ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
2331 if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
2332 (eval $ac_link_default) 2>&5
2333 ac_status=$?
2334 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2335 (exit $ac_status); }; then
2336 # Find the output, starting from the most likely. This scheme is
2337 # not robust to junk in `.', hence go to wildcards (a.*) only as a last
2338 # resort.
2339
2340 # Be careful to initialize this variable, since it used to be cached.
2341 # Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
2342 ac_cv_exeext=
2343 # b.out is created by i960 compilers.
2344 for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
2345 do
2346 test -f "$ac_file" || continue
2347 case $ac_file in
2348 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
2349 ;;
2350 conftest.$ac_ext )
2351 # This is the source file.
2352 ;;
2353 [ab].out )
2354 # We found the default executable, but exeext='' is most
2355 # certainly right.
2356 break;;
2357 *.* )
2358 ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
2359 # FIXME: I believe we export ac_cv_exeext for Libtool,
2360 # but it would be cool to find out if it's true. Does anybody
2361 # maintain Libtool? --akim.
2362 export ac_cv_exeext
2363 break;;
2364 * )
2365 break;;
2366 esac
2367 done
2368 else
2369 echo "$as_me: failed program was:" >&5
2370 sed 's/^/| /' conftest.$ac_ext >&5
2371
2372 { { echo "$as_me:$LINENO: error: C compiler cannot create executables
2373 See \`config.log' for more details." >&5
2374 echo "$as_me: error: C compiler cannot create executables
2375 See \`config.log' for more details." >&2;}
2376 { (exit 77); exit 77; }; }
2377 fi
2378
2379 ac_exeext=$ac_cv_exeext
2380 echo "$as_me:$LINENO: result: $ac_file" >&5
2381 echo "${ECHO_T}$ac_file" >&6
2382
2383 # Check the compiler produces executables we can run. If not, either
2384 # the compiler is broken, or we cross compile.
2385 echo "$as_me:$LINENO: checking whether the C compiler works" >&5
2386 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
2387 # FIXME: These cross compiler hacks should be removed for Autoconf 3.0
2388 # If not cross compiling, check that we can run a simple program.
2389 if test "$cross_compiling" != yes; then
2390 if { ac_try='./$ac_file'
2391 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2392 (eval $ac_try) 2>&5
2393 ac_status=$?
2394 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2395 (exit $ac_status); }; }; then
2396 cross_compiling=no
2397 else
2398 if test "$cross_compiling" = maybe; then
2399 cross_compiling=yes
2400 else
2401 { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
2402 If you meant to cross compile, use \`--host'.
2403 See \`config.log' for more details." >&5
2404 echo "$as_me: error: cannot run C compiled programs.
2405 If you meant to cross compile, use \`--host'.
2406 See \`config.log' for more details." >&2;}
2407 { (exit 1); exit 1; }; }
2408 fi
2409 fi
2410 fi
2411 echo "$as_me:$LINENO: result: yes" >&5
2412 echo "${ECHO_T}yes" >&6
2413
2414 rm -f a.out a.exe conftest$ac_cv_exeext b.out
2415 ac_clean_files=$ac_clean_files_save
2416 # Check the compiler produces executables we can run. If not, either
2417 # the compiler is broken, or we cross compile.
2418 echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
2419 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
2420 echo "$as_me:$LINENO: result: $cross_compiling" >&5
2421 echo "${ECHO_T}$cross_compiling" >&6
2422
2423 echo "$as_me:$LINENO: checking for suffix of executables" >&5
2424 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
2425 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
2426 (eval $ac_link) 2>&5
2427 ac_status=$?
2428 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2429 (exit $ac_status); }; then
2430 # If both `conftest.exe' and `conftest' are `present' (well, observable)
2431 # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
2432 # work properly (i.e., refer to `conftest.exe'), while it won't with
2433 # `rm'.
2434 for ac_file in conftest.exe conftest conftest.*; do
2435 test -f "$ac_file" || continue
2436 case $ac_file in
2437 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
2438 *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
2439 export ac_cv_exeext
2440 break;;
2441 * ) break;;
2442 esac
2443 done
2444 else
2445 { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
2446 See \`config.log' for more details." >&5
2447 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
2448 See \`config.log' for more details." >&2;}
2449 { (exit 1); exit 1; }; }
2450 fi
2451
2452 rm -f conftest$ac_cv_exeext
2453 echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
2454 echo "${ECHO_T}$ac_cv_exeext" >&6
2455
2456 rm -f conftest.$ac_ext
2457 EXEEXT=$ac_cv_exeext
2458 ac_exeext=$EXEEXT
2459 echo "$as_me:$LINENO: checking for suffix of object files" >&5
2460 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
2461 if test "${ac_cv_objext+set}" = set; then
2462 echo $ECHO_N "(cached) $ECHO_C" >&6
2463 else
2464 cat >conftest.$ac_ext <<_ACEOF
2465 /* confdefs.h. */
2466 _ACEOF
2467 cat confdefs.h >>conftest.$ac_ext
2468 cat >>conftest.$ac_ext <<_ACEOF
2469 /* end confdefs.h. */
2470
2471 int
2472 main ()
2473 {
2474
2475 ;
2476 return 0;
2477 }
2478 _ACEOF
2479 rm -f conftest.o conftest.obj
2480 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2481 (eval $ac_compile) 2>&5
2482 ac_status=$?
2483 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2484 (exit $ac_status); }; then
2485 for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
2486 case $ac_file in
2487 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
2488 *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
2489 break;;
2490 esac
2491 done
2492 else
2493 echo "$as_me: failed program was:" >&5
2494 sed 's/^/| /' conftest.$ac_ext >&5
2495
2496 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
2497 See \`config.log' for more details." >&5
2498 echo "$as_me: error: cannot compute suffix of object files: cannot compile
2499 See \`config.log' for more details." >&2;}
2500 { (exit 1); exit 1; }; }
2501 fi
2502
2503 rm -f conftest.$ac_cv_objext conftest.$ac_ext
2504 fi
2505 echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
2506 echo "${ECHO_T}$ac_cv_objext" >&6
2507 OBJEXT=$ac_cv_objext
2508 ac_objext=$OBJEXT
2509 echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
2510 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
2511 if test "${ac_cv_c_compiler_gnu+set}" = set; then
2512 echo $ECHO_N "(cached) $ECHO_C" >&6
2513 else
2514 cat >conftest.$ac_ext <<_ACEOF
2515 /* confdefs.h. */
2516 _ACEOF
2517 cat confdefs.h >>conftest.$ac_ext
2518 cat >>conftest.$ac_ext <<_ACEOF
2519 /* end confdefs.h. */
2520
2521 int
2522 main ()
2523 {
2524 #ifndef __GNUC__
2525 choke me
2526 #endif
2527
2528 ;
2529 return 0;
2530 }
2531 _ACEOF
2532 rm -f conftest.$ac_objext
2533 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2534 (eval $ac_compile) 2>conftest.er1
2535 ac_status=$?
2536 grep -v '^ *+' conftest.er1 >conftest.err
2537 rm -f conftest.er1
2538 cat conftest.err >&5
2539 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2540 (exit $ac_status); } &&
2541 { ac_try='test -z "$ac_c_werror_flag"
2542 || test ! -s conftest.err'
2543 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2544 (eval $ac_try) 2>&5
2545 ac_status=$?
2546 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2547 (exit $ac_status); }; } &&
2548 { ac_try='test -s conftest.$ac_objext'
2549 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2550 (eval $ac_try) 2>&5
2551 ac_status=$?
2552 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2553 (exit $ac_status); }; }; then
2554 ac_compiler_gnu=yes
2555 else
2556 echo "$as_me: failed program was:" >&5
2557 sed 's/^/| /' conftest.$ac_ext >&5
2558
2559 ac_compiler_gnu=no
2560 fi
2561 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
2562 ac_cv_c_compiler_gnu=$ac_compiler_gnu
2563
2564 fi
2565 echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
2566 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
2567 GCC=`test $ac_compiler_gnu = yes && echo yes`
2568 ac_test_CFLAGS=${CFLAGS+set}
2569 ac_save_CFLAGS=$CFLAGS
2570 CFLAGS="-g"
2571 echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
2572 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
2573 if test "${ac_cv_prog_cc_g+set}" = set; then
2574 echo $ECHO_N "(cached) $ECHO_C" >&6
2575 else
2576 cat >conftest.$ac_ext <<_ACEOF
2577 /* confdefs.h. */
2578 _ACEOF
2579 cat confdefs.h >>conftest.$ac_ext
2580 cat >>conftest.$ac_ext <<_ACEOF
2581 /* end confdefs.h. */
2582
2583 int
2584 main ()
2585 {
2586
2587 ;
2588 return 0;
2589 }
2590 _ACEOF
2591 rm -f conftest.$ac_objext
2592 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2593 (eval $ac_compile) 2>conftest.er1
2594 ac_status=$?
2595 grep -v '^ *+' conftest.er1 >conftest.err
2596 rm -f conftest.er1
2597 cat conftest.err >&5
2598 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2599 (exit $ac_status); } &&
2600 { ac_try='test -z "$ac_c_werror_flag"
2601 || test ! -s conftest.err'
2602 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2603 (eval $ac_try) 2>&5
2604 ac_status=$?
2605 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2606 (exit $ac_status); }; } &&
2607 { ac_try='test -s conftest.$ac_objext'
2608 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2609 (eval $ac_try) 2>&5
2610 ac_status=$?
2611 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2612 (exit $ac_status); }; }; then
2613 ac_cv_prog_cc_g=yes
2614 else
2615 echo "$as_me: failed program was:" >&5
2616 sed 's/^/| /' conftest.$ac_ext >&5
2617
2618 ac_cv_prog_cc_g=no
2619 fi
2620 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
2621 fi
2622 echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
2623 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
2624 if test "$ac_test_CFLAGS" = set; then
2625 CFLAGS=$ac_save_CFLAGS
2626 elif test $ac_cv_prog_cc_g = yes; then
2627 if test "$GCC" = yes; then
2628 CFLAGS="-g -O2"
2629 else
2630 CFLAGS="-g"
2631 fi
2632 else
2633 if test "$GCC" = yes; then
2634 CFLAGS="-O2"
2635 else
2636 CFLAGS=
2637 fi
2638 fi
2639 echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
2640 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
2641 if test "${ac_cv_prog_cc_stdc+set}" = set; then
2642 echo $ECHO_N "(cached) $ECHO_C" >&6
2643 else
2644 ac_cv_prog_cc_stdc=no
2645 ac_save_CC=$CC
2646 cat >conftest.$ac_ext <<_ACEOF
2647 /* confdefs.h. */
2648 _ACEOF
2649 cat confdefs.h >>conftest.$ac_ext
2650 cat >>conftest.$ac_ext <<_ACEOF
2651 /* end confdefs.h. */
2652 #include <stdarg.h>
2653 #include <stdio.h>
2654 #include <sys/types.h>
2655 #include <sys/stat.h>
2656 /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
2657 struct buf { int x; };
2658 FILE * (*rcsopen) (struct buf *, struct stat *, int);
2659 static char *e (p, i)
2660 char **p;
2661 int i;
2662 {
2663 return p[i];
2664 }
2665 static char *f (char * (*g) (char **, int), char **p, ...)
2666 {
2667 char *s;
2668 va_list v;
2669 va_start (v,p);
2670 s = g (p, va_arg (v,int));
2671 va_end (v);
2672 return s;
2673 }
2674
2675 /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
2676 function prototypes and stuff, but not '\xHH' hex character constants.
2677 These don't provoke an error unfortunately, instead are silently treated
2678 as 'x'. The following induces an error, until -std1 is added to get
2679 proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
2680 array size at least. It's necessary to write '\x00'==0 to get something
2681 that's true only with -std1. */
2682 int osf4_cc_array ['\x00' == 0 ? 1 : -1];
2683
2684 int test (int i, double x);
2685 struct s1 {int (*f) (int a);};
2686 struct s2 {int (*f) (double a);};
2687 int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
2688 int argc;
2689 char **argv;
2690 int
2691 main ()
2692 {
2693 return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
2694 ;
2695 return 0;
2696 }
2697 _ACEOF
2698 # Don't try gcc -ansi; that turns off useful extensions and
2699 # breaks some systems' header files.
2700 # AIX -qlanglvl=ansi
2701 # Ultrix and OSF/1 -std1
2702 # HP-UX 10.20 and later -Ae
2703 # HP-UX older versions -Aa -D_HPUX_SOURCE
2704 # SVR4 -Xc -D__EXTENSIONS__
2705 for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
2706 do
2707 CC="$ac_save_CC $ac_arg"
2708 rm -f conftest.$ac_objext
2709 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2710 (eval $ac_compile) 2>conftest.er1
2711 ac_status=$?
2712 grep -v '^ *+' conftest.er1 >conftest.err
2713 rm -f conftest.er1
2714 cat conftest.err >&5
2715 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2716 (exit $ac_status); } &&
2717 { ac_try='test -z "$ac_c_werror_flag"
2718 || test ! -s conftest.err'
2719 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2720 (eval $ac_try) 2>&5
2721 ac_status=$?
2722 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2723 (exit $ac_status); }; } &&
2724 { ac_try='test -s conftest.$ac_objext'
2725 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2726 (eval $ac_try) 2>&5
2727 ac_status=$?
2728 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2729 (exit $ac_status); }; }; then
2730 ac_cv_prog_cc_stdc=$ac_arg
2731 break
2732 else
2733 echo "$as_me: failed program was:" >&5
2734 sed 's/^/| /' conftest.$ac_ext >&5
2735
2736 fi
2737 rm -f conftest.err conftest.$ac_objext
2738 done
2739 rm -f conftest.$ac_ext conftest.$ac_objext
2740 CC=$ac_save_CC
2741
2742 fi
2743
2744 case "x$ac_cv_prog_cc_stdc" in
2745 x|xno)
2746 echo "$as_me:$LINENO: result: none needed" >&5
2747 echo "${ECHO_T}none needed" >&6 ;;
2748 *)
2749 echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
2750 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
2751 CC="$CC $ac_cv_prog_cc_stdc" ;;
2752 esac
2753
2754 # Some people use a C++ compiler to compile C. Since we use `exit',
2755 # in C++ we need to declare it. In case someone uses the same compiler
2756 # for both compiling C and C++ we need to have the C++ compiler decide
2757 # the declaration of exit, since it's the most demanding environment.
2758 cat >conftest.$ac_ext <<_ACEOF
2759 #ifndef __cplusplus
2760 choke me
2761 #endif
2762 _ACEOF
2763 rm -f conftest.$ac_objext
2764 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2765 (eval $ac_compile) 2>conftest.er1
2766 ac_status=$?
2767 grep -v '^ *+' conftest.er1 >conftest.err
2768 rm -f conftest.er1
2769 cat conftest.err >&5
2770 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2771 (exit $ac_status); } &&
2772 { ac_try='test -z "$ac_c_werror_flag"
2773 || test ! -s conftest.err'
2774 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2775 (eval $ac_try) 2>&5
2776 ac_status=$?
2777 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2778 (exit $ac_status); }; } &&
2779 { ac_try='test -s conftest.$ac_objext'
2780 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2781 (eval $ac_try) 2>&5
2782 ac_status=$?
2783 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2784 (exit $ac_status); }; }; then
2785 for ac_declaration in \
2786 '' \
2787 'extern "C" void std::exit (int) throw (); using std::exit;' \
2788 'extern "C" void std::exit (int); using std::exit;' \
2789 'extern "C" void exit (int) throw ();' \
2790 'extern "C" void exit (int);' \
2791 'void exit (int);'
2792 do
2793 cat >conftest.$ac_ext <<_ACEOF
2794 /* confdefs.h. */
2795 _ACEOF
2796 cat confdefs.h >>conftest.$ac_ext
2797 cat >>conftest.$ac_ext <<_ACEOF
2798 /* end confdefs.h. */
2799 $ac_declaration
2800 #include <stdlib.h>
2801 int
2802 main ()
2803 {
2804 exit (42);
2805 ;
2806 return 0;
2807 }
2808 _ACEOF
2809 rm -f conftest.$ac_objext
2810 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2811 (eval $ac_compile) 2>conftest.er1
2812 ac_status=$?
2813 grep -v '^ *+' conftest.er1 >conftest.err
2814 rm -f conftest.er1
2815 cat conftest.err >&5
2816 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2817 (exit $ac_status); } &&
2818 { ac_try='test -z "$ac_c_werror_flag"
2819 || test ! -s conftest.err'
2820 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2821 (eval $ac_try) 2>&5
2822 ac_status=$?
2823 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2824 (exit $ac_status); }; } &&
2825 { ac_try='test -s conftest.$ac_objext'
2826 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2827 (eval $ac_try) 2>&5
2828 ac_status=$?
2829 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2830 (exit $ac_status); }; }; then
2831 :
2832 else
2833 echo "$as_me: failed program was:" >&5
2834 sed 's/^/| /' conftest.$ac_ext >&5
2835
2836 continue
2837 fi
2838 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
2839 cat >conftest.$ac_ext <<_ACEOF
2840 /* confdefs.h. */
2841 _ACEOF
2842 cat confdefs.h >>conftest.$ac_ext
2843 cat >>conftest.$ac_ext <<_ACEOF
2844 /* end confdefs.h. */
2845 $ac_declaration
2846 int
2847 main ()
2848 {
2849 exit (42);
2850 ;
2851 return 0;
2852 }
2853 _ACEOF
2854 rm -f conftest.$ac_objext
2855 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
2856 (eval $ac_compile) 2>conftest.er1
2857 ac_status=$?
2858 grep -v '^ *+' conftest.er1 >conftest.err
2859 rm -f conftest.er1
2860 cat conftest.err >&5
2861 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2862 (exit $ac_status); } &&
2863 { ac_try='test -z "$ac_c_werror_flag"
2864 || test ! -s conftest.err'
2865 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2866 (eval $ac_try) 2>&5
2867 ac_status=$?
2868 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2869 (exit $ac_status); }; } &&
2870 { ac_try='test -s conftest.$ac_objext'
2871 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
2872 (eval $ac_try) 2>&5
2873 ac_status=$?
2874 echo "$as_me:$LINENO: \$? = $ac_status" >&5
2875 (exit $ac_status); }; }; then
2876 break
2877 else
2878 echo "$as_me: failed program was:" >&5
2879 sed 's/^/| /' conftest.$ac_ext >&5
2880
2881 fi
2882 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
2883 done
2884 rm -f conftest*
2885 if test -n "$ac_declaration"; then
2886 echo '#ifdef __cplusplus' >>confdefs.h
2887 echo $ac_declaration >>confdefs.h
2888 echo '#endif' >>confdefs.h
2889 fi
2890
2891 else
2892 echo "$as_me: failed program was:" >&5
2893 sed 's/^/| /' conftest.$ac_ext >&5
2894
2895 fi
2896 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
2897 ac_ext=c
2898 ac_cpp='$CPP $CPPFLAGS'
2899 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
2900 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
2901 ac_compiler_gnu=$ac_cv_c_compiler_gnu
2902 DEPDIR="${am__leading_dot}deps"
2903
2904 ac_config_commands="$ac_config_commands depfiles"
2905
2906
2907 am_make=${MAKE-make}
2908 cat > confinc << 'END'
2909 am__doit:
2910 @echo done
2911 .PHONY: am__doit
2912 END
2913 # If we don't find an include directive, just comment out the code.
2914 echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5
2915 echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6
2916 am__include="#"
2917 am__quote=
2918 _am_result=none
2919 # First try GNU make style include.
2920 echo "include confinc" > confmf
2921 # We grep out `Entering directory' and `Leaving directory'
2922 # messages which can occur if `w' ends up in MAKEFLAGS.
2923 # In particular we don't look at `^make:' because GNU make might
2924 # be invoked under some other name (usually "gmake"), in which
2925 # case it prints its new name instead of `make'.
2926 if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
2927 am__include=include
2928 am__quote=
2929 _am_result=GNU
2930 fi
2931 # Now try BSD make style include.
2932 if test "$am__include" = "#"; then
2933 echo '.include "confinc"' > confmf
2934 if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
2935 am__include=.include
2936 am__quote="\""
2937 _am_result=BSD
2938 fi
2939 fi
2940
2941
2942 echo "$as_me:$LINENO: result: $_am_result" >&5
2943 echo "${ECHO_T}$_am_result" >&6
2944 rm -f confinc confmf
2945
2946 # Check whether --enable-dependency-tracking or --disable-dependency-tracking was given.
2947 if test "${enable_dependency_tracking+set}" = set; then
2948 enableval="$enable_dependency_tracking"
2949
2950 fi;
2951 if test "x$enable_dependency_tracking" != xno; then
2952 am_depcomp="$ac_aux_dir/depcomp"
2953 AMDEPBACKSLASH='\'
2954 fi
2955
2956
2957 if test "x$enable_dependency_tracking" != xno; then
2958 AMDEP_TRUE=
2959 AMDEP_FALSE='#'
2960 else
2961 AMDEP_TRUE='#'
2962 AMDEP_FALSE=
2963 fi
2964
2965
2966
2967
2968 depcc="$CC" am_compiler_list=
2969
2970 echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
2971 echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6
2972 if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
2973 echo $ECHO_N "(cached) $ECHO_C" >&6
2974 else
2975 if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
2976 # We make a subdir and do the tests there. Otherwise we can end up
2977 # making bogus files that we don't know about and never remove. For
2978 # instance it was reported that on HP-UX the gcc test will end up
2979 # making a dummy file named `D' -- because `-MD' means `put the output
2980 # in D'.
2981 mkdir conftest.dir
2982 # Copy depcomp to subdir because otherwise we won't find it if we're
2983 # using a relative directory.
2984 cp "$am_depcomp" conftest.dir
2985 cd conftest.dir
2986 # We will build objects and dependencies in a subdirectory because
2987 # it helps to detect inapplicable dependency modes. For instance
2988 # both Tru64's cc and ICC support -MD to output dependencies as a
2989 # side effect of compilation, but ICC will put the dependencies in
2990 # the current directory while Tru64 will put them in the object
2991 # directory.
2992 mkdir sub
2993
2994 am_cv_CC_dependencies_compiler_type=none
2995 if test "$am_compiler_list" = ""; then
2996 am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
2997 fi
2998 for depmode in $am_compiler_list; do
2999 # Setup a source with many dependencies, because some compilers
3000 # like to wrap large dependency lists on column 80 (with \), and
3001 # we should not choose a depcomp mode which is confused by this.
3002 #
3003 # We need to recreate these files for each test, as the compiler may
3004 # overwrite some of them when testing with obscure command lines.
3005 # This happens at least with the AIX C compiler.
3006 : > sub/conftest.c
3007 for i in 1 2 3 4 5 6; do
3008 echo '#include "conftst'$i'.h"' >> sub/conftest.c
3009 # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
3010 # Solaris 8's {/usr,}/bin/sh.
3011 touch sub/conftst$i.h
3012 done
3013 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
3014
3015 case $depmode in
3016 nosideeffect)
3017 # after this tag, mechanisms are not by side-effect, so they'll
3018 # only be used when explicitly requested
3019 if test "x$enable_dependency_tracking" = xyes; then
3020 continue
3021 else
3022 break
3023 fi
3024 ;;
3025 none) break ;;
3026 esac
3027 # We check with `-c' and `-o' for the sake of the "dashmstdout"
3028 # mode. It turns out that the SunPro C++ compiler does not properly
3029 # handle `-M -o', and we need to detect this.
3030 if depmode=$depmode \
3031 source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
3032 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
3033 $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
3034 >/dev/null 2>conftest.err &&
3035 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
3036 grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
3037 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
3038 # icc doesn't choke on unknown options, it will just issue warnings
3039 # or remarks (even with -Werror). So we grep stderr for any message
3040 # that says an option was ignored or not supported.
3041 # When given -MP, icc 7.0 and 7.1 complain thusly:
3042 # icc: Command line warning: ignoring option '-M'; no argument required
3043 # The diagnosis changed in icc 8.0:
3044 # icc: Command line remark: option '-MP' not supported
3045 if (grep 'ignoring option' conftest.err ||
3046 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
3047 am_cv_CC_dependencies_compiler_type=$depmode
3048 break
3049 fi
3050 fi
3051 done
3052
3053 cd ..
3054 rm -rf conftest.dir
3055 else
3056 am_cv_CC_dependencies_compiler_type=none
3057 fi
3058
3059 fi
3060 echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
3061 echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6
3062 CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
3063
3064
3065
3066 if
3067 test "x$enable_dependency_tracking" != xno \
3068 && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
3069 am__fastdepCC_TRUE=
3070 am__fastdepCC_FALSE='#'
3071 else
3072 am__fastdepCC_TRUE='#'
3073 am__fastdepCC_FALSE=
3074 fi
3075
3076
3077 ac_ext=c
3078 ac_cpp='$CPP $CPPFLAGS'
3079 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
3080 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
3081 ac_compiler_gnu=$ac_cv_c_compiler_gnu
3082 if test -n "$ac_tool_prefix"; then
3083 # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
3084 set dummy ${ac_tool_prefix}gcc; ac_word=$2
3085 echo "$as_me:$LINENO: checking for $ac_word" >&5
3086 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3087 if test "${ac_cv_prog_CC+set}" = set; then
3088 echo $ECHO_N "(cached) $ECHO_C" >&6
3089 else
3090 if test -n "$CC"; then
3091 ac_cv_prog_CC="$CC" # Let the user override the test.
3092 else
3093 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3094 for as_dir in $PATH
3095 do
3096 IFS=$as_save_IFS
3097 test -z "$as_dir" && as_dir=.
3098 for ac_exec_ext in '' $ac_executable_extensions; do
3099 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3100 ac_cv_prog_CC="${ac_tool_prefix}gcc"
3101 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3102 break 2
3103 fi
3104 done
3105 done
3106
3107 fi
3108 fi
3109 CC=$ac_cv_prog_CC
3110 if test -n "$CC"; then
3111 echo "$as_me:$LINENO: result: $CC" >&5
3112 echo "${ECHO_T}$CC" >&6
3113 else
3114 echo "$as_me:$LINENO: result: no" >&5
3115 echo "${ECHO_T}no" >&6
3116 fi
3117
3118 fi
3119 if test -z "$ac_cv_prog_CC"; then
3120 ac_ct_CC=$CC
3121 # Extract the first word of "gcc", so it can be a program name with args.
3122 set dummy gcc; ac_word=$2
3123 echo "$as_me:$LINENO: checking for $ac_word" >&5
3124 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3125 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
3126 echo $ECHO_N "(cached) $ECHO_C" >&6
3127 else
3128 if test -n "$ac_ct_CC"; then
3129 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
3130 else
3131 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3132 for as_dir in $PATH
3133 do
3134 IFS=$as_save_IFS
3135 test -z "$as_dir" && as_dir=.
3136 for ac_exec_ext in '' $ac_executable_extensions; do
3137 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3138 ac_cv_prog_ac_ct_CC="gcc"
3139 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3140 break 2
3141 fi
3142 done
3143 done
3144
3145 fi
3146 fi
3147 ac_ct_CC=$ac_cv_prog_ac_ct_CC
3148 if test -n "$ac_ct_CC"; then
3149 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
3150 echo "${ECHO_T}$ac_ct_CC" >&6
3151 else
3152 echo "$as_me:$LINENO: result: no" >&5
3153 echo "${ECHO_T}no" >&6
3154 fi
3155
3156 CC=$ac_ct_CC
3157 else
3158 CC="$ac_cv_prog_CC"
3159 fi
3160
3161 if test -z "$CC"; then
3162 if test -n "$ac_tool_prefix"; then
3163 # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
3164 set dummy ${ac_tool_prefix}cc; ac_word=$2
3165 echo "$as_me:$LINENO: checking for $ac_word" >&5
3166 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3167 if test "${ac_cv_prog_CC+set}" = set; then
3168 echo $ECHO_N "(cached) $ECHO_C" >&6
3169 else
3170 if test -n "$CC"; then
3171 ac_cv_prog_CC="$CC" # Let the user override the test.
3172 else
3173 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3174 for as_dir in $PATH
3175 do
3176 IFS=$as_save_IFS
3177 test -z "$as_dir" && as_dir=.
3178 for ac_exec_ext in '' $ac_executable_extensions; do
3179 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3180 ac_cv_prog_CC="${ac_tool_prefix}cc"
3181 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3182 break 2
3183 fi
3184 done
3185 done
3186
3187 fi
3188 fi
3189 CC=$ac_cv_prog_CC
3190 if test -n "$CC"; then
3191 echo "$as_me:$LINENO: result: $CC" >&5
3192 echo "${ECHO_T}$CC" >&6
3193 else
3194 echo "$as_me:$LINENO: result: no" >&5
3195 echo "${ECHO_T}no" >&6
3196 fi
3197
3198 fi
3199 if test -z "$ac_cv_prog_CC"; then
3200 ac_ct_CC=$CC
3201 # Extract the first word of "cc", so it can be a program name with args.
3202 set dummy cc; ac_word=$2
3203 echo "$as_me:$LINENO: checking for $ac_word" >&5
3204 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3205 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
3206 echo $ECHO_N "(cached) $ECHO_C" >&6
3207 else
3208 if test -n "$ac_ct_CC"; then
3209 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
3210 else
3211 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3212 for as_dir in $PATH
3213 do
3214 IFS=$as_save_IFS
3215 test -z "$as_dir" && as_dir=.
3216 for ac_exec_ext in '' $ac_executable_extensions; do
3217 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3218 ac_cv_prog_ac_ct_CC="cc"
3219 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3220 break 2
3221 fi
3222 done
3223 done
3224
3225 fi
3226 fi
3227 ac_ct_CC=$ac_cv_prog_ac_ct_CC
3228 if test -n "$ac_ct_CC"; then
3229 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
3230 echo "${ECHO_T}$ac_ct_CC" >&6
3231 else
3232 echo "$as_me:$LINENO: result: no" >&5
3233 echo "${ECHO_T}no" >&6
3234 fi
3235
3236 CC=$ac_ct_CC
3237 else
3238 CC="$ac_cv_prog_CC"
3239 fi
3240
3241 fi
3242 if test -z "$CC"; then
3243 # Extract the first word of "cc", so it can be a program name with args.
3244 set dummy cc; ac_word=$2
3245 echo "$as_me:$LINENO: checking for $ac_word" >&5
3246 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3247 if test "${ac_cv_prog_CC+set}" = set; then
3248 echo $ECHO_N "(cached) $ECHO_C" >&6
3249 else
3250 if test -n "$CC"; then
3251 ac_cv_prog_CC="$CC" # Let the user override the test.
3252 else
3253 ac_prog_rejected=no
3254 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3255 for as_dir in $PATH
3256 do
3257 IFS=$as_save_IFS
3258 test -z "$as_dir" && as_dir=.
3259 for ac_exec_ext in '' $ac_executable_extensions; do
3260 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3261 if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
3262 ac_prog_rejected=yes
3263 continue
3264 fi
3265 ac_cv_prog_CC="cc"
3266 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3267 break 2
3268 fi
3269 done
3270 done
3271
3272 if test $ac_prog_rejected = yes; then
3273 # We found a bogon in the path, so make sure we never use it.
3274 set dummy $ac_cv_prog_CC
3275 shift
3276 if test $# != 0; then
3277 # We chose a different compiler from the bogus one.
3278 # However, it has the same basename, so the bogon will be chosen
3279 # first if we set CC to just the basename; use the full file name.
3280 shift
3281 ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
3282 fi
3283 fi
3284 fi
3285 fi
3286 CC=$ac_cv_prog_CC
3287 if test -n "$CC"; then
3288 echo "$as_me:$LINENO: result: $CC" >&5
3289 echo "${ECHO_T}$CC" >&6
3290 else
3291 echo "$as_me:$LINENO: result: no" >&5
3292 echo "${ECHO_T}no" >&6
3293 fi
3294
3295 fi
3296 if test -z "$CC"; then
3297 if test -n "$ac_tool_prefix"; then
3298 for ac_prog in cl
3299 do
3300 # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
3301 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
3302 echo "$as_me:$LINENO: checking for $ac_word" >&5
3303 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3304 if test "${ac_cv_prog_CC+set}" = set; then
3305 echo $ECHO_N "(cached) $ECHO_C" >&6
3306 else
3307 if test -n "$CC"; then
3308 ac_cv_prog_CC="$CC" # Let the user override the test.
3309 else
3310 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3311 for as_dir in $PATH
3312 do
3313 IFS=$as_save_IFS
3314 test -z "$as_dir" && as_dir=.
3315 for ac_exec_ext in '' $ac_executable_extensions; do
3316 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3317 ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
3318 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3319 break 2
3320 fi
3321 done
3322 done
3323
3324 fi
3325 fi
3326 CC=$ac_cv_prog_CC
3327 if test -n "$CC"; then
3328 echo "$as_me:$LINENO: result: $CC" >&5
3329 echo "${ECHO_T}$CC" >&6
3330 else
3331 echo "$as_me:$LINENO: result: no" >&5
3332 echo "${ECHO_T}no" >&6
3333 fi
3334
3335 test -n "$CC" && break
3336 done
3337 fi
3338 if test -z "$CC"; then
3339 ac_ct_CC=$CC
3340 for ac_prog in cl
3341 do
3342 # Extract the first word of "$ac_prog", so it can be a program name with args.
3343 set dummy $ac_prog; ac_word=$2
3344 echo "$as_me:$LINENO: checking for $ac_word" >&5
3345 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3346 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
3347 echo $ECHO_N "(cached) $ECHO_C" >&6
3348 else
3349 if test -n "$ac_ct_CC"; then
3350 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
3351 else
3352 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3353 for as_dir in $PATH
3354 do
3355 IFS=$as_save_IFS
3356 test -z "$as_dir" && as_dir=.
3357 for ac_exec_ext in '' $ac_executable_extensions; do
3358 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3359 ac_cv_prog_ac_ct_CC="$ac_prog"
3360 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3361 break 2
3362 fi
3363 done
3364 done
3365
3366 fi
3367 fi
3368 ac_ct_CC=$ac_cv_prog_ac_ct_CC
3369 if test -n "$ac_ct_CC"; then
3370 echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
3371 echo "${ECHO_T}$ac_ct_CC" >&6
3372 else
3373 echo "$as_me:$LINENO: result: no" >&5
3374 echo "${ECHO_T}no" >&6
3375 fi
3376
3377 test -n "$ac_ct_CC" && break
3378 done
3379
3380 CC=$ac_ct_CC
3381 fi
3382
3383 fi
3384
3385
3386 test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
3387 See \`config.log' for more details." >&5
3388 echo "$as_me: error: no acceptable C compiler found in \$PATH
3389 See \`config.log' for more details." >&2;}
3390 { (exit 1); exit 1; }; }
3391
3392 # Provide some information about the compiler.
3393 echo "$as_me:$LINENO:" \
3394 "checking for C compiler version" >&5
3395 ac_compiler=`set X $ac_compile; echo $2`
3396 { (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
3397 (eval $ac_compiler --version </dev/null >&5) 2>&5
3398 ac_status=$?
3399 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3400 (exit $ac_status); }
3401 { (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
3402 (eval $ac_compiler -v </dev/null >&5) 2>&5
3403 ac_status=$?
3404 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3405 (exit $ac_status); }
3406 { (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
3407 (eval $ac_compiler -V </dev/null >&5) 2>&5
3408 ac_status=$?
3409 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3410 (exit $ac_status); }
3411
3412 echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
3413 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
3414 if test "${ac_cv_c_compiler_gnu+set}" = set; then
3415 echo $ECHO_N "(cached) $ECHO_C" >&6
3416 else
3417 cat >conftest.$ac_ext <<_ACEOF
3418 /* confdefs.h. */
3419 _ACEOF
3420 cat confdefs.h >>conftest.$ac_ext
3421 cat >>conftest.$ac_ext <<_ACEOF
3422 /* end confdefs.h. */
3423
3424 int
3425 main ()
3426 {
3427 #ifndef __GNUC__
3428 choke me
3429 #endif
3430
3431 ;
3432 return 0;
3433 }
3434 _ACEOF
3435 rm -f conftest.$ac_objext
3436 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3437 (eval $ac_compile) 2>conftest.er1
3438 ac_status=$?
3439 grep -v '^ *+' conftest.er1 >conftest.err
3440 rm -f conftest.er1
3441 cat conftest.err >&5
3442 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3443 (exit $ac_status); } &&
3444 { ac_try='test -z "$ac_c_werror_flag"
3445 || test ! -s conftest.err'
3446 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3447 (eval $ac_try) 2>&5
3448 ac_status=$?
3449 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3450 (exit $ac_status); }; } &&
3451 { ac_try='test -s conftest.$ac_objext'
3452 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3453 (eval $ac_try) 2>&5
3454 ac_status=$?
3455 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3456 (exit $ac_status); }; }; then
3457 ac_compiler_gnu=yes
3458 else
3459 echo "$as_me: failed program was:" >&5
3460 sed 's/^/| /' conftest.$ac_ext >&5
3461
3462 ac_compiler_gnu=no
3463 fi
3464 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
3465 ac_cv_c_compiler_gnu=$ac_compiler_gnu
3466
3467 fi
3468 echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
3469 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
3470 GCC=`test $ac_compiler_gnu = yes && echo yes`
3471 ac_test_CFLAGS=${CFLAGS+set}
3472 ac_save_CFLAGS=$CFLAGS
3473 CFLAGS="-g"
3474 echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
3475 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
3476 if test "${ac_cv_prog_cc_g+set}" = set; then
3477 echo $ECHO_N "(cached) $ECHO_C" >&6
3478 else
3479 cat >conftest.$ac_ext <<_ACEOF
3480 /* confdefs.h. */
3481 _ACEOF
3482 cat confdefs.h >>conftest.$ac_ext
3483 cat >>conftest.$ac_ext <<_ACEOF
3484 /* end confdefs.h. */
3485
3486 int
3487 main ()
3488 {
3489
3490 ;
3491 return 0;
3492 }
3493 _ACEOF
3494 rm -f conftest.$ac_objext
3495 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3496 (eval $ac_compile) 2>conftest.er1
3497 ac_status=$?
3498 grep -v '^ *+' conftest.er1 >conftest.err
3499 rm -f conftest.er1
3500 cat conftest.err >&5
3501 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3502 (exit $ac_status); } &&
3503 { ac_try='test -z "$ac_c_werror_flag"
3504 || test ! -s conftest.err'
3505 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3506 (eval $ac_try) 2>&5
3507 ac_status=$?
3508 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3509 (exit $ac_status); }; } &&
3510 { ac_try='test -s conftest.$ac_objext'
3511 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3512 (eval $ac_try) 2>&5
3513 ac_status=$?
3514 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3515 (exit $ac_status); }; }; then
3516 ac_cv_prog_cc_g=yes
3517 else
3518 echo "$as_me: failed program was:" >&5
3519 sed 's/^/| /' conftest.$ac_ext >&5
3520
3521 ac_cv_prog_cc_g=no
3522 fi
3523 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
3524 fi
3525 echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
3526 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
3527 if test "$ac_test_CFLAGS" = set; then
3528 CFLAGS=$ac_save_CFLAGS
3529 elif test $ac_cv_prog_cc_g = yes; then
3530 if test "$GCC" = yes; then
3531 CFLAGS="-g -O2"
3532 else
3533 CFLAGS="-g"
3534 fi
3535 else
3536 if test "$GCC" = yes; then
3537 CFLAGS="-O2"
3538 else
3539 CFLAGS=
3540 fi
3541 fi
3542 echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
3543 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
3544 if test "${ac_cv_prog_cc_stdc+set}" = set; then
3545 echo $ECHO_N "(cached) $ECHO_C" >&6
3546 else
3547 ac_cv_prog_cc_stdc=no
3548 ac_save_CC=$CC
3549 cat >conftest.$ac_ext <<_ACEOF
3550 /* confdefs.h. */
3551 _ACEOF
3552 cat confdefs.h >>conftest.$ac_ext
3553 cat >>conftest.$ac_ext <<_ACEOF
3554 /* end confdefs.h. */
3555 #include <stdarg.h>
3556 #include <stdio.h>
3557 #include <sys/types.h>
3558 #include <sys/stat.h>
3559 /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
3560 struct buf { int x; };
3561 FILE * (*rcsopen) (struct buf *, struct stat *, int);
3562 static char *e (p, i)
3563 char **p;
3564 int i;
3565 {
3566 return p[i];
3567 }
3568 static char *f (char * (*g) (char **, int), char **p, ...)
3569 {
3570 char *s;
3571 va_list v;
3572 va_start (v,p);
3573 s = g (p, va_arg (v,int));
3574 va_end (v);
3575 return s;
3576 }
3577
3578 /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
3579 function prototypes and stuff, but not '\xHH' hex character constants.
3580 These don't provoke an error unfortunately, instead are silently treated
3581 as 'x'. The following induces an error, until -std1 is added to get
3582 proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
3583 array size at least. It's necessary to write '\x00'==0 to get something
3584 that's true only with -std1. */
3585 int osf4_cc_array ['\x00' == 0 ? 1 : -1];
3586
3587 int test (int i, double x);
3588 struct s1 {int (*f) (int a);};
3589 struct s2 {int (*f) (double a);};
3590 int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
3591 int argc;
3592 char **argv;
3593 int
3594 main ()
3595 {
3596 return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
3597 ;
3598 return 0;
3599 }
3600 _ACEOF
3601 # Don't try gcc -ansi; that turns off useful extensions and
3602 # breaks some systems' header files.
3603 # AIX -qlanglvl=ansi
3604 # Ultrix and OSF/1 -std1
3605 # HP-UX 10.20 and later -Ae
3606 # HP-UX older versions -Aa -D_HPUX_SOURCE
3607 # SVR4 -Xc -D__EXTENSIONS__
3608 for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
3609 do
3610 CC="$ac_save_CC $ac_arg"
3611 rm -f conftest.$ac_objext
3612 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3613 (eval $ac_compile) 2>conftest.er1
3614 ac_status=$?
3615 grep -v '^ *+' conftest.er1 >conftest.err
3616 rm -f conftest.er1
3617 cat conftest.err >&5
3618 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3619 (exit $ac_status); } &&
3620 { ac_try='test -z "$ac_c_werror_flag"
3621 || test ! -s conftest.err'
3622 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3623 (eval $ac_try) 2>&5
3624 ac_status=$?
3625 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3626 (exit $ac_status); }; } &&
3627 { ac_try='test -s conftest.$ac_objext'
3628 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3629 (eval $ac_try) 2>&5
3630 ac_status=$?
3631 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3632 (exit $ac_status); }; }; then
3633 ac_cv_prog_cc_stdc=$ac_arg
3634 break
3635 else
3636 echo "$as_me: failed program was:" >&5
3637 sed 's/^/| /' conftest.$ac_ext >&5
3638
3639 fi
3640 rm -f conftest.err conftest.$ac_objext
3641 done
3642 rm -f conftest.$ac_ext conftest.$ac_objext
3643 CC=$ac_save_CC
3644
3645 fi
3646
3647 case "x$ac_cv_prog_cc_stdc" in
3648 x|xno)
3649 echo "$as_me:$LINENO: result: none needed" >&5
3650 echo "${ECHO_T}none needed" >&6 ;;
3651 *)
3652 echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
3653 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
3654 CC="$CC $ac_cv_prog_cc_stdc" ;;
3655 esac
3656
3657 # Some people use a C++ compiler to compile C. Since we use `exit',
3658 # in C++ we need to declare it. In case someone uses the same compiler
3659 # for both compiling C and C++ we need to have the C++ compiler decide
3660 # the declaration of exit, since it's the most demanding environment.
3661 cat >conftest.$ac_ext <<_ACEOF
3662 #ifndef __cplusplus
3663 choke me
3664 #endif
3665 _ACEOF
3666 rm -f conftest.$ac_objext
3667 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3668 (eval $ac_compile) 2>conftest.er1
3669 ac_status=$?
3670 grep -v '^ *+' conftest.er1 >conftest.err
3671 rm -f conftest.er1
3672 cat conftest.err >&5
3673 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3674 (exit $ac_status); } &&
3675 { ac_try='test -z "$ac_c_werror_flag"
3676 || test ! -s conftest.err'
3677 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3678 (eval $ac_try) 2>&5
3679 ac_status=$?
3680 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3681 (exit $ac_status); }; } &&
3682 { ac_try='test -s conftest.$ac_objext'
3683 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3684 (eval $ac_try) 2>&5
3685 ac_status=$?
3686 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3687 (exit $ac_status); }; }; then
3688 for ac_declaration in \
3689 '' \
3690 'extern "C" void std::exit (int) throw (); using std::exit;' \
3691 'extern "C" void std::exit (int); using std::exit;' \
3692 'extern "C" void exit (int) throw ();' \
3693 'extern "C" void exit (int);' \
3694 'void exit (int);'
3695 do
3696 cat >conftest.$ac_ext <<_ACEOF
3697 /* confdefs.h. */
3698 _ACEOF
3699 cat confdefs.h >>conftest.$ac_ext
3700 cat >>conftest.$ac_ext <<_ACEOF
3701 /* end confdefs.h. */
3702 $ac_declaration
3703 #include <stdlib.h>
3704 int
3705 main ()
3706 {
3707 exit (42);
3708 ;
3709 return 0;
3710 }
3711 _ACEOF
3712 rm -f conftest.$ac_objext
3713 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3714 (eval $ac_compile) 2>conftest.er1
3715 ac_status=$?
3716 grep -v '^ *+' conftest.er1 >conftest.err
3717 rm -f conftest.er1
3718 cat conftest.err >&5
3719 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3720 (exit $ac_status); } &&
3721 { ac_try='test -z "$ac_c_werror_flag"
3722 || test ! -s conftest.err'
3723 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3724 (eval $ac_try) 2>&5
3725 ac_status=$?
3726 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3727 (exit $ac_status); }; } &&
3728 { ac_try='test -s conftest.$ac_objext'
3729 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3730 (eval $ac_try) 2>&5
3731 ac_status=$?
3732 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3733 (exit $ac_status); }; }; then
3734 :
3735 else
3736 echo "$as_me: failed program was:" >&5
3737 sed 's/^/| /' conftest.$ac_ext >&5
3738
3739 continue
3740 fi
3741 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
3742 cat >conftest.$ac_ext <<_ACEOF
3743 /* confdefs.h. */
3744 _ACEOF
3745 cat confdefs.h >>conftest.$ac_ext
3746 cat >>conftest.$ac_ext <<_ACEOF
3747 /* end confdefs.h. */
3748 $ac_declaration
3749 int
3750 main ()
3751 {
3752 exit (42);
3753 ;
3754 return 0;
3755 }
3756 _ACEOF
3757 rm -f conftest.$ac_objext
3758 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
3759 (eval $ac_compile) 2>conftest.er1
3760 ac_status=$?
3761 grep -v '^ *+' conftest.er1 >conftest.err
3762 rm -f conftest.er1
3763 cat conftest.err >&5
3764 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3765 (exit $ac_status); } &&
3766 { ac_try='test -z "$ac_c_werror_flag"
3767 || test ! -s conftest.err'
3768 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3769 (eval $ac_try) 2>&5
3770 ac_status=$?
3771 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3772 (exit $ac_status); }; } &&
3773 { ac_try='test -s conftest.$ac_objext'
3774 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
3775 (eval $ac_try) 2>&5
3776 ac_status=$?
3777 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3778 (exit $ac_status); }; }; then
3779 break
3780 else
3781 echo "$as_me: failed program was:" >&5
3782 sed 's/^/| /' conftest.$ac_ext >&5
3783
3784 fi
3785 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
3786 done
3787 rm -f conftest*
3788 if test -n "$ac_declaration"; then
3789 echo '#ifdef __cplusplus' >>confdefs.h
3790 echo $ac_declaration >>confdefs.h
3791 echo '#endif' >>confdefs.h
3792 fi
3793
3794 else
3795 echo "$as_me: failed program was:" >&5
3796 sed 's/^/| /' conftest.$ac_ext >&5
3797
3798 fi
3799 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
3800 ac_ext=c
3801 ac_cpp='$CPP $CPPFLAGS'
3802 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
3803 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
3804 ac_compiler_gnu=$ac_cv_c_compiler_gnu
3805
3806 depcc="$CC" am_compiler_list=
3807
3808 echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
3809 echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6
3810 if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
3811 echo $ECHO_N "(cached) $ECHO_C" >&6
3812 else
3813 if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
3814 # We make a subdir and do the tests there. Otherwise we can end up
3815 # making bogus files that we don't know about and never remove. For
3816 # instance it was reported that on HP-UX the gcc test will end up
3817 # making a dummy file named `D' -- because `-MD' means `put the output
3818 # in D'.
3819 mkdir conftest.dir
3820 # Copy depcomp to subdir because otherwise we won't find it if we're
3821 # using a relative directory.
3822 cp "$am_depcomp" conftest.dir
3823 cd conftest.dir
3824 # We will build objects and dependencies in a subdirectory because
3825 # it helps to detect inapplicable dependency modes. For instance
3826 # both Tru64's cc and ICC support -MD to output dependencies as a
3827 # side effect of compilation, but ICC will put the dependencies in
3828 # the current directory while Tru64 will put them in the object
3829 # directory.
3830 mkdir sub
3831
3832 am_cv_CC_dependencies_compiler_type=none
3833 if test "$am_compiler_list" = ""; then
3834 am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
3835 fi
3836 for depmode in $am_compiler_list; do
3837 # Setup a source with many dependencies, because some compilers
3838 # like to wrap large dependency lists on column 80 (with \), and
3839 # we should not choose a depcomp mode which is confused by this.
3840 #
3841 # We need to recreate these files for each test, as the compiler may
3842 # overwrite some of them when testing with obscure command lines.
3843 # This happens at least with the AIX C compiler.
3844 : > sub/conftest.c
3845 for i in 1 2 3 4 5 6; do
3846 echo '#include "conftst'$i'.h"' >> sub/conftest.c
3847 # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
3848 # Solaris 8's {/usr,}/bin/sh.
3849 touch sub/conftst$i.h
3850 done
3851 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
3852
3853 case $depmode in
3854 nosideeffect)
3855 # after this tag, mechanisms are not by side-effect, so they'll
3856 # only be used when explicitly requested
3857 if test "x$enable_dependency_tracking" = xyes; then
3858 continue
3859 else
3860 break
3861 fi
3862 ;;
3863 none) break ;;
3864 esac
3865 # We check with `-c' and `-o' for the sake of the "dashmstdout"
3866 # mode. It turns out that the SunPro C++ compiler does not properly
3867 # handle `-M -o', and we need to detect this.
3868 if depmode=$depmode \
3869 source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
3870 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
3871 $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
3872 >/dev/null 2>conftest.err &&
3873 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
3874 grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
3875 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
3876 # icc doesn't choke on unknown options, it will just issue warnings
3877 # or remarks (even with -Werror). So we grep stderr for any message
3878 # that says an option was ignored or not supported.
3879 # When given -MP, icc 7.0 and 7.1 complain thusly:
3880 # icc: Command line warning: ignoring option '-M'; no argument required
3881 # The diagnosis changed in icc 8.0:
3882 # icc: Command line remark: option '-MP' not supported
3883 if (grep 'ignoring option' conftest.err ||
3884 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
3885 am_cv_CC_dependencies_compiler_type=$depmode
3886 break
3887 fi
3888 fi
3889 done
3890
3891 cd ..
3892 rm -rf conftest.dir
3893 else
3894 am_cv_CC_dependencies_compiler_type=none
3895 fi
3896
3897 fi
3898 echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
3899 echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6
3900 CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
3901
3902
3903
3904 if
3905 test "x$enable_dependency_tracking" != xno \
3906 && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
3907 am__fastdepCC_TRUE=
3908 am__fastdepCC_FALSE='#'
3909 else
3910 am__fastdepCC_TRUE='#'
3911 am__fastdepCC_FALSE=
3912 fi
3913
3914
3915
3916 am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc
3917
3918 if test -n "$ac_tool_prefix"; then
3919 # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
3920 set dummy ${ac_tool_prefix}ranlib; ac_word=$2
3921 echo "$as_me:$LINENO: checking for $ac_word" >&5
3922 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3923 if test "${ac_cv_prog_RANLIB+set}" = set; then
3924 echo $ECHO_N "(cached) $ECHO_C" >&6
3925 else
3926 if test -n "$RANLIB"; then
3927 ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
3928 else
3929 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3930 for as_dir in $PATH
3931 do
3932 IFS=$as_save_IFS
3933 test -z "$as_dir" && as_dir=.
3934 for ac_exec_ext in '' $ac_executable_extensions; do
3935 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3936 ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
3937 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3938 break 2
3939 fi
3940 done
3941 done
3942
3943 fi
3944 fi
3945 RANLIB=$ac_cv_prog_RANLIB
3946 if test -n "$RANLIB"; then
3947 echo "$as_me:$LINENO: result: $RANLIB" >&5
3948 echo "${ECHO_T}$RANLIB" >&6
3949 else
3950 echo "$as_me:$LINENO: result: no" >&5
3951 echo "${ECHO_T}no" >&6
3952 fi
3953
3954 fi
3955 if test -z "$ac_cv_prog_RANLIB"; then
3956 ac_ct_RANLIB=$RANLIB
3957 # Extract the first word of "ranlib", so it can be a program name with args.
3958 set dummy ranlib; ac_word=$2
3959 echo "$as_me:$LINENO: checking for $ac_word" >&5
3960 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
3961 if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
3962 echo $ECHO_N "(cached) $ECHO_C" >&6
3963 else
3964 if test -n "$ac_ct_RANLIB"; then
3965 ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
3966 else
3967 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3968 for as_dir in $PATH
3969 do
3970 IFS=$as_save_IFS
3971 test -z "$as_dir" && as_dir=.
3972 for ac_exec_ext in '' $ac_executable_extensions; do
3973 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3974 ac_cv_prog_ac_ct_RANLIB="ranlib"
3975 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
3976 break 2
3977 fi
3978 done
3979 done
3980
3981 test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
3982 fi
3983 fi
3984 ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
3985 if test -n "$ac_ct_RANLIB"; then
3986 echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
3987 echo "${ECHO_T}$ac_ct_RANLIB" >&6
3988 else
3989 echo "$as_me:$LINENO: result: no" >&5
3990 echo "${ECHO_T}no" >&6
3991 fi
3992
3993 RANLIB=$ac_ct_RANLIB
3994 else
3995 RANLIB="$ac_cv_prog_RANLIB"
3996 fi
3997
3998 ac_ext=c
3999 ac_cpp='$CPP $CPPFLAGS'
4000 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4001 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4002 ac_compiler_gnu=$ac_cv_c_compiler_gnu
4003 echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
4004 echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
4005 # On Suns, sometimes $CPP names a directory.
4006 if test -n "$CPP" && test -d "$CPP"; then
4007 CPP=
4008 fi
4009 if test -z "$CPP"; then
4010 if test "${ac_cv_prog_CPP+set}" = set; then
4011 echo $ECHO_N "(cached) $ECHO_C" >&6
4012 else
4013 # Double quotes because CPP needs to be expanded
4014 for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
4015 do
4016 ac_preproc_ok=false
4017 for ac_c_preproc_warn_flag in '' yes
4018 do
4019 # Use a header file that comes with gcc, so configuring glibc
4020 # with a fresh cross-compiler works.
4021 # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
4022 # <limits.h> exists even on freestanding compilers.
4023 # On the NeXT, cc -E runs the code through the compiler's parser,
4024 # not just through cpp. "Syntax error" is here to catch this case.
4025 cat >conftest.$ac_ext <<_ACEOF
4026 /* confdefs.h. */
4027 _ACEOF
4028 cat confdefs.h >>conftest.$ac_ext
4029 cat >>conftest.$ac_ext <<_ACEOF
4030 /* end confdefs.h. */
4031 #ifdef __STDC__
4032 # include <limits.h>
4033 #else
4034 # include <assert.h>
4035 #endif
4036 Syntax error
4037 _ACEOF
4038 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
4039 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
4040 ac_status=$?
4041 grep -v '^ *+' conftest.er1 >conftest.err
4042 rm -f conftest.er1
4043 cat conftest.err >&5
4044 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4045 (exit $ac_status); } >/dev/null; then
4046 if test -s conftest.err; then
4047 ac_cpp_err=$ac_c_preproc_warn_flag
4048 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
4049 else
4050 ac_cpp_err=
4051 fi
4052 else
4053 ac_cpp_err=yes
4054 fi
4055 if test -z "$ac_cpp_err"; then
4056 :
4057 else
4058 echo "$as_me: failed program was:" >&5
4059 sed 's/^/| /' conftest.$ac_ext >&5
4060
4061 # Broken: fails on valid input.
4062 continue
4063 fi
4064 rm -f conftest.err conftest.$ac_ext
4065
4066 # OK, works on sane cases. Now check whether non-existent headers
4067 # can be detected and how.
4068 cat >conftest.$ac_ext <<_ACEOF
4069 /* confdefs.h. */
4070 _ACEOF
4071 cat confdefs.h >>conftest.$ac_ext
4072 cat >>conftest.$ac_ext <<_ACEOF
4073 /* end confdefs.h. */
4074 #include <ac_nonexistent.h>
4075 _ACEOF
4076 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
4077 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
4078 ac_status=$?
4079 grep -v '^ *+' conftest.er1 >conftest.err
4080 rm -f conftest.er1
4081 cat conftest.err >&5
4082 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4083 (exit $ac_status); } >/dev/null; then
4084 if test -s conftest.err; then
4085 ac_cpp_err=$ac_c_preproc_warn_flag
4086 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
4087 else
4088 ac_cpp_err=
4089 fi
4090 else
4091 ac_cpp_err=yes
4092 fi
4093 if test -z "$ac_cpp_err"; then
4094 # Broken: success on invalid input.
4095 continue
4096 else
4097 echo "$as_me: failed program was:" >&5
4098 sed 's/^/| /' conftest.$ac_ext >&5
4099
4100 # Passes both tests.
4101 ac_preproc_ok=:
4102 break
4103 fi
4104 rm -f conftest.err conftest.$ac_ext
4105
4106 done
4107 # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
4108 rm -f conftest.err conftest.$ac_ext
4109 if $ac_preproc_ok; then
4110 break
4111 fi
4112
4113 done
4114 ac_cv_prog_CPP=$CPP
4115
4116 fi
4117 CPP=$ac_cv_prog_CPP
4118 else
4119 ac_cv_prog_CPP=$CPP
4120 fi
4121 echo "$as_me:$LINENO: result: $CPP" >&5
4122 echo "${ECHO_T}$CPP" >&6
4123 ac_preproc_ok=false
4124 for ac_c_preproc_warn_flag in '' yes
4125 do
4126 # Use a header file that comes with gcc, so configuring glibc
4127 # with a fresh cross-compiler works.
4128 # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
4129 # <limits.h> exists even on freestanding compilers.
4130 # On the NeXT, cc -E runs the code through the compiler's parser,
4131 # not just through cpp. "Syntax error" is here to catch this case.
4132 cat >conftest.$ac_ext <<_ACEOF
4133 /* confdefs.h. */
4134 _ACEOF
4135 cat confdefs.h >>conftest.$ac_ext
4136 cat >>conftest.$ac_ext <<_ACEOF
4137 /* end confdefs.h. */
4138 #ifdef __STDC__
4139 # include <limits.h>
4140 #else
4141 # include <assert.h>
4142 #endif
4143 Syntax error
4144 _ACEOF
4145 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
4146 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
4147 ac_status=$?
4148 grep -v '^ *+' conftest.er1 >conftest.err
4149 rm -f conftest.er1
4150 cat conftest.err >&5
4151 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4152 (exit $ac_status); } >/dev/null; then
4153 if test -s conftest.err; then
4154 ac_cpp_err=$ac_c_preproc_warn_flag
4155 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
4156 else
4157 ac_cpp_err=
4158 fi
4159 else
4160 ac_cpp_err=yes
4161 fi
4162 if test -z "$ac_cpp_err"; then
4163 :
4164 else
4165 echo "$as_me: failed program was:" >&5
4166 sed 's/^/| /' conftest.$ac_ext >&5
4167
4168 # Broken: fails on valid input.
4169 continue
4170 fi
4171 rm -f conftest.err conftest.$ac_ext
4172
4173 # OK, works on sane cases. Now check whether non-existent headers
4174 # can be detected and how.
4175 cat >conftest.$ac_ext <<_ACEOF
4176 /* confdefs.h. */
4177 _ACEOF
4178 cat confdefs.h >>conftest.$ac_ext
4179 cat >>conftest.$ac_ext <<_ACEOF
4180 /* end confdefs.h. */
4181 #include <ac_nonexistent.h>
4182 _ACEOF
4183 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
4184 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
4185 ac_status=$?
4186 grep -v '^ *+' conftest.er1 >conftest.err
4187 rm -f conftest.er1
4188 cat conftest.err >&5
4189 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4190 (exit $ac_status); } >/dev/null; then
4191 if test -s conftest.err; then
4192 ac_cpp_err=$ac_c_preproc_warn_flag
4193 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
4194 else
4195 ac_cpp_err=
4196 fi
4197 else
4198 ac_cpp_err=yes
4199 fi
4200 if test -z "$ac_cpp_err"; then
4201 # Broken: success on invalid input.
4202 continue
4203 else
4204 echo "$as_me: failed program was:" >&5
4205 sed 's/^/| /' conftest.$ac_ext >&5
4206
4207 # Passes both tests.
4208 ac_preproc_ok=:
4209 break
4210 fi
4211 rm -f conftest.err conftest.$ac_ext
4212
4213 done
4214 # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
4215 rm -f conftest.err conftest.$ac_ext
4216 if $ac_preproc_ok; then
4217 :
4218 else
4219 { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
4220 See \`config.log' for more details." >&5
4221 echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
4222 See \`config.log' for more details." >&2;}
4223 { (exit 1); exit 1; }; }
4224 fi
4225
4226 ac_ext=c
4227 ac_cpp='$CPP $CPPFLAGS'
4228 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4229 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4230 ac_compiler_gnu=$ac_cv_c_compiler_gnu
4231
4232 ac_ext=cc
4233 ac_cpp='$CXXCPP $CPPFLAGS'
4234 ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4235 ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4236 ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
4237 if test -n "$ac_tool_prefix"; then
4238 for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
4239 do
4240 # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
4241 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
4242 echo "$as_me:$LINENO: checking for $ac_word" >&5
4243 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
4244 if test "${ac_cv_prog_CXX+set}" = set; then
4245 echo $ECHO_N "(cached) $ECHO_C" >&6
4246 else
4247 if test -n "$CXX"; then
4248 ac_cv_prog_CXX="$CXX" # Let the user override the test.
4249 else
4250 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4251 for as_dir in $PATH
4252 do
4253 IFS=$as_save_IFS
4254 test -z "$as_dir" && as_dir=.
4255 for ac_exec_ext in '' $ac_executable_extensions; do
4256 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4257 ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
4258 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
4259 break 2
4260 fi
4261 done
4262 done
4263
4264 fi
4265 fi
4266 CXX=$ac_cv_prog_CXX
4267 if test -n "$CXX"; then
4268 echo "$as_me:$LINENO: result: $CXX" >&5
4269 echo "${ECHO_T}$CXX" >&6
4270 else
4271 echo "$as_me:$LINENO: result: no" >&5
4272 echo "${ECHO_T}no" >&6
4273 fi
4274
4275 test -n "$CXX" && break
4276 done
4277 fi
4278 if test -z "$CXX"; then
4279 ac_ct_CXX=$CXX
4280 for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
4281 do
4282 # Extract the first word of "$ac_prog", so it can be a program name with args.
4283 set dummy $ac_prog; ac_word=$2
4284 echo "$as_me:$LINENO: checking for $ac_word" >&5
4285 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
4286 if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
4287 echo $ECHO_N "(cached) $ECHO_C" >&6
4288 else
4289 if test -n "$ac_ct_CXX"; then
4290 ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
4291 else
4292 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4293 for as_dir in $PATH
4294 do
4295 IFS=$as_save_IFS
4296 test -z "$as_dir" && as_dir=.
4297 for ac_exec_ext in '' $ac_executable_extensions; do
4298 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4299 ac_cv_prog_ac_ct_CXX="$ac_prog"
4300 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
4301 break 2
4302 fi
4303 done
4304 done
4305
4306 fi
4307 fi
4308 ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
4309 if test -n "$ac_ct_CXX"; then
4310 echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
4311 echo "${ECHO_T}$ac_ct_CXX" >&6
4312 else
4313 echo "$as_me:$LINENO: result: no" >&5
4314 echo "${ECHO_T}no" >&6
4315 fi
4316
4317 test -n "$ac_ct_CXX" && break
4318 done
4319 test -n "$ac_ct_CXX" || ac_ct_CXX="g++"
4320
4321 CXX=$ac_ct_CXX
4322 fi
4323
4324
4325 # Provide some information about the compiler.
4326 echo "$as_me:$LINENO:" \
4327 "checking for C++ compiler version" >&5
4328 ac_compiler=`set X $ac_compile; echo $2`
4329 { (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
4330 (eval $ac_compiler --version </dev/null >&5) 2>&5
4331 ac_status=$?
4332 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4333 (exit $ac_status); }
4334 { (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
4335 (eval $ac_compiler -v </dev/null >&5) 2>&5
4336 ac_status=$?
4337 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4338 (exit $ac_status); }
4339 { (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
4340 (eval $ac_compiler -V </dev/null >&5) 2>&5
4341 ac_status=$?
4342 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4343 (exit $ac_status); }
4344
4345 echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
4346 echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6
4347 if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
4348 echo $ECHO_N "(cached) $ECHO_C" >&6
4349 else
4350 cat >conftest.$ac_ext <<_ACEOF
4351 /* confdefs.h. */
4352 _ACEOF
4353 cat confdefs.h >>conftest.$ac_ext
4354 cat >>conftest.$ac_ext <<_ACEOF
4355 /* end confdefs.h. */
4356
4357 int
4358 main ()
4359 {
4360 #ifndef __GNUC__
4361 choke me
4362 #endif
4363
4364 ;
4365 return 0;
4366 }
4367 _ACEOF
4368 rm -f conftest.$ac_objext
4369 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
4370 (eval $ac_compile) 2>conftest.er1
4371 ac_status=$?
4372 grep -v '^ *+' conftest.er1 >conftest.err
4373 rm -f conftest.er1
4374 cat conftest.err >&5
4375 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4376 (exit $ac_status); } &&
4377 { ac_try='test -z "$ac_cxx_werror_flag"
4378 || test ! -s conftest.err'
4379 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4380 (eval $ac_try) 2>&5
4381 ac_status=$?
4382 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4383 (exit $ac_status); }; } &&
4384 { ac_try='test -s conftest.$ac_objext'
4385 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4386 (eval $ac_try) 2>&5
4387 ac_status=$?
4388 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4389 (exit $ac_status); }; }; then
4390 ac_compiler_gnu=yes
4391 else
4392 echo "$as_me: failed program was:" >&5
4393 sed 's/^/| /' conftest.$ac_ext >&5
4394
4395 ac_compiler_gnu=no
4396 fi
4397 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
4398 ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
4399
4400 fi
4401 echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
4402 echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6
4403 GXX=`test $ac_compiler_gnu = yes && echo yes`
4404 ac_test_CXXFLAGS=${CXXFLAGS+set}
4405 ac_save_CXXFLAGS=$CXXFLAGS
4406 CXXFLAGS="-g"
4407 echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
4408 echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6
4409 if test "${ac_cv_prog_cxx_g+set}" = set; then
4410 echo $ECHO_N "(cached) $ECHO_C" >&6
4411 else
4412 cat >conftest.$ac_ext <<_ACEOF
4413 /* confdefs.h. */
4414 _ACEOF
4415 cat confdefs.h >>conftest.$ac_ext
4416 cat >>conftest.$ac_ext <<_ACEOF
4417 /* end confdefs.h. */
4418
4419 int
4420 main ()
4421 {
4422
4423 ;
4424 return 0;
4425 }
4426 _ACEOF
4427 rm -f conftest.$ac_objext
4428 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
4429 (eval $ac_compile) 2>conftest.er1
4430 ac_status=$?
4431 grep -v '^ *+' conftest.er1 >conftest.err
4432 rm -f conftest.er1
4433 cat conftest.err >&5
4434 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4435 (exit $ac_status); } &&
4436 { ac_try='test -z "$ac_cxx_werror_flag"
4437 || test ! -s conftest.err'
4438 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4439 (eval $ac_try) 2>&5
4440 ac_status=$?
4441 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4442 (exit $ac_status); }; } &&
4443 { ac_try='test -s conftest.$ac_objext'
4444 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4445 (eval $ac_try) 2>&5
4446 ac_status=$?
4447 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4448 (exit $ac_status); }; }; then
4449 ac_cv_prog_cxx_g=yes
4450 else
4451 echo "$as_me: failed program was:" >&5
4452 sed 's/^/| /' conftest.$ac_ext >&5
4453
4454 ac_cv_prog_cxx_g=no
4455 fi
4456 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
4457 fi
4458 echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
4459 echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6
4460 if test "$ac_test_CXXFLAGS" = set; then
4461 CXXFLAGS=$ac_save_CXXFLAGS
4462 elif test $ac_cv_prog_cxx_g = yes; then
4463 if test "$GXX" = yes; then
4464 CXXFLAGS="-g -O2"
4465 else
4466 CXXFLAGS="-g"
4467 fi
4468 else
4469 if test "$GXX" = yes; then
4470 CXXFLAGS="-O2"
4471 else
4472 CXXFLAGS=
4473 fi
4474 fi
4475 for ac_declaration in \
4476 '' \
4477 'extern "C" void std::exit (int) throw (); using std::exit;' \
4478 'extern "C" void std::exit (int); using std::exit;' \
4479 'extern "C" void exit (int) throw ();' \
4480 'extern "C" void exit (int);' \
4481 'void exit (int);'
4482 do
4483 cat >conftest.$ac_ext <<_ACEOF
4484 /* confdefs.h. */
4485 _ACEOF
4486 cat confdefs.h >>conftest.$ac_ext
4487 cat >>conftest.$ac_ext <<_ACEOF
4488 /* end confdefs.h. */
4489 $ac_declaration
4490 #include <stdlib.h>
4491 int
4492 main ()
4493 {
4494 exit (42);
4495 ;
4496 return 0;
4497 }
4498 _ACEOF
4499 rm -f conftest.$ac_objext
4500 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
4501 (eval $ac_compile) 2>conftest.er1
4502 ac_status=$?
4503 grep -v '^ *+' conftest.er1 >conftest.err
4504 rm -f conftest.er1
4505 cat conftest.err >&5
4506 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4507 (exit $ac_status); } &&
4508 { ac_try='test -z "$ac_cxx_werror_flag"
4509 || test ! -s conftest.err'
4510 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4511 (eval $ac_try) 2>&5
4512 ac_status=$?
4513 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4514 (exit $ac_status); }; } &&
4515 { ac_try='test -s conftest.$ac_objext'
4516 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4517 (eval $ac_try) 2>&5
4518 ac_status=$?
4519 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4520 (exit $ac_status); }; }; then
4521 :
4522 else
4523 echo "$as_me: failed program was:" >&5
4524 sed 's/^/| /' conftest.$ac_ext >&5
4525
4526 continue
4527 fi
4528 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
4529 cat >conftest.$ac_ext <<_ACEOF
4530 /* confdefs.h. */
4531 _ACEOF
4532 cat confdefs.h >>conftest.$ac_ext
4533 cat >>conftest.$ac_ext <<_ACEOF
4534 /* end confdefs.h. */
4535 $ac_declaration
4536 int
4537 main ()
4538 {
4539 exit (42);
4540 ;
4541 return 0;
4542 }
4543 _ACEOF
4544 rm -f conftest.$ac_objext
4545 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
4546 (eval $ac_compile) 2>conftest.er1
4547 ac_status=$?
4548 grep -v '^ *+' conftest.er1 >conftest.err
4549 rm -f conftest.er1
4550 cat conftest.err >&5
4551 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4552 (exit $ac_status); } &&
4553 { ac_try='test -z "$ac_cxx_werror_flag"
4554 || test ! -s conftest.err'
4555 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4556 (eval $ac_try) 2>&5
4557 ac_status=$?
4558 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4559 (exit $ac_status); }; } &&
4560 { ac_try='test -s conftest.$ac_objext'
4561 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4562 (eval $ac_try) 2>&5
4563 ac_status=$?
4564 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4565 (exit $ac_status); }; }; then
4566 break
4567 else
4568 echo "$as_me: failed program was:" >&5
4569 sed 's/^/| /' conftest.$ac_ext >&5
4570
4571 fi
4572 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
4573 done
4574 rm -f conftest*
4575 if test -n "$ac_declaration"; then
4576 echo '#ifdef __cplusplus' >>confdefs.h
4577 echo $ac_declaration >>confdefs.h
4578 echo '#endif' >>confdefs.h
4579 fi
4580
4581 ac_ext=c
4582 ac_cpp='$CPP $CPPFLAGS'
4583 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4584 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4585 ac_compiler_gnu=$ac_cv_c_compiler_gnu
4586
4587 depcc="$CXX" am_compiler_list=
4588
4589 echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
4590 echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6
4591 if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then
4592 echo $ECHO_N "(cached) $ECHO_C" >&6
4593 else
4594 if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
4595 # We make a subdir and do the tests there. Otherwise we can end up
4596 # making bogus files that we don't know about and never remove. For
4597 # instance it was reported that on HP-UX the gcc test will end up
4598 # making a dummy file named `D' -- because `-MD' means `put the output
4599 # in D'.
4600 mkdir conftest.dir
4601 # Copy depcomp to subdir because otherwise we won't find it if we're
4602 # using a relative directory.
4603 cp "$am_depcomp" conftest.dir
4604 cd conftest.dir
4605 # We will build objects and dependencies in a subdirectory because
4606 # it helps to detect inapplicable dependency modes. For instance
4607 # both Tru64's cc and ICC support -MD to output dependencies as a
4608 # side effect of compilation, but ICC will put the dependencies in
4609 # the current directory while Tru64 will put them in the object
4610 # directory.
4611 mkdir sub
4612
4613 am_cv_CXX_dependencies_compiler_type=none
4614 if test "$am_compiler_list" = ""; then
4615 am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
4616 fi
4617 for depmode in $am_compiler_list; do
4618 # Setup a source with many dependencies, because some compilers
4619 # like to wrap large dependency lists on column 80 (with \), and
4620 # we should not choose a depcomp mode which is confused by this.
4621 #
4622 # We need to recreate these files for each test, as the compiler may
4623 # overwrite some of them when testing with obscure command lines.
4624 # This happens at least with the AIX C compiler.
4625 : > sub/conftest.c
4626 for i in 1 2 3 4 5 6; do
4627 echo '#include "conftst'$i'.h"' >> sub/conftest.c
4628 # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
4629 # Solaris 8's {/usr,}/bin/sh.
4630 touch sub/conftst$i.h
4631 done
4632 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
4633
4634 case $depmode in
4635 nosideeffect)
4636 # after this tag, mechanisms are not by side-effect, so they'll
4637 # only be used when explicitly requested
4638 if test "x$enable_dependency_tracking" = xyes; then
4639 continue
4640 else
4641 break
4642 fi
4643 ;;
4644 none) break ;;
4645 esac
4646 # We check with `-c' and `-o' for the sake of the "dashmstdout"
4647 # mode. It turns out that the SunPro C++ compiler does not properly
4648 # handle `-M -o', and we need to detect this.
4649 if depmode=$depmode \
4650 source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
4651 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
4652 $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
4653 >/dev/null 2>conftest.err &&
4654 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
4655 grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
4656 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
4657 # icc doesn't choke on unknown options, it will just issue warnings
4658 # or remarks (even with -Werror). So we grep stderr for any message
4659 # that says an option was ignored or not supported.
4660 # When given -MP, icc 7.0 and 7.1 complain thusly:
4661 # icc: Command line warning: ignoring option '-M'; no argument required
4662 # The diagnosis changed in icc 8.0:
4663 # icc: Command line remark: option '-MP' not supported
4664 if (grep 'ignoring option' conftest.err ||
4665 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
4666 am_cv_CXX_dependencies_compiler_type=$depmode
4667 break
4668 fi
4669 fi
4670 done
4671
4672 cd ..
4673 rm -rf conftest.dir
4674 else
4675 am_cv_CXX_dependencies_compiler_type=none
4676 fi
4677
4678 fi
4679 echo "$as_me:$LINENO: result: $am_cv_CXX_dependencies_compiler_type" >&5
4680 echo "${ECHO_T}$am_cv_CXX_dependencies_compiler_type" >&6
4681 CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type
4682
4683
4684
4685 if
4686 test "x$enable_dependency_tracking" != xno \
4687 && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then
4688 am__fastdepCXX_TRUE=
4689 am__fastdepCXX_FALSE='#'
4690 else
4691 am__fastdepCXX_TRUE='#'
4692 am__fastdepCXX_FALSE=
4693 fi
4694
4695
4696
4697 if test "`$CPP -v < /dev/null 2>&1 | grep '/usr/local/include' 2>&1`" = ""; then
4698 CPPFLAGS="$CPPFLAGS -I/usr/local/include"
4699 LDFLAGS="$LDFLAGS -L/usr/local/lib"
4700 fi
4701
4702
4703
4704 for ac_func in gethostbyname
4705 do
4706 as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
4707 echo "$as_me:$LINENO: checking for $ac_func" >&5
4708 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
4709 if eval "test \"\${$as_ac_var+set}\" = set"; then
4710 echo $ECHO_N "(cached) $ECHO_C" >&6
4711 else
4712 cat >conftest.$ac_ext <<_ACEOF
4713 /* confdefs.h. */
4714 _ACEOF
4715 cat confdefs.h >>conftest.$ac_ext
4716 cat >>conftest.$ac_ext <<_ACEOF
4717 /* end confdefs.h. */
4718 /* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
4719 For example, HP-UX 11i <limits.h> declares gettimeofday. */
4720 #define $ac_func innocuous_$ac_func
4721
4722 /* System header to define __stub macros and hopefully few prototypes,
4723 which can conflict with char $ac_func (); below.
4724 Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
4725 <limits.h> exists even on freestanding compilers. */
4726
4727 #ifdef __STDC__
4728 # include <limits.h>
4729 #else
4730 # include <assert.h>
4731 #endif
4732
4733 #undef $ac_func
4734
4735 /* Override any gcc2 internal prototype to avoid an error. */
4736 #ifdef __cplusplus
4737 extern "C"
4738 {
4739 #endif
4740 /* We use char because int might match the return type of a gcc2
4741 builtin and then its argument prototype would still apply. */
4742 char $ac_func ();
4743 /* The GNU C library defines this for functions which it implements
4744 to always fail with ENOSYS. Some functions are actually named
4745 something starting with __ and the normal name is an alias. */
4746 #if defined (__stub_$ac_func) || defined (__stub___$ac_func)
4747 choke me
4748 #else
4749 char (*f) () = $ac_func;
4750 #endif
4751 #ifdef __cplusplus
4752 }
4753 #endif
4754
4755 int
4756 main ()
4757 {
4758 return f != $ac_func;
4759 ;
4760 return 0;
4761 }
4762 _ACEOF
4763 rm -f conftest.$ac_objext conftest$ac_exeext
4764 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
4765 (eval $ac_link) 2>conftest.er1
4766 ac_status=$?
4767 grep -v '^ *+' conftest.er1 >conftest.err
4768 rm -f conftest.er1
4769 cat conftest.err >&5
4770 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4771 (exit $ac_status); } &&
4772 { ac_try='test -z "$ac_c_werror_flag"
4773 || test ! -s conftest.err'
4774 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4775 (eval $ac_try) 2>&5
4776 ac_status=$?
4777 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4778 (exit $ac_status); }; } &&
4779 { ac_try='test -s conftest$ac_exeext'
4780 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4781 (eval $ac_try) 2>&5
4782 ac_status=$?
4783 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4784 (exit $ac_status); }; }; then
4785 eval "$as_ac_var=yes"
4786 else
4787 echo "$as_me: failed program was:" >&5
4788 sed 's/^/| /' conftest.$ac_ext >&5
4789
4790 eval "$as_ac_var=no"
4791 fi
4792 rm -f conftest.err conftest.$ac_objext \
4793 conftest$ac_exeext conftest.$ac_ext
4794 fi
4795 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
4796 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
4797 if test `eval echo '${'$as_ac_var'}'` = yes; then
4798 cat >>confdefs.h <<_ACEOF
4799 #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
4800 _ACEOF
4801
4802 else
4803
4804 echo "$as_me:$LINENO: checking for gethostbyname in -lnsl" >&5
4805 echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6
4806 if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then
4807 echo $ECHO_N "(cached) $ECHO_C" >&6
4808 else
4809 ac_check_lib_save_LIBS=$LIBS
4810 LIBS="-lnsl $LIBS"
4811 cat >conftest.$ac_ext <<_ACEOF
4812 /* confdefs.h. */
4813 _ACEOF
4814 cat confdefs.h >>conftest.$ac_ext
4815 cat >>conftest.$ac_ext <<_ACEOF
4816 /* end confdefs.h. */
4817
4818 /* Override any gcc2 internal prototype to avoid an error. */
4819 #ifdef __cplusplus
4820 extern "C"
4821 #endif
4822 /* We use char because int might match the return type of a gcc2
4823 builtin and then its argument prototype would still apply. */
4824 char gethostbyname ();
4825 int
4826 main ()
4827 {
4828 gethostbyname ();
4829 ;
4830 return 0;
4831 }
4832 _ACEOF
4833 rm -f conftest.$ac_objext conftest$ac_exeext
4834 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
4835 (eval $ac_link) 2>conftest.er1
4836 ac_status=$?
4837 grep -v '^ *+' conftest.er1 >conftest.err
4838 rm -f conftest.er1
4839 cat conftest.err >&5
4840 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4841 (exit $ac_status); } &&
4842 { ac_try='test -z "$ac_c_werror_flag"
4843 || test ! -s conftest.err'
4844 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4845 (eval $ac_try) 2>&5
4846 ac_status=$?
4847 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4848 (exit $ac_status); }; } &&
4849 { ac_try='test -s conftest$ac_exeext'
4850 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4851 (eval $ac_try) 2>&5
4852 ac_status=$?
4853 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4854 (exit $ac_status); }; }; then
4855 ac_cv_lib_nsl_gethostbyname=yes
4856 else
4857 echo "$as_me: failed program was:" >&5
4858 sed 's/^/| /' conftest.$ac_ext >&5
4859
4860 ac_cv_lib_nsl_gethostbyname=no
4861 fi
4862 rm -f conftest.err conftest.$ac_objext \
4863 conftest$ac_exeext conftest.$ac_ext
4864 LIBS=$ac_check_lib_save_LIBS
4865 fi
4866 echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_gethostbyname" >&5
4867 echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6
4868 if test $ac_cv_lib_nsl_gethostbyname = yes; then
4869 cat >>confdefs.h <<_ACEOF
4870 #define HAVE_LIBNSL 1
4871 _ACEOF
4872
4873 LIBS="-lnsl $LIBS"
4874
4875 else
4876
4877 echo "$as_me:$LINENO: checking for gethostbyname in -lsocket" >&5
4878 echo $ECHO_N "checking for gethostbyname in -lsocket... $ECHO_C" >&6
4879 if test "${ac_cv_lib_socket_gethostbyname+set}" = set; then
4880 echo $ECHO_N "(cached) $ECHO_C" >&6
4881 else
4882 ac_check_lib_save_LIBS=$LIBS
4883 LIBS="-lsocket $LIBS"
4884 cat >conftest.$ac_ext <<_ACEOF
4885 /* confdefs.h. */
4886 _ACEOF
4887 cat confdefs.h >>conftest.$ac_ext
4888 cat >>conftest.$ac_ext <<_ACEOF
4889 /* end confdefs.h. */
4890
4891 /* Override any gcc2 internal prototype to avoid an error. */
4892 #ifdef __cplusplus
4893 extern "C"
4894 #endif
4895 /* We use char because int might match the return type of a gcc2
4896 builtin and then its argument prototype would still apply. */
4897 char gethostbyname ();
4898 int
4899 main ()
4900 {
4901 gethostbyname ();
4902 ;
4903 return 0;
4904 }
4905 _ACEOF
4906 rm -f conftest.$ac_objext conftest$ac_exeext
4907 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
4908 (eval $ac_link) 2>conftest.er1
4909 ac_status=$?
4910 grep -v '^ *+' conftest.er1 >conftest.err
4911 rm -f conftest.er1
4912 cat conftest.err >&5
4913 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4914 (exit $ac_status); } &&
4915 { ac_try='test -z "$ac_c_werror_flag"
4916 || test ! -s conftest.err'
4917 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4918 (eval $ac_try) 2>&5
4919 ac_status=$?
4920 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4921 (exit $ac_status); }; } &&
4922 { ac_try='test -s conftest$ac_exeext'
4923 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
4924 (eval $ac_try) 2>&5
4925 ac_status=$?
4926 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4927 (exit $ac_status); }; }; then
4928 ac_cv_lib_socket_gethostbyname=yes
4929 else
4930 echo "$as_me: failed program was:" >&5
4931 sed 's/^/| /' conftest.$ac_ext >&5
4932
4933 ac_cv_lib_socket_gethostbyname=no
4934 fi
4935 rm -f conftest.err conftest.$ac_objext \
4936 conftest$ac_exeext conftest.$ac_ext
4937 LIBS=$ac_check_lib_save_LIBS
4938 fi
4939 echo "$as_me:$LINENO: result: $ac_cv_lib_socket_gethostbyname" >&5
4940 echo "${ECHO_T}$ac_cv_lib_socket_gethostbyname" >&6
4941 if test $ac_cv_lib_socket_gethostbyname = yes; then
4942 cat >>confdefs.h <<_ACEOF
4943 #define HAVE_LIBSOCKET 1
4944 _ACEOF
4945
4946 LIBS="-lsocket $LIBS"
4947
4948 fi
4949
4950 fi
4951
4952 fi
4953 done
4954
4955
4956 for ac_func in setsockopt
4957 do
4958 as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
4959 echo "$as_me:$LINENO: checking for $ac_func" >&5
4960 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
4961 if eval "test \"\${$as_ac_var+set}\" = set"; then
4962 echo $ECHO_N "(cached) $ECHO_C" >&6
4963 else
4964 cat >conftest.$ac_ext <<_ACEOF
4965 /* confdefs.h. */
4966 _ACEOF
4967 cat confdefs.h >>conftest.$ac_ext
4968 cat >>conftest.$ac_ext <<_ACEOF
4969 /* end confdefs.h. */
4970 /* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
4971 For example, HP-UX 11i <limits.h> declares gettimeofday. */
4972 #define $ac_func innocuous_$ac_func
4973
4974 /* System header to define __stub macros and hopefully few prototypes,
4975 which can conflict with char $ac_func (); below.
4976 Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
4977 <limits.h> exists even on freestanding compilers. */
4978
4979 #ifdef __STDC__
4980 # include <limits.h>
4981 #else
4982 # include <assert.h>
4983 #endif
4984
4985 #undef $ac_func
4986
4987 /* Override any gcc2 internal prototype to avoid an error. */
4988 #ifdef __cplusplus
4989 extern "C"
4990 {
4991 #endif
4992 /* We use char because int might match the return type of a gcc2
4993 builtin and then its argument prototype would still apply. */
4994 char $ac_func ();
4995 /* The GNU C library defines this for functions which it implements
4996 to always fail with ENOSYS. Some functions are actually named
4997 something starting with __ and the normal name is an alias. */
4998 #if defined (__stub_$ac_func) || defined (__stub___$ac_func)
4999 choke me
5000 #else
5001 char (*f) () = $ac_func;
5002 #endif
5003 #ifdef __cplusplus
5004 }
5005 #endif
5006
5007 int
5008 main ()
5009 {
5010 return f != $ac_func;
5011 ;
5012 return 0;
5013 }
5014 _ACEOF
5015 rm -f conftest.$ac_objext conftest$ac_exeext
5016 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5017 (eval $ac_link) 2>conftest.er1
5018 ac_status=$?
5019 grep -v '^ *+' conftest.er1 >conftest.err
5020 rm -f conftest.er1
5021 cat conftest.err >&5
5022 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5023 (exit $ac_status); } &&
5024 { ac_try='test -z "$ac_c_werror_flag"
5025 || test ! -s conftest.err'
5026 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5027 (eval $ac_try) 2>&5
5028 ac_status=$?
5029 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5030 (exit $ac_status); }; } &&
5031 { ac_try='test -s conftest$ac_exeext'
5032 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5033 (eval $ac_try) 2>&5
5034 ac_status=$?
5035 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5036 (exit $ac_status); }; }; then
5037 eval "$as_ac_var=yes"
5038 else
5039 echo "$as_me: failed program was:" >&5
5040 sed 's/^/| /' conftest.$ac_ext >&5
5041
5042 eval "$as_ac_var=no"
5043 fi
5044 rm -f conftest.err conftest.$ac_objext \
5045 conftest$ac_exeext conftest.$ac_ext
5046 fi
5047 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
5048 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
5049 if test `eval echo '${'$as_ac_var'}'` = yes; then
5050 cat >>confdefs.h <<_ACEOF
5051 #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
5052 _ACEOF
5053
5054 else
5055
5056 echo "$as_me:$LINENO: checking for setsockopt in -lsocket" >&5
5057 echo $ECHO_N "checking for setsockopt in -lsocket... $ECHO_C" >&6
5058 if test "${ac_cv_lib_socket_setsockopt+set}" = set; then
5059 echo $ECHO_N "(cached) $ECHO_C" >&6
5060 else
5061 ac_check_lib_save_LIBS=$LIBS
5062 LIBS="-lsocket $LIBS"
5063 cat >conftest.$ac_ext <<_ACEOF
5064 /* confdefs.h. */
5065 _ACEOF
5066 cat confdefs.h >>conftest.$ac_ext
5067 cat >>conftest.$ac_ext <<_ACEOF
5068 /* end confdefs.h. */
5069
5070 /* Override any gcc2 internal prototype to avoid an error. */
5071 #ifdef __cplusplus
5072 extern "C"
5073 #endif
5074 /* We use char because int might match the return type of a gcc2
5075 builtin and then its argument prototype would still apply. */
5076 char setsockopt ();
5077 int
5078 main ()
5079 {
5080 setsockopt ();
5081 ;
5082 return 0;
5083 }
5084 _ACEOF
5085 rm -f conftest.$ac_objext conftest$ac_exeext
5086 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5087 (eval $ac_link) 2>conftest.er1
5088 ac_status=$?
5089 grep -v '^ *+' conftest.er1 >conftest.err
5090 rm -f conftest.er1
5091 cat conftest.err >&5
5092 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5093 (exit $ac_status); } &&
5094 { ac_try='test -z "$ac_c_werror_flag"
5095 || test ! -s conftest.err'
5096 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5097 (eval $ac_try) 2>&5
5098 ac_status=$?
5099 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5100 (exit $ac_status); }; } &&
5101 { ac_try='test -s conftest$ac_exeext'
5102 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5103 (eval $ac_try) 2>&5
5104 ac_status=$?
5105 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5106 (exit $ac_status); }; }; then
5107 ac_cv_lib_socket_setsockopt=yes
5108 else
5109 echo "$as_me: failed program was:" >&5
5110 sed 's/^/| /' conftest.$ac_ext >&5
5111
5112 ac_cv_lib_socket_setsockopt=no
5113 fi
5114 rm -f conftest.err conftest.$ac_objext \
5115 conftest$ac_exeext conftest.$ac_ext
5116 LIBS=$ac_check_lib_save_LIBS
5117 fi
5118 echo "$as_me:$LINENO: result: $ac_cv_lib_socket_setsockopt" >&5
5119 echo "${ECHO_T}$ac_cv_lib_socket_setsockopt" >&6
5120 if test $ac_cv_lib_socket_setsockopt = yes; then
5121 cat >>confdefs.h <<_ACEOF
5122 #define HAVE_LIBSOCKET 1
5123 _ACEOF
5124
5125 LIBS="-lsocket $LIBS"
5126
5127 fi
5128
5129 fi
5130 done
5131
5132
5133 echo "$as_me:$LINENO: checking for socklen_t" >&5
5134 echo $ECHO_N "checking for socklen_t... $ECHO_C" >&6
5135 ac_cv_socklen_t=""
5136 cat >conftest.$ac_ext <<_ACEOF
5137 /* confdefs.h. */
5138 _ACEOF
5139 cat confdefs.h >>conftest.$ac_ext
5140 cat >>conftest.$ac_ext <<_ACEOF
5141 /* end confdefs.h. */
5142
5143 #include <sys/types.h>
5144 #include <sys/socket.h>
5145
5146 int
5147 main ()
5148 {
5149
5150 socklen_t a=0;
5151 getsockname(0,(struct sockaddr*)0, &a);
5152
5153 ;
5154 return 0;
5155 }
5156 _ACEOF
5157 rm -f conftest.$ac_objext
5158 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
5159 (eval $ac_compile) 2>conftest.er1
5160 ac_status=$?
5161 grep -v '^ *+' conftest.er1 >conftest.err
5162 rm -f conftest.er1
5163 cat conftest.err >&5
5164 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5165 (exit $ac_status); } &&
5166 { ac_try='test -z "$ac_c_werror_flag"
5167 || test ! -s conftest.err'
5168 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5169 (eval $ac_try) 2>&5
5170 ac_status=$?
5171 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5172 (exit $ac_status); }; } &&
5173 { ac_try='test -s conftest.$ac_objext'
5174 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5175 (eval $ac_try) 2>&5
5176 ac_status=$?
5177 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5178 (exit $ac_status); }; }; then
5179 ac_cv_socklen_t="socklen_t"
5180 else
5181 echo "$as_me: failed program was:" >&5
5182 sed 's/^/| /' conftest.$ac_ext >&5
5183
5184 cat >conftest.$ac_ext <<_ACEOF
5185 /* confdefs.h. */
5186 _ACEOF
5187 cat confdefs.h >>conftest.$ac_ext
5188 cat >>conftest.$ac_ext <<_ACEOF
5189 /* end confdefs.h. */
5190
5191 #include <sys/types.h>
5192 #include <sys/socket.h>
5193
5194 int
5195 main ()
5196 {
5197
5198 int a=0;
5199 getsockname(0,(struct sockaddr*)0, &a);
5200
5201 ;
5202 return 0;
5203 }
5204 _ACEOF
5205 rm -f conftest.$ac_objext
5206 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
5207 (eval $ac_compile) 2>conftest.er1
5208 ac_status=$?
5209 grep -v '^ *+' conftest.er1 >conftest.err
5210 rm -f conftest.er1
5211 cat conftest.err >&5
5212 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5213 (exit $ac_status); } &&
5214 { ac_try='test -z "$ac_c_werror_flag"
5215 || test ! -s conftest.err'
5216 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5217 (eval $ac_try) 2>&5
5218 ac_status=$?
5219 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5220 (exit $ac_status); }; } &&
5221 { ac_try='test -s conftest.$ac_objext'
5222 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5223 (eval $ac_try) 2>&5
5224 ac_status=$?
5225 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5226 (exit $ac_status); }; }; then
5227 ac_cv_socklen_t="int"
5228 else
5229 echo "$as_me: failed program was:" >&5
5230 sed 's/^/| /' conftest.$ac_ext >&5
5231
5232 ac_cv_socklen_t="size_t"
5233
5234 fi
5235 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
5236
5237 fi
5238 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
5239 echo "$as_me:$LINENO: result: $ac_cv_socklen_t" >&5
5240 echo "${ECHO_T}$ac_cv_socklen_t" >&6
5241 if test "$ac_cv_socklen_t" != "socklen_t"; then
5242
5243 cat >>confdefs.h <<_ACEOF
5244 #define socklen_t $ac_cv_socklen_t
5245 _ACEOF
5246
5247 fi
5248
5249
5250
5251 # Check whether --with-glib-prefix or --without-glib-prefix was given.
5252 if test "${with_glib_prefix+set}" = set; then
5253 withval="$with_glib_prefix"
5254 glib_config_prefix="$withval"
5255 else
5256 glib_config_prefix=""
5257 fi;
5258
5259 # Check whether --with-glib-exec-prefix or --without-glib-exec-prefix was given.
5260 if test "${with_glib_exec_prefix+set}" = set; then
5261 withval="$with_glib_exec_prefix"
5262 glib_config_exec_prefix="$withval"
5263 else
5264 glib_config_exec_prefix=""
5265 fi;
5266 # Check whether --enable-glibtest or --disable-glibtest was given.
5267 if test "${enable_glibtest+set}" = set; then
5268 enableval="$enable_glibtest"
5269
5270 else
5271 enable_glibtest=yes
5272 fi;
5273
5274 if test x$glib_config_exec_prefix != x ; then
5275 glib_config_args="$glib_config_args --exec-prefix=$glib_config_exec_prefix"
5276 if test x${GLIB_CONFIG+set} != xset ; then
5277 GLIB_CONFIG=$glib_config_exec_prefix/bin/glib-config
5278 fi
5279 fi
5280 if test x$glib_config_prefix != x ; then
5281 glib_config_args="$glib_config_args --prefix=$glib_config_prefix"
5282 if test x${GLIB_CONFIG+set} != xset ; then
5283 GLIB_CONFIG=$glib_config_prefix/bin/glib-config
5284 fi
5285 fi
5286
5287 for module in .
5288 do
5289 case "$module" in
5290 gmodule)
5291 glib_config_args="$glib_config_args gmodule"
5292 ;;
5293 gthread)
5294 glib_config_args="$glib_config_args gthread"
5295 ;;
5296 esac
5297 done
5298
5299 # Extract the first word of "glib-config", so it can be a program name with args.
5300 set dummy glib-config; ac_word=$2
5301 echo "$as_me:$LINENO: checking for $ac_word" >&5
5302 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
5303 if test "${ac_cv_path_GLIB_CONFIG+set}" = set; then
5304 echo $ECHO_N "(cached) $ECHO_C" >&6
5305 else
5306 case $GLIB_CONFIG in
5307 [\\/]* | ?:[\\/]*)
5308 ac_cv_path_GLIB_CONFIG="$GLIB_CONFIG" # Let the user override the test with a path.
5309 ;;
5310 *)
5311 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
5312 for as_dir in $PATH
5313 do
5314 IFS=$as_save_IFS
5315 test -z "$as_dir" && as_dir=.
5316 for ac_exec_ext in '' $ac_executable_extensions; do
5317 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
5318 ac_cv_path_GLIB_CONFIG="$as_dir/$ac_word$ac_exec_ext"
5319 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
5320 break 2
5321 fi
5322 done
5323 done
5324
5325 test -z "$ac_cv_path_GLIB_CONFIG" && ac_cv_path_GLIB_CONFIG="no"
5326 ;;
5327 esac
5328 fi
5329 GLIB_CONFIG=$ac_cv_path_GLIB_CONFIG
5330
5331 if test -n "$GLIB_CONFIG"; then
5332 echo "$as_me:$LINENO: result: $GLIB_CONFIG" >&5
5333 echo "${ECHO_T}$GLIB_CONFIG" >&6
5334 else
5335 echo "$as_me:$LINENO: result: no" >&5
5336 echo "${ECHO_T}no" >&6
5337 fi
5338
5339 min_glib_version=1.2.0
5340 echo "$as_me:$LINENO: checking for GLIB - version >= $min_glib_version" >&5
5341 echo $ECHO_N "checking for GLIB - version >= $min_glib_version... $ECHO_C" >&6
5342 no_glib=""
5343 if test "$GLIB_CONFIG" = "no" ; then
5344 no_glib=yes
5345 else
5346 GLIB_CFLAGS=`$GLIB_CONFIG $glib_config_args --cflags`
5347 GLIB_LIBS=`$GLIB_CONFIG $glib_config_args --libs`
5348 glib_config_major_version=`$GLIB_CONFIG $glib_config_args --version | \
5349 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'`
5350 glib_config_minor_version=`$GLIB_CONFIG $glib_config_args --version | \
5351 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'`
5352 glib_config_micro_version=`$GLIB_CONFIG $glib_config_args --version | \
5353 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'`
5354 if test "x$enable_glibtest" = "xyes" ; then
5355 ac_save_CFLAGS="$CFLAGS"
5356 ac_save_LIBS="$LIBS"
5357 CFLAGS="$CFLAGS $GLIB_CFLAGS"
5358 LIBS="$GLIB_LIBS $LIBS"
5359 rm -f conf.glibtest
5360 if test "$cross_compiling" = yes; then
5361 echo $ac_n "cross compiling; assumed OK... $ac_c"
5362 else
5363 cat >conftest.$ac_ext <<_ACEOF
5364 /* confdefs.h. */
5365 _ACEOF
5366 cat confdefs.h >>conftest.$ac_ext
5367 cat >>conftest.$ac_ext <<_ACEOF
5368 /* end confdefs.h. */
5369
5370 #include <glib.h>
5371 #include <stdio.h>
5372 #include <stdlib.h>
5373
5374 int
5375 main ()
5376 {
5377 int major, minor, micro;
5378 char *tmp_version;
5379
5380 system ("touch conf.glibtest");
5381
5382 /* HP/UX 9 (%@#!) writes to sscanf strings */
5383 tmp_version = g_strdup("$min_glib_version");
5384 if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
5385 printf("%s, bad version string\n", "$min_glib_version");
5386 exit(1);
5387 }
5388
5389 if ((glib_major_version != $glib_config_major_version) ||
5390 (glib_minor_version != $glib_config_minor_version) ||
5391 (glib_micro_version != $glib_config_micro_version))
5392 {
5393 printf("\n*** 'glib-config --version' returned %d.%d.%d, but GLIB (%d.%d.%d)\n",
5394 $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version,
5395 glib_major_version, glib_minor_version, glib_micro_version);
5396 printf ("*** was found! If glib-config was correct, then it is best\n");
5397 printf ("*** to remove the old version of GLIB. You may also be able to fix the error\n");
5398 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
5399 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
5400 printf("*** required on your system.\n");
5401 printf("*** If glib-config was wrong, set the environment variable GLIB_CONFIG\n");
5402 printf("*** to point to the correct copy of glib-config, and remove the file config.cache\n");
5403 printf("*** before re-running configure\n");
5404 }
5405 else if ((glib_major_version != GLIB_MAJOR_VERSION) ||
5406 (glib_minor_version != GLIB_MINOR_VERSION) ||
5407 (glib_micro_version != GLIB_MICRO_VERSION))
5408 {
5409 printf("*** GLIB header files (version %d.%d.%d) do not match\n",
5410 GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
5411 printf("*** library (version %d.%d.%d)\n",
5412 glib_major_version, glib_minor_version, glib_micro_version);
5413 }
5414 else
5415 {
5416 if ((glib_major_version > major) ||
5417 ((glib_major_version == major) && (glib_minor_version > minor)) ||
5418 ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro)))
5419 {
5420 return 0;
5421 }
5422 else
5423 {
5424 printf("\n*** An old version of GLIB (%d.%d.%d) was found.\n",
5425 glib_major_version, glib_minor_version, glib_micro_version);
5426 printf("*** You need a version of GLIB newer than %d.%d.%d. The latest version of\n",
5427 major, minor, micro);
5428 printf("*** GLIB is always available from ftp://ftp.gtk.org.\n");
5429 printf("***\n");
5430 printf("*** If you have already installed a sufficiently new version, this error\n");
5431 printf("*** probably means that the wrong copy of the glib-config shell script is\n");
5432 printf("*** being found. The easiest way to fix this is to remove the old version\n");
5433 printf("*** of GLIB, but you can also set the GLIB_CONFIG environment to point to the\n");
5434 printf("*** correct copy of glib-config. (In this case, you will have to\n");
5435 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
5436 printf("*** so that the correct libraries are found at run-time))\n");
5437 }
5438 }
5439 return 1;
5440 }
5441
5442 _ACEOF
5443 rm -f conftest$ac_exeext
5444 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5445 (eval $ac_link) 2>&5
5446 ac_status=$?
5447 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5448 (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
5449 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5450 (eval $ac_try) 2>&5
5451 ac_status=$?
5452 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5453 (exit $ac_status); }; }; then
5454 :
5455 else
5456 echo "$as_me: program exited with status $ac_status" >&5
5457 echo "$as_me: failed program was:" >&5
5458 sed 's/^/| /' conftest.$ac_ext >&5
5459
5460 ( exit $ac_status )
5461 no_glib=yes
5462 fi
5463 rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
5464 fi
5465 CFLAGS="$ac_save_CFLAGS"
5466 LIBS="$ac_save_LIBS"
5467 fi
5468 fi
5469 if test "x$no_glib" = x ; then
5470 echo "$as_me:$LINENO: result: yes" >&5
5471 echo "${ECHO_T}yes" >&6
5472 :
5473 else
5474 echo "$as_me:$LINENO: result: no" >&5
5475 echo "${ECHO_T}no" >&6
5476 if test "$GLIB_CONFIG" = "no" ; then
5477 echo "*** The glib-config script installed by GLIB could not be found"
5478 echo "*** If GLIB was installed in PREFIX, make sure PREFIX/bin is in"
5479 echo "*** your path, or set the GLIB_CONFIG environment variable to the"
5480 echo "*** full path to glib-config."
5481 else
5482 if test -f conf.glibtest ; then
5483 :
5484 else
5485 echo "*** Could not run GLIB test program, checking why..."
5486 CFLAGS="$CFLAGS $GLIB_CFLAGS"
5487 LIBS="$LIBS $GLIB_LIBS"
5488 cat >conftest.$ac_ext <<_ACEOF
5489 /* confdefs.h. */
5490 _ACEOF
5491 cat confdefs.h >>conftest.$ac_ext
5492 cat >>conftest.$ac_ext <<_ACEOF
5493 /* end confdefs.h. */
5494
5495 #include <glib.h>
5496 #include <stdio.h>
5497
5498 int
5499 main ()
5500 {
5501 return ((glib_major_version) || (glib_minor_version) || (glib_micro_version));
5502 ;
5503 return 0;
5504 }
5505 _ACEOF
5506 rm -f conftest.$ac_objext conftest$ac_exeext
5507 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5508 (eval $ac_link) 2>conftest.er1
5509 ac_status=$?
5510 grep -v '^ *+' conftest.er1 >conftest.err
5511 rm -f conftest.er1
5512 cat conftest.err >&5
5513 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5514 (exit $ac_status); } &&
5515 { ac_try='test -z "$ac_c_werror_flag"
5516 || test ! -s conftest.err'
5517 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5518 (eval $ac_try) 2>&5
5519 ac_status=$?
5520 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5521 (exit $ac_status); }; } &&
5522 { ac_try='test -s conftest$ac_exeext'
5523 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5524 (eval $ac_try) 2>&5
5525 ac_status=$?
5526 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5527 (exit $ac_status); }; }; then
5528 echo "*** The test program compiled, but did not run. This usually means"
5529 echo "*** that the run-time linker is not finding GLIB or finding the wrong"
5530 echo "*** version of GLIB. If it is not finding GLIB, you'll need to set your"
5531 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
5532 echo "*** to the installed location Also, make sure you have run ldconfig if that"
5533 echo "*** is required on your system"
5534 echo "***"
5535 echo "*** If you have an old version installed, it is best to remove it, although"
5536 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
5537 echo "***"
5538 echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
5539 echo "*** came with the system with the command"
5540 echo "***"
5541 echo "*** rpm --erase --nodeps gtk gtk-devel"
5542 else
5543 echo "$as_me: failed program was:" >&5
5544 sed 's/^/| /' conftest.$ac_ext >&5
5545
5546 echo "*** The test program failed to compile or link. See the file config.log for the"
5547 echo "*** exact error that occured. This usually means GLIB was incorrectly installed"
5548 echo "*** or that you have moved GLIB since it was installed. In the latter case, you"
5549 echo "*** may want to edit the glib-config script: $GLIB_CONFIG"
5550 fi
5551 rm -f conftest.err conftest.$ac_objext \
5552 conftest$ac_exeext conftest.$ac_ext
5553 CFLAGS="$ac_save_CFLAGS"
5554 LIBS="$ac_save_LIBS"
5555 fi
5556 fi
5557 GLIB_CFLAGS=""
5558 GLIB_LIBS=""
5559 { { echo "$as_me:$LINENO: error: Unable to find glib with a version >= 1.2.0. Dillo NEEDS glib" >&5
5560 echo "$as_me: error: Unable to find glib with a version >= 1.2.0. Dillo NEEDS glib" >&2;}
5561 { (exit 1); exit 1; }; }
5562 fi
5563
5564
5565 rm -f conf.glibtest
5566
5567
5568
5569
5570 # Check whether --with-gtk-prefix or --without-gtk-prefix was given.
5571 if test "${with_gtk_prefix+set}" = set; then
5572 withval="$with_gtk_prefix"
5573 gtk_config_prefix="$withval"
5574 else
5575 gtk_config_prefix=""
5576 fi;
5577
5578 # Check whether --with-gtk-exec-prefix or --without-gtk-exec-prefix was given.
5579 if test "${with_gtk_exec_prefix+set}" = set; then
5580 withval="$with_gtk_exec_prefix"
5581 gtk_config_exec_prefix="$withval"
5582 else
5583 gtk_config_exec_prefix=""
5584 fi;
5585 # Check whether --enable-gtktest or --disable-gtktest was given.
5586 if test "${enable_gtktest+set}" = set; then
5587 enableval="$enable_gtktest"
5588
5589 else
5590 enable_gtktest=yes
5591 fi;
5592
5593 for module in .
5594 do
5595 case "$module" in
5596 gthread)
5597 gtk_config_args="$gtk_config_args gthread"
5598 ;;
5599 esac
5600 done
5601
5602 if test x$gtk_config_exec_prefix != x ; then
5603 gtk_config_args="$gtk_config_args --exec-prefix=$gtk_config_exec_prefix"
5604 if test x${GTK_CONFIG+set} != xset ; then
5605 GTK_CONFIG=$gtk_config_exec_prefix/bin/gtk-config
5606 fi
5607 fi
5608 if test x$gtk_config_prefix != x ; then
5609 gtk_config_args="$gtk_config_args --prefix=$gtk_config_prefix"
5610 if test x${GTK_CONFIG+set} != xset ; then
5611 GTK_CONFIG=$gtk_config_prefix/bin/gtk-config
5612 fi
5613 fi
5614
5615 # Extract the first word of "gtk-config", so it can be a program name with args.
5616 set dummy gtk-config; ac_word=$2
5617 echo "$as_me:$LINENO: checking for $ac_word" >&5
5618 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
5619 if test "${ac_cv_path_GTK_CONFIG+set}" = set; then
5620 echo $ECHO_N "(cached) $ECHO_C" >&6
5621 else
5622 case $GTK_CONFIG in
5623 [\\/]* | ?:[\\/]*)
5624 ac_cv_path_GTK_CONFIG="$GTK_CONFIG" # Let the user override the test with a path.
5625 ;;
5626 *)
5627 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
5628 for as_dir in $PATH
5629 do
5630 IFS=$as_save_IFS
5631 test -z "$as_dir" && as_dir=.
5632 for ac_exec_ext in '' $ac_executable_extensions; do
5633 if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
5634 ac_cv_path_GTK_CONFIG="$as_dir/$ac_word$ac_exec_ext"
5635 echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
5636 break 2
5637 fi
5638 done
5639 done
5640
5641 test -z "$ac_cv_path_GTK_CONFIG" && ac_cv_path_GTK_CONFIG="no"
5642 ;;
5643 esac
5644 fi
5645 GTK_CONFIG=$ac_cv_path_GTK_CONFIG
5646
5647 if test -n "$GTK_CONFIG"; then
5648 echo "$as_me:$LINENO: result: $GTK_CONFIG" >&5
5649 echo "${ECHO_T}$GTK_CONFIG" >&6
5650 else
5651 echo "$as_me:$LINENO: result: no" >&5
5652 echo "${ECHO_T}no" >&6
5653 fi
5654
5655 min_gtk_version=1.2.0
5656 echo "$as_me:$LINENO: checking for GTK - version >= $min_gtk_version" >&5
5657 echo $ECHO_N "checking for GTK - version >= $min_gtk_version... $ECHO_C" >&6
5658 no_gtk=""
5659 if test "$GTK_CONFIG" = "no" ; then
5660 no_gtk=yes
5661 else
5662 GTK_CFLAGS=`$GTK_CONFIG $gtk_config_args --cflags`
5663 GTK_LIBS=`$GTK_CONFIG $gtk_config_args --libs`
5664 gtk_config_major_version=`$GTK_CONFIG $gtk_config_args --version | \
5665 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'`
5666 gtk_config_minor_version=`$GTK_CONFIG $gtk_config_args --version | \
5667 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'`
5668 gtk_config_micro_version=`$GTK_CONFIG $gtk_config_args --version | \
5669 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'`
5670 if test "x$enable_gtktest" = "xyes" ; then
5671 ac_save_CFLAGS="$CFLAGS"
5672 ac_save_LIBS="$LIBS"
5673 CFLAGS="$CFLAGS $GTK_CFLAGS"
5674 LIBS="$GTK_LIBS $LIBS"
5675 rm -f conf.gtktest
5676 if test "$cross_compiling" = yes; then
5677 echo $ac_n "cross compiling; assumed OK... $ac_c"
5678 else
5679 cat >conftest.$ac_ext <<_ACEOF
5680 /* confdefs.h. */
5681 _ACEOF
5682 cat confdefs.h >>conftest.$ac_ext
5683 cat >>conftest.$ac_ext <<_ACEOF
5684 /* end confdefs.h. */
5685
5686 #include <gtk/gtk.h>
5687 #include <stdio.h>
5688 #include <stdlib.h>
5689
5690 int
5691 main ()
5692 {
5693 int major, minor, micro;
5694 char *tmp_version;
5695
5696 system ("touch conf.gtktest");
5697
5698 /* HP/UX 9 (%@#!) writes to sscanf strings */
5699 tmp_version = g_strdup("$min_gtk_version");
5700 if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
5701 printf("%s, bad version string\n", "$min_gtk_version");
5702 exit(1);
5703 }
5704
5705 if ((gtk_major_version != $gtk_config_major_version) ||
5706 (gtk_minor_version != $gtk_config_minor_version) ||
5707 (gtk_micro_version != $gtk_config_micro_version))
5708 {
5709 printf("\n*** 'gtk-config --version' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n",
5710 $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
5711 gtk_major_version, gtk_minor_version, gtk_micro_version);
5712 printf ("*** was found! If gtk-config was correct, then it is best\n");
5713 printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
5714 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
5715 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
5716 printf("*** required on your system.\n");
5717 printf("*** If gtk-config was wrong, set the environment variable GTK_CONFIG\n");
5718 printf("*** to point to the correct copy of gtk-config, and remove the file config.cache\n");
5719 printf("*** before re-running configure\n");
5720 }
5721 #if defined (GTK_MAJOR_VERSION) && defined (GTK_MINOR_VERSION) && defined (GTK_MICRO_VERSION)
5722 else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
5723 (gtk_minor_version != GTK_MINOR_VERSION) ||
5724 (gtk_micro_version != GTK_MICRO_VERSION))
5725 {
5726 printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
5727 GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
5728 printf("*** library (version %d.%d.%d)\n",
5729 gtk_major_version, gtk_minor_version, gtk_micro_version);
5730 }
5731 #endif /* defined (GTK_MAJOR_VERSION) ... */
5732 else
5733 {
5734 if ((gtk_major_version > major) ||
5735 ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
5736 ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
5737 {
5738 return 0;
5739 }
5740 else
5741 {
5742 printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n",
5743 gtk_major_version, gtk_minor_version, gtk_micro_version);
5744 printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n",
5745 major, minor, micro);
5746 printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
5747 printf("***\n");
5748 printf("*** If you have already installed a sufficiently new version, this error\n");
5749 printf("*** probably means that the wrong copy of the gtk-config shell script is\n");
5750 printf("*** being found. The easiest way to fix this is to remove the old version\n");
5751 printf("*** of GTK+, but you can also set the GTK_CONFIG environment to point to the\n");
5752 printf("*** correct copy of gtk-config. (In this case, you will have to\n");
5753 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
5754 printf("*** so that the correct libraries are found at run-time))\n");
5755 }
5756 }
5757 return 1;
5758 }
5759
5760 _ACEOF
5761 rm -f conftest$ac_exeext
5762 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5763 (eval $ac_link) 2>&5
5764 ac_status=$?
5765 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5766 (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
5767 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5768 (eval $ac_try) 2>&5
5769 ac_status=$?
5770 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5771 (exit $ac_status); }; }; then
5772 :
5773 else
5774 echo "$as_me: program exited with status $ac_status" >&5
5775 echo "$as_me: failed program was:" >&5
5776 sed 's/^/| /' conftest.$ac_ext >&5
5777
5778 ( exit $ac_status )
5779 no_gtk=yes
5780 fi
5781 rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
5782 fi
5783 CFLAGS="$ac_save_CFLAGS"
5784 LIBS="$ac_save_LIBS"
5785 fi
5786 fi
5787 if test "x$no_gtk" = x ; then
5788 echo "$as_me:$LINENO: result: yes" >&5
5789 echo "${ECHO_T}yes" >&6
5790 :
5791 else
5792 echo "$as_me:$LINENO: result: no" >&5
5793 echo "${ECHO_T}no" >&6
5794 if test "$GTK_CONFIG" = "no" ; then
5795 echo "*** The gtk-config script installed by GTK could not be found"
5796 echo "*** If GTK was installed in PREFIX, make sure PREFIX/bin is in"
5797 echo "*** your path, or set the GTK_CONFIG environment variable to the"
5798 echo "*** full path to gtk-config."
5799 else
5800 if test -f conf.gtktest ; then
5801 :
5802 else
5803 echo "*** Could not run GTK test program, checking why..."
5804 CFLAGS="$CFLAGS $GTK_CFLAGS"
5805 LIBS="$LIBS $GTK_LIBS"
5806 cat >conftest.$ac_ext <<_ACEOF
5807 /* confdefs.h. */
5808 _ACEOF
5809 cat confdefs.h >>conftest.$ac_ext
5810 cat >>conftest.$ac_ext <<_ACEOF
5811 /* end confdefs.h. */
5812
5813 #include <gtk/gtk.h>
5814 #include <stdio.h>
5815
5816 int
5817 main ()
5818 {
5819 return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version));
5820 ;
5821 return 0;
5822 }
5823 _ACEOF
5824 rm -f conftest.$ac_objext conftest$ac_exeext
5825 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
5826 (eval $ac_link) 2>conftest.er1
5827 ac_status=$?
5828 grep -v '^ *+' conftest.er1 >conftest.err
5829 rm -f conftest.er1
5830 cat conftest.err >&5
5831 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5832 (exit $ac_status); } &&
5833 { ac_try='test -z "$ac_c_werror_flag"
5834 || test ! -s conftest.err'
5835 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5836 (eval $ac_try) 2>&5
5837 ac_status=$?
5838 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5839 (exit $ac_status); }; } &&
5840 { ac_try='test -s conftest$ac_exeext'
5841 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5842 (eval $ac_try) 2>&5
5843 ac_status=$?
5844 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5845 (exit $ac_status); }; }; then
5846 echo "*** The test program compiled, but did not run. This usually means"
5847 echo "*** that the run-time linker is not finding GTK or finding the wrong"
5848 echo "*** version of GTK. If it is not finding GTK, you'll need to set your"
5849 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
5850 echo "*** to the installed location Also, make sure you have run ldconfig if that"
5851 echo "*** is required on your system"
5852 echo "***"
5853 echo "*** If you have an old version installed, it is best to remove it, although"
5854 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
5855 echo "***"
5856 echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
5857 echo "*** came with the system with the command"
5858 echo "***"
5859 echo "*** rpm --erase --nodeps gtk gtk-devel"
5860 else
5861 echo "$as_me: failed program was:" >&5
5862 sed 's/^/| /' conftest.$ac_ext >&5
5863
5864 echo "*** The test program failed to compile or link. See the file config.log for the"
5865 echo "*** exact error that occured. This usually means GTK was incorrectly installed"
5866 echo "*** or that you have moved GTK since it was installed. In the latter case, you"
5867 echo "*** may want to edit the gtk-config script: $GTK_CONFIG"
5868 fi
5869 rm -f conftest.err conftest.$ac_objext \
5870 conftest$ac_exeext conftest.$ac_ext
5871 CFLAGS="$ac_save_CFLAGS"
5872 LIBS="$ac_save_LIBS"
5873 fi
5874 fi
5875 GTK_CFLAGS=""
5876 GTK_LIBS=""
5877 { { echo "$as_me:$LINENO: error: Unable to find Gtk+ with a version >= 1.2.0. Dillo NEEDS Gtk+" >&5
5878 echo "$as_me: error: Unable to find Gtk+ with a version >= 1.2.0. Dillo NEEDS Gtk+" >&2;}
5879 { (exit 1); exit 1; }; }
5880 fi
5881
5882
5883 rm -f conf.gtktest
5884
5885
5886 echo "$as_me:$LINENO: checking Hackish check for FLTK" >&5
5887 echo $ECHO_N "checking Hackish check for FLTK... $ECHO_C" >&6
5888 LIBFLTK_CXXFLAGS=`fltk-config --cxxflags`
5889 LIBFLTK_LIBS=`fltk-config --ldflags`
5890
5891
5892 if test "x$enable_jpeg" = "xyes"; then
5893
5894 echo "$as_me:$LINENO: checking for egrep" >&5
5895 echo $ECHO_N "checking for egrep... $ECHO_C" >&6
5896 if test "${ac_cv_prog_egrep+set}" = set; then
5897 echo $ECHO_N "(cached) $ECHO_C" >&6
5898 else
5899 if echo a | (grep -E '(a|b)') >/dev/null 2>&1
5900 then ac_cv_prog_egrep='grep -E'
5901 else ac_cv_prog_egrep='egrep'
5902 fi
5903 fi
5904 echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
5905 echo "${ECHO_T}$ac_cv_prog_egrep" >&6
5906 EGREP=$ac_cv_prog_egrep
5907
5908
5909 echo "$as_me:$LINENO: checking for ANSI C header files" >&5
5910 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
5911 if test "${ac_cv_header_stdc+set}" = set; then
5912 echo $ECHO_N "(cached) $ECHO_C" >&6
5913 else
5914 cat >conftest.$ac_ext <<_ACEOF
5915 /* confdefs.h. */
5916 _ACEOF
5917 cat confdefs.h >>conftest.$ac_ext
5918 cat >>conftest.$ac_ext <<_ACEOF
5919 /* end confdefs.h. */
5920 #include <stdlib.h>
5921 #include <stdarg.h>
5922 #include <string.h>
5923 #include <float.h>
5924
5925 int
5926 main ()
5927 {
5928
5929 ;
5930 return 0;
5931 }
5932 _ACEOF
5933 rm -f conftest.$ac_objext
5934 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
5935 (eval $ac_compile) 2>conftest.er1
5936 ac_status=$?
5937 grep -v '^ *+' conftest.er1 >conftest.err
5938 rm -f conftest.er1
5939 cat conftest.err >&5
5940 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5941 (exit $ac_status); } &&
5942 { ac_try='test -z "$ac_c_werror_flag"
5943 || test ! -s conftest.err'
5944 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5945 (eval $ac_try) 2>&5
5946 ac_status=$?
5947 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5948 (exit $ac_status); }; } &&
5949 { ac_try='test -s conftest.$ac_objext'
5950 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
5951 (eval $ac_try) 2>&5
5952 ac_status=$?
5953 echo "$as_me:$LINENO: \$? = $ac_status" >&5
5954 (exit $ac_status); }; }; then
5955 ac_cv_header_stdc=yes
5956 else
5957 echo "$as_me: failed program was:" >&5
5958 sed 's/^/| /' conftest.$ac_ext >&5
5959
5960 ac_cv_header_stdc=no
5961 fi
5962 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
5963
5964 if test $ac_cv_header_stdc = yes; then
5965 # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
5966 cat >conftest.$ac_ext <<_ACEOF
5967 /* confdefs.h. */
5968 _ACEOF
5969 cat confdefs.h >>conftest.$ac_ext
5970 cat >>conftest.$ac_ext <<_ACEOF
5971 /* end confdefs.h. */
5972 #include <string.h>
5973
5974 _ACEOF
5975 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
5976 $EGREP "memchr" >/dev/null 2>&1; then
5977 :
5978 else
5979 ac_cv_header_stdc=no
5980 fi
5981 rm -f conftest*
5982
5983 fi
5984
5985 if test $ac_cv_header_stdc = yes; then
5986 # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
5987 cat >conftest.$ac_ext <<_ACEOF
5988 /* confdefs.h. */
5989 _ACEOF
5990 cat confdefs.h >>conftest.$ac_ext
5991 cat >>conftest.$ac_ext <<_ACEOF
5992 /* end confdefs.h. */
5993 #include <stdlib.h>
5994
5995 _ACEOF
5996 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
5997 $EGREP "free" >/dev/null 2>&1; then
5998 :
5999 else
6000 ac_cv_header_stdc=no
6001 fi
6002 rm -f conftest*
6003
6004 fi
6005
6006 if test $ac_cv_header_stdc = yes; then
6007 # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
6008 if test "$cross_compiling" = yes; then
6009 :
6010 else
6011 cat >conftest.$ac_ext <<_ACEOF
6012 /* confdefs.h. */
6013 _ACEOF
6014 cat confdefs.h >>conftest.$ac_ext
6015 cat >>conftest.$ac_ext <<_ACEOF
6016 /* end confdefs.h. */
6017 #include <ctype.h>
6018 #if ((' ' & 0x0FF) == 0x020)
6019 # define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
6020 # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
6021 #else
6022 # define ISLOWER(c) \
6023 (('a' <= (c) && (c) <= 'i') \
6024 || ('j' <= (c) && (c) <= 'r') \
6025 || ('s' <= (c) && (c) <= 'z'))
6026 # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
6027 #endif
6028
6029 #define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
6030 int
6031 main ()
6032 {
6033 int i;
6034 for (i = 0; i < 256; i++)
6035 if (XOR (islower (i), ISLOWER (i))
6036 || toupper (i) != TOUPPER (i))
6037 exit(2);
6038 exit (0);
6039 }
6040 _ACEOF
6041 rm -f conftest$ac_exeext
6042 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
6043 (eval $ac_link) 2>&5
6044 ac_status=$?
6045 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6046 (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
6047 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6048 (eval $ac_try) 2>&5
6049 ac_status=$?
6050 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6051 (exit $ac_status); }; }; then
6052 :
6053 else
6054 echo "$as_me: program exited with status $ac_status" >&5
6055 echo "$as_me: failed program was:" >&5
6056 sed 's/^/| /' conftest.$ac_ext >&5
6057
6058 ( exit $ac_status )
6059 ac_cv_header_stdc=no
6060 fi
6061 rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
6062 fi
6063 fi
6064 fi
6065 echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
6066 echo "${ECHO_T}$ac_cv_header_stdc" >&6
6067 if test $ac_cv_header_stdc = yes; then
6068
6069 cat >>confdefs.h <<\_ACEOF
6070 #define STDC_HEADERS 1
6071 _ACEOF
6072
6073 fi
6074
6075 # On IRIX 5.3, sys/types and inttypes.h are conflicting.
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085 for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
6086 inttypes.h stdint.h unistd.h
6087 do
6088 as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
6089 echo "$as_me:$LINENO: checking for $ac_header" >&5
6090 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
6091 if eval "test \"\${$as_ac_Header+set}\" = set"; then
6092 echo $ECHO_N "(cached) $ECHO_C" >&6
6093 else
6094 cat >conftest.$ac_ext <<_ACEOF
6095 /* confdefs.h. */
6096 _ACEOF
6097 cat confdefs.h >>conftest.$ac_ext
6098 cat >>conftest.$ac_ext <<_ACEOF
6099 /* end confdefs.h. */
6100 $ac_includes_default
6101
6102 #include <$ac_header>
6103 _ACEOF
6104 rm -f conftest.$ac_objext
6105 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
6106 (eval $ac_compile) 2>conftest.er1
6107 ac_status=$?
6108 grep -v '^ *+' conftest.er1 >conftest.err
6109 rm -f conftest.er1
6110 cat conftest.err >&5
6111 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6112 (exit $ac_status); } &&
6113 { ac_try='test -z "$ac_c_werror_flag"
6114 || test ! -s conftest.err'
6115 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6116 (eval $ac_try) 2>&5
6117 ac_status=$?
6118 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6119 (exit $ac_status); }; } &&
6120 { ac_try='test -s conftest.$ac_objext'
6121 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6122 (eval $ac_try) 2>&5
6123 ac_status=$?
6124 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6125 (exit $ac_status); }; }; then
6126 eval "$as_ac_Header=yes"
6127 else
6128 echo "$as_me: failed program was:" >&5
6129 sed 's/^/| /' conftest.$ac_ext >&5
6130
6131 eval "$as_ac_Header=no"
6132 fi
6133 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
6134 fi
6135 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
6136 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
6137 if test `eval echo '${'$as_ac_Header'}'` = yes; then
6138 cat >>confdefs.h <<_ACEOF
6139 #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
6140 _ACEOF
6141
6142 fi
6143
6144 done
6145
6146
6147 if test "${ac_cv_header_jpeglib_h+set}" = set; then
6148 echo "$as_me:$LINENO: checking for jpeglib.h" >&5
6149 echo $ECHO_N "checking for jpeglib.h... $ECHO_C" >&6
6150 if test "${ac_cv_header_jpeglib_h+set}" = set; then
6151 echo $ECHO_N "(cached) $ECHO_C" >&6
6152 fi
6153 echo "$as_me:$LINENO: result: $ac_cv_header_jpeglib_h" >&5
6154 echo "${ECHO_T}$ac_cv_header_jpeglib_h" >&6
6155 else
6156 # Is the header compilable?
6157 echo "$as_me:$LINENO: checking jpeglib.h usability" >&5
6158 echo $ECHO_N "checking jpeglib.h usability... $ECHO_C" >&6
6159 cat >conftest.$ac_ext <<_ACEOF
6160 /* confdefs.h. */
6161 _ACEOF
6162 cat confdefs.h >>conftest.$ac_ext
6163 cat >>conftest.$ac_ext <<_ACEOF
6164 /* end confdefs.h. */
6165 $ac_includes_default
6166 #include <jpeglib.h>
6167 _ACEOF
6168 rm -f conftest.$ac_objext
6169 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
6170 (eval $ac_compile) 2>conftest.er1
6171 ac_status=$?
6172 grep -v '^ *+' conftest.er1 >conftest.err
6173 rm -f conftest.er1
6174 cat conftest.err >&5
6175 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6176 (exit $ac_status); } &&
6177 { ac_try='test -z "$ac_c_werror_flag"
6178 || test ! -s conftest.err'
6179 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6180 (eval $ac_try) 2>&5
6181 ac_status=$?
6182 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6183 (exit $ac_status); }; } &&
6184 { ac_try='test -s conftest.$ac_objext'
6185 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6186 (eval $ac_try) 2>&5
6187 ac_status=$?
6188 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6189 (exit $ac_status); }; }; then
6190 ac_header_compiler=yes
6191 else
6192 echo "$as_me: failed program was:" >&5
6193 sed 's/^/| /' conftest.$ac_ext >&5
6194
6195 ac_header_compiler=no
6196 fi
6197 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
6198 echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
6199 echo "${ECHO_T}$ac_header_compiler" >&6
6200
6201 # Is the header present?
6202 echo "$as_me:$LINENO: checking jpeglib.h presence" >&5
6203 echo $ECHO_N "checking jpeglib.h presence... $ECHO_C" >&6
6204 cat >conftest.$ac_ext <<_ACEOF
6205 /* confdefs.h. */
6206 _ACEOF
6207 cat confdefs.h >>conftest.$ac_ext
6208 cat >>conftest.$ac_ext <<_ACEOF
6209 /* end confdefs.h. */
6210 #include <jpeglib.h>
6211 _ACEOF
6212 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
6213 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
6214 ac_status=$?
6215 grep -v '^ *+' conftest.er1 >conftest.err
6216 rm -f conftest.er1
6217 cat conftest.err >&5
6218 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6219 (exit $ac_status); } >/dev/null; then
6220 if test -s conftest.err; then
6221 ac_cpp_err=$ac_c_preproc_warn_flag
6222 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
6223 else
6224 ac_cpp_err=
6225 fi
6226 else
6227 ac_cpp_err=yes
6228 fi
6229 if test -z "$ac_cpp_err"; then
6230 ac_header_preproc=yes
6231 else
6232 echo "$as_me: failed program was:" >&5
6233 sed 's/^/| /' conftest.$ac_ext >&5
6234
6235 ac_header_preproc=no
6236 fi
6237 rm -f conftest.err conftest.$ac_ext
6238 echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
6239 echo "${ECHO_T}$ac_header_preproc" >&6
6240
6241 # So? What about this header?
6242 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
6243 yes:no: )
6244 { echo "$as_me:$LINENO: WARNING: jpeglib.h: accepted by the compiler, rejected by the preprocessor!" >&5
6245 echo "$as_me: WARNING: jpeglib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
6246 { echo "$as_me:$LINENO: WARNING: jpeglib.h: proceeding with the compiler's result" >&5
6247 echo "$as_me: WARNING: jpeglib.h: proceeding with the compiler's result" >&2;}
6248 ac_header_preproc=yes
6249 ;;
6250 no:yes:* )
6251 { echo "$as_me:$LINENO: WARNING: jpeglib.h: present but cannot be compiled" >&5
6252 echo "$as_me: WARNING: jpeglib.h: present but cannot be compiled" >&2;}
6253 { echo "$as_me:$LINENO: WARNING: jpeglib.h: check for missing prerequisite headers?" >&5
6254 echo "$as_me: WARNING: jpeglib.h: check for missing prerequisite headers?" >&2;}
6255 { echo "$as_me:$LINENO: WARNING: jpeglib.h: see the Autoconf documentation" >&5
6256 echo "$as_me: WARNING: jpeglib.h: see the Autoconf documentation" >&2;}
6257 { echo "$as_me:$LINENO: WARNING: jpeglib.h: section \"Present But Cannot Be Compiled\"" >&5
6258 echo "$as_me: WARNING: jpeglib.h: section \"Present But Cannot Be Compiled\"" >&2;}
6259 { echo "$as_me:$LINENO: WARNING: jpeglib.h: proceeding with the preprocessor's result" >&5
6260 echo "$as_me: WARNING: jpeglib.h: proceeding with the preprocessor's result" >&2;}
6261 { echo "$as_me:$LINENO: WARNING: jpeglib.h: in the future, the compiler will take precedence" >&5
6262 echo "$as_me: WARNING: jpeglib.h: in the future, the compiler will take precedence" >&2;}
6263 (
6264 cat <<\_ASBOX
6265 ## ------------------------------------------ ##
6266 ## Report this to the AC_PACKAGE_NAME lists. ##
6267 ## ------------------------------------------ ##
6268 _ASBOX
6269 ) |
6270 sed "s/^/$as_me: WARNING: /" >&2
6271 ;;
6272 esac
6273 echo "$as_me:$LINENO: checking for jpeglib.h" >&5
6274 echo $ECHO_N "checking for jpeglib.h... $ECHO_C" >&6
6275 if test "${ac_cv_header_jpeglib_h+set}" = set; then
6276 echo $ECHO_N "(cached) $ECHO_C" >&6
6277 else
6278 ac_cv_header_jpeglib_h=$ac_header_preproc
6279 fi
6280 echo "$as_me:$LINENO: result: $ac_cv_header_jpeglib_h" >&5
6281 echo "${ECHO_T}$ac_cv_header_jpeglib_h" >&6
6282
6283 fi
6284 if test $ac_cv_header_jpeglib_h = yes; then
6285 jpeg_ok=yes
6286 else
6287 jpeg_ok=no
6288 fi
6289
6290
6291
6292 if test "x$jpeg_ok" = "xyes"; then
6293 old_libs="$LIBS"
6294 echo "$as_me:$LINENO: checking for jpeg_destroy_decompress in -ljpeg" >&5
6295 echo $ECHO_N "checking for jpeg_destroy_decompress in -ljpeg... $ECHO_C" >&6
6296 if test "${ac_cv_lib_jpeg_jpeg_destroy_decompress+set}" = set; then
6297 echo $ECHO_N "(cached) $ECHO_C" >&6
6298 else
6299 ac_check_lib_save_LIBS=$LIBS
6300 LIBS="-ljpeg $LIBS"
6301 cat >conftest.$ac_ext <<_ACEOF
6302 /* confdefs.h. */
6303 _ACEOF
6304 cat confdefs.h >>conftest.$ac_ext
6305 cat >>conftest.$ac_ext <<_ACEOF
6306 /* end confdefs.h. */
6307
6308 /* Override any gcc2 internal prototype to avoid an error. */
6309 #ifdef __cplusplus
6310 extern "C"
6311 #endif
6312 /* We use char because int might match the return type of a gcc2
6313 builtin and then its argument prototype would still apply. */
6314 char jpeg_destroy_decompress ();
6315 int
6316 main ()
6317 {
6318 jpeg_destroy_decompress ();
6319 ;
6320 return 0;
6321 }
6322 _ACEOF
6323 rm -f conftest.$ac_objext conftest$ac_exeext
6324 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
6325 (eval $ac_link) 2>conftest.er1
6326 ac_status=$?
6327 grep -v '^ *+' conftest.er1 >conftest.err
6328 rm -f conftest.er1
6329 cat conftest.err >&5
6330 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6331 (exit $ac_status); } &&
6332 { ac_try='test -z "$ac_c_werror_flag"
6333 || test ! -s conftest.err'
6334 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6335 (eval $ac_try) 2>&5
6336 ac_status=$?
6337 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6338 (exit $ac_status); }; } &&
6339 { ac_try='test -s conftest$ac_exeext'
6340 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6341 (eval $ac_try) 2>&5
6342 ac_status=$?
6343 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6344 (exit $ac_status); }; }; then
6345 ac_cv_lib_jpeg_jpeg_destroy_decompress=yes
6346 else
6347 echo "$as_me: failed program was:" >&5
6348 sed 's/^/| /' conftest.$ac_ext >&5
6349
6350 ac_cv_lib_jpeg_jpeg_destroy_decompress=no
6351 fi
6352 rm -f conftest.err conftest.$ac_objext \
6353 conftest$ac_exeext conftest.$ac_ext
6354 LIBS=$ac_check_lib_save_LIBS
6355 fi
6356 echo "$as_me:$LINENO: result: $ac_cv_lib_jpeg_jpeg_destroy_decompress" >&5
6357 echo "${ECHO_T}$ac_cv_lib_jpeg_jpeg_destroy_decompress" >&6
6358 if test $ac_cv_lib_jpeg_jpeg_destroy_decompress = yes; then
6359 jpeg_ok=yes
6360 else
6361 jpeg_ok=no
6362 fi
6363
6364 LIBS="$old_libs"
6365 fi
6366
6367 if test "x$jpeg_ok" = "xyes"; then
6368 LIBJPEG_LIBS="-ljpeg"
6369 if test -n "$LIBJPEG_LIBDIR"; then
6370 LIBJPEG_LDFLAGS="-L$LIBJPEG_LIBDIR"
6371 fi
6372 if test -n "$LIBJPEG_INCDIR"; then
6373 LIBJPEG_CPPFLAGS="-I$LIBJPEG_INCDIR"
6374 fi
6375 else
6376 { echo "$as_me:$LINENO: WARNING: *** No libjpeg found. Disabling jpeg images.***" >&5
6377 echo "$as_me: WARNING: *** No libjpeg found. Disabling jpeg images.***" >&2;}
6378 fi
6379 fi
6380
6381 if test "x$jpeg_ok" = "xyes"; then
6382
6383 cat >>confdefs.h <<\_ACEOF
6384 #define ENABLE_JPEG
6385 _ACEOF
6386
6387 fi
6388
6389 if test "x$enable_png" = "xyes"; then
6390 if test "${ac_cv_header_zlib_h+set}" = set; then
6391 echo "$as_me:$LINENO: checking for zlib.h" >&5
6392 echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6
6393 if test "${ac_cv_header_zlib_h+set}" = set; then
6394 echo $ECHO_N "(cached) $ECHO_C" >&6
6395 fi
6396 echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5
6397 echo "${ECHO_T}$ac_cv_header_zlib_h" >&6
6398 else
6399 # Is the header compilable?
6400 echo "$as_me:$LINENO: checking zlib.h usability" >&5
6401 echo $ECHO_N "checking zlib.h usability... $ECHO_C" >&6
6402 cat >conftest.$ac_ext <<_ACEOF
6403 /* confdefs.h. */
6404 _ACEOF
6405 cat confdefs.h >>conftest.$ac_ext
6406 cat >>conftest.$ac_ext <<_ACEOF
6407 /* end confdefs.h. */
6408 $ac_includes_default
6409 #include <zlib.h>
6410 _ACEOF
6411 rm -f conftest.$ac_objext
6412 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
6413 (eval $ac_compile) 2>conftest.er1
6414 ac_status=$?
6415 grep -v '^ *+' conftest.er1 >conftest.err
6416 rm -f conftest.er1
6417 cat conftest.err >&5
6418 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6419 (exit $ac_status); } &&
6420 { ac_try='test -z "$ac_c_werror_flag"
6421 || test ! -s conftest.err'
6422 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6423 (eval $ac_try) 2>&5
6424 ac_status=$?
6425 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6426 (exit $ac_status); }; } &&
6427 { ac_try='test -s conftest.$ac_objext'
6428 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6429 (eval $ac_try) 2>&5
6430 ac_status=$?
6431 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6432 (exit $ac_status); }; }; then
6433 ac_header_compiler=yes
6434 else
6435 echo "$as_me: failed program was:" >&5
6436 sed 's/^/| /' conftest.$ac_ext >&5
6437
6438 ac_header_compiler=no
6439 fi
6440 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
6441 echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
6442 echo "${ECHO_T}$ac_header_compiler" >&6
6443
6444 # Is the header present?
6445 echo "$as_me:$LINENO: checking zlib.h presence" >&5
6446 echo $ECHO_N "checking zlib.h presence... $ECHO_C" >&6
6447 cat >conftest.$ac_ext <<_ACEOF
6448 /* confdefs.h. */
6449 _ACEOF
6450 cat confdefs.h >>conftest.$ac_ext
6451 cat >>conftest.$ac_ext <<_ACEOF
6452 /* end confdefs.h. */
6453 #include <zlib.h>
6454 _ACEOF
6455 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
6456 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
6457 ac_status=$?
6458 grep -v '^ *+' conftest.er1 >conftest.err
6459 rm -f conftest.er1
6460 cat conftest.err >&5
6461 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6462 (exit $ac_status); } >/dev/null; then
6463 if test -s conftest.err; then
6464 ac_cpp_err=$ac_c_preproc_warn_flag
6465 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
6466 else
6467 ac_cpp_err=
6468 fi
6469 else
6470 ac_cpp_err=yes
6471 fi
6472 if test -z "$ac_cpp_err"; then
6473 ac_header_preproc=yes
6474 else
6475 echo "$as_me: failed program was:" >&5
6476 sed 's/^/| /' conftest.$ac_ext >&5
6477
6478 ac_header_preproc=no
6479 fi
6480 rm -f conftest.err conftest.$ac_ext
6481 echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
6482 echo "${ECHO_T}$ac_header_preproc" >&6
6483
6484 # So? What about this header?
6485 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
6486 yes:no: )
6487 { echo "$as_me:$LINENO: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
6488 echo "$as_me: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
6489 { echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the compiler's result" >&5
6490 echo "$as_me: WARNING: zlib.h: proceeding with the compiler's result" >&2;}
6491 ac_header_preproc=yes
6492 ;;
6493 no:yes:* )
6494 { echo "$as_me:$LINENO: WARNING: zlib.h: present but cannot be compiled" >&5
6495 echo "$as_me: WARNING: zlib.h: present but cannot be compiled" >&2;}
6496 { echo "$as_me:$LINENO: WARNING: zlib.h: check for missing prerequisite headers?" >&5
6497 echo "$as_me: WARNING: zlib.h: check for missing prerequisite headers?" >&2;}
6498 { echo "$as_me:$LINENO: WARNING: zlib.h: see the Autoconf documentation" >&5
6499 echo "$as_me: WARNING: zlib.h: see the Autoconf documentation" >&2;}
6500 { echo "$as_me:$LINENO: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&5
6501 echo "$as_me: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
6502 { echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the preprocessor's result" >&5
6503 echo "$as_me: WARNING: zlib.h: proceeding with the preprocessor's result" >&2;}
6504 { echo "$as_me:$LINENO: WARNING: zlib.h: in the future, the compiler will take precedence" >&5
6505 echo "$as_me: WARNING: zlib.h: in the future, the compiler will take precedence" >&2;}
6506 (
6507 cat <<\_ASBOX
6508 ## ------------------------------------------ ##
6509 ## Report this to the AC_PACKAGE_NAME lists. ##
6510 ## ------------------------------------------ ##
6511 _ASBOX
6512 ) |
6513 sed "s/^/$as_me: WARNING: /" >&2
6514 ;;
6515 esac
6516 echo "$as_me:$LINENO: checking for zlib.h" >&5
6517 echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6
6518 if test "${ac_cv_header_zlib_h+set}" = set; then
6519 echo $ECHO_N "(cached) $ECHO_C" >&6
6520 else
6521 ac_cv_header_zlib_h=$ac_header_preproc
6522 fi
6523 echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5
6524 echo "${ECHO_T}$ac_cv_header_zlib_h" >&6
6525
6526 fi
6527 if test $ac_cv_header_zlib_h = yes; then
6528 libz_ok=yes
6529 else
6530 libz_ok=no
6531 fi
6532
6533
6534
6535 if test "x$libz_ok" = "xyes"; then
6536 old_libs="$LIBS"
6537 echo "$as_me:$LINENO: checking for zlibVersion in -lz" >&5
6538 echo $ECHO_N "checking for zlibVersion in -lz... $ECHO_C" >&6
6539 if test "${ac_cv_lib_z_zlibVersion+set}" = set; then
6540 echo $ECHO_N "(cached) $ECHO_C" >&6
6541 else
6542 ac_check_lib_save_LIBS=$LIBS
6543 LIBS="-lz $LIBS"
6544 cat >conftest.$ac_ext <<_ACEOF
6545 /* confdefs.h. */
6546 _ACEOF
6547 cat confdefs.h >>conftest.$ac_ext
6548 cat >>conftest.$ac_ext <<_ACEOF
6549 /* end confdefs.h. */
6550
6551 /* Override any gcc2 internal prototype to avoid an error. */
6552 #ifdef __cplusplus
6553 extern "C"
6554 #endif
6555 /* We use char because int might match the return type of a gcc2
6556 builtin and then its argument prototype would still apply. */
6557 char zlibVersion ();
6558 int
6559 main ()
6560 {
6561 zlibVersion ();
6562 ;
6563 return 0;
6564 }
6565 _ACEOF
6566 rm -f conftest.$ac_objext conftest$ac_exeext
6567 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
6568 (eval $ac_link) 2>conftest.er1
6569 ac_status=$?
6570 grep -v '^ *+' conftest.er1 >conftest.err
6571 rm -f conftest.er1
6572 cat conftest.err >&5
6573 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6574 (exit $ac_status); } &&
6575 { ac_try='test -z "$ac_c_werror_flag"
6576 || test ! -s conftest.err'
6577 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6578 (eval $ac_try) 2>&5
6579 ac_status=$?
6580 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6581 (exit $ac_status); }; } &&
6582 { ac_try='test -s conftest$ac_exeext'
6583 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6584 (eval $ac_try) 2>&5
6585 ac_status=$?
6586 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6587 (exit $ac_status); }; }; then
6588 ac_cv_lib_z_zlibVersion=yes
6589 else
6590 echo "$as_me: failed program was:" >&5
6591 sed 's/^/| /' conftest.$ac_ext >&5
6592
6593 ac_cv_lib_z_zlibVersion=no
6594 fi
6595 rm -f conftest.err conftest.$ac_objext \
6596 conftest$ac_exeext conftest.$ac_ext
6597 LIBS=$ac_check_lib_save_LIBS
6598 fi
6599 echo "$as_me:$LINENO: result: $ac_cv_lib_z_zlibVersion" >&5
6600 echo "${ECHO_T}$ac_cv_lib_z_zlibVersion" >&6
6601 if test $ac_cv_lib_z_zlibVersion = yes; then
6602 libz_ok=yes
6603 else
6604 libz_ok=no
6605 fi
6606
6607 LIBS="$old_libs"
6608 fi
6609
6610 if test "x$libz_ok" = xyes; then
6611 LIBZ_LIBS="-lz"
6612 else
6613 { echo "$as_me:$LINENO: WARNING: *** No libz found. Disabling PNG images ***" >&5
6614 echo "$as_me: WARNING: *** No libz found. Disabling PNG images ***" >&2;}
6615 fi
6616 fi
6617
6618 if test "x$enable_png" = "xyes" && test "x$libz_ok" = "xyes"; then
6619 echo "$as_me:$LINENO: checking for libpng-config" >&5
6620 echo $ECHO_N "checking for libpng-config... $ECHO_C" >&6
6621
6622 if test -z "$PNG_CONFIG"; then
6623 PNG_CONFIG=`which libpng12-config`
6624 if test -z "$PNG_CONFIG"; then
6625 PNG_CONFIG=`which libpng-config`
6626 fi
6627 if test -z "$PNG_CONFIG"; then
6628 PNG_CONFIG=`which libpng10-config`
6629 fi
6630 fi
6631
6632 if test -n "$PNG_CONFIG" && test -x "$PNG_CONFIG"; then
6633 echo "$as_me:$LINENO: result: $PNG_CONFIG" >&5
6634 echo "${ECHO_T}$PNG_CONFIG" >&6
6635 png_ok="yes"
6636 else
6637 echo "$as_me:$LINENO: result: missing" >&5
6638 echo "${ECHO_T}missing" >&6
6639 png_ok="no"
6640 fi
6641
6642 if test "x$png_ok" = "xyes"; then
6643 echo "$as_me:$LINENO: checking for libpng version" >&5
6644 echo $ECHO_N "checking for libpng version... $ECHO_C" >&6
6645 png_version=`$PNG_CONFIG --version`
6646 case $png_version in
6647 1.2.*) echo "$as_me:$LINENO: result: $png_version (newer version)" >&5
6648 echo "${ECHO_T}$png_version (newer version)" >&6 ;;
6649 1.0.*) echo "$as_me:$LINENO: result: $png_version (older version)" >&5
6650 echo "${ECHO_T}$png_version (older version)" >&6 ;;
6651 *) echo "$as_me:$LINENO: result: ERROR" >&5
6652 echo "${ECHO_T}ERROR" >&6 ;;
6653 esac
6654
6655 LIBPNG_CFLAGS=`$PNG_CONFIG --cflags`
6656 LIBPNG_LIBS=`$PNG_CONFIG --ldflags`
6657 case $png_version in
6658 1.2.4*) LIBPNG_LIBS="$LIBPNG_LIBS `$PNG_CONFIG --libs`" ;;
6659 esac
6660 else
6661
6662
6663 for ac_header in png.h libpng/png.h
6664 do
6665 as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
6666 if eval "test \"\${$as_ac_Header+set}\" = set"; then
6667 echo "$as_me:$LINENO: checking for $ac_header" >&5
6668 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
6669 if eval "test \"\${$as_ac_Header+set}\" = set"; then
6670 echo $ECHO_N "(cached) $ECHO_C" >&6
6671 fi
6672 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
6673 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
6674 else
6675 # Is the header compilable?
6676 echo "$as_me:$LINENO: checking $ac_header usability" >&5
6677 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
6678 cat >conftest.$ac_ext <<_ACEOF
6679 /* confdefs.h. */
6680 _ACEOF
6681 cat confdefs.h >>conftest.$ac_ext
6682 cat >>conftest.$ac_ext <<_ACEOF
6683 /* end confdefs.h. */
6684 $ac_includes_default
6685 #include <$ac_header>
6686 _ACEOF
6687 rm -f conftest.$ac_objext
6688 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
6689 (eval $ac_compile) 2>conftest.er1
6690 ac_status=$?
6691 grep -v '^ *+' conftest.er1 >conftest.err
6692 rm -f conftest.er1
6693 cat conftest.err >&5
6694 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6695 (exit $ac_status); } &&
6696 { ac_try='test -z "$ac_c_werror_flag"
6697 || test ! -s conftest.err'
6698 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6699 (eval $ac_try) 2>&5
6700 ac_status=$?
6701 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6702 (exit $ac_status); }; } &&
6703 { ac_try='test -s conftest.$ac_objext'
6704 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6705 (eval $ac_try) 2>&5
6706 ac_status=$?
6707 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6708 (exit $ac_status); }; }; then
6709 ac_header_compiler=yes
6710 else
6711 echo "$as_me: failed program was:" >&5
6712 sed 's/^/| /' conftest.$ac_ext >&5
6713
6714 ac_header_compiler=no
6715 fi
6716 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
6717 echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
6718 echo "${ECHO_T}$ac_header_compiler" >&6
6719
6720 # Is the header present?
6721 echo "$as_me:$LINENO: checking $ac_header presence" >&5
6722 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
6723 cat >conftest.$ac_ext <<_ACEOF
6724 /* confdefs.h. */
6725 _ACEOF
6726 cat confdefs.h >>conftest.$ac_ext
6727 cat >>conftest.$ac_ext <<_ACEOF
6728 /* end confdefs.h. */
6729 #include <$ac_header>
6730 _ACEOF
6731 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
6732 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
6733 ac_status=$?
6734 grep -v '^ *+' conftest.er1 >conftest.err
6735 rm -f conftest.er1
6736 cat conftest.err >&5
6737 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6738 (exit $ac_status); } >/dev/null; then
6739 if test -s conftest.err; then
6740 ac_cpp_err=$ac_c_preproc_warn_flag
6741 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
6742 else
6743 ac_cpp_err=
6744 fi
6745 else
6746 ac_cpp_err=yes
6747 fi
6748 if test -z "$ac_cpp_err"; then
6749 ac_header_preproc=yes
6750 else
6751 echo "$as_me: failed program was:" >&5
6752 sed 's/^/| /' conftest.$ac_ext >&5
6753
6754 ac_header_preproc=no
6755 fi
6756 rm -f conftest.err conftest.$ac_ext
6757 echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
6758 echo "${ECHO_T}$ac_header_preproc" >&6
6759
6760 # So? What about this header?
6761 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
6762 yes:no: )
6763 { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
6764 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
6765 { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
6766 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
6767 ac_header_preproc=yes
6768 ;;
6769 no:yes:* )
6770 { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
6771 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
6772 { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
6773 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
6774 { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
6775 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
6776 { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
6777 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
6778 { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
6779 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
6780 { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
6781 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
6782 (
6783 cat <<\_ASBOX
6784 ## ------------------------------------------ ##
6785 ## Report this to the AC_PACKAGE_NAME lists. ##
6786 ## ------------------------------------------ ##
6787 _ASBOX
6788 ) |
6789 sed "s/^/$as_me: WARNING: /" >&2
6790 ;;
6791 esac
6792 echo "$as_me:$LINENO: checking for $ac_header" >&5
6793 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
6794 if eval "test \"\${$as_ac_Header+set}\" = set"; then
6795 echo $ECHO_N "(cached) $ECHO_C" >&6
6796 else
6797 eval "$as_ac_Header=\$ac_header_preproc"
6798 fi
6799 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
6800 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
6801
6802 fi
6803 if test `eval echo '${'$as_ac_Header'}'` = yes; then
6804 cat >>confdefs.h <<_ACEOF
6805 #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
6806 _ACEOF
6807 png_ok=yes && break
6808 else
6809 png_ok=no
6810 fi
6811
6812 done
6813
6814
6815 if test "x$png_ok" = "xyes"; then
6816 old_libs="$LIBS"
6817 echo "$as_me:$LINENO: checking for png_check_sig in -lpng" >&5
6818 echo $ECHO_N "checking for png_check_sig in -lpng... $ECHO_C" >&6
6819 if test "${ac_cv_lib_png_png_check_sig+set}" = set; then
6820 echo $ECHO_N "(cached) $ECHO_C" >&6
6821 else
6822 ac_check_lib_save_LIBS=$LIBS
6823 LIBS="-lpng $LIBZ_LIBS -lm $LIBS"
6824 cat >conftest.$ac_ext <<_ACEOF
6825 /* confdefs.h. */
6826 _ACEOF
6827 cat confdefs.h >>conftest.$ac_ext
6828 cat >>conftest.$ac_ext <<_ACEOF
6829 /* end confdefs.h. */
6830
6831 /* Override any gcc2 internal prototype to avoid an error. */
6832 #ifdef __cplusplus
6833 extern "C"
6834 #endif
6835 /* We use char because int might match the return type of a gcc2
6836 builtin and then its argument prototype would still apply. */
6837 char png_check_sig ();
6838 int
6839 main ()
6840 {
6841 png_check_sig ();
6842 ;
6843 return 0;
6844 }
6845 _ACEOF
6846 rm -f conftest.$ac_objext conftest$ac_exeext
6847 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
6848 (eval $ac_link) 2>conftest.er1
6849 ac_status=$?
6850 grep -v '^ *+' conftest.er1 >conftest.err
6851 rm -f conftest.er1
6852 cat conftest.err >&5
6853 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6854 (exit $ac_status); } &&
6855 { ac_try='test -z "$ac_c_werror_flag"
6856 || test ! -s conftest.err'
6857 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6858 (eval $ac_try) 2>&5
6859 ac_status=$?
6860 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6861 (exit $ac_status); }; } &&
6862 { ac_try='test -s conftest$ac_exeext'
6863 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6864 (eval $ac_try) 2>&5
6865 ac_status=$?
6866 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6867 (exit $ac_status); }; }; then
6868 ac_cv_lib_png_png_check_sig=yes
6869 else
6870 echo "$as_me: failed program was:" >&5
6871 sed 's/^/| /' conftest.$ac_ext >&5
6872
6873 ac_cv_lib_png_png_check_sig=no
6874 fi
6875 rm -f conftest.err conftest.$ac_objext \
6876 conftest$ac_exeext conftest.$ac_ext
6877 LIBS=$ac_check_lib_save_LIBS
6878 fi
6879 echo "$as_me:$LINENO: result: $ac_cv_lib_png_png_check_sig" >&5
6880 echo "${ECHO_T}$ac_cv_lib_png_png_check_sig" >&6
6881 if test $ac_cv_lib_png_png_check_sig = yes; then
6882 png_ok=yes
6883 else
6884 png_ok=no
6885 fi
6886
6887 LIBS="$old_libs"
6888
6889 if test "x$png_ok" = "xyes"; then
6890 LIBPNG_LIBS="-lpng -lm"
6891 fi
6892 fi
6893
6894 if test "x$png_ok" = "xno"; then
6895 { echo "$as_me:$LINENO: WARNING: *** No libpng found. Disabling PNG images ***" >&5
6896 echo "$as_me: WARNING: *** No libpng found. Disabling PNG images ***" >&2;}
6897 fi
6898 fi
6899 fi
6900
6901 if test "x$png_ok" = "xyes"; then
6902
6903 cat >>confdefs.h <<\_ACEOF
6904 #define ENABLE_PNG
6905 _ACEOF
6906
6907 fi
6908
6909 if test "x$enable_gif" = "xyes"; then
6910
6911 cat >>confdefs.h <<\_ACEOF
6912 #define ENABLE_GIF
6913 _ACEOF
6914
6915 fi
6916
6917 if test "x$enable_ssl" = "xyes"; then
6918 if test "${ac_cv_header_openssl_ssl_h+set}" = set; then
6919 echo "$as_me:$LINENO: checking for openssl/ssl.h" >&5
6920 echo $ECHO_N "checking for openssl/ssl.h... $ECHO_C" >&6
6921 if test "${ac_cv_header_openssl_ssl_h+set}" = set; then
6922 echo $ECHO_N "(cached) $ECHO_C" >&6
6923 fi
6924 echo "$as_me:$LINENO: result: $ac_cv_header_openssl_ssl_h" >&5
6925 echo "${ECHO_T}$ac_cv_header_openssl_ssl_h" >&6
6926 else
6927 # Is the header compilable?
6928 echo "$as_me:$LINENO: checking openssl/ssl.h usability" >&5
6929 echo $ECHO_N "checking openssl/ssl.h usability... $ECHO_C" >&6
6930 cat >conftest.$ac_ext <<_ACEOF
6931 /* confdefs.h. */
6932 _ACEOF
6933 cat confdefs.h >>conftest.$ac_ext
6934 cat >>conftest.$ac_ext <<_ACEOF
6935 /* end confdefs.h. */
6936 $ac_includes_default
6937 #include <openssl/ssl.h>
6938 _ACEOF
6939 rm -f conftest.$ac_objext
6940 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
6941 (eval $ac_compile) 2>conftest.er1
6942 ac_status=$?
6943 grep -v '^ *+' conftest.er1 >conftest.err
6944 rm -f conftest.er1
6945 cat conftest.err >&5
6946 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6947 (exit $ac_status); } &&
6948 { ac_try='test -z "$ac_c_werror_flag"
6949 || test ! -s conftest.err'
6950 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6951 (eval $ac_try) 2>&5
6952 ac_status=$?
6953 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6954 (exit $ac_status); }; } &&
6955 { ac_try='test -s conftest.$ac_objext'
6956 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
6957 (eval $ac_try) 2>&5
6958 ac_status=$?
6959 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6960 (exit $ac_status); }; }; then
6961 ac_header_compiler=yes
6962 else
6963 echo "$as_me: failed program was:" >&5
6964 sed 's/^/| /' conftest.$ac_ext >&5
6965
6966 ac_header_compiler=no
6967 fi
6968 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
6969 echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
6970 echo "${ECHO_T}$ac_header_compiler" >&6
6971
6972 # Is the header present?
6973 echo "$as_me:$LINENO: checking openssl/ssl.h presence" >&5
6974 echo $ECHO_N "checking openssl/ssl.h presence... $ECHO_C" >&6
6975 cat >conftest.$ac_ext <<_ACEOF
6976 /* confdefs.h. */
6977 _ACEOF
6978 cat confdefs.h >>conftest.$ac_ext
6979 cat >>conftest.$ac_ext <<_ACEOF
6980 /* end confdefs.h. */
6981 #include <openssl/ssl.h>
6982 _ACEOF
6983 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
6984 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
6985 ac_status=$?
6986 grep -v '^ *+' conftest.er1 >conftest.err
6987 rm -f conftest.er1
6988 cat conftest.err >&5
6989 echo "$as_me:$LINENO: \$? = $ac_status" >&5
6990 (exit $ac_status); } >/dev/null; then
6991 if test -s conftest.err; then
6992 ac_cpp_err=$ac_c_preproc_warn_flag
6993 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
6994 else
6995 ac_cpp_err=
6996 fi
6997 else
6998 ac_cpp_err=yes
6999 fi
7000 if test -z "$ac_cpp_err"; then
7001 ac_header_preproc=yes
7002 else
7003 echo "$as_me: failed program was:" >&5
7004 sed 's/^/| /' conftest.$ac_ext >&5
7005
7006 ac_header_preproc=no
7007 fi
7008 rm -f conftest.err conftest.$ac_ext
7009 echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
7010 echo "${ECHO_T}$ac_header_preproc" >&6
7011
7012 # So? What about this header?
7013 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
7014 yes:no: )
7015 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: accepted by the compiler, rejected by the preprocessor!" >&5
7016 echo "$as_me: WARNING: openssl/ssl.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
7017 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: proceeding with the compiler's result" >&5
7018 echo "$as_me: WARNING: openssl/ssl.h: proceeding with the compiler's result" >&2;}
7019 ac_header_preproc=yes
7020 ;;
7021 no:yes:* )
7022 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: present but cannot be compiled" >&5
7023 echo "$as_me: WARNING: openssl/ssl.h: present but cannot be compiled" >&2;}
7024 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: check for missing prerequisite headers?" >&5
7025 echo "$as_me: WARNING: openssl/ssl.h: check for missing prerequisite headers?" >&2;}
7026 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: see the Autoconf documentation" >&5
7027 echo "$as_me: WARNING: openssl/ssl.h: see the Autoconf documentation" >&2;}
7028 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: section \"Present But Cannot Be Compiled\"" >&5
7029 echo "$as_me: WARNING: openssl/ssl.h: section \"Present But Cannot Be Compiled\"" >&2;}
7030 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: proceeding with the preprocessor's result" >&5
7031 echo "$as_me: WARNING: openssl/ssl.h: proceeding with the preprocessor's result" >&2;}
7032 { echo "$as_me:$LINENO: WARNING: openssl/ssl.h: in the future, the compiler will take precedence" >&5
7033 echo "$as_me: WARNING: openssl/ssl.h: in the future, the compiler will take precedence" >&2;}
7034 (
7035 cat <<\_ASBOX
7036 ## ------------------------------------------ ##
7037 ## Report this to the AC_PACKAGE_NAME lists. ##
7038 ## ------------------------------------------ ##
7039 _ASBOX
7040 ) |
7041 sed "s/^/$as_me: WARNING: /" >&2
7042 ;;
7043 esac
7044 echo "$as_me:$LINENO: checking for openssl/ssl.h" >&5
7045 echo $ECHO_N "checking for openssl/ssl.h... $ECHO_C" >&6
7046 if test "${ac_cv_header_openssl_ssl_h+set}" = set; then
7047 echo $ECHO_N "(cached) $ECHO_C" >&6
7048 else
7049 ac_cv_header_openssl_ssl_h=$ac_header_preproc
7050 fi
7051 echo "$as_me:$LINENO: result: $ac_cv_header_openssl_ssl_h" >&5
7052 echo "${ECHO_T}$ac_cv_header_openssl_ssl_h" >&6
7053
7054 fi
7055 if test $ac_cv_header_openssl_ssl_h = yes; then
7056 ssl_ok=yes
7057 else
7058 ssl_ok=no
7059 fi
7060
7061
7062
7063 if test "x$ssl_ok" = "xyes"; then
7064 old_libs="$LIBS"
7065 echo "$as_me:$LINENO: checking for SSL_library_init in -lssl" >&5
7066 echo $ECHO_N "checking for SSL_library_init in -lssl... $ECHO_C" >&6
7067 if test "${ac_cv_lib_ssl_SSL_library_init+set}" = set; then
7068 echo $ECHO_N "(cached) $ECHO_C" >&6
7069 else
7070 ac_check_lib_save_LIBS=$LIBS
7071 LIBS="-lssl -lcrypto $LIBS"
7072 cat >conftest.$ac_ext <<_ACEOF
7073 /* confdefs.h. */
7074 _ACEOF
7075 cat confdefs.h >>conftest.$ac_ext
7076 cat >>conftest.$ac_ext <<_ACEOF
7077 /* end confdefs.h. */
7078
7079 /* Override any gcc2 internal prototype to avoid an error. */
7080 #ifdef __cplusplus
7081 extern "C"
7082 #endif
7083 /* We use char because int might match the return type of a gcc2
7084 builtin and then its argument prototype would still apply. */
7085 char SSL_library_init ();
7086 int
7087 main ()
7088 {
7089 SSL_library_init ();
7090 ;
7091 return 0;
7092 }
7093 _ACEOF
7094 rm -f conftest.$ac_objext conftest$ac_exeext
7095 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7096 (eval $ac_link) 2>conftest.er1
7097 ac_status=$?
7098 grep -v '^ *+' conftest.er1 >conftest.err
7099 rm -f conftest.er1
7100 cat conftest.err >&5
7101 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7102 (exit $ac_status); } &&
7103 { ac_try='test -z "$ac_c_werror_flag"
7104 || test ! -s conftest.err'
7105 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7106 (eval $ac_try) 2>&5
7107 ac_status=$?
7108 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7109 (exit $ac_status); }; } &&
7110 { ac_try='test -s conftest$ac_exeext'
7111 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7112 (eval $ac_try) 2>&5
7113 ac_status=$?
7114 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7115 (exit $ac_status); }; }; then
7116 ac_cv_lib_ssl_SSL_library_init=yes
7117 else
7118 echo "$as_me: failed program was:" >&5
7119 sed 's/^/| /' conftest.$ac_ext >&5
7120
7121 ac_cv_lib_ssl_SSL_library_init=no
7122 fi
7123 rm -f conftest.err conftest.$ac_objext \
7124 conftest$ac_exeext conftest.$ac_ext
7125 LIBS=$ac_check_lib_save_LIBS
7126 fi
7127 echo "$as_me:$LINENO: result: $ac_cv_lib_ssl_SSL_library_init" >&5
7128 echo "${ECHO_T}$ac_cv_lib_ssl_SSL_library_init" >&6
7129 if test $ac_cv_lib_ssl_SSL_library_init = yes; then
7130 ssl_ok=yes
7131 else
7132 ssl_ok=no
7133 fi
7134
7135 LIBS="$old_libs"
7136 fi
7137
7138 if test "x$ssl_ok" = "xyes"; then
7139 LIBSSL_LIBS="-lcrypto -lssl"
7140 else
7141 { echo "$as_me:$LINENO: WARNING: *** No libssl found. Disabling ssl support.***" >&5
7142 echo "$as_me: WARNING: *** No libssl found. Disabling ssl support.***" >&2;}
7143 fi
7144 fi
7145
7146 if test "x$ssl_ok" = "xyes"; then
7147
7148 cat >>confdefs.h <<\_ACEOF
7149 #define ENABLE_SSL
7150 _ACEOF
7151
7152 fi
7153
7154
7155 if test -z "$LIBPTHREAD_LIBS"; then
7156 case $target in
7157 *-*-linux*|*-*-solaris*)
7158 old_libs="$LIBS"
7159 echo "$as_me:$LINENO: checking for pthread_create in -lpthread" >&5
7160 echo $ECHO_N "checking for pthread_create in -lpthread... $ECHO_C" >&6
7161 if test "${ac_cv_lib_pthread_pthread_create+set}" = set; then
7162 echo $ECHO_N "(cached) $ECHO_C" >&6
7163 else
7164 ac_check_lib_save_LIBS=$LIBS
7165 LIBS="-lpthread $LIBS"
7166 cat >conftest.$ac_ext <<_ACEOF
7167 /* confdefs.h. */
7168 _ACEOF
7169 cat confdefs.h >>conftest.$ac_ext
7170 cat >>conftest.$ac_ext <<_ACEOF
7171 /* end confdefs.h. */
7172
7173 /* Override any gcc2 internal prototype to avoid an error. */
7174 #ifdef __cplusplus
7175 extern "C"
7176 #endif
7177 /* We use char because int might match the return type of a gcc2
7178 builtin and then its argument prototype would still apply. */
7179 char pthread_create ();
7180 int
7181 main ()
7182 {
7183 pthread_create ();
7184 ;
7185 return 0;
7186 }
7187 _ACEOF
7188 rm -f conftest.$ac_objext conftest$ac_exeext
7189 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7190 (eval $ac_link) 2>conftest.er1
7191 ac_status=$?
7192 grep -v '^ *+' conftest.er1 >conftest.err
7193 rm -f conftest.er1
7194 cat conftest.err >&5
7195 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7196 (exit $ac_status); } &&
7197 { ac_try='test -z "$ac_c_werror_flag"
7198 || test ! -s conftest.err'
7199 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7200 (eval $ac_try) 2>&5
7201 ac_status=$?
7202 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7203 (exit $ac_status); }; } &&
7204 { ac_try='test -s conftest$ac_exeext'
7205 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7206 (eval $ac_try) 2>&5
7207 ac_status=$?
7208 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7209 (exit $ac_status); }; }; then
7210 ac_cv_lib_pthread_pthread_create=yes
7211 else
7212 echo "$as_me: failed program was:" >&5
7213 sed 's/^/| /' conftest.$ac_ext >&5
7214
7215 ac_cv_lib_pthread_pthread_create=no
7216 fi
7217 rm -f conftest.err conftest.$ac_objext \
7218 conftest$ac_exeext conftest.$ac_ext
7219 LIBS=$ac_check_lib_save_LIBS
7220 fi
7221 echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_pthread_create" >&5
7222 echo "${ECHO_T}$ac_cv_lib_pthread_pthread_create" >&6
7223 if test $ac_cv_lib_pthread_pthread_create = yes; then
7224 LIBPTHREAD_LIBS="-lpthread"
7225 fi
7226
7227 LIBS="$old_libs"
7228 ;;
7229
7230 *-*-osf1*)
7231 echo "$as_me:$LINENO: checking whether pthreads work" >&5
7232 echo $ECHO_N "checking whether pthreads work... $ECHO_C" >&6
7233 LIBPTHREAD_LIBS="-lpthread -lexc -ldb"
7234 { echo "$as_me:$LINENO: WARNING: *** _Untested pthreads_ try setting LIBPTHREAD_LIBS manually if it doesn't work ***" >&5
7235 echo "$as_me: WARNING: *** _Untested pthreads_ try setting LIBPTHREAD_LIBS manually if it doesn't work ***" >&2;}
7236 ;;
7237
7238 *)
7239 echo "$as_me:$LINENO: checking whether threads work with -pthread" >&5
7240 echo $ECHO_N "checking whether threads work with -pthread... $ECHO_C" >&6
7241 LDSAVEFLAGS=$LDFLAGS
7242 LDFLAGS="$LDFLAGS -pthread"
7243 cat >conftest.$ac_ext <<_ACEOF
7244 /* confdefs.h. */
7245 _ACEOF
7246 cat confdefs.h >>conftest.$ac_ext
7247 cat >>conftest.$ac_ext <<_ACEOF
7248 /* end confdefs.h. */
7249
7250 /* Override any gcc2 internal prototype to avoid an error. */
7251 #ifdef __cplusplus
7252 extern "C"
7253 #endif
7254 /* We use char because int might match the return type of a gcc2
7255 builtin and then its argument prototype would still apply. */
7256 char pthread_create ();
7257 int
7258 main ()
7259 {
7260 pthread_create ();
7261 ;
7262 return 0;
7263 }
7264 _ACEOF
7265 rm -f conftest.$ac_objext conftest$ac_exeext
7266 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7267 (eval $ac_link) 2>conftest.er1
7268 ac_status=$?
7269 grep -v '^ *+' conftest.er1 >conftest.err
7270 rm -f conftest.er1
7271 cat conftest.err >&5
7272 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7273 (exit $ac_status); } &&
7274 { ac_try='test -z "$ac_c_werror_flag"
7275 || test ! -s conftest.err'
7276 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7277 (eval $ac_try) 2>&5
7278 ac_status=$?
7279 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7280 (exit $ac_status); }; } &&
7281 { ac_try='test -s conftest$ac_exeext'
7282 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7283 (eval $ac_try) 2>&5
7284 ac_status=$?
7285 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7286 (exit $ac_status); }; }; then
7287 pthread_ok=yes
7288 else
7289 echo "$as_me: failed program was:" >&5
7290 sed 's/^/| /' conftest.$ac_ext >&5
7291
7292 pthread_ok=no
7293 fi
7294 rm -f conftest.err conftest.$ac_objext \
7295 conftest$ac_exeext conftest.$ac_ext
7296 LDFLAGS=$LDSAVEFLAGS
7297
7298 if test "x$pthread_ok" = "xyes"; then
7299 echo "$as_me:$LINENO: result: yes" >&5
7300 echo "${ECHO_T}yes" >&6
7301 LIBPTHREAD_LDFLAGS="-pthread"
7302 else
7303 echo "$as_me:$LINENO: result: no. Now we will try some libraries." >&5
7304 echo "${ECHO_T}no. Now we will try some libraries." >&6
7305
7306 echo "$as_me:$LINENO: checking for library containing pthread_create" >&5
7307 echo $ECHO_N "checking for library containing pthread_create... $ECHO_C" >&6
7308 if test "${ac_cv_search_pthread_create+set}" = set; then
7309 echo $ECHO_N "(cached) $ECHO_C" >&6
7310 else
7311 ac_func_search_save_LIBS=$LIBS
7312 ac_cv_search_pthread_create=no
7313 cat >conftest.$ac_ext <<_ACEOF
7314 /* confdefs.h. */
7315 _ACEOF
7316 cat confdefs.h >>conftest.$ac_ext
7317 cat >>conftest.$ac_ext <<_ACEOF
7318 /* end confdefs.h. */
7319
7320 /* Override any gcc2 internal prototype to avoid an error. */
7321 #ifdef __cplusplus
7322 extern "C"
7323 #endif
7324 /* We use char because int might match the return type of a gcc2
7325 builtin and then its argument prototype would still apply. */
7326 char pthread_create ();
7327 int
7328 main ()
7329 {
7330 pthread_create ();
7331 ;
7332 return 0;
7333 }
7334 _ACEOF
7335 rm -f conftest.$ac_objext conftest$ac_exeext
7336 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7337 (eval $ac_link) 2>conftest.er1
7338 ac_status=$?
7339 grep -v '^ *+' conftest.er1 >conftest.err
7340 rm -f conftest.er1
7341 cat conftest.err >&5
7342 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7343 (exit $ac_status); } &&
7344 { ac_try='test -z "$ac_c_werror_flag"
7345 || test ! -s conftest.err'
7346 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7347 (eval $ac_try) 2>&5
7348 ac_status=$?
7349 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7350 (exit $ac_status); }; } &&
7351 { ac_try='test -s conftest$ac_exeext'
7352 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7353 (eval $ac_try) 2>&5
7354 ac_status=$?
7355 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7356 (exit $ac_status); }; }; then
7357 ac_cv_search_pthread_create="none required"
7358 else
7359 echo "$as_me: failed program was:" >&5
7360 sed 's/^/| /' conftest.$ac_ext >&5
7361
7362 fi
7363 rm -f conftest.err conftest.$ac_objext \
7364 conftest$ac_exeext conftest.$ac_ext
7365 if test "$ac_cv_search_pthread_create" = no; then
7366 for ac_lib in pthread; do
7367 LIBS="-l$ac_lib $ac_func_search_save_LIBS"
7368 cat >conftest.$ac_ext <<_ACEOF
7369 /* confdefs.h. */
7370 _ACEOF
7371 cat confdefs.h >>conftest.$ac_ext
7372 cat >>conftest.$ac_ext <<_ACEOF
7373 /* end confdefs.h. */
7374
7375 /* Override any gcc2 internal prototype to avoid an error. */
7376 #ifdef __cplusplus
7377 extern "C"
7378 #endif
7379 /* We use char because int might match the return type of a gcc2
7380 builtin and then its argument prototype would still apply. */
7381 char pthread_create ();
7382 int
7383 main ()
7384 {
7385 pthread_create ();
7386 ;
7387 return 0;
7388 }
7389 _ACEOF
7390 rm -f conftest.$ac_objext conftest$ac_exeext
7391 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7392 (eval $ac_link) 2>conftest.er1
7393 ac_status=$?
7394 grep -v '^ *+' conftest.er1 >conftest.err
7395 rm -f conftest.er1
7396 cat conftest.err >&5
7397 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7398 (exit $ac_status); } &&
7399 { ac_try='test -z "$ac_c_werror_flag"
7400 || test ! -s conftest.err'
7401 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7402 (eval $ac_try) 2>&5
7403 ac_status=$?
7404 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7405 (exit $ac_status); }; } &&
7406 { ac_try='test -s conftest$ac_exeext'
7407 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7408 (eval $ac_try) 2>&5
7409 ac_status=$?
7410 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7411 (exit $ac_status); }; }; then
7412 ac_cv_search_pthread_create="-l$ac_lib"
7413 break
7414 else
7415 echo "$as_me: failed program was:" >&5
7416 sed 's/^/| /' conftest.$ac_ext >&5
7417
7418 fi
7419 rm -f conftest.err conftest.$ac_objext \
7420 conftest$ac_exeext conftest.$ac_ext
7421 done
7422 fi
7423 LIBS=$ac_func_search_save_LIBS
7424 fi
7425 echo "$as_me:$LINENO: result: $ac_cv_search_pthread_create" >&5
7426 echo "${ECHO_T}$ac_cv_search_pthread_create" >&6
7427 if test "$ac_cv_search_pthread_create" != no; then
7428 test "$ac_cv_search_pthread_create" = "none required" || LIBS="$ac_cv_search_pthread_create $LIBS"
7429 LIBPTHREADS_LIBS="-lpthread"
7430 else
7431 echo "$as_me:$LINENO: checking for library containing pthread_create" >&5
7432 echo $ECHO_N "checking for library containing pthread_create... $ECHO_C" >&6
7433 if test "${ac_cv_search_pthread_create+set}" = set; then
7434 echo $ECHO_N "(cached) $ECHO_C" >&6
7435 else
7436 ac_func_search_save_LIBS=$LIBS
7437 ac_cv_search_pthread_create=no
7438 cat >conftest.$ac_ext <<_ACEOF
7439 /* confdefs.h. */
7440 _ACEOF
7441 cat confdefs.h >>conftest.$ac_ext
7442 cat >>conftest.$ac_ext <<_ACEOF
7443 /* end confdefs.h. */
7444
7445 /* Override any gcc2 internal prototype to avoid an error. */
7446 #ifdef __cplusplus
7447 extern "C"
7448 #endif
7449 /* We use char because int might match the return type of a gcc2
7450 builtin and then its argument prototype would still apply. */
7451 char pthread_create ();
7452 int
7453 main ()
7454 {
7455 pthread_create ();
7456 ;
7457 return 0;
7458 }
7459 _ACEOF
7460 rm -f conftest.$ac_objext conftest$ac_exeext
7461 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7462 (eval $ac_link) 2>conftest.er1
7463 ac_status=$?
7464 grep -v '^ *+' conftest.er1 >conftest.err
7465 rm -f conftest.er1
7466 cat conftest.err >&5
7467 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7468 (exit $ac_status); } &&
7469 { ac_try='test -z "$ac_c_werror_flag"
7470 || test ! -s conftest.err'
7471 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7472 (eval $ac_try) 2>&5
7473 ac_status=$?
7474 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7475 (exit $ac_status); }; } &&
7476 { ac_try='test -s conftest$ac_exeext'
7477 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7478 (eval $ac_try) 2>&5
7479 ac_status=$?
7480 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7481 (exit $ac_status); }; }; then
7482 ac_cv_search_pthread_create="none required"
7483 else
7484 echo "$as_me: failed program was:" >&5
7485 sed 's/^/| /' conftest.$ac_ext >&5
7486
7487 fi
7488 rm -f conftest.err conftest.$ac_objext \
7489 conftest$ac_exeext conftest.$ac_ext
7490 if test "$ac_cv_search_pthread_create" = no; then
7491 for ac_lib in pthreads; do
7492 LIBS="-l$ac_lib $ac_func_search_save_LIBS"
7493 cat >conftest.$ac_ext <<_ACEOF
7494 /* confdefs.h. */
7495 _ACEOF
7496 cat confdefs.h >>conftest.$ac_ext
7497 cat >>conftest.$ac_ext <<_ACEOF
7498 /* end confdefs.h. */
7499
7500 /* Override any gcc2 internal prototype to avoid an error. */
7501 #ifdef __cplusplus
7502 extern "C"
7503 #endif
7504 /* We use char because int might match the return type of a gcc2
7505 builtin and then its argument prototype would still apply. */
7506 char pthread_create ();
7507 int
7508 main ()
7509 {
7510 pthread_create ();
7511 ;
7512 return 0;
7513 }
7514 _ACEOF
7515 rm -f conftest.$ac_objext conftest$ac_exeext
7516 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7517 (eval $ac_link) 2>conftest.er1
7518 ac_status=$?
7519 grep -v '^ *+' conftest.er1 >conftest.err
7520 rm -f conftest.er1
7521 cat conftest.err >&5
7522 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7523 (exit $ac_status); } &&
7524 { ac_try='test -z "$ac_c_werror_flag"
7525 || test ! -s conftest.err'
7526 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7527 (eval $ac_try) 2>&5
7528 ac_status=$?
7529 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7530 (exit $ac_status); }; } &&
7531 { ac_try='test -s conftest$ac_exeext'
7532 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7533 (eval $ac_try) 2>&5
7534 ac_status=$?
7535 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7536 (exit $ac_status); }; }; then
7537 ac_cv_search_pthread_create="-l$ac_lib"
7538 break
7539 else
7540 echo "$as_me: failed program was:" >&5
7541 sed 's/^/| /' conftest.$ac_ext >&5
7542
7543 fi
7544 rm -f conftest.err conftest.$ac_objext \
7545 conftest$ac_exeext conftest.$ac_ext
7546 done
7547 fi
7548 LIBS=$ac_func_search_save_LIBS
7549 fi
7550 echo "$as_me:$LINENO: result: $ac_cv_search_pthread_create" >&5
7551 echo "${ECHO_T}$ac_cv_search_pthread_create" >&6
7552 if test "$ac_cv_search_pthread_create" != no; then
7553 test "$ac_cv_search_pthread_create" = "none required" || LIBS="$ac_cv_search_pthread_create $LIBS"
7554 LIBPTHREADS_LIBS="-lpthreads"
7555 else
7556 echo "$as_me:$LINENO: checking for library containing pthread_create" >&5
7557 echo $ECHO_N "checking for library containing pthread_create... $ECHO_C" >&6
7558 if test "${ac_cv_search_pthread_create+set}" = set; then
7559 echo $ECHO_N "(cached) $ECHO_C" >&6
7560 else
7561 ac_func_search_save_LIBS=$LIBS
7562 ac_cv_search_pthread_create=no
7563 cat >conftest.$ac_ext <<_ACEOF
7564 /* confdefs.h. */
7565 _ACEOF
7566 cat confdefs.h >>conftest.$ac_ext
7567 cat >>conftest.$ac_ext <<_ACEOF
7568 /* end confdefs.h. */
7569
7570 /* Override any gcc2 internal prototype to avoid an error. */
7571 #ifdef __cplusplus
7572 extern "C"
7573 #endif
7574 /* We use char because int might match the return type of a gcc2
7575 builtin and then its argument prototype would still apply. */
7576 char pthread_create ();
7577 int
7578 main ()
7579 {
7580 pthread_create ();
7581 ;
7582 return 0;
7583 }
7584 _ACEOF
7585 rm -f conftest.$ac_objext conftest$ac_exeext
7586 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7587 (eval $ac_link) 2>conftest.er1
7588 ac_status=$?
7589 grep -v '^ *+' conftest.er1 >conftest.err
7590 rm -f conftest.er1
7591 cat conftest.err >&5
7592 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7593 (exit $ac_status); } &&
7594 { ac_try='test -z "$ac_c_werror_flag"
7595 || test ! -s conftest.err'
7596 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7597 (eval $ac_try) 2>&5
7598 ac_status=$?
7599 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7600 (exit $ac_status); }; } &&
7601 { ac_try='test -s conftest$ac_exeext'
7602 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7603 (eval $ac_try) 2>&5
7604 ac_status=$?
7605 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7606 (exit $ac_status); }; }; then
7607 ac_cv_search_pthread_create="none required"
7608 else
7609 echo "$as_me: failed program was:" >&5
7610 sed 's/^/| /' conftest.$ac_ext >&5
7611
7612 fi
7613 rm -f conftest.err conftest.$ac_objext \
7614 conftest$ac_exeext conftest.$ac_ext
7615 if test "$ac_cv_search_pthread_create" = no; then
7616 for ac_lib in c_r; do
7617 LIBS="-l$ac_lib $ac_func_search_save_LIBS"
7618 cat >conftest.$ac_ext <<_ACEOF
7619 /* confdefs.h. */
7620 _ACEOF
7621 cat confdefs.h >>conftest.$ac_ext
7622 cat >>conftest.$ac_ext <<_ACEOF
7623 /* end confdefs.h. */
7624
7625 /* Override any gcc2 internal prototype to avoid an error. */
7626 #ifdef __cplusplus
7627 extern "C"
7628 #endif
7629 /* We use char because int might match the return type of a gcc2
7630 builtin and then its argument prototype would still apply. */
7631 char pthread_create ();
7632 int
7633 main ()
7634 {
7635 pthread_create ();
7636 ;
7637 return 0;
7638 }
7639 _ACEOF
7640 rm -f conftest.$ac_objext conftest$ac_exeext
7641 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7642 (eval $ac_link) 2>conftest.er1
7643 ac_status=$?
7644 grep -v '^ *+' conftest.er1 >conftest.err
7645 rm -f conftest.er1
7646 cat conftest.err >&5
7647 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7648 (exit $ac_status); } &&
7649 { ac_try='test -z "$ac_c_werror_flag"
7650 || test ! -s conftest.err'
7651 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7652 (eval $ac_try) 2>&5
7653 ac_status=$?
7654 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7655 (exit $ac_status); }; } &&
7656 { ac_try='test -s conftest$ac_exeext'
7657 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7658 (eval $ac_try) 2>&5
7659 ac_status=$?
7660 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7661 (exit $ac_status); }; }; then
7662 ac_cv_search_pthread_create="-l$ac_lib"
7663 break
7664 else
7665 echo "$as_me: failed program was:" >&5
7666 sed 's/^/| /' conftest.$ac_ext >&5
7667
7668 fi
7669 rm -f conftest.err conftest.$ac_objext \
7670 conftest$ac_exeext conftest.$ac_ext
7671 done
7672 fi
7673 LIBS=$ac_func_search_save_LIBS
7674 fi
7675 echo "$as_me:$LINENO: result: $ac_cv_search_pthread_create" >&5
7676 echo "${ECHO_T}$ac_cv_search_pthread_create" >&6
7677 if test "$ac_cv_search_pthread_create" != no; then
7678 test "$ac_cv_search_pthread_create" = "none required" || LIBS="$ac_cv_search_pthread_create $LIBS"
7679 LIBPTHREADS_LIBS="-lc_r"
7680 else
7681 thread_ok=no
7682 fi
7683
7684 fi
7685
7686 fi
7687
7688
7689 if test "x$thread_ok" = "xno"; then
7690 { echo "$as_me:$LINENO: WARNING: *** No pthreads found. ***" >&5
7691 echo "$as_me: WARNING: *** No pthreads found. ***" >&2;}
7692 { { echo "$as_me:$LINENO: error: *** Try setting LIBPTHREAD_LIBS manually to point to your pthreads library. ***" >&5
7693 echo "$as_me: error: *** Try setting LIBPTHREAD_LIBS manually to point to your pthreads library. ***" >&2;}
7694 { (exit 1); exit 1; }; }
7695 exit 1
7696 else
7697 { echo "$as_me:$LINENO: WARNING: found a way to link threads, but it may not work..." >&5
7698 echo "$as_me: WARNING: found a way to link threads, but it may not work..." >&2;}
7699 fi
7700 fi
7701 ;;
7702
7703 esac
7704 fi
7705
7706 case $target in
7707 *-*-solaris*)
7708 echo "$as_me:$LINENO: checking whether SunOS has -lrt " >&5
7709 echo $ECHO_N "checking whether SunOS has -lrt ... $ECHO_C" >&6
7710 LDSAVEFLAGS="$LDFLAGS"
7711 LDFLAGS="$LDFLAGS -lrt"
7712 cat >conftest.$ac_ext <<_ACEOF
7713 /* confdefs.h. */
7714 _ACEOF
7715 cat confdefs.h >>conftest.$ac_ext
7716 cat >>conftest.$ac_ext <<_ACEOF
7717 /* end confdefs.h. */
7718
7719 /* Override any gcc2 internal prototype to avoid an error. */
7720 #ifdef __cplusplus
7721 extern "C"
7722 #endif
7723 /* We use char because int might match the return type of a gcc2
7724 builtin and then its argument prototype would still apply. */
7725 char nanosleep ();
7726 int
7727 main ()
7728 {
7729 nanosleep ();
7730 ;
7731 return 0;
7732 }
7733 _ACEOF
7734 rm -f conftest.$ac_objext conftest$ac_exeext
7735 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7736 (eval $ac_link) 2>conftest.er1
7737 ac_status=$?
7738 grep -v '^ *+' conftest.er1 >conftest.err
7739 rm -f conftest.er1
7740 cat conftest.err >&5
7741 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7742 (exit $ac_status); } &&
7743 { ac_try='test -z "$ac_c_werror_flag"
7744 || test ! -s conftest.err'
7745 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7746 (eval $ac_try) 2>&5
7747 ac_status=$?
7748 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7749 (exit $ac_status); }; } &&
7750 { ac_try='test -s conftest$ac_exeext'
7751 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7752 (eval $ac_try) 2>&5
7753 ac_status=$?
7754 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7755 (exit $ac_status); }; }; then
7756 rt_ok=yes
7757 else
7758 echo "$as_me: failed program was:" >&5
7759 sed 's/^/| /' conftest.$ac_ext >&5
7760
7761 rt_ok=no
7762 fi
7763 rm -f conftest.err conftest.$ac_objext \
7764 conftest$ac_exeext conftest.$ac_ext
7765 if test "x$rt_ok" = "xyes"; then
7766 echo "$as_me:$LINENO: result: yes" >&5
7767 echo "${ECHO_T}yes" >&6
7768 else
7769 echo "$as_me:$LINENO: result: no" >&5
7770 echo "${ECHO_T}no" >&6
7771 echo "$as_me:$LINENO: checking whether SunOS has -lposix4 " >&5
7772 echo $ECHO_N "checking whether SunOS has -lposix4 ... $ECHO_C" >&6
7773 LDFLAGS="$LDSAVEFLAGS -lposix4"
7774 cat >conftest.$ac_ext <<_ACEOF
7775 /* confdefs.h. */
7776 _ACEOF
7777 cat confdefs.h >>conftest.$ac_ext
7778 cat >>conftest.$ac_ext <<_ACEOF
7779 /* end confdefs.h. */
7780
7781 /* Override any gcc2 internal prototype to avoid an error. */
7782 #ifdef __cplusplus
7783 extern "C"
7784 #endif
7785 /* We use char because int might match the return type of a gcc2
7786 builtin and then its argument prototype would still apply. */
7787 char nanosleep ();
7788 int
7789 main ()
7790 {
7791 nanosleep ();
7792 ;
7793 return 0;
7794 }
7795 _ACEOF
7796 rm -f conftest.$ac_objext conftest$ac_exeext
7797 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7798 (eval $ac_link) 2>conftest.er1
7799 ac_status=$?
7800 grep -v '^ *+' conftest.er1 >conftest.err
7801 rm -f conftest.er1
7802 cat conftest.err >&5
7803 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7804 (exit $ac_status); } &&
7805 { ac_try='test -z "$ac_c_werror_flag"
7806 || test ! -s conftest.err'
7807 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7808 (eval $ac_try) 2>&5
7809 ac_status=$?
7810 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7811 (exit $ac_status); }; } &&
7812 { ac_try='test -s conftest$ac_exeext'
7813 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7814 (eval $ac_try) 2>&5
7815 ac_status=$?
7816 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7817 (exit $ac_status); }; }; then
7818 posix_ok=yes
7819 else
7820 echo "$as_me: failed program was:" >&5
7821 sed 's/^/| /' conftest.$ac_ext >&5
7822
7823 posix_ok=no
7824 fi
7825 rm -f conftest.err conftest.$ac_objext \
7826 conftest$ac_exeext conftest.$ac_ext
7827 if test "x$posix_ok" = "xyes"; then
7828 echo "$as_me:$LINENO: result: yes" >&5
7829 echo "${ECHO_T}yes" >&6
7830 else
7831 LDFLAGS=$LDSAVEFLAGS
7832 echo "$as_me:$LINENO: result: no" >&5
7833 echo "${ECHO_T}no" >&6
7834 { echo "$as_me:$LINENO: WARNING: *** Try setting LIBS or LDFLAGS manually to point to the library with nanosleep()***" >&5
7835 echo "$as_me: WARNING: *** Try setting LIBS or LDFLAGS manually to point to the library with nanosleep()***" >&2;}
7836 fi
7837 fi
7838 ;;
7839 esac
7840
7841 if test "x$enable_cookies" = "xno" ; then
7842 CFLAGS="$CFLAGS -DDISABLE_COOKIES"
7843 fi
7844 if test "x$enable_ipv6" = "xyes" ; then
7845 CFLAGS="$CFLAGS -DENABLE_IPV6"
7846 fi
7847 if test "x$enable_efence" = "xyes" ; then
7848 LIBS="-lefence $LIBS"
7849 fi
7850 if test "x$enable_gprof" = "xyes" ; then
7851 CFLAGS="$CFLAGS -pg"
7852 fi
7853 if test "x$enable_insure" = "xyes" ; then
7854 CC="insure -Zoi \"compiler $CC\""
7855 LIBS="$LIBS -lstdc++-2-libc6.1-1-2.9.0"
7856 fi
7857 if test "x$enable_rtfl" = "xyes" ; then
7858 CFLAGS="$CFLAGS -DDBG_RTFL"
7859 fi
7860 if test "x$enable_threaded_dns" = "xyes" ; then
7861 CFLAGS="$CFLAGS -DD_DNS_THREADED"
7862 fi
7863
7864 echo "$as_me:$LINENO: checking for ANSI C header files" >&5
7865 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
7866 if test "${ac_cv_header_stdc+set}" = set; then
7867 echo $ECHO_N "(cached) $ECHO_C" >&6
7868 else
7869 cat >conftest.$ac_ext <<_ACEOF
7870 /* confdefs.h. */
7871 _ACEOF
7872 cat confdefs.h >>conftest.$ac_ext
7873 cat >>conftest.$ac_ext <<_ACEOF
7874 /* end confdefs.h. */
7875 #include <stdlib.h>
7876 #include <stdarg.h>
7877 #include <string.h>
7878 #include <float.h>
7879
7880 int
7881 main ()
7882 {
7883
7884 ;
7885 return 0;
7886 }
7887 _ACEOF
7888 rm -f conftest.$ac_objext
7889 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
7890 (eval $ac_compile) 2>conftest.er1
7891 ac_status=$?
7892 grep -v '^ *+' conftest.er1 >conftest.err
7893 rm -f conftest.er1
7894 cat conftest.err >&5
7895 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7896 (exit $ac_status); } &&
7897 { ac_try='test -z "$ac_c_werror_flag"
7898 || test ! -s conftest.err'
7899 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7900 (eval $ac_try) 2>&5
7901 ac_status=$?
7902 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7903 (exit $ac_status); }; } &&
7904 { ac_try='test -s conftest.$ac_objext'
7905 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
7906 (eval $ac_try) 2>&5
7907 ac_status=$?
7908 echo "$as_me:$LINENO: \$? = $ac_status" >&5
7909 (exit $ac_status); }; }; then
7910 ac_cv_header_stdc=yes
7911 else
7912 echo "$as_me: failed program was:" >&5
7913 sed 's/^/| /' conftest.$ac_ext >&5
7914
7915 ac_cv_header_stdc=no
7916 fi
7917 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
7918
7919 if test $ac_cv_header_stdc = yes; then
7920 # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
7921 cat >conftest.$ac_ext <<_ACEOF
7922 /* confdefs.h. */
7923 _ACEOF
7924 cat confdefs.h >>conftest.$ac_ext
7925 cat >>conftest.$ac_ext <<_ACEOF
7926 /* end confdefs.h. */
7927 #include <string.h>
7928
7929 _ACEOF
7930 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
7931 $EGREP "memchr" >/dev/null 2>&1; then
7932 :
7933 else
7934 ac_cv_header_stdc=no
7935 fi
7936 rm -f conftest*
7937
7938 fi
7939
7940 if test $ac_cv_header_stdc = yes; then
7941 # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
7942 cat >conftest.$ac_ext <<_ACEOF
7943 /* confdefs.h. */
7944 _ACEOF
7945 cat confdefs.h >>conftest.$ac_ext
7946 cat >>conftest.$ac_ext <<_ACEOF
7947 /* end confdefs.h. */
7948 #include <stdlib.h>
7949
7950 _ACEOF
7951 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
7952 $EGREP "free" >/dev/null 2>&1; then
7953 :
7954 else
7955 ac_cv_header_stdc=no
7956 fi
7957 rm -f conftest*
7958
7959 fi
7960
7961 if test $ac_cv_header_stdc = yes; then
7962 # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
7963 if test "$cross_compiling" = yes; then
7964 :
7965 else
7966 cat >conftest.$ac_ext <<_ACEOF
7967 /* confdefs.h. */
7968 _ACEOF
7969 cat confdefs.h >>conftest.$ac_ext
7970 cat >>conftest.$ac_ext <<_ACEOF
7971 /* end confdefs.h. */
7972 #include <ctype.h>
7973 #if ((' ' & 0x0FF) == 0x020)
7974 # define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
7975 # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
7976 #else
7977 # define ISLOWER(c) \
7978 (('a' <= (c) && (c) <= 'i') \
7979 || ('j' <= (c) && (c) <= 'r') \
7980 || ('s' <= (c) && (c) <= 'z'))
7981 # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
7982 #endif
7983
7984 #define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
7985 int
7986 main ()
7987 {
7988 int i;
7989 for (i = 0; i < 256; i++)
7990 if (XOR (islower (i), ISLOWER (i))
7991 || toupper (i) != TOUPPER (i))
7992 exit(2);
7993 exit (0);
7994 }
7995 _ACEOF
7996 rm -f conftest$ac_exeext
7997 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
7998 (eval $ac_link) 2>&5
7999 ac_status=$?
8000 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8001 (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
8002 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
8003 (eval $ac_try) 2>&5
8004 ac_status=$?
8005 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8006 (exit $ac_status); }; }; then
8007 :
8008 else
8009 echo "$as_me: program exited with status $ac_status" >&5
8010 echo "$as_me: failed program was:" >&5
8011 sed 's/^/| /' conftest.$ac_ext >&5
8012
8013 ( exit $ac_status )
8014 ac_cv_header_stdc=no
8015 fi
8016 rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
8017 fi
8018 fi
8019 fi
8020 echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
8021 echo "${ECHO_T}$ac_cv_header_stdc" >&6
8022 if test $ac_cv_header_stdc = yes; then
8023
8024 cat >>confdefs.h <<\_ACEOF
8025 #define STDC_HEADERS 1
8026 _ACEOF
8027
8028 fi
8029
8030
8031
8032
8033 for ac_header in fcntl.h unistd.h sys/uio.h
8034 do
8035 as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
8036 if eval "test \"\${$as_ac_Header+set}\" = set"; then
8037 echo "$as_me:$LINENO: checking for $ac_header" >&5
8038 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
8039 if eval "test \"\${$as_ac_Header+set}\" = set"; then
8040 echo $ECHO_N "(cached) $ECHO_C" >&6
8041 fi
8042 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
8043 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
8044 else
8045 # Is the header compilable?
8046 echo "$as_me:$LINENO: checking $ac_header usability" >&5
8047 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
8048 cat >conftest.$ac_ext <<_ACEOF
8049 /* confdefs.h. */
8050 _ACEOF
8051 cat confdefs.h >>conftest.$ac_ext
8052 cat >>conftest.$ac_ext <<_ACEOF
8053 /* end confdefs.h. */
8054 $ac_includes_default
8055 #include <$ac_header>
8056 _ACEOF
8057 rm -f conftest.$ac_objext
8058 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
8059 (eval $ac_compile) 2>conftest.er1
8060 ac_status=$?
8061 grep -v '^ *+' conftest.er1 >conftest.err
8062 rm -f conftest.er1
8063 cat conftest.err >&5
8064 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8065 (exit $ac_status); } &&
8066 { ac_try='test -z "$ac_c_werror_flag"
8067 || test ! -s conftest.err'
8068 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
8069 (eval $ac_try) 2>&5
8070 ac_status=$?
8071 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8072 (exit $ac_status); }; } &&
8073 { ac_try='test -s conftest.$ac_objext'
8074 { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
8075 (eval $ac_try) 2>&5
8076 ac_status=$?
8077 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8078 (exit $ac_status); }; }; then
8079 ac_header_compiler=yes
8080 else
8081 echo "$as_me: failed program was:" >&5
8082 sed 's/^/| /' conftest.$ac_ext >&5
8083
8084 ac_header_compiler=no
8085 fi
8086 rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
8087 echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
8088 echo "${ECHO_T}$ac_header_compiler" >&6
8089
8090 # Is the header present?
8091 echo "$as_me:$LINENO: checking $ac_header presence" >&5
8092 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
8093 cat >conftest.$ac_ext <<_ACEOF
8094 /* confdefs.h. */
8095 _ACEOF
8096 cat confdefs.h >>conftest.$ac_ext
8097 cat >>conftest.$ac_ext <<_ACEOF
8098 /* end confdefs.h. */
8099 #include <$ac_header>
8100 _ACEOF
8101 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
8102 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
8103 ac_status=$?
8104 grep -v '^ *+' conftest.er1 >conftest.err
8105 rm -f conftest.er1
8106 cat conftest.err >&5
8107 echo "$as_me:$LINENO: \$? = $ac_status" >&5
8108 (exit $ac_status); } >/dev/null; then
8109 if test -s conftest.err; then
8110 ac_cpp_err=$ac_c_preproc_warn_flag
8111 ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
8112 else
8113 ac_cpp_err=
8114 fi
8115 else
8116 ac_cpp_err=yes
8117 fi
8118 if test -z "$ac_cpp_err"; then
8119 ac_header_preproc=yes
8120 else
8121 echo "$as_me: failed program was:" >&5
8122 sed 's/^/| /' conftest.$ac_ext >&5
8123
8124 ac_header_preproc=no
8125 fi
8126 rm -f conftest.err conftest.$ac_ext
8127 echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
8128 echo "${ECHO_T}$ac_header_preproc" >&6
8129
8130 # So? What about this header?
8131 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
8132 yes:no: )
8133 { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
8134 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
8135 { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
8136 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
8137 ac_header_preproc=yes
8138 ;;
8139 no:yes:* )
8140 { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
8141 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
8142 { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
8143 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
8144 { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
8145 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
8146 { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
8147 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
8148 { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
8149 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
8150 { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
8151 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
8152 (
8153 cat <<\_ASBOX
8154 ## ------------------------------------------ ##
8155 ## Report this to the AC_PACKAGE_NAME lists. ##
8156 ## ------------------------------------------ ##
8157 _ASBOX
8158 ) |
8159 sed "s/^/$as_me: WARNING: /" >&2
8160 ;;
8161 esac
8162 echo "$as_me:$LINENO: checking for $ac_header" >&5
8163 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
8164 if eval "test \"\${$as_ac_Header+set}\" = set"; then
8165 echo $ECHO_N "(cached) $ECHO_C" >&6
8166 else
8167 eval "$as_ac_Header=\$ac_header_preproc"
8168 fi
8169 echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
8170 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
8171
8172 fi
8173 if test `eval echo '${'$as_ac_Header'}'` = yes; then
8174 cat >>confdefs.h <<_ACEOF
8175 #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
8176 _ACEOF
8177
8178 fi
8179
8180 done
8181
8182
8183 if eval "test x$GCC = xyes"; then
8184 if test "`echo $CFLAGS | grep '\-D_REENTRANT' 2> /dev/null`" = ""; then
8185 CFLAGS="$CFLAGS -D_REENTRANT"
8186 fi
8187 if test "`echo $CFLAGS | grep '\-D_THREAD_SAFE' 2> /dev/null`" = ""; then
8188 CFLAGS="$CFLAGS -D_THREAD_SAFE"
8189 fi
8190 if test "`echo $CFLAGS | grep '\-Wall' 2> /dev/null`" = ""; then
8191 CFLAGS="$CFLAGS -Wall"
8192 fi
8193 if test "`echo $CFLAGS | grep '\-W^a' 2> /dev/null`" = ""; then
8194 if test "`$CC -v 2>&1 | grep 'version 3'`" != ""; then
8195 CFLAGS="$CFLAGS -W -Wno-unused-parameter"
8196 fi
8197 fi
8198 if test "`echo $CFLAGS | grep '\-Waggregate-return' 2> /dev/null`" = ""; then
8199 CFLAGS="$CFLAGS -Waggregate-return"
8200 fi
8201
8202 if eval "test x$enable_ansi = xyes"; then
8203 if test "`echo $CFLAGS | grep '\-ansi' 2> /dev/null`" = ""; then
8204 CFLAGS="$CFLAGS -ansi"
8205 fi
8206
8207 if test "`echo $CFLAGS | grep '\-pedantic' 2> /dev/null`" = ""; then
8208 CFLAGS="$CFLAGS -pedantic"
8209 fi
8210 fi
8211 fi
8212
8213
8214
8215
8216
8217
8218
8219
8220
8221
8222
8223
8224
8225
8226
8227 ac_config_files="$ac_config_files Makefile dpip/Makefile dpid/Makefile dpi/Makefile doc/Makefile src/Makefile src/IO/Makefile"
8228 cat >confcache <<\_ACEOF
8229 # This file is a shell script that caches the results of configure
8230 # tests run on this system so they can be shared between configure
8231 # scripts and configure runs, see configure's option --config-cache.
8232 # It is not useful on other systems. If it contains results you don't
8233 # want to keep, you may remove or edit it.
8234 #
8235 # config.status only pays attention to the cache file if you give it
8236 # the --recheck option to rerun configure.
8237 #
8238 # `ac_cv_env_foo' variables (set or unset) will be overridden when
8239 # loading this file, other *unset* `ac_cv_foo' will be assigned the
8240 # following values.
8241
8242 _ACEOF
8243
8244 # The following way of writing the cache mishandles newlines in values,
8245 # but we know of no workaround that is simple, portable, and efficient.
8246 # So, don't put newlines in cache variables' values.
8247 # Ultrix sh set writes to stderr and can't be redirected directly,
8248 # and sets the high bit in the cache file unless we assign to the vars.
8249 {
8250 (set) 2>&1 |
8251 case `(ac_space=' '; set | grep ac_space) 2>&1` in
8252 *ac_space=\ *)
8253 # `set' does not quote correctly, so add quotes (double-quote
8254 # substitution turns \\\\ into \\, and sed turns \\ into \).
8255 sed -n \
8256 "s/'/'\\\\''/g;
8257 s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
8258 ;;
8259 *)
8260 # `set' quotes correctly as required by POSIX, so do not add quotes.
8261 sed -n \
8262 "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
8263 ;;
8264 esac;
8265 } |
8266 sed '
8267 t clear
8268 : clear
8269 s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
8270 t end
8271 /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
8272 : end' >>confcache
8273 if diff $cache_file confcache >/dev/null 2>&1; then :; else
8274 if test -w $cache_file; then
8275 test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
8276 cat confcache >$cache_file
8277 else
8278 echo "not updating unwritable cache $cache_file"
8279 fi
8280 fi
8281 rm -f confcache
8282
8283 test "x$prefix" = xNONE && prefix=$ac_default_prefix
8284 # Let make expand exec_prefix.
8285 test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
8286
8287 # VPATH may cause trouble with some makes, so we remove $(srcdir),
8288 # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
8289 # trailing colons and then remove the whole line if VPATH becomes empty
8290 # (actually we leave an empty line to preserve line numbers).
8291 if test "x$srcdir" = x.; then
8292 ac_vpsub='/^[ ]*VPATH[ ]*=/{
8293 s/:*\$(srcdir):*/:/;
8294 s/:*\${srcdir}:*/:/;
8295 s/:*@srcdir@:*/:/;
8296 s/^\([^=]*=[ ]*\):*/\1/;
8297 s/:*$//;
8298 s/^[^=]*=[ ]*$//;
8299 }'
8300 fi
8301
8302 DEFS=-DHAVE_CONFIG_H
8303
8304 ac_libobjs=
8305 ac_ltlibobjs=
8306 for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
8307 # 1. Remove the extension, and $U if already installed.
8308 ac_i=`echo "$ac_i" |
8309 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
8310 # 2. Add them.
8311 ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
8312 ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
8313 done
8314 LIBOBJS=$ac_libobjs
8315
8316 LTLIBOBJS=$ac_ltlibobjs
8317
8318
8319 if test -z "${DLGUI_TRUE}" && test -z "${DLGUI_FALSE}"; then
8320 { { echo "$as_me:$LINENO: error: conditional \"DLGUI\" was never defined.
8321 Usually this means the macro was only invoked conditionally." >&5
8322 echo "$as_me: error: conditional \"DLGUI\" was never defined.
8323 Usually this means the macro was only invoked conditionally." >&2;}
8324 { (exit 1); exit 1; }; }
8325 fi
8326 if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
8327 { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined.
8328 Usually this means the macro was only invoked conditionally." >&5
8329 echo "$as_me: error: conditional \"AMDEP\" was never defined.
8330 Usually this means the macro was only invoked conditionally." >&2;}
8331 { (exit 1); exit 1; }; }
8332 fi
8333 if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
8334 { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
8335 Usually this means the macro was only invoked conditionally." >&5
8336 echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
8337 Usually this means the macro was only invoked conditionally." >&2;}
8338 { (exit 1); exit 1; }; }
8339 fi
8340 if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
8341 { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
8342 Usually this means the macro was only invoked conditionally." >&5
8343 echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
8344 Usually this means the macro was only invoked conditionally." >&2;}
8345 { (exit 1); exit 1; }; }
8346 fi
8347 if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
8348 { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCXX\" was never defined.
8349 Usually this means the macro was only invoked conditionally." >&5
8350 echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined.
8351 Usually this means the macro was only invoked conditionally." >&2;}
8352 { (exit 1); exit 1; }; }
8353 fi
8354
8355 : ${CONFIG_STATUS=./config.status}
8356 ac_clean_files_save=$ac_clean_files
8357 ac_clean_files="$ac_clean_files $CONFIG_STATUS"
8358 { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
8359 echo "$as_me: creating $CONFIG_STATUS" >&6;}
8360 cat >$CONFIG_STATUS <<_ACEOF
8361 #! $SHELL
8362 # Generated by $as_me.
8363 # Run this file to recreate the current configuration.
8364 # Compiler output produced by configure, useful for debugging
8365 # configure, is in config.log if it exists.
8366
8367 debug=false
8368 ac_cs_recheck=false
8369 ac_cs_silent=false
8370 SHELL=\${CONFIG_SHELL-$SHELL}
8371 _ACEOF
8372
8373 cat >>$CONFIG_STATUS <<\_ACEOF
8374 ## --------------------- ##
8375 ## M4sh Initialization. ##
8376 ## --------------------- ##
8377
8378 # Be Bourne compatible
8379 if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
8380 emulate sh
8381 NULLCMD=:
8382 # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
8383 # is contrary to our usage. Disable this feature.
8384 alias -g '${1+"$@"}'='"$@"'
8385 elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
8386 set -o posix
8387 fi
8388 DUALCASE=1; export DUALCASE # for MKS sh
8389
8390 # Support unset when possible.
8391 if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
8392 as_unset=unset
8393 else
8394 as_unset=false
8395 fi
8396
8397
8398 # Work around bugs in pre-3.0 UWIN ksh.
8399 $as_unset ENV MAIL MAILPATH
8400 PS1='$ '
8401 PS2='> '
8402 PS4='+ '
8403
8404 # NLS nuisances.
8405 for as_var in \
8406 LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
8407 LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
8408 LC_TELEPHONE LC_TIME
8409 do
8410 if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
8411 eval $as_var=C; export $as_var
8412 else
8413 $as_unset $as_var
8414 fi
8415 done
8416
8417 # Required to use basename.
8418 if expr a : '\(a\)' >/dev/null 2>&1; then
8419 as_expr=expr
8420 else
8421 as_expr=false
8422 fi
8423
8424 if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
8425 as_basename=basename
8426 else
8427 as_basename=false
8428 fi
8429
8430
8431 # Name of the executable.
8432 as_me=`$as_basename "$0" ||
8433 $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
8434 X"$0" : 'X\(//\)$' \| \
8435 X"$0" : 'X\(/\)$' \| \
8436 . : '\(.\)' 2>/dev/null ||
8437 echo X/"$0" |
8438 sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
8439 /^X\/\(\/\/\)$/{ s//\1/; q; }
8440 /^X\/\(\/\).*/{ s//\1/; q; }
8441 s/.*/./; q'`
8442
8443
8444 # PATH needs CR, and LINENO needs CR and PATH.
8445 # Avoid depending upon Character Ranges.
8446 as_cr_letters='abcdefghijklmnopqrstuvwxyz'
8447 as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
8448 as_cr_Letters=$as_cr_letters$as_cr_LETTERS
8449 as_cr_digits='0123456789'
8450 as_cr_alnum=$as_cr_Letters$as_cr_digits
8451
8452 # The user is always right.
8453 if test "${PATH_SEPARATOR+set}" != set; then
8454 echo "#! /bin/sh" >conf$$.sh
8455 echo "exit 0" >>conf$$.sh
8456 chmod +x conf$$.sh
8457 if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
8458 PATH_SEPARATOR=';'
8459 else
8460 PATH_SEPARATOR=:
8461 fi
8462 rm -f conf$$.sh
8463 fi
8464
8465
8466 as_lineno_1=$LINENO
8467 as_lineno_2=$LINENO
8468 as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
8469 test "x$as_lineno_1" != "x$as_lineno_2" &&
8470 test "x$as_lineno_3" = "x$as_lineno_2" || {
8471 # Find who we are. Look in the path if we contain no path at all
8472 # relative or not.
8473 case $0 in
8474 *[\\/]* ) as_myself=$0 ;;
8475 *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8476 for as_dir in $PATH
8477 do
8478 IFS=$as_save_IFS
8479 test -z "$as_dir" && as_dir=.
8480 test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
8481 done
8482
8483 ;;
8484 esac
8485 # We did not find ourselves, most probably we were run as `sh COMMAND'
8486 # in which case we are not to be found in the path.
8487 if test "x$as_myself" = x; then
8488 as_myself=$0
8489 fi
8490 if test ! -f "$as_myself"; then
8491 { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
8492 echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
8493 { (exit 1); exit 1; }; }
8494 fi
8495 case $CONFIG_SHELL in
8496 '')
8497 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8498 for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
8499 do
8500 IFS=$as_save_IFS
8501 test -z "$as_dir" && as_dir=.
8502 for as_base in sh bash ksh sh5; do
8503 case $as_dir in
8504 /*)
8505 if ("$as_dir/$as_base" -c '
8506 as_lineno_1=$LINENO
8507 as_lineno_2=$LINENO
8508 as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
8509 test "x$as_lineno_1" != "x$as_lineno_2" &&
8510 test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
8511 $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
8512 $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
8513 CONFIG_SHELL=$as_dir/$as_base
8514 export CONFIG_SHELL
8515 exec "$CONFIG_SHELL" "$0" ${1+"$@"}
8516 fi;;
8517 esac
8518 done
8519 done
8520 ;;
8521 esac
8522
8523 # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
8524 # uniformly replaced by the line number. The first 'sed' inserts a
8525 # line-number line before each line; the second 'sed' does the real
8526 # work. The second script uses 'N' to pair each line-number line
8527 # with the numbered line, and appends trailing '-' during
8528 # substitution so that $LINENO is not a special case at line end.
8529 # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
8530 # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
8531 sed '=' <$as_myself |
8532 sed '
8533 N
8534 s,$,-,
8535 : loop
8536 s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
8537 t loop
8538 s,-$,,
8539 s,^['$as_cr_digits']*\n,,
8540 ' >$as_me.lineno &&
8541 chmod +x $as_me.lineno ||
8542 { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
8543 echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
8544 { (exit 1); exit 1; }; }
8545
8546 # Don't try to exec as it changes $[0], causing all sort of problems
8547 # (the dirname of $[0] is not the place where we might find the
8548 # original and so on. Autoconf is especially sensible to this).
8549 . ./$as_me.lineno
8550 # Exit status is that of the last command.
8551 exit
8552 }
8553
8554
8555 case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
8556 *c*,-n*) ECHO_N= ECHO_C='
8557 ' ECHO_T=' ' ;;
8558 *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
8559 *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
8560 esac
8561
8562 if expr a : '\(a\)' >/dev/null 2>&1; then
8563 as_expr=expr
8564 else
8565 as_expr=false
8566 fi
8567
8568 rm -f conf$$ conf$$.exe conf$$.file
8569 echo >conf$$.file
8570 if ln -s conf$$.file conf$$ 2>/dev/null; then
8571 # We could just check for DJGPP; but this test a) works b) is more generic
8572 # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
8573 if test -f conf$$.exe; then
8574 # Don't use ln at all; we don't have any links
8575 as_ln_s='cp -p'
8576 else
8577 as_ln_s='ln -s'
8578 fi
8579 elif ln conf$$.file conf$$ 2>/dev/null; then
8580 as_ln_s=ln
8581 else
8582 as_ln_s='cp -p'
8583 fi
8584 rm -f conf$$ conf$$.exe conf$$.file
8585
8586 if mkdir -p . 2>/dev/null; then
8587 as_mkdir_p=:
8588 else
8589 test -d ./-p && rmdir ./-p
8590 as_mkdir_p=false
8591 fi
8592
8593 as_executable_p="test -f"
8594
8595 # Sed expression to map a string onto a valid CPP name.
8596 as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
8597
8598 # Sed expression to map a string onto a valid variable name.
8599 as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
8600
8601
8602 # IFS
8603 # We need space, tab and new line, in precisely that order.
8604 as_nl='
8605 '
8606 IFS=" $as_nl"
8607
8608 # CDPATH.
8609 $as_unset CDPATH
8610
8611 exec 6>&1
8612
8613 # Open the log real soon, to keep \$[0] and so on meaningful, and to
8614 # report actual input values of CONFIG_FILES etc. instead of their
8615 # values after options handling. Logging --version etc. is OK.
8616 exec 5>>config.log
8617 {
8618 echo
8619 sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
8620 ## Running $as_me. ##
8621 _ASBOX
8622 } >&5
8623 cat >&5 <<_CSEOF
8624
8625 This file was extended by $as_me, which was
8626 generated by GNU Autoconf 2.59. Invocation command line was
8627
8628 CONFIG_FILES = $CONFIG_FILES
8629 CONFIG_HEADERS = $CONFIG_HEADERS
8630 CONFIG_LINKS = $CONFIG_LINKS
8631 CONFIG_COMMANDS = $CONFIG_COMMANDS
8632 $ $0 $@
8633
8634 _CSEOF
8635 echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
8636 echo >&5
8637 _ACEOF
8638
8639 # Files that config.status was made for.
8640 if test -n "$ac_config_files"; then
8641 echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
8642 fi
8643
8644 if test -n "$ac_config_headers"; then
8645 echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
8646 fi
8647
8648 if test -n "$ac_config_links"; then
8649 echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
8650 fi
8651
8652 if test -n "$ac_config_commands"; then
8653 echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
8654 fi
8655
8656 cat >>$CONFIG_STATUS <<\_ACEOF
8657
8658 ac_cs_usage="\
8659 \`$as_me' instantiates files from templates according to the
8660 current configuration.
8661
8662 Usage: $0 [OPTIONS] [FILE]...
8663
8664 -h, --help print this help, then exit
8665 -V, --version print version number, then exit
8666 -q, --quiet do not print progress messages
8667 -d, --debug don't remove temporary files
8668 --recheck update $as_me by reconfiguring in the same conditions
8669 --file=FILE[:TEMPLATE]
8670 instantiate the configuration file FILE
8671 --header=FILE[:TEMPLATE]
8672 instantiate the configuration header FILE
8673
8674 Configuration files:
8675 $config_files
8676
8677 Configuration headers:
8678 $config_headers
8679
8680 Configuration commands:
8681 $config_commands
8682
8683 Report bugs to <bug-autoconf@gnu.org>."
8684 _ACEOF
8685
8686 cat >>$CONFIG_STATUS <<_ACEOF
8687 ac_cs_version="\\
8688 config.status
8689 configured by $0, generated by GNU Autoconf 2.59,
8690 with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
8691
8692 Copyright (C) 2003 Free Software Foundation, Inc.
8693 This config.status script is free software; the Free Software Foundation
8694 gives unlimited permission to copy, distribute and modify it."
8695 srcdir=$srcdir
8696 INSTALL="$INSTALL"
8697 _ACEOF
8698
8699 cat >>$CONFIG_STATUS <<\_ACEOF
8700 # If no file are specified by the user, then we need to provide default
8701 # value. By we need to know if files were specified by the user.
8702 ac_need_defaults=:
8703 while test $# != 0
8704 do
8705 case $1 in
8706 --*=*)
8707 ac_option=`expr "x$1" : 'x\([^=]*\)='`
8708 ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
8709 ac_shift=:
8710 ;;
8711 -*)
8712 ac_option=$1
8713 ac_optarg=$2
8714 ac_shift=shift
8715 ;;
8716 *) # This is not an option, so the user has probably given explicit
8717 # arguments.
8718 ac_option=$1
8719 ac_need_defaults=false;;
8720 esac
8721
8722 case $ac_option in
8723 # Handling of the options.
8724 _ACEOF
8725 cat >>$CONFIG_STATUS <<\_ACEOF
8726 -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
8727 ac_cs_recheck=: ;;
8728 --version | --vers* | -V )
8729 echo "$ac_cs_version"; exit 0 ;;
8730 --he | --h)
8731 # Conflict between --help and --header
8732 { { echo "$as_me:$LINENO: error: ambiguous option: $1
8733 Try \`$0 --help' for more information." >&5
8734 echo "$as_me: error: ambiguous option: $1
8735 Try \`$0 --help' for more information." >&2;}
8736 { (exit 1); exit 1; }; };;
8737 --help | --hel | -h )
8738 echo "$ac_cs_usage"; exit 0 ;;
8739 --debug | --d* | -d )
8740 debug=: ;;
8741 --file | --fil | --fi | --f )
8742 $ac_shift
8743 CONFIG_FILES="$CONFIG_FILES $ac_optarg"
8744 ac_need_defaults=false;;
8745 --header | --heade | --head | --hea )
8746 $ac_shift
8747 CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
8748 ac_need_defaults=false;;
8749 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
8750 | -silent | --silent | --silen | --sile | --sil | --si | --s)
8751 ac_cs_silent=: ;;
8752
8753 # This is an error.
8754 -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
8755 Try \`$0 --help' for more information." >&5
8756 echo "$as_me: error: unrecognized option: $1
8757 Try \`$0 --help' for more information." >&2;}
8758 { (exit 1); exit 1; }; } ;;
8759
8760 *) ac_config_targets="$ac_config_targets $1" ;;
8761
8762 esac
8763 shift
8764 done
8765
8766 ac_configure_extra_args=
8767
8768 if $ac_cs_silent; then
8769 exec 6>/dev/null
8770 ac_configure_extra_args="$ac_configure_extra_args --silent"
8771 fi
8772
8773 _ACEOF
8774 cat >>$CONFIG_STATUS <<_ACEOF
8775 if \$ac_cs_recheck; then
8776 echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
8777 exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
8778 fi
8779
8780 _ACEOF
8781
8782 cat >>$CONFIG_STATUS <<_ACEOF
8783 #
8784 # INIT-COMMANDS section.
8785 #
8786
8787 AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
8788
8789 _ACEOF
8790
8791
8792
8793 cat >>$CONFIG_STATUS <<\_ACEOF
8794 for ac_config_target in $ac_config_targets
8795 do
8796 case "$ac_config_target" in
8797 # Handling of arguments.
8798 "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
8799 "dpip/Makefile" ) CONFIG_FILES="$CONFIG_FILES dpip/Makefile" ;;
8800 "dpid/Makefile" ) CONFIG_FILES="$CONFIG_FILES dpid/Makefile" ;;
8801 "dpi/Makefile" ) CONFIG_FILES="$CONFIG_FILES dpi/Makefile" ;;
8802 "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
8803 "src/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
8804 "src/IO/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/IO/Makefile" ;;
8805 "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
8806 "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
8807 *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
8808 echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
8809 { (exit 1); exit 1; }; };;
8810 esac
8811 done
8812
8813 # If the user did not use the arguments to specify the items to instantiate,
8814 # then the envvar interface is used. Set only those that are not.
8815 # We use the long form for the default assignment because of an extremely
8816 # bizarre bug on SunOS 4.1.3.
8817 if $ac_need_defaults; then
8818 test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
8819 test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
8820 test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
8821 fi
8822
8823 # Have a temporary directory for convenience. Make it in the build tree
8824 # simply because there is no reason to put it here, and in addition,
8825 # creating and moving files from /tmp can sometimes cause problems.
8826 # Create a temporary directory, and hook for its removal unless debugging.
8827 $debug ||
8828 {
8829 trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
8830 trap '{ (exit 1); exit 1; }' 1 2 13 15
8831 }
8832
8833 # Create a (secure) tmp directory for tmp files.
8834
8835 {
8836 tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
8837 test -n "$tmp" && test -d "$tmp"
8838 } ||
8839 {
8840 tmp=./confstat$$-$RANDOM
8841 (umask 077 && mkdir $tmp)
8842 } ||
8843 {
8844 echo "$me: cannot create a temporary directory in ." >&2
8845 { (exit 1); exit 1; }
8846 }
8847
8848 _ACEOF
8849
8850 cat >>$CONFIG_STATUS <<_ACEOF
8851
8852 #
8853 # CONFIG_FILES section.
8854 #
8855
8856 # No need to generate the scripts if there are no CONFIG_FILES.
8857 # This happens for instance when ./config.status config.h
8858 if test -n "\$CONFIG_FILES"; then
8859 # Protect against being on the right side of a sed subst in config.status.
8860 sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
8861 s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
8862 s,@SHELL@,$SHELL,;t t
8863 s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
8864 s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
8865 s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
8866 s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
8867 s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
8868 s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
8869 s,@exec_prefix@,$exec_prefix,;t t
8870 s,@prefix@,$prefix,;t t
8871 s,@program_transform_name@,$program_transform_name,;t t
8872 s,@bindir@,$bindir,;t t
8873 s,@sbindir@,$sbindir,;t t
8874 s,@libexecdir@,$libexecdir,;t t
8875 s,@datadir@,$datadir,;t t
8876 s,@sysconfdir@,$sysconfdir,;t t
8877 s,@sharedstatedir@,$sharedstatedir,;t t
8878 s,@localstatedir@,$localstatedir,;t t
8879 s,@libdir@,$libdir,;t t
8880 s,@includedir@,$includedir,;t t
8881 s,@oldincludedir@,$oldincludedir,;t t
8882 s,@infodir@,$infodir,;t t
8883 s,@mandir@,$mandir,;t t
8884 s,@build_alias@,$build_alias,;t t
8885 s,@host_alias@,$host_alias,;t t
8886 s,@target_alias@,$target_alias,;t t
8887 s,@DEFS@,$DEFS,;t t
8888 s,@ECHO_C@,$ECHO_C,;t t
8889 s,@ECHO_N@,$ECHO_N,;t t
8890 s,@ECHO_T@,$ECHO_T,;t t
8891 s,@LIBS@,$LIBS,;t t
8892 s,@build@,$build,;t t
8893 s,@build_cpu@,$build_cpu,;t t
8894 s,@build_vendor@,$build_vendor,;t t
8895 s,@build_os@,$build_os,;t t
8896 s,@host@,$host,;t t
8897 s,@host_cpu@,$host_cpu,;t t
8898 s,@host_vendor@,$host_vendor,;t t
8899 s,@host_os@,$host_os,;t t
8900 s,@target@,$target,;t t
8901 s,@target_cpu@,$target_cpu,;t t
8902 s,@target_vendor@,$target_vendor,;t t
8903 s,@target_os@,$target_os,;t t
8904 s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
8905 s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
8906 s,@INSTALL_DATA@,$INSTALL_DATA,;t t
8907 s,@CYGPATH_W@,$CYGPATH_W,;t t
8908 s,@PACKAGE@,$PACKAGE,;t t
8909 s,@VERSION@,$VERSION,;t t
8910 s,@ACLOCAL@,$ACLOCAL,;t t
8911 s,@AUTOCONF@,$AUTOCONF,;t t
8912 s,@AUTOMAKE@,$AUTOMAKE,;t t
8913 s,@AUTOHEADER@,$AUTOHEADER,;t t
8914 s,@MAKEINFO@,$MAKEINFO,;t t
8915 s,@install_sh@,$install_sh,;t t
8916 s,@STRIP@,$STRIP,;t t
8917 s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t
8918 s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t
8919 s,@mkdir_p@,$mkdir_p,;t t
8920 s,@AWK@,$AWK,;t t
8921 s,@SET_MAKE@,$SET_MAKE,;t t
8922 s,@am__leading_dot@,$am__leading_dot,;t t
8923 s,@AMTAR@,$AMTAR,;t t
8924 s,@am__tar@,$am__tar,;t t
8925 s,@am__untar@,$am__untar,;t t
8926 s,@DLGUI_TRUE@,$DLGUI_TRUE,;t t
8927 s,@DLGUI_FALSE@,$DLGUI_FALSE,;t t
8928 s,@CC@,$CC,;t t
8929 s,@CFLAGS@,$CFLAGS,;t t
8930 s,@LDFLAGS@,$LDFLAGS,;t t
8931 s,@CPPFLAGS@,$CPPFLAGS,;t t
8932 s,@ac_ct_CC@,$ac_ct_CC,;t t
8933 s,@EXEEXT@,$EXEEXT,;t t
8934 s,@OBJEXT@,$OBJEXT,;t t
8935 s,@DEPDIR@,$DEPDIR,;t t
8936 s,@am__include@,$am__include,;t t
8937 s,@am__quote@,$am__quote,;t t
8938 s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t
8939 s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t
8940 s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t
8941 s,@CCDEPMODE@,$CCDEPMODE,;t t
8942 s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t
8943 s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t
8944 s,@RANLIB@,$RANLIB,;t t
8945 s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
8946 s,@CPP@,$CPP,;t t
8947 s,@CXX@,$CXX,;t t
8948 s,@CXXFLAGS@,$CXXFLAGS,;t t
8949 s,@ac_ct_CXX@,$ac_ct_CXX,;t t
8950 s,@CXXDEPMODE@,$CXXDEPMODE,;t t
8951 s,@am__fastdepCXX_TRUE@,$am__fastdepCXX_TRUE,;t t
8952 s,@am__fastdepCXX_FALSE@,$am__fastdepCXX_FALSE,;t t
8953 s,@GLIB_CONFIG@,$GLIB_CONFIG,;t t
8954 s,@GLIB_CFLAGS@,$GLIB_CFLAGS,;t t
8955 s,@GLIB_LIBS@,$GLIB_LIBS,;t t
8956 s,@GTK_CONFIG@,$GTK_CONFIG,;t t
8957 s,@GTK_CFLAGS@,$GTK_CFLAGS,;t t
8958 s,@GTK_LIBS@,$GTK_LIBS,;t t
8959 s,@EGREP@,$EGREP,;t t
8960 s,@LIBJPEG_LIBS@,$LIBJPEG_LIBS,;t t
8961 s,@LIBJPEG_LDFLAGS@,$LIBJPEG_LDFLAGS,;t t
8962 s,@LIBJPEG_CPPFLAGS@,$LIBJPEG_CPPFLAGS,;t t
8963 s,@LIBPNG_LIBS@,$LIBPNG_LIBS,;t t
8964 s,@LIBPNG_CFLAGS@,$LIBPNG_CFLAGS,;t t
8965 s,@LIBZ_LIBS@,$LIBZ_LIBS,;t t
8966 s,@LIBSSL_LIBS@,$LIBSSL_LIBS,;t t
8967 s,@LIBPTHREAD_LIBS@,$LIBPTHREAD_LIBS,;t t
8968 s,@LIBPTHREAD_LDFLAGS@,$LIBPTHREAD_LDFLAGS,;t t
8969 s,@LIBFLTK_CXXFLAGS@,$LIBFLTK_CXXFLAGS,;t t
8970 s,@LIBFLTK_LIBS@,$LIBFLTK_LIBS,;t t
8971 s,@src@,$src,;t t
8972 s,@doc@,$doc,;t t
8973 s,@bin@,$bin,;t t
8974 s,@util@,$util,;t t
8975 s,@lib@,$lib,;t t
8976 s,@LIBOBJS@,$LIBOBJS,;t t
8977 s,@LTLIBOBJS@,$LTLIBOBJS,;t t
8978 CEOF
8979
8980 _ACEOF
8981
8982 cat >>$CONFIG_STATUS <<\_ACEOF
8983 # Split the substitutions into bite-sized pieces for seds with
8984 # small command number limits, like on Digital OSF/1 and HP-UX.
8985 ac_max_sed_lines=48
8986 ac_sed_frag=1 # Number of current file.
8987 ac_beg=1 # First line for current file.
8988 ac_end=$ac_max_sed_lines # Line after last line for current file.
8989 ac_more_lines=:
8990 ac_sed_cmds=
8991 while $ac_more_lines; do
8992 if test $ac_beg -gt 1; then
8993 sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
8994 else
8995 sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
8996 fi
8997 if test ! -s $tmp/subs.frag; then
8998 ac_more_lines=false
8999 else
9000 # The purpose of the label and of the branching condition is to
9001 # speed up the sed processing (if there are no `@' at all, there
9002 # is no need to browse any of the substitutions).
9003 # These are the two extra sed commands mentioned above.
9004 (echo ':t
9005 /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
9006 if test -z "$ac_sed_cmds"; then
9007 ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
9008 else
9009 ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
9010 fi
9011 ac_sed_frag=`expr $ac_sed_frag + 1`
9012 ac_beg=$ac_end
9013 ac_end=`expr $ac_end + $ac_max_sed_lines`
9014 fi
9015 done
9016 if test -z "$ac_sed_cmds"; then
9017 ac_sed_cmds=cat
9018 fi
9019 fi # test -n "$CONFIG_FILES"
9020
9021 _ACEOF
9022 cat >>$CONFIG_STATUS <<\_ACEOF
9023 for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
9024 # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
9025 case $ac_file in
9026 - | *:- | *:-:* ) # input from stdin
9027 cat >$tmp/stdin
9028 ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
9029 ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
9030 *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
9031 ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
9032 * ) ac_file_in=$ac_file.in ;;
9033 esac
9034
9035 # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
9036 ac_dir=`(dirname "$ac_file") 2>/dev/null ||
9037 $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9038 X"$ac_file" : 'X\(//\)[^/]' \| \
9039 X"$ac_file" : 'X\(//\)$' \| \
9040 X"$ac_file" : 'X\(/\)' \| \
9041 . : '\(.\)' 2>/dev/null ||
9042 echo X"$ac_file" |
9043 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9044 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9045 /^X\(\/\/\)$/{ s//\1/; q; }
9046 /^X\(\/\).*/{ s//\1/; q; }
9047 s/.*/./; q'`
9048 { if $as_mkdir_p; then
9049 mkdir -p "$ac_dir"
9050 else
9051 as_dir="$ac_dir"
9052 as_dirs=
9053 while test ! -d "$as_dir"; do
9054 as_dirs="$as_dir $as_dirs"
9055 as_dir=`(dirname "$as_dir") 2>/dev/null ||
9056 $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9057 X"$as_dir" : 'X\(//\)[^/]' \| \
9058 X"$as_dir" : 'X\(//\)$' \| \
9059 X"$as_dir" : 'X\(/\)' \| \
9060 . : '\(.\)' 2>/dev/null ||
9061 echo X"$as_dir" |
9062 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9063 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9064 /^X\(\/\/\)$/{ s//\1/; q; }
9065 /^X\(\/\).*/{ s//\1/; q; }
9066 s/.*/./; q'`
9067 done
9068 test ! -n "$as_dirs" || mkdir $as_dirs
9069 fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
9070 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
9071 { (exit 1); exit 1; }; }; }
9072
9073 ac_builddir=.
9074
9075 if test "$ac_dir" != .; then
9076 ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
9077 # A "../" for each directory in $ac_dir_suffix.
9078 ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
9079 else
9080 ac_dir_suffix= ac_top_builddir=
9081 fi
9082
9083 case $srcdir in
9084 .) # No --srcdir option. We are building in place.
9085 ac_srcdir=.
9086 if test -z "$ac_top_builddir"; then
9087 ac_top_srcdir=.
9088 else
9089 ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
9090 fi ;;
9091 [\\/]* | ?:[\\/]* ) # Absolute path.
9092 ac_srcdir=$srcdir$ac_dir_suffix;
9093 ac_top_srcdir=$srcdir ;;
9094 *) # Relative path.
9095 ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
9096 ac_top_srcdir=$ac_top_builddir$srcdir ;;
9097 esac
9098
9099 # Do not use `cd foo && pwd` to compute absolute paths, because
9100 # the directories may not exist.
9101 case `pwd` in
9102 .) ac_abs_builddir="$ac_dir";;
9103 *)
9104 case "$ac_dir" in
9105 .) ac_abs_builddir=`pwd`;;
9106 [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
9107 *) ac_abs_builddir=`pwd`/"$ac_dir";;
9108 esac;;
9109 esac
9110 case $ac_abs_builddir in
9111 .) ac_abs_top_builddir=${ac_top_builddir}.;;
9112 *)
9113 case ${ac_top_builddir}. in
9114 .) ac_abs_top_builddir=$ac_abs_builddir;;
9115 [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
9116 *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
9117 esac;;
9118 esac
9119 case $ac_abs_builddir in
9120 .) ac_abs_srcdir=$ac_srcdir;;
9121 *)
9122 case $ac_srcdir in
9123 .) ac_abs_srcdir=$ac_abs_builddir;;
9124 [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
9125 *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
9126 esac;;
9127 esac
9128 case $ac_abs_builddir in
9129 .) ac_abs_top_srcdir=$ac_top_srcdir;;
9130 *)
9131 case $ac_top_srcdir in
9132 .) ac_abs_top_srcdir=$ac_abs_builddir;;
9133 [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
9134 *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
9135 esac;;
9136 esac
9137
9138
9139 case $INSTALL in
9140 [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
9141 *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
9142 esac
9143
9144 if test x"$ac_file" != x-; then
9145 { echo "$as_me:$LINENO: creating $ac_file" >&5
9146 echo "$as_me: creating $ac_file" >&6;}
9147 rm -f "$ac_file"
9148 fi
9149 # Let's still pretend it is `configure' which instantiates (i.e., don't
9150 # use $as_me), people would be surprised to read:
9151 # /* config.h. Generated by config.status. */
9152 if test x"$ac_file" = x-; then
9153 configure_input=
9154 else
9155 configure_input="$ac_file. "
9156 fi
9157 configure_input=$configure_input"Generated from `echo $ac_file_in |
9158 sed 's,.*/,,'` by configure."
9159
9160 # First look for the input files in the build tree, otherwise in the
9161 # src tree.
9162 ac_file_inputs=`IFS=:
9163 for f in $ac_file_in; do
9164 case $f in
9165 -) echo $tmp/stdin ;;
9166 [\\/$]*)
9167 # Absolute (can't be DOS-style, as IFS=:)
9168 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
9169 echo "$as_me: error: cannot find input file: $f" >&2;}
9170 { (exit 1); exit 1; }; }
9171 echo "$f";;
9172 *) # Relative
9173 if test -f "$f"; then
9174 # Build tree
9175 echo "$f"
9176 elif test -f "$srcdir/$f"; then
9177 # Source tree
9178 echo "$srcdir/$f"
9179 else
9180 # /dev/null tree
9181 { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
9182 echo "$as_me: error: cannot find input file: $f" >&2;}
9183 { (exit 1); exit 1; }; }
9184 fi;;
9185 esac
9186 done` || { (exit 1); exit 1; }
9187 _ACEOF
9188 cat >>$CONFIG_STATUS <<_ACEOF
9189 sed "$ac_vpsub
9190 $extrasub
9191 _ACEOF
9192 cat >>$CONFIG_STATUS <<\_ACEOF
9193 :t
9194 /@[a-zA-Z_][a-zA-Z_0-9]*@/!b
9195 s,@configure_input@,$configure_input,;t t
9196 s,@srcdir@,$ac_srcdir,;t t
9197 s,@abs_srcdir@,$ac_abs_srcdir,;t t
9198 s,@top_srcdir@,$ac_top_srcdir,;t t
9199 s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
9200 s,@builddir@,$ac_builddir,;t t
9201 s,@abs_builddir@,$ac_abs_builddir,;t t
9202 s,@top_builddir@,$ac_top_builddir,;t t
9203 s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
9204 s,@INSTALL@,$ac_INSTALL,;t t
9205 " $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
9206 rm -f $tmp/stdin
9207 if test x"$ac_file" != x-; then
9208 mv $tmp/out $ac_file
9209 else
9210 cat $tmp/out
9211 rm -f $tmp/out
9212 fi
9213
9214 done
9215 _ACEOF
9216 cat >>$CONFIG_STATUS <<\_ACEOF
9217
9218 #
9219 # CONFIG_HEADER section.
9220 #
9221
9222 # These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
9223 # NAME is the cpp macro being defined and VALUE is the value it is being given.
9224 #
9225 # ac_d sets the value in "#define NAME VALUE" lines.
9226 ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)'
9227 ac_dB='[ ].*$,\1#\2'
9228 ac_dC=' '
9229 ac_dD=',;t'
9230 # ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
9231 ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
9232 ac_uB='$,\1#\2define\3'
9233 ac_uC=' '
9234 ac_uD=',;t'
9235
9236 for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
9237 # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
9238 case $ac_file in
9239 - | *:- | *:-:* ) # input from stdin
9240 cat >$tmp/stdin
9241 ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
9242 ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
9243 *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
9244 ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
9245 * ) ac_file_in=$ac_file.in ;;
9246 esac
9247
9248 test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
9249 echo "$as_me: creating $ac_file" >&6;}
9250
9251 # First look for the input files in the build tree, otherwise in the
9252 # src tree.
9253 ac_file_inputs=`IFS=:
9254 for f in $ac_file_in; do
9255 case $f in
9256 -) echo $tmp/stdin ;;
9257 [\\/$]*)
9258 # Absolute (can't be DOS-style, as IFS=:)
9259 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
9260 echo "$as_me: error: cannot find input file: $f" >&2;}
9261 { (exit 1); exit 1; }; }
9262 # Do quote $f, to prevent DOS paths from being IFS'd.
9263 echo "$f";;
9264 *) # Relative
9265 if test -f "$f"; then
9266 # Build tree
9267 echo "$f"
9268 elif test -f "$srcdir/$f"; then
9269 # Source tree
9270 echo "$srcdir/$f"
9271 else
9272 # /dev/null tree
9273 { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
9274 echo "$as_me: error: cannot find input file: $f" >&2;}
9275 { (exit 1); exit 1; }; }
9276 fi;;
9277 esac
9278 done` || { (exit 1); exit 1; }
9279 # Remove the trailing spaces.
9280 sed 's/[ ]*$//' $ac_file_inputs >$tmp/in
9281
9282 _ACEOF
9283
9284 # Transform confdefs.h into two sed scripts, `conftest.defines' and
9285 # `conftest.undefs', that substitutes the proper values into
9286 # config.h.in to produce config.h. The first handles `#define'
9287 # templates, and the second `#undef' templates.
9288 # And first: Protect against being on the right side of a sed subst in
9289 # config.status. Protect against being in an unquoted here document
9290 # in config.status.
9291 rm -f conftest.defines conftest.undefs
9292 # Using a here document instead of a string reduces the quoting nightmare.
9293 # Putting comments in sed scripts is not portable.
9294 #
9295 # `end' is used to avoid that the second main sed command (meant for
9296 # 0-ary CPP macros) applies to n-ary macro definitions.
9297 # See the Autoconf documentation for `clear'.
9298 cat >confdef2sed.sed <<\_ACEOF
9299 s/[\\&,]/\\&/g
9300 s,[\\$`],\\&,g
9301 t clear
9302 : clear
9303 s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
9304 t end
9305 s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
9306 : end
9307 _ACEOF
9308 # If some macros were called several times there might be several times
9309 # the same #defines, which is useless. Nevertheless, we may not want to
9310 # sort them, since we want the *last* AC-DEFINE to be honored.
9311 uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
9312 sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
9313 rm -f confdef2sed.sed
9314
9315 # This sed command replaces #undef with comments. This is necessary, for
9316 # example, in the case of _POSIX_SOURCE, which is predefined and required
9317 # on some systems where configure will not decide to define it.
9318 cat >>conftest.undefs <<\_ACEOF
9319 s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
9320 _ACEOF
9321
9322 # Break up conftest.defines because some shells have a limit on the size
9323 # of here documents, and old seds have small limits too (100 cmds).
9324 echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
9325 echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
9326 echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
9327 echo ' :' >>$CONFIG_STATUS
9328 rm -f conftest.tail
9329 while grep . conftest.defines >/dev/null
9330 do
9331 # Write a limited-size here document to $tmp/defines.sed.
9332 echo ' cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
9333 # Speed up: don't consider the non `#define' lines.
9334 echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS
9335 # Work around the forget-to-reset-the-flag bug.
9336 echo 't clr' >>$CONFIG_STATUS
9337 echo ': clr' >>$CONFIG_STATUS
9338 sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
9339 echo 'CEOF
9340 sed -f $tmp/defines.sed $tmp/in >$tmp/out
9341 rm -f $tmp/in
9342 mv $tmp/out $tmp/in
9343 ' >>$CONFIG_STATUS
9344 sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
9345 rm -f conftest.defines
9346 mv conftest.tail conftest.defines
9347 done
9348 rm -f conftest.defines
9349 echo ' fi # grep' >>$CONFIG_STATUS
9350 echo >>$CONFIG_STATUS
9351
9352 # Break up conftest.undefs because some shells have a limit on the size
9353 # of here documents, and old seds have small limits too (100 cmds).
9354 echo ' # Handle all the #undef templates' >>$CONFIG_STATUS
9355 rm -f conftest.tail
9356 while grep . conftest.undefs >/dev/null
9357 do
9358 # Write a limited-size here document to $tmp/undefs.sed.
9359 echo ' cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
9360 # Speed up: don't consider the non `#undef'
9361 echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS
9362 # Work around the forget-to-reset-the-flag bug.
9363 echo 't clr' >>$CONFIG_STATUS
9364 echo ': clr' >>$CONFIG_STATUS
9365 sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
9366 echo 'CEOF
9367 sed -f $tmp/undefs.sed $tmp/in >$tmp/out
9368 rm -f $tmp/in
9369 mv $tmp/out $tmp/in
9370 ' >>$CONFIG_STATUS
9371 sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
9372 rm -f conftest.undefs
9373 mv conftest.tail conftest.undefs
9374 done
9375 rm -f conftest.undefs
9376
9377 cat >>$CONFIG_STATUS <<\_ACEOF
9378 # Let's still pretend it is `configure' which instantiates (i.e., don't
9379 # use $as_me), people would be surprised to read:
9380 # /* config.h. Generated by config.status. */
9381 if test x"$ac_file" = x-; then
9382 echo "/* Generated by configure. */" >$tmp/config.h
9383 else
9384 echo "/* $ac_file. Generated by configure. */" >$tmp/config.h
9385 fi
9386 cat $tmp/in >>$tmp/config.h
9387 rm -f $tmp/in
9388 if test x"$ac_file" != x-; then
9389 if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
9390 { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
9391 echo "$as_me: $ac_file is unchanged" >&6;}
9392 else
9393 ac_dir=`(dirname "$ac_file") 2>/dev/null ||
9394 $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9395 X"$ac_file" : 'X\(//\)[^/]' \| \
9396 X"$ac_file" : 'X\(//\)$' \| \
9397 X"$ac_file" : 'X\(/\)' \| \
9398 . : '\(.\)' 2>/dev/null ||
9399 echo X"$ac_file" |
9400 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9401 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9402 /^X\(\/\/\)$/{ s//\1/; q; }
9403 /^X\(\/\).*/{ s//\1/; q; }
9404 s/.*/./; q'`
9405 { if $as_mkdir_p; then
9406 mkdir -p "$ac_dir"
9407 else
9408 as_dir="$ac_dir"
9409 as_dirs=
9410 while test ! -d "$as_dir"; do
9411 as_dirs="$as_dir $as_dirs"
9412 as_dir=`(dirname "$as_dir") 2>/dev/null ||
9413 $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9414 X"$as_dir" : 'X\(//\)[^/]' \| \
9415 X"$as_dir" : 'X\(//\)$' \| \
9416 X"$as_dir" : 'X\(/\)' \| \
9417 . : '\(.\)' 2>/dev/null ||
9418 echo X"$as_dir" |
9419 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9420 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9421 /^X\(\/\/\)$/{ s//\1/; q; }
9422 /^X\(\/\).*/{ s//\1/; q; }
9423 s/.*/./; q'`
9424 done
9425 test ! -n "$as_dirs" || mkdir $as_dirs
9426 fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
9427 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
9428 { (exit 1); exit 1; }; }; }
9429
9430 rm -f $ac_file
9431 mv $tmp/config.h $ac_file
9432 fi
9433 else
9434 cat $tmp/config.h
9435 rm -f $tmp/config.h
9436 fi
9437 # Compute $ac_file's index in $config_headers.
9438 _am_stamp_count=1
9439 for _am_header in $config_headers :; do
9440 case $_am_header in
9441 $ac_file | $ac_file:* )
9442 break ;;
9443 * )
9444 _am_stamp_count=`expr $_am_stamp_count + 1` ;;
9445 esac
9446 done
9447 echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null ||
9448 $as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9449 X$ac_file : 'X\(//\)[^/]' \| \
9450 X$ac_file : 'X\(//\)$' \| \
9451 X$ac_file : 'X\(/\)' \| \
9452 . : '\(.\)' 2>/dev/null ||
9453 echo X$ac_file |
9454 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9455 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9456 /^X\(\/\/\)$/{ s//\1/; q; }
9457 /^X\(\/\).*/{ s//\1/; q; }
9458 s/.*/./; q'`/stamp-h$_am_stamp_count
9459 done
9460 _ACEOF
9461 cat >>$CONFIG_STATUS <<\_ACEOF
9462
9463 #
9464 # CONFIG_COMMANDS section.
9465 #
9466 for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue
9467 ac_dest=`echo "$ac_file" | sed 's,:.*,,'`
9468 ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'`
9469 ac_dir=`(dirname "$ac_dest") 2>/dev/null ||
9470 $as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9471 X"$ac_dest" : 'X\(//\)[^/]' \| \
9472 X"$ac_dest" : 'X\(//\)$' \| \
9473 X"$ac_dest" : 'X\(/\)' \| \
9474 . : '\(.\)' 2>/dev/null ||
9475 echo X"$ac_dest" |
9476 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9477 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9478 /^X\(\/\/\)$/{ s//\1/; q; }
9479 /^X\(\/\).*/{ s//\1/; q; }
9480 s/.*/./; q'`
9481 { if $as_mkdir_p; then
9482 mkdir -p "$ac_dir"
9483 else
9484 as_dir="$ac_dir"
9485 as_dirs=
9486 while test ! -d "$as_dir"; do
9487 as_dirs="$as_dir $as_dirs"
9488 as_dir=`(dirname "$as_dir") 2>/dev/null ||
9489 $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9490 X"$as_dir" : 'X\(//\)[^/]' \| \
9491 X"$as_dir" : 'X\(//\)$' \| \
9492 X"$as_dir" : 'X\(/\)' \| \
9493 . : '\(.\)' 2>/dev/null ||
9494 echo X"$as_dir" |
9495 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9496 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9497 /^X\(\/\/\)$/{ s//\1/; q; }
9498 /^X\(\/\).*/{ s//\1/; q; }
9499 s/.*/./; q'`
9500 done
9501 test ! -n "$as_dirs" || mkdir $as_dirs
9502 fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
9503 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
9504 { (exit 1); exit 1; }; }; }
9505
9506 ac_builddir=.
9507
9508 if test "$ac_dir" != .; then
9509 ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
9510 # A "../" for each directory in $ac_dir_suffix.
9511 ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
9512 else
9513 ac_dir_suffix= ac_top_builddir=
9514 fi
9515
9516 case $srcdir in
9517 .) # No --srcdir option. We are building in place.
9518 ac_srcdir=.
9519 if test -z "$ac_top_builddir"; then
9520 ac_top_srcdir=.
9521 else
9522 ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
9523 fi ;;
9524 [\\/]* | ?:[\\/]* ) # Absolute path.
9525 ac_srcdir=$srcdir$ac_dir_suffix;
9526 ac_top_srcdir=$srcdir ;;
9527 *) # Relative path.
9528 ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
9529 ac_top_srcdir=$ac_top_builddir$srcdir ;;
9530 esac
9531
9532 # Do not use `cd foo && pwd` to compute absolute paths, because
9533 # the directories may not exist.
9534 case `pwd` in
9535 .) ac_abs_builddir="$ac_dir";;
9536 *)
9537 case "$ac_dir" in
9538 .) ac_abs_builddir=`pwd`;;
9539 [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
9540 *) ac_abs_builddir=`pwd`/"$ac_dir";;
9541 esac;;
9542 esac
9543 case $ac_abs_builddir in
9544 .) ac_abs_top_builddir=${ac_top_builddir}.;;
9545 *)
9546 case ${ac_top_builddir}. in
9547 .) ac_abs_top_builddir=$ac_abs_builddir;;
9548 [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
9549 *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
9550 esac;;
9551 esac
9552 case $ac_abs_builddir in
9553 .) ac_abs_srcdir=$ac_srcdir;;
9554 *)
9555 case $ac_srcdir in
9556 .) ac_abs_srcdir=$ac_abs_builddir;;
9557 [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
9558 *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
9559 esac;;
9560 esac
9561 case $ac_abs_builddir in
9562 .) ac_abs_top_srcdir=$ac_top_srcdir;;
9563 *)
9564 case $ac_top_srcdir in
9565 .) ac_abs_top_srcdir=$ac_abs_builddir;;
9566 [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
9567 *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
9568 esac;;
9569 esac
9570
9571
9572 { echo "$as_me:$LINENO: executing $ac_dest commands" >&5
9573 echo "$as_me: executing $ac_dest commands" >&6;}
9574 case $ac_dest in
9575 depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do
9576 # Strip MF so we end up with the name of the file.
9577 mf=`echo "$mf" | sed -e 's/:.*$//'`
9578 # Check whether this is an Automake generated Makefile or not.
9579 # We used to match only the files named `Makefile.in', but
9580 # some people rename them; so instead we look at the file content.
9581 # Grep'ing the first line is not enough: some people post-process
9582 # each Makefile.in and add a new line on top of each file to say so.
9583 # So let's grep whole file.
9584 if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
9585 dirpart=`(dirname "$mf") 2>/dev/null ||
9586 $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9587 X"$mf" : 'X\(//\)[^/]' \| \
9588 X"$mf" : 'X\(//\)$' \| \
9589 X"$mf" : 'X\(/\)' \| \
9590 . : '\(.\)' 2>/dev/null ||
9591 echo X"$mf" |
9592 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9593 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9594 /^X\(\/\/\)$/{ s//\1/; q; }
9595 /^X\(\/\).*/{ s//\1/; q; }
9596 s/.*/./; q'`
9597 else
9598 continue
9599 fi
9600 # Extract the definition of DEPDIR, am__include, and am__quote
9601 # from the Makefile without running `make'.
9602 DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
9603 test -z "$DEPDIR" && continue
9604 am__include=`sed -n 's/^am__include = //p' < "$mf"`
9605 test -z "am__include" && continue
9606 am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
9607 # When using ansi2knr, U may be empty or an underscore; expand it
9608 U=`sed -n 's/^U = //p' < "$mf"`
9609 # Find all dependency output files, they are included files with
9610 # $(DEPDIR) in their names. We invoke sed twice because it is the
9611 # simplest approach to changing $(DEPDIR) to its actual value in the
9612 # expansion.
9613 for file in `sed -n "
9614 s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
9615 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
9616 # Make sure the directory exists.
9617 test -f "$dirpart/$file" && continue
9618 fdir=`(dirname "$file") 2>/dev/null ||
9619 $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9620 X"$file" : 'X\(//\)[^/]' \| \
9621 X"$file" : 'X\(//\)$' \| \
9622 X"$file" : 'X\(/\)' \| \
9623 . : '\(.\)' 2>/dev/null ||
9624 echo X"$file" |
9625 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9626 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9627 /^X\(\/\/\)$/{ s//\1/; q; }
9628 /^X\(\/\).*/{ s//\1/; q; }
9629 s/.*/./; q'`
9630 { if $as_mkdir_p; then
9631 mkdir -p $dirpart/$fdir
9632 else
9633 as_dir=$dirpart/$fdir
9634 as_dirs=
9635 while test ! -d "$as_dir"; do
9636 as_dirs="$as_dir $as_dirs"
9637 as_dir=`(dirname "$as_dir") 2>/dev/null ||
9638 $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
9639 X"$as_dir" : 'X\(//\)[^/]' \| \
9640 X"$as_dir" : 'X\(//\)$' \| \
9641 X"$as_dir" : 'X\(/\)' \| \
9642 . : '\(.\)' 2>/dev/null ||
9643 echo X"$as_dir" |
9644 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
9645 /^X\(\/\/\)[^/].*/{ s//\1/; q; }
9646 /^X\(\/\/\)$/{ s//\1/; q; }
9647 /^X\(\/\).*/{ s//\1/; q; }
9648 s/.*/./; q'`
9649 done
9650 test ! -n "$as_dirs" || mkdir $as_dirs
9651 fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5
9652 echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;}
9653 { (exit 1); exit 1; }; }; }
9654
9655 # echo "creating $dirpart/$file"
9656 echo '# dummy' > "$dirpart/$file"
9657 done
9658 done
9659 ;;
9660 esac
9661 done
9662 _ACEOF
9663
9664 cat >>$CONFIG_STATUS <<\_ACEOF
9665
9666 { (exit 0); exit 0; }
9667 _ACEOF
9668 chmod +x $CONFIG_STATUS
9669 ac_clean_files=$ac_clean_files_save
9670
9671
9672 # configure is writing to config.log, and then calls config.status.
9673 # config.status does its own redirection, appending to config.log.
9674 # Unfortunately, on DOS this fails, as config.log is still kept open
9675 # by configure, so config.status won't be able to write to it; its
9676 # output is simply discarded. So we exec the FD to /dev/null,
9677 # effectively closing config.log, so it can be properly (re)opened and
9678 # appended to by config.status. When coming back to configure, we
9679 # need to make the FD available again.
9680 if test "$no_create" != yes; then
9681 ac_cs_success=:
9682 ac_config_status_args=
9683 test "$silent" = yes &&
9684 ac_config_status_args="$ac_config_status_args --quiet"
9685 exec 5>/dev/null
9686 $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
9687 exec 5>>config.log
9688 # Use ||, not &&, to avoid exiting from the if with $? = 1, which
9689 # would make configure fail if this is the last instruction.
9690 $ac_cs_success || { (exit 1); exit 1; }
9691 fi
9692
9693
00 dnl Process this file with aclocal, autoconf and automake.
11
2 AC_INIT(src/dillo.c)
3
4 dnl Detect the canonical host and target build environment
5 AC_CANONICAL_SYSTEM
6
7 AM_INIT_AUTOMAKE(dillo, 0.8.6)
8 AM_CONFIG_HEADER(config.h)
2 AC_INIT([dillo], [3.0-pre])
3
4 dnl Detect the canonical target build environment
5 AC_CANONICAL_TARGET
6
7 AM_INIT_AUTOMAKE
8 AC_CONFIG_SRCDIR([src/dillo.cc])
9 AC_CONFIG_HEADERS([config.h])
10
11 sysconfdir=${sysconfdir}/${PACKAGE}
912
1013 dnl Options
1114
1821 , enable_gprof=no)
1922 AC_ARG_ENABLE(insure, [ --enable-insure Try to compile and run with Insure++],
2023 , enable_insure=no)
21 AC_ARG_ENABLE(ansi, [ --enable-ansi Try to compile and run with ANSI flags],
22 , enable_ansi=no)
24 AC_ARG_ENABLE(ssl, [ --enable-ssl Enable ssl, https (ALPHA CODE)],
25 , enable_ssl=no)
2326 AC_ARG_ENABLE(ipv6, [ --enable-ipv6 Build with support for IPv6], , )
24 AC_ARG_ENABLE(rtfl, [ --enable-rtfl Build with rtfl messages], enable_rtfl=yes)
2527 AC_ARG_ENABLE(cookies,[ --disable-cookies Don't compile support for cookies],
2628 , enable_cookies=yes)
2729 AC_ARG_ENABLE(png, [ --disable-png Disable support for PNG images],
3032 enable_jpeg=$enableval, enable_jpeg=yes)
3133 AC_ARG_ENABLE(gif, [ --disable-gif Disable support for GIF images],
3234 enable_gif=$enableval, enable_gif=yes)
33 AC_ARG_ENABLE(ssl, [ --disable-ssl Disable ssl features (eg. https)],
34 enable_ssl=$enableval, enable_ssl=yes)
35 AC_ARG_ENABLE(dlgui, [ --disable-dlgui Disable FLTK2 GUI for downloads],
36 enable_dlgui=$enableval, enable_dlgui=yes)
3735 AC_ARG_ENABLE(threaded-dns,[ --disable-threaded-dns Disable the advantage of a reentrant resolver library],
3836 enable_threaded_dns=$enableval, enable_threaded_dns=yes)
39 AM_CONDITIONAL(DLGUI, test x$enable_dlgui = xyes)
40
37 AC_ARG_ENABLE(rtfl, [ --enable-rtfl Build with rtfl messages (for debugging rendering)])
4138 AC_PROG_CC
42 AM_PROG_CC_STDC
39 AC_PROG_CXX
4340 AC_PROG_RANLIB
4441 AC_PROG_CPP
45 AC_PROG_CXX
42
43 dnl ----------------------------
44 dnl Check our char and int types
45 dnl ----------------------------
46 dnl
47 AC_CHECK_SIZEOF(char)
48 AC_CHECK_SIZEOF(short)
49 AC_CHECK_SIZEOF(long)
50 AC_CHECK_SIZEOF(int)
51 AC_CHECK_SIZEOF(void *)
52
53 AC_TYPE_INT16_T
54 AC_TYPE_UINT16_T
55 AC_TYPE_INT32_T
56 AC_TYPE_UINT32_T
4657
4758 dnl --------------------------------------
4859 dnl Check whether to add /usr/local or not
6879 dnl
6980 AC_MSG_CHECKING([for socklen_t])
7081 ac_cv_socklen_t=""
71 AC_TRY_COMPILE([
82 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
7283 #include <sys/types.h>
7384 #include <sys/socket.h>
74 ],[
85 ]],[[
7586 socklen_t a=0;
7687 getsockname(0,(struct sockaddr*)0, &a);
77 ],
88 ]])],
7889 ac_cv_socklen_t="socklen_t",
79 AC_TRY_COMPILE([
90 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
8091 #include <sys/types.h>
8192 #include <sys/socket.h>
82 ],[
93 ]],[[
8394 int a=0;
8495 getsockname(0,(struct sockaddr*)0, &a);
85 ],
96 ]])],
8697 ac_cv_socklen_t="int",
8798 ac_cv_socklen_t="size_t"
8899 )
89100 )
90101 AC_MSG_RESULT($ac_cv_socklen_t)
91102 if test "$ac_cv_socklen_t" != "socklen_t"; then
92 AC_DEFINE_UNQUOTED(socklen_t, $ac_cv_socklen_t,
103 AC_DEFINE_UNQUOTED([socklen_t], [$ac_cv_socklen_t],
93104 [Define the real type of socklen_t])
94105 fi
95106
96107
97 dnl -----------------------------------
98 dnl Check for glib
99 dnl -----------------------------------
100 dnl
101 AM_PATH_GLIB(1.2.0, ,
102 AC_MSG_ERROR([Unable to find glib with a version >= 1.2.0. Dillo NEEDS glib]))
103
104
105 dnl -----------------------------------
106 dnl Check for Gtk+
107 dnl -----------------------------------
108 dnl
109 AM_PATH_GTK(1.2.0, ,
110 AC_MSG_ERROR(Unable to find Gtk+ with a version >= 1.2.0. Dillo NEEDS Gtk+))
111
112 dnl -----------------------------------
113 dnl Check for FLTK
114 dnl -----------------------------------
108 dnl ----------------------
109 dnl Test for FLTK 1.3 library
110 dnl ----------------------
115111 dnl
116112 dnl For debugging and to be user friendly
117 AC_MSG_CHECKING([Hackish check for FLTK])
118 dnl Call the config sripts.
119 LIBFLTK_CXXFLAGS=`fltk-config --cxxflags`
120 LIBFLTK_LIBS=`fltk-config --ldflags`
113 AC_MSG_CHECKING([FLTK 1.3])
114 fltk_version="`fltk-config --version 2>/dev/null`"
115 case $fltk_version in
116 1.3.*) AC_MSG_RESULT(yes)
117 LIBFLTK_CXXFLAGS=`fltk-config --cxxflags`
118 LIBFLTK_CFLAGS=`fltk-config --cflags`
119 LIBFLTK_LIBS=`fltk-config --ldflags`;;
120 ?*) AC_MSG_RESULT(no)
121 AC_ERROR(FLTK 1.3 required; version found: $fltk_version);;
122 *) AC_MSG_RESULT(no)
123 AC_ERROR(FLTK 1.3 required; fltk-config not found)
124 esac
121125
122126
123127 dnl ----------------
147151 fi
148152
149153 if test "x$jpeg_ok" = "xyes"; then
150 AC_DEFINE([ENABLE_JPEG], [], [Enable JPEG images])
151 fi
152
153 dnl ------------------------------
154 dnl Test for zlib (libpng uses it)
155 dnl ------------------------------
156 dnl
157 if test "x$enable_png" = "xyes"; then
158 AC_CHECK_HEADER(zlib.h, libz_ok=yes, libz_ok=no)
159
160 if test "x$libz_ok" = "xyes"; then
161 old_libs="$LIBS"
162 AC_CHECK_LIB(z, zlibVersion, libz_ok=yes, libz_ok=no)
163 LIBS="$old_libs"
164 fi
165
166 if test "x$libz_ok" = xyes; then
167 LIBZ_LIBS="-lz"
168 else
169 AC_MSG_WARN([*** No libz found. Disabling PNG images ***])
170 fi
154 AC_DEFINE([ENABLE_JPEG], [1], [Enable JPEG images])
155 fi
156
157 dnl -------------
158 dnl Test for zlib
159 dnl -------------
160 dnl
161 AC_CHECK_HEADER(zlib.h, libz_ok=yes, libz_ok=no)
162
163 if test "x$libz_ok" = "xyes"; then
164 old_libs="$LIBS"
165 AC_CHECK_LIB(z, zlibVersion, libz_ok=yes, libz_ok=no)
166 LIBS="$old_libs"
167 fi
168
169 if test "x$libz_ok" = xyes; then
170 LIBZ_LIBS="-lz"
171 else
172 AC_MSG_ERROR(zlib must be installed!)
171173 fi
172174
173175 dnl ---------------
174176 dnl Test for libpng
175177 dnl ---------------
176178 dnl
177 if test "x$enable_png" = "xyes" && test "x$libz_ok" = "xyes"; then
179 if test "x$enable_png" = "xyes"; then
178180 AC_MSG_CHECKING([for libpng-config])
179181
180182 dnl Check if the user hasn't set the variable $PNG_CONFIG
181183 if test -z "$PNG_CONFIG"; then
182 PNG_CONFIG=`which libpng12-config`
184 PNG_CONFIG=`which libpng14-config`
185 if test -z "$PNG_CONFIG"; then
186 PNG_CONFIG=`which libpng12-config`
187 fi
183188 if test -z "$PNG_CONFIG"; then
184189 PNG_CONFIG=`which libpng-config`
185190 fi
202207 AC_MSG_CHECKING([for libpng version])
203208 png_version=`$PNG_CONFIG --version`
204209 case $png_version in
210 1.4.*) AC_MSG_RESULT([$png_version (newer version)]) ;;
205211 1.2.*) AC_MSG_RESULT([$png_version (newer version)]) ;;
206212 1.0.*) AC_MSG_RESULT([$png_version (older version)]) ;;
207213 *) AC_MSG_RESULT([ERROR]) ;;
219225
220226 if test "x$png_ok" = "xyes"; then
221227 old_libs="$LIBS"
222 AC_CHECK_LIB(png, png_check_sig, png_ok=yes, png_ok=no, $LIBZ_LIBS -lm)
228 AC_CHECK_LIB(png, png_sig_cmp, png_ok=yes, png_ok=no, $LIBZ_LIBS -lm)
223229 LIBS="$old_libs"
224230
225231 if test "x$png_ok" = "xyes"; then
234240 fi
235241
236242 if test "x$png_ok" = "xyes"; then
237 AC_DEFINE([ENABLE_PNG], [], [Enable PNG images])
243 AC_DEFINE([ENABLE_PNG], [1], [Enable PNG images])
238244 fi
239245
240246 dnl Check if support for GIF images should be compiled in
241247 if test "x$enable_gif" = "xyes"; then
242 AC_DEFINE([ENABLE_GIF], [], [Enable GIF images])
248 AC_DEFINE([ENABLE_GIF], [1], [Enable GIF images])
243249 fi
244250
245251 dnl --------------------------
257263
258264 if test "x$ssl_ok" = "xyes"; then
259265 LIBSSL_LIBS="-lcrypto -lssl"
266 AC_MSG_WARN([*** Enabling ssl support. THIS IS ALPHA CODE!***])
260267 else
261268 AC_MSG_WARN([*** No libssl found. Disabling ssl support.***])
262269 fi
263270 fi
264271
265272 if test "x$ssl_ok" = "xyes"; then
266 AC_DEFINE([ENABLE_SSL], [], [Enable SSL support])
267 fi
268
273 AC_DEFINE([ENABLE_SSL], [1], [Enable SSL support])
274 fi
275
276 dnl --------------------------------------------------------------
277 dnl Test for iconv functionality in libc or for libiconv usability
278 dnl --------------------------------------------------------------
279 AC_CHECK_HEADER(iconv.h, iconv_ok=yes, iconv_ok=no)
280 if test "x$iconv_ok" = "xyes"; then
281 AC_CHECK_LIB(c, iconv_open, LIBICONV_LIBS="",
282 AC_CHECK_LIB(iconv, iconv_open, LIBICONV_LIBS="-liconv", iconv_ok=no))
283 fi
284 if test "x$iconv_ok" = "xno"; then
285 dnl Test for OpenBSD
286 old_libs="$LIBS"
287 LIBS="$old_libs -liconv"
288 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
289 #include <iconv.h>
290 ]],[[
291 iconv_open("","");
292 ]])],
293 iconv_ok=yes,iconv_ok=no)
294 LIBS="$old_libs"
295 if test "x$iconv_ok" = "xyes"; then
296 LIBICONV_LIBS="-liconv"
297 fi
298 fi
299
300 if test "x$iconv_ok" = "xno"; then
301 AC_MSG_ERROR(libiconv must be installed!)
302 fi
303
304 dnl ----------------------
305 dnl Check if we need to
306 dnl support the old
307 dnl iconv interface
308 dnl ----------------------
309 if test "x$iconv_ok" = "xyes"; then
310 old_libs="$LIBS"
311 LIBS="$old_libs $LIBICONV_LIBS"
312 old_cflags="$CFLAGS"
313 CFLAGS="$CFLAGS -Werror"
314 AC_LANG_PUSH([C++])
315 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
316 #include <iconv.h>
317 ]],[[
318 const char *inPtr;
319 char *outPtr;
320 size_t inLeft = 0, outRoom = 0;
321 iconv_t encoder = iconv_open("ASCII", "UTF-8");
322 iconv(encoder, &inPtr, &inLeft, &outPtr, &outRoom);
323 ]])],
324 iconv_old=yes,iconv_old=no)
325 AC_LANG_POP([C++])
326 LIBS="$old_libs"
327 CFLAGS="$old_cflags"
328
329 if test "x$iconv_old" = "xyes"; then
330 AC_DEFINE([inbuf_t], [const char], [Use const char pointers for older libiconv])
331 else
332 AC_DEFINE([inbuf_t], [char], [Use char pointers for newer libiconv])
333 fi
334 fi
269335
270336 dnl ----------------------
271337 dnl Test for POSIX threads
285351 AC_MSG_WARN([*** _Untested pthreads_ try setting LIBPTHREAD_LIBS manually if it doesn't work ***])
286352 ;;
287353
354 *-*-minix*)
355 AC_MSG_NOTICE([Minix detected, skipping pthread detection])
356 ;;
357
288358 *)
289359 AC_MSG_CHECKING(whether threads work with -pthread)
290360 LDSAVEFLAGS=$LDFLAGS
291361 LDFLAGS="$LDFLAGS -pthread"
292 AC_TRY_LINK_FUNC(pthread_create, pthread_ok=yes, pthread_ok=no)
362 AC_LINK_IFELSE([AC_LANG_CALL([],[pthread_create])],
363 pthread_ok=yes, pthread_ok=no)
293364 LDFLAGS=$LDSAVEFLAGS
294365
295366 if test "x$pthread_ok" = "xyes"; then
318389 esac
319390 fi
320391
321 dnl ------------------------------------
322 dnl Workaround for nanosleep and solaris
323 dnl ------------------------------------
324 dnl
325 case $target in
326 *-*-solaris*)
327 AC_MSG_CHECKING(whether SunOS has -lrt )
328 LDSAVEFLAGS="$LDFLAGS"
329 LDFLAGS="$LDFLAGS -lrt"
330 AC_TRY_LINK_FUNC(nanosleep, rt_ok=yes, rt_ok=no)
331 if test "x$rt_ok" = "xyes"; then
332 AC_MSG_RESULT(yes)
333 else
334 AC_MSG_RESULT(no)
335 AC_MSG_CHECKING(whether SunOS has -lposix4 )
336 LDFLAGS="$LDSAVEFLAGS -lposix4"
337 AC_TRY_LINK_FUNC(nanosleep, posix_ok=yes, posix_ok=no)
338 if test "x$posix_ok" = "xyes"; then
339 AC_MSG_RESULT(yes)
340 else
341 LDFLAGS=$LDSAVEFLAGS
342 AC_MSG_RESULT(no)
343 AC_MSG_WARN([*** Try setting LIBS or LDFLAGS manually to point to the library with nanosleep()***])
344 fi
345 fi
346 ;;
347 esac
348
349392 dnl --------------------
350393 dnl Command line options
351394 dnl --------------------
352395 dnl
353396 if test "x$enable_cookies" = "xno" ; then
354397 CFLAGS="$CFLAGS -DDISABLE_COOKIES"
398 CXXFLAGS="$CXXFLAGS -DDISABLE_COOKIES"
355399 fi
356400 if test "x$enable_ipv6" = "xyes" ; then
357401 CFLAGS="$CFLAGS -DENABLE_IPV6"
361405 fi
362406 if test "x$enable_gprof" = "xyes" ; then
363407 CFLAGS="$CFLAGS -pg"
408 CXXFLAGS="$CXXFLAGS -pg"
364409 fi
365410 if test "x$enable_insure" = "xyes" ; then
366411 CC="insure -Zoi \"compiler $CC\""
367412 LIBS="$LIBS -lstdc++-2-libc6.1-1-2.9.0"
368413 fi
369 if test "x$enable_rtfl" = "xyes" ; then
370 CFLAGS="$CFLAGS -DDBG_RTFL"
371 fi
372414 if test "x$enable_threaded_dns" = "xyes" ; then
373415 CFLAGS="$CFLAGS -DD_DNS_THREADED"
416 fi
417 if test "x$enable_rtfl" = "xyes" ; then
418 CXXFLAGS="$CXXFLAGS -DDBG_RTFL"
374419 fi
375420
376421 dnl -----------------------
377422 dnl Checks for header files
378423 dnl -----------------------
379424 dnl
380 AC_HEADER_STDC
381425 AC_CHECK_HEADERS(fcntl.h unistd.h sys/uio.h)
382426
383427 dnl --------------------------
394438 if test "`echo $CFLAGS | grep '\-Wall' 2> /dev/null`" = ""; then
395439 CFLAGS="$CFLAGS -Wall"
396440 fi
397 if test "`echo $CFLAGS | grep '\-W[^a]' 2> /dev/null`" = ""; then
398 if test "`$CC -v 2>&1 | grep 'version 3'`" != ""; then
399 CFLAGS="$CFLAGS -W -Wno-unused-parameter"
400 fi
441 if test "`echo $CFLAGS | grep -e '-W ' -e '-W$' 2> /dev/null`" = ""; then
442 CFLAGS="$CFLAGS -W"
443 fi
444 if test "`echo $CFLAGS | grep '\-Wno-unused-parameter' 2> /dev/null`" = ""; then
445 CFLAGS="$CFLAGS -Wno-unused-parameter"
401446 fi
402447 if test "`echo $CFLAGS | grep '\-Waggregate-return' 2> /dev/null`" = ""; then
403448 CFLAGS="$CFLAGS -Waggregate-return"
404449 fi
405
406 if eval "test x$enable_ansi = xyes"; then
407 if test "`echo $CFLAGS | grep '\-ansi' 2> /dev/null`" = ""; then
408 CFLAGS="$CFLAGS -ansi"
409 fi
410
411 if test "`echo $CFLAGS | grep '\-pedantic' 2> /dev/null`" = ""; then
412 CFLAGS="$CFLAGS -pedantic"
413 fi
414 fi
450 fi
451 dnl -----------
452 dnl CXX options
453 dnl -----------
454 dnl
455
456 if eval "test x$GCC = xyes"; then
457 CXXFLAGS="$CXXFLAGS -Wall -W -Wno-unused-parameter -fno-rtti -fno-exceptions"
415458 fi
416459
417460 AC_SUBST(LIBJPEG_LIBS)
424467 AC_SUBST(LIBPTHREAD_LIBS)
425468 AC_SUBST(LIBPTHREAD_LDFLAGS)
426469 AC_SUBST(LIBFLTK_CXXFLAGS)
470 AC_SUBST(LIBFLTK_CFLAGS)
427471 AC_SUBST(LIBFLTK_LIBS)
472 AC_SUBST(LIBICONV_LIBS)
428473 AC_SUBST(datadir)
429 AC_SUBST(src doc bin util lib)
430
431 AC_OUTPUT(Makefile dpip/Makefile dpid/Makefile dpi/Makefile doc/Makefile src/Makefile src/IO/Makefile)
432
474
475 AC_CONFIG_FILES([
476 Makefile
477 dlib/Makefile
478 dpip/Makefile
479 dpid/Makefile
480 dpi/Makefile
481 doc/Makefile
482 dw/Makefile
483 lout/Makefile
484 src/Makefile
485 src/IO/Makefile
486 test/Makefile
487 ])
488
489 AC_OUTPUT
0 #ifndef __D_SIZE_H__
1 #define __D_SIZE_H__
2
3
4 #include "config.h"
5
6 #ifdef HAVE_STDINT_H
7 #include <stdint.h>
8 #else
9 #ifdef HAVE_INTTYPES_H
10 #include <inttypes.h>
11 #else
12 /* config.h defines {int,uint}*_t */
13 #endif /* HAVE_INTTYPES_H */
14 #endif /*HAVE_STDINT_H */
15
16 typedef unsigned char uchar_t;
17 typedef unsigned short ushort_t;
18 typedef unsigned long ulong_t;
19 typedef unsigned int uint_t;
20 typedef unsigned char bool_t;
21
22
23 #endif /* __D_SIZE_H__ */
+0
-529
depcomp less more
0 #! /bin/sh
1 # depcomp - compile a program generating dependencies as side-effects
2
3 scriptversion=2005-02-09.22
4
5 # Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc.
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2, or (at your option)
10 # any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 # 02111-1307, USA.
21
22 # As a special exception to the GNU General Public License, if you
23 # distribute this file as part of a program that contains a
24 # configuration script generated by Autoconf, you may include it under
25 # the same distribution terms that you use for the rest of that program.
26
27 # Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
28
29 case $1 in
30 '')
31 echo "$0: No command. Try \`$0 --help' for more information." 1>&2
32 exit 1;
33 ;;
34 -h | --h*)
35 cat <<\EOF
36 Usage: depcomp [--help] [--version] PROGRAM [ARGS]
37
38 Run PROGRAMS ARGS to compile a file, generating dependencies
39 as side-effects.
40
41 Environment variables:
42 depmode Dependency tracking mode.
43 source Source file read by `PROGRAMS ARGS'.
44 object Object file output by `PROGRAMS ARGS'.
45 DEPDIR directory where to store dependencies.
46 depfile Dependency file to output.
47 tmpdepfile Temporary file to use when outputing dependencies.
48 libtool Whether libtool is used (yes/no).
49
50 Report bugs to <bug-automake@gnu.org>.
51 EOF
52 exit $?
53 ;;
54 -v | --v*)
55 echo "depcomp $scriptversion"
56 exit $?
57 ;;
58 esac
59
60 if test -z "$depmode" || test -z "$source" || test -z "$object"; then
61 echo "depcomp: Variables source, object and depmode must be set" 1>&2
62 exit 1
63 fi
64
65 # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
66 depfile=${depfile-`echo "$object" |
67 sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
68 tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
69
70 rm -f "$tmpdepfile"
71
72 # Some modes work just like other modes, but use different flags. We
73 # parameterize here, but still list the modes in the big case below,
74 # to make depend.m4 easier to write. Note that we *cannot* use a case
75 # here, because this file can only contain one case statement.
76 if test "$depmode" = hp; then
77 # HP compiler uses -M and no extra arg.
78 gccflag=-M
79 depmode=gcc
80 fi
81
82 if test "$depmode" = dashXmstdout; then
83 # This is just like dashmstdout with a different argument.
84 dashmflag=-xM
85 depmode=dashmstdout
86 fi
87
88 case "$depmode" in
89 gcc3)
90 ## gcc 3 implements dependency tracking that does exactly what
91 ## we want. Yay! Note: for some reason libtool 1.4 doesn't like
92 ## it if -MD -MP comes after the -MF stuff. Hmm.
93 "$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
94 stat=$?
95 if test $stat -eq 0; then :
96 else
97 rm -f "$tmpdepfile"
98 exit $stat
99 fi
100 mv "$tmpdepfile" "$depfile"
101 ;;
102
103 gcc)
104 ## There are various ways to get dependency output from gcc. Here's
105 ## why we pick this rather obscure method:
106 ## - Don't want to use -MD because we'd like the dependencies to end
107 ## up in a subdir. Having to rename by hand is ugly.
108 ## (We might end up doing this anyway to support other compilers.)
109 ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
110 ## -MM, not -M (despite what the docs say).
111 ## - Using -M directly means running the compiler twice (even worse
112 ## than renaming).
113 if test -z "$gccflag"; then
114 gccflag=-MD,
115 fi
116 "$@" -Wp,"$gccflag$tmpdepfile"
117 stat=$?
118 if test $stat -eq 0; then :
119 else
120 rm -f "$tmpdepfile"
121 exit $stat
122 fi
123 rm -f "$depfile"
124 echo "$object : \\" > "$depfile"
125 alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
126 ## The second -e expression handles DOS-style file names with drive letters.
127 sed -e 's/^[^:]*: / /' \
128 -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
129 ## This next piece of magic avoids the `deleted header file' problem.
130 ## The problem is that when a header file which appears in a .P file
131 ## is deleted, the dependency causes make to die (because there is
132 ## typically no way to rebuild the header). We avoid this by adding
133 ## dummy dependencies for each header file. Too bad gcc doesn't do
134 ## this for us directly.
135 tr ' ' '
136 ' < "$tmpdepfile" |
137 ## Some versions of gcc put a space before the `:'. On the theory
138 ## that the space means something, we add a space to the output as
139 ## well.
140 ## Some versions of the HPUX 10.20 sed can't process this invocation
141 ## correctly. Breaking it into two sed invocations is a workaround.
142 sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
143 rm -f "$tmpdepfile"
144 ;;
145
146 hp)
147 # This case exists only to let depend.m4 do its work. It works by
148 # looking at the text of this script. This case will never be run,
149 # since it is checked for above.
150 exit 1
151 ;;
152
153 sgi)
154 if test "$libtool" = yes; then
155 "$@" "-Wp,-MDupdate,$tmpdepfile"
156 else
157 "$@" -MDupdate "$tmpdepfile"
158 fi
159 stat=$?
160 if test $stat -eq 0; then :
161 else
162 rm -f "$tmpdepfile"
163 exit $stat
164 fi
165 rm -f "$depfile"
166
167 if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
168 echo "$object : \\" > "$depfile"
169
170 # Clip off the initial element (the dependent). Don't try to be
171 # clever and replace this with sed code, as IRIX sed won't handle
172 # lines with more than a fixed number of characters (4096 in
173 # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
174 # the IRIX cc adds comments like `#:fec' to the end of the
175 # dependency line.
176 tr ' ' '
177 ' < "$tmpdepfile" \
178 | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
179 tr '
180 ' ' ' >> $depfile
181 echo >> $depfile
182
183 # The second pass generates a dummy entry for each header file.
184 tr ' ' '
185 ' < "$tmpdepfile" \
186 | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
187 >> $depfile
188 else
189 # The sourcefile does not contain any dependencies, so just
190 # store a dummy comment line, to avoid errors with the Makefile
191 # "include basename.Plo" scheme.
192 echo "#dummy" > "$depfile"
193 fi
194 rm -f "$tmpdepfile"
195 ;;
196
197 aix)
198 # The C for AIX Compiler uses -M and outputs the dependencies
199 # in a .u file. In older versions, this file always lives in the
200 # current directory. Also, the AIX compiler puts `$object:' at the
201 # start of each line; $object doesn't have directory information.
202 # Version 6 uses the directory in both cases.
203 stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
204 tmpdepfile="$stripped.u"
205 if test "$libtool" = yes; then
206 "$@" -Wc,-M
207 else
208 "$@" -M
209 fi
210 stat=$?
211
212 if test -f "$tmpdepfile"; then :
213 else
214 stripped=`echo "$stripped" | sed 's,^.*/,,'`
215 tmpdepfile="$stripped.u"
216 fi
217
218 if test $stat -eq 0; then :
219 else
220 rm -f "$tmpdepfile"
221 exit $stat
222 fi
223
224 if test -f "$tmpdepfile"; then
225 outname="$stripped.o"
226 # Each line is of the form `foo.o: dependent.h'.
227 # Do two passes, one to just change these to
228 # `$object: dependent.h' and one to simply `dependent.h:'.
229 sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
230 sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
231 else
232 # The sourcefile does not contain any dependencies, so just
233 # store a dummy comment line, to avoid errors with the Makefile
234 # "include basename.Plo" scheme.
235 echo "#dummy" > "$depfile"
236 fi
237 rm -f "$tmpdepfile"
238 ;;
239
240 icc)
241 # Intel's C compiler understands `-MD -MF file'. However on
242 # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
243 # ICC 7.0 will fill foo.d with something like
244 # foo.o: sub/foo.c
245 # foo.o: sub/foo.h
246 # which is wrong. We want:
247 # sub/foo.o: sub/foo.c
248 # sub/foo.o: sub/foo.h
249 # sub/foo.c:
250 # sub/foo.h:
251 # ICC 7.1 will output
252 # foo.o: sub/foo.c sub/foo.h
253 # and will wrap long lines using \ :
254 # foo.o: sub/foo.c ... \
255 # sub/foo.h ... \
256 # ...
257
258 "$@" -MD -MF "$tmpdepfile"
259 stat=$?
260 if test $stat -eq 0; then :
261 else
262 rm -f "$tmpdepfile"
263 exit $stat
264 fi
265 rm -f "$depfile"
266 # Each line is of the form `foo.o: dependent.h',
267 # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
268 # Do two passes, one to just change these to
269 # `$object: dependent.h' and one to simply `dependent.h:'.
270 sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
271 # Some versions of the HPUX 10.20 sed can't process this invocation
272 # correctly. Breaking it into two sed invocations is a workaround.
273 sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
274 sed -e 's/$/ :/' >> "$depfile"
275 rm -f "$tmpdepfile"
276 ;;
277
278 tru64)
279 # The Tru64 compiler uses -MD to generate dependencies as a side
280 # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
281 # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
282 # dependencies in `foo.d' instead, so we check for that too.
283 # Subdirectories are respected.
284 dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
285 test "x$dir" = "x$object" && dir=
286 base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
287
288 if test "$libtool" = yes; then
289 # With Tru64 cc, shared objects can also be used to make a
290 # static library. This mecanism is used in libtool 1.4 series to
291 # handle both shared and static libraries in a single compilation.
292 # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
293 #
294 # With libtool 1.5 this exception was removed, and libtool now
295 # generates 2 separate objects for the 2 libraries. These two
296 # compilations output dependencies in in $dir.libs/$base.o.d and
297 # in $dir$base.o.d. We have to check for both files, because
298 # one of the two compilations can be disabled. We should prefer
299 # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
300 # automatically cleaned when .libs/ is deleted, while ignoring
301 # the former would cause a distcleancheck panic.
302 tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
303 tmpdepfile2=$dir$base.o.d # libtool 1.5
304 tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
305 tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
306 "$@" -Wc,-MD
307 else
308 tmpdepfile1=$dir$base.o.d
309 tmpdepfile2=$dir$base.d
310 tmpdepfile3=$dir$base.d
311 tmpdepfile4=$dir$base.d
312 "$@" -MD
313 fi
314
315 stat=$?
316 if test $stat -eq 0; then :
317 else
318 rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
319 exit $stat
320 fi
321
322 for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
323 do
324 test -f "$tmpdepfile" && break
325 done
326 if test -f "$tmpdepfile"; then
327 sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
328 # That's a tab and a space in the [].
329 sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
330 else
331 echo "#dummy" > "$depfile"
332 fi
333 rm -f "$tmpdepfile"
334 ;;
335
336 #nosideeffect)
337 # This comment above is used by automake to tell side-effect
338 # dependency tracking mechanisms from slower ones.
339
340 dashmstdout)
341 # Important note: in order to support this mode, a compiler *must*
342 # always write the preprocessed file to stdout, regardless of -o.
343 "$@" || exit $?
344
345 # Remove the call to Libtool.
346 if test "$libtool" = yes; then
347 while test $1 != '--mode=compile'; do
348 shift
349 done
350 shift
351 fi
352
353 # Remove `-o $object'.
354 IFS=" "
355 for arg
356 do
357 case $arg in
358 -o)
359 shift
360 ;;
361 $object)
362 shift
363 ;;
364 *)
365 set fnord "$@" "$arg"
366 shift # fnord
367 shift # $arg
368 ;;
369 esac
370 done
371
372 test -z "$dashmflag" && dashmflag=-M
373 # Require at least two characters before searching for `:'
374 # in the target name. This is to cope with DOS-style filenames:
375 # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
376 "$@" $dashmflag |
377 sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
378 rm -f "$depfile"
379 cat < "$tmpdepfile" > "$depfile"
380 tr ' ' '
381 ' < "$tmpdepfile" | \
382 ## Some versions of the HPUX 10.20 sed can't process this invocation
383 ## correctly. Breaking it into two sed invocations is a workaround.
384 sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
385 rm -f "$tmpdepfile"
386 ;;
387
388 dashXmstdout)
389 # This case only exists to satisfy depend.m4. It is never actually
390 # run, as this mode is specially recognized in the preamble.
391 exit 1
392 ;;
393
394 makedepend)
395 "$@" || exit $?
396 # Remove any Libtool call
397 if test "$libtool" = yes; then
398 while test $1 != '--mode=compile'; do
399 shift
400 done
401 shift
402 fi
403 # X makedepend
404 shift
405 cleared=no
406 for arg in "$@"; do
407 case $cleared in
408 no)
409 set ""; shift
410 cleared=yes ;;
411 esac
412 case "$arg" in
413 -D*|-I*)
414 set fnord "$@" "$arg"; shift ;;
415 # Strip any option that makedepend may not understand. Remove
416 # the object too, otherwise makedepend will parse it as a source file.
417 -*|$object)
418 ;;
419 *)
420 set fnord "$@" "$arg"; shift ;;
421 esac
422 done
423 obj_suffix="`echo $object | sed 's/^.*\././'`"
424 touch "$tmpdepfile"
425 ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
426 rm -f "$depfile"
427 cat < "$tmpdepfile" > "$depfile"
428 sed '1,2d' "$tmpdepfile" | tr ' ' '
429 ' | \
430 ## Some versions of the HPUX 10.20 sed can't process this invocation
431 ## correctly. Breaking it into two sed invocations is a workaround.
432 sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
433 rm -f "$tmpdepfile" "$tmpdepfile".bak
434 ;;
435
436 cpp)
437 # Important note: in order to support this mode, a compiler *must*
438 # always write the preprocessed file to stdout.
439 "$@" || exit $?
440
441 # Remove the call to Libtool.
442 if test "$libtool" = yes; then
443 while test $1 != '--mode=compile'; do
444 shift
445 done
446 shift
447 fi
448
449 # Remove `-o $object'.
450 IFS=" "
451 for arg
452 do
453 case $arg in
454 -o)
455 shift
456 ;;
457 $object)
458 shift
459 ;;
460 *)
461 set fnord "$@" "$arg"
462 shift # fnord
463 shift # $arg
464 ;;
465 esac
466 done
467
468 "$@" -E |
469 sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
470 sed '$ s: \\$::' > "$tmpdepfile"
471 rm -f "$depfile"
472 echo "$object : \\" > "$depfile"
473 cat < "$tmpdepfile" >> "$depfile"
474 sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
475 rm -f "$tmpdepfile"
476 ;;
477
478 msvisualcpp)
479 # Important note: in order to support this mode, a compiler *must*
480 # always write the preprocessed file to stdout, regardless of -o,
481 # because we must use -o when running libtool.
482 "$@" || exit $?
483 IFS=" "
484 for arg
485 do
486 case "$arg" in
487 "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
488 set fnord "$@"
489 shift
490 shift
491 ;;
492 *)
493 set fnord "$@" "$arg"
494 shift
495 shift
496 ;;
497 esac
498 done
499 "$@" -E |
500 sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
501 rm -f "$depfile"
502 echo "$object : \\" > "$depfile"
503 . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
504 echo " " >> "$depfile"
505 . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
506 rm -f "$tmpdepfile"
507 ;;
508
509 none)
510 exec "$@"
511 ;;
512
513 *)
514 echo "Unknown depmode $depmode" 1>&2
515 exit 1
516 ;;
517 esac
518
519 exit 0
520
521 # Local Variables:
522 # mode: shell-script
523 # sh-indentation: 2
524 # eval: (add-hook 'write-file-hooks 'time-stamp)
525 # time-stamp-start: "scriptversion="
526 # time-stamp-format: "%:y-%02m-%02d.%02H"
527 # time-stamp-end: "$"
528 # End:
+197
-111
dillorc less more
00 # dillorc
11 # Sample dillo initialization file.
2 # Copy this file to ~/.dillo/dillorc and edit to your taste.
2 #
33 # Lines that start with a '#' are comments.
4
4 # "#option=..." shows the built-in default.
5 # "# option=..." is an additional example.
6 # "option=..." overrides the built-in value.
57
68 #-------------------------------------------------------------------------
79 # FIRST SECTION :)
810 #-------------------------------------------------------------------------
911
1012 # Set the desired initial browser size
11 geometry=640x550
12
13 # Dicache is where the Decompressed Images are cached (not the original ones).
14 # If you have a lot of memory and a slow CPU, use YES, otherwise use NO
15 use_dicache=NO
16
13 # geometry=650x545+0+20
14 #geometry=780x580
15
16 # Change this if you want to have text-only browsing from the start.
17 # (While browsing, this can be changed from the tools/settings menu.)
18 #load_images=YES
19
20 # Change this if you want to disable loading of CSS stylesheets initially.
21 # (While browsing, this can be changed from the tools/settings menu.)
22 #load_stylesheets=YES
23
24 # Change this if you want to disable parsing of embedded CSS initially.
25 # (While browsing, this can be changed from the tools/settings menu.)
26 #parse_embedded_css=YES
27
28 # How should Dillo restrict automatic requests (e.g., redirections,
29 # pages containing images or stylesheets)?
30 # allow_all
31 # same_domain : Permit www.example.org to load an image from img.example.org,
32 # but not from the unrelated ad.doubleclick.net.
33 #filter_auto_requests=same_domain
34
35 # Change the buffering scheme for drawing
36 # 0 no double buffering - useful for debugging
37 # 1 light buffering using a single back buffer for all windows
38 # 2 full fltk-based double buffering for all windows
39 #buffered_drawing=1
40
41 # Set your default directory for download/save operations
42 #save_dir=/tmp
1743
1844 #-------------------------------------------------------------------------
1945 # RENDERING SECTION
2046 #-------------------------------------------------------------------------
2147
22 # Fontname for variable width rendering (most of the text).
23 # - some fonts may slow down rendering, some others not!
24 # - try to tune a fontname/font_factor combination.
25 # Ex. {helvetica, lucida, times, "new century schoolbook", utopia, ...}
26 vw_fontname=helvetica
27
28 # Fontname for fixed width rendering (mainly <pre> quoted text)
29 fw_fontname=courier
30
31 # All fontsizes are scaled by this value (default is 1.0)
32 #font_factor=1.2
33
34 # If you prefer oblique over italic fonts, uncoment next line
35 #use_oblique=YES
36
37 # Show tooltip popup for images?
38 # Note: We use the "title" attribute and not "alt".
39 # More info at: http://bugzilla.mozilla.org/show_bug.cgi?id=25537
40 show_tooltip=YES
41
42 # Set this to YES, if you want to limit the word wrap width to the vieport
48 # Default fonts:
49 #
50 # If FLTK has been configured with Xft enabled (the default), you can use
51 # scalable fonts such as DejaVu or Liberation (try running
52 # "fc-list : family | cut -d ',' -f 2 | sort").
53 #font_serif="DejaVu Serif"
54 #font_sans_serif="DejaVu Sans"
55 #font_cursive="URW Chancery L"
56 #font_fantasy="DejaVu Sans"
57 #font_monospace="DejaVu Sans Mono"
58 #
59 # Otherwise, use bitmapped fonts like the following (for a list, try running
60 # "xlsfonts -fn *-iso10646-1 | grep -v -e -0-0 | cut -d - -f 3 | sort | uniq").
61 # font_serif="times"
62 # font_sans_serif="helvetica"
63 # font_cursive="helvetica"
64 # font_fantasy="helvetica"
65 # font_monospace="courier"
66
67 # All font sizes are scaled by this value
68 # font_factor=1.5
69 #font_factor=1.0
70
71 # Maximum font size in pixels
72 #font_max_size=100
73
74 # Minimum font size in pixels
75 #font_min_size=6
76
77 # Show tooltip popups for UI and for HTML title attributes
78 #show_tooltip=YES
79
80 # Set this to YES if you want to limit the word wrap width to the viewport
4381 # width (may be useful for iPAQ)
44 limit_text_width=NO
82 # *** NOT HOOKED UP YET ***
83 #
84 #limit_text_width=NO
4585
4686
4787 #-------------------------------------------------------------------------
4888 # PARSING SECTION
4989 #-------------------------------------------------------------------------
5090
51 # If you prefer more accurate HTML bug diagnose, over better rendering
91 # If you prefer more accurate HTML bug diagnosis over better rendering
5292 # (page authors and webmasters) set the following to "NO".
5393 #
54 w3c_plus_heuristics=YES
94 #w3c_plus_heuristics=YES
5595
5696
5797 #-------------------------------------------------------------------------
5999 #-------------------------------------------------------------------------
60100
61101 # Set the start page.
62 # Uncomment if you want to override the default start page.
63 #start_page="file:/home/user/custom.html"
102 # start_page="about:blank"
103 # start_page="http://www.dillo.org"
104 # start_page="file:/home/jcid/custom_page.html"
105 #start_page="about:splash"
64106
65107 # Set the home location
66 home="http://www.dillo.org/"
67
68 # Set search url to use with the search dialog.
69 # %s is replaced with urlencoded keywords, and %% by '%'.
70 search_url="http://www.google.com/search?q=%s"
71 #search_url="http://search.lycos.com/default.asp?query=%s"
72 #search_url="http://www.alltheweb.com/search?cat=web&query=%s"
73
74 # Set the proxy information for http
75 #http_proxy=http://localhost:8080/
76
77 # if you need to provide a user/password pair for the proxy,
108 # home="file:/home/jcid/HomePage/Home.html"
109 #home="http://www.dillo.org/"
110
111 # Set the URL used by the web search dialog.
112 # "%s" is replaced with the search keywords separated by '+'.
113 # search_url="http://www.wikipedia.org/wiki/Special:Search?search=%s"
114 # search_url="http://search.lycos.com/?query=%s"
115 # search_url="http://duckduckgo.com/html?q=%s"
116 #search_url="http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s"
117
118 # If set, dillo will ask web servers to send pages in this language.
119 # This setting does NOT change dillo's user interface.
120 # Format explained: www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
121 # Language-REGION values: www.iana.org/assignments/language-subtag-registry
122 # (by default, no Accept-Language header is sent)
123 # http_language="de"
124 # http_language="pt-BR"
125 # http_language="vi,de-CH,de;q=0.5,th;q=0.3"
126
127 # Maximum number of simultaneous TCP connections to a single server or proxy.
128 # http_max_conns=6
129
130 # Set the proxy information for http.
131 # Note that the http_proxy environment variable overrides this setting.
132 # WARNING: FTP and downloads plugins use wget. To use a proxy with them,
133 # you will need to configure wget accordingly. See
134 # http://www.gnu.org/software/wget/manual/html_node/Proxies.html
135 # http_proxy="http://localhost:8080/"
136 #(by default, no proxy is used)
137
138 # If you need to provide a user/password pair for the proxy,
78139 # set the proxy user name here and Dillo will ask for the password later.
79 #http_proxyuser="joe"
80
81 # When using a proxy, this sets the domains to access without proxy.
82 # (separated with a single space -- see examples below)
83 #no_proxy = ".mynet.com"
84 #no_proxy = ".mynet.com .other.net .foo.bar.org"
85
140 # http_proxyuser="joe"
141 #(by default, no proxy is used)
142
143 # Set the domains to access without proxy
144 # no_proxy = ".hola.com .mynet.cl .hi.de"
145 #no_proxy="localhost 127.0.0.1"
146
147 # Set the HTTP Referer (sic) header.
148 # Note that there is no option to reveal the page that you came from because it
149 # would endanger your privacy. 'host' and 'path' allow you to pretend that the
150 # link you followed was on the same site that you're going to.
151 # none : Don't send any Referer header at all.
152 # host : Send the requested URI's hostname.
153 # path : Send the requested URI's host and path.
154 #http_referer=host
155
156 # Set the HTTP User-Agent header.
157 # This can be useful for privacy and for working around servers who think
158 # Dillo is less capable than it really is. However, if you pretend to use a
159 # different browser, servers may send you pages that work with the features
160 # and bugs of that other browser -- or even disallow access in cases like
161 # wget or googlebot. Remember this before submitting bug reports.
162 #
163 # See http://zytrax.com/tech/web/browser_ids.htm for a compilation of strings.
164 #
165 # http_user_agent="Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6"
166 # http_user_agent="Wget/1.11.4"
167 #The default is Dillo/(current version number)
86168
87169 #-------------------------------------------------------------------------
88170 # COLORS SECTION
89171 #-------------------------------------------------------------------------
90
91 # Here we can use the HTML (standard and extended) or C syntax.
92172
93173 # Set the background color
94174 # bg_color=gray
95175 # bg_color=0xd6d6c0
96 bg_color=0xdcd1ba
97
98 # Set the text color
99 text_color=black
100
101 # Set the link color
102 link_color=blue
103
104 # If your eyes suffer with white backgrounds, or you have headaches after
105 # lengthy computer sessions, and you don't need high contrast to see sharply,
106 # uncomment next line (it'll use 'bg_color' instead). -- It works!
107 #allow_white_bg=NO
108
109 # Use the same colors with all documents?
110 force_my_colors=NO
111
112 # When set to YES, visited links will always have a contrasting color,
113 # independent of the page author's setting.
114 contrast_visited_color=YES
176 #bg_color=0xdcd1ba
177
178 # If your eyes suffer with white backgrounds, change this.
179 #allow_white_bg=YES
180
181 # When set to YES, the page author's visited link color may be overridden
182 # to allow better contrast with text/links/background
183 #contrast_visited_color=YES
184
115185
116186 #-------------------------------------------------------------------------
117187 # USER INTERFACE SECTION
122192 # small : very nice! (it's "medium" without icon titles)
123193 # medium : nice!
124194 # large : Traditional
125 panel_size=medium
126 small_icons=NO
195 # panel_size=tiny
196 # panel_size=small
197 #panel_size=medium
198 # panel_size=large
199
200 #small_icons=NO
127201
128202 # Here you can choose to hide some widgets of the dillo panel...
129 #show_back=NO
130 #show_forw=NO
131 #show_home=NO
132 #show_reload=NO
133 #show_save=NO
134 #show_stop=NO
135 #show_bookmarks=NO
136 #show_menubar=NO
137 #show_clear_url=NO
138 #show_url=NO
139 #show_search=NO
140 #show_progress_box=NO
141
142 # Start dillo windows with a hidden panel?
143 fullwindow_start=NO
144
145 # Enabling this will restrain OpenUrl and FindText, but may be required
146 # for the ION window manager.
147 transient_dialogs=NO
148
149 # When filling forms, our default behaviour is to submit on enterpress,
203 #show_back=YES
204 #show_forw=YES
205 #show_home=YES
206 #show_reload=YES
207 #show_save=YES
208 #show_stop=YES
209 #show_bookmarks=YES
210 #show_tools=YES
211 #show_filemenu=YES
212 #show_clear_url=YES
213 #show_url=YES
214 #show_search=YES
215 #show_help=YES
216 #show_progress_box=YES
217
218 # Start dillo with the panels hidden?
219 #fullwindow_start=NO
220
221 # When filling out forms, our default behaviour is to submit on enterpress,
150222 # but only when there's a single text entry (to avoid incomplete submits).
151 # OTOH, if you have to fill the same form lots of times, you may find
223 # OTOH, if you have to fill out the same form repeatedly, you may find it
152224 # useful to keep away from the mouse by forcing enter to submit.
153 enterpress_forces_submit=NO
154
155 # Some forms lack a submit button, and dillo can generate a custom one
156 # internally. Unfortunately there's no guarantee for it to work. :(
157 # (my experience is that forms that lack a submit rely on Javascript)
158 generate_submit=NO
225 #enterpress_forces_submit=NO
226
227 # A mouse's middle click over a link opens a new Tab.
228 # If you prefer to open a new Window instead, set it to NO.
229 #middle_click_opens_new_tab=YES
230
231 # A mouse's middle click over a tab closes the Tab.
232 # With mousewheel mouses, right click feels way better (set to YES).
233 #right_click_closes_tab=NO
234
235 # Mouse middle click by default drives drag-scrolling.
236 # To paste an URL into the window instead of scrolling, set it to NO.
237 # Note: You could always paste the URL onto the URL box clear button.
238 #middle_click_drags_page=YES
239
240 # Focus follows new Tabs.
241 # You can hold SHIFT to temporarily revert this behaviour.
242 #focus_new_tab=YES
243
159244
160245 #-------------------------------------------------------------------------
161246 # DEBUG MESSAGES SECTION
162247 #-------------------------------------------------------------------------
163248
164 # Generic messsages (mainly for debugging specific parts)
165 # Uncomment the following line to disable them.
166 #show_msg=NO
167
168 # Soon we'll add the "show_debug_messages=NO" option...
249 # Soon we should add the "show_debug_messages=NO" option...
250
251 # Generic messages (mainly for debugging specific parts)
252 # Change this to disable them.
253 #show_msg=YES
254
169255
170256 #-------------------------------------------------------------------------
171257 # HTML BUG MESSAGES SECTION
172258 #-------------------------------------------------------------------------
173259
174260 # Accepted by the W3C validator but "strongly discouraged" by the SPEC.
175 # (As "TAB character inside <PRE>").
176 #show_extra_warnings=YES
261 # (Such as "TAB character inside <PRE>").
262 #show_extra_warnings=NO
177263
178264
179265 # -----------------------------------------------------------------------
0 AM_CPPFLAGS = \
1 -I$(top_srcdir)
2
3 noinst_LIBRARIES = libDlib.a
4
5 libDlib_a_SOURCES = \
6 dlib.h \
7 dlib.c
0 /*
1 * File: dlib.c
2 *
3 * Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 /* Memory allocation, Simple dynamic strings, Lists (simple and sorted),
12 * and a few utility functions
13 */
14
15 /*
16 * TODO: vsnprintf() is in C99, maybe a simple replacement if necessary.
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <ctype.h>
26
27 #include "dlib.h"
28
29 static bool_t dLib_show_msg = TRUE;
30
31 /* dlib msgs go to stderr to avoid problems with filter dpis */
32 #define DLIB_MSG(...) \
33 D_STMT_START { \
34 if (dLib_show_msg) \
35 fprintf(stderr, __VA_ARGS__); \
36 } D_STMT_END
37
38 /*
39 *- Memory --------------------------------------------------------------------
40 */
41
42 void *dMalloc (size_t size)
43 {
44 void *value = malloc (size);
45 if (value == 0)
46 exit(1);
47 return value;
48 }
49
50 void *dRealloc (void *mem, size_t size)
51 {
52 void *value = realloc (mem, size);
53 if (value == 0)
54 exit(1);
55 return value;
56 }
57
58 void *dMalloc0 (size_t size)
59 {
60 void *value = dMalloc (size);
61 memset (value, 0, size);
62 return value;
63 }
64
65 void dFree (void *mem)
66 {
67 if (mem)
68 free(mem);
69 }
70
71 /*
72 *- strings (char *) ----------------------------------------------------------
73 */
74
75 char *dStrdup(const char *s)
76 {
77 if (s) {
78 int len = strlen(s)+1;
79 char *ns = dNew(char, len);
80 memcpy(ns, s, len);
81 return ns;
82 }
83 return NULL;
84 }
85
86 char *dStrndup(const char *s, size_t sz)
87 {
88 if (s) {
89 char *ns = dNew(char, sz+1);
90 memcpy(ns, s, sz);
91 ns[sz] = 0;
92 return ns;
93 }
94 return NULL;
95 }
96
97 /*
98 * Concatenate a NULL-terminated list of strings
99 */
100 char *dStrconcat(const char *s1, ...)
101 {
102 va_list args;
103 char *s, *ns = NULL;
104
105 if (s1) {
106 Dstr *dstr = dStr_sized_new(64);
107 va_start(args, s1);
108 for (s = (char*)s1; s; s = va_arg(args, char*))
109 dStr_append(dstr, s);
110 va_end(args);
111 ns = dstr->str;
112 dStr_free(dstr, 0);
113 }
114 return ns;
115 }
116
117 /*
118 * Remove leading and trailing whitespace
119 */
120 char *dStrstrip(char *s)
121 {
122 char *p;
123 int len;
124
125 if (s && *s) {
126 for (p = s; dIsspace(*p); ++p);
127 for (len = strlen(p); len && dIsspace(p[len-1]); --len);
128 if (p > s)
129 memmove(s, p, len);
130 s[len] = 0;
131 }
132 return s;
133 }
134
135 /*
136 * Return a new string of length 'len' filled with 'c' characters
137 */
138 char *dStrnfill(size_t len, char c)
139 {
140 char *ret = dNew(char, len+1);
141 for (ret[len] = 0; len > 0; ret[--len] = c);
142 return ret;
143 }
144
145 /*
146 * strsep() implementation
147 */
148 char *dStrsep(char **orig, const char *delim)
149 {
150 char *str, *p;
151
152 if (!(str = *orig))
153 return NULL;
154
155 p = strpbrk(str, delim);
156 if (p) {
157 *p++ = 0;
158 *orig = p;
159 } else {
160 *orig = NULL;
161 }
162 return str;
163 }
164
165 /*
166 * Case insensitive strstr
167 */
168 char *dStristr(const char *haystack, const char *needle)
169 {
170 int i, j;
171 char *ret = NULL;
172
173 if (haystack && needle) {
174 for (i = 0, j = 0; haystack[i] && needle[j]; ++i)
175 if (tolower(haystack[i]) == tolower(needle[j])) {
176 ++j;
177 } else if (j) {
178 i -= j;
179 j = 0;
180 }
181 if (!needle[j]) /* Got all */
182 ret = (char *)(haystack + i - j);
183 }
184 return ret;
185 }
186
187
188 /*
189 *- dStr ----------------------------------------------------------------------
190 */
191
192 /*
193 * Private allocator
194 */
195 static void dStr_resize(Dstr *ds, int n_sz, int keep)
196 {
197 if (n_sz >= 0) {
198 if (keep && n_sz > ds->len) {
199 ds->str = (Dstr_char_t*) dRealloc (ds->str, n_sz*sizeof(Dstr_char_t));
200 ds->sz = n_sz;
201 } else {
202 dFree(ds->str);
203 ds->str = dNew(Dstr_char_t, n_sz);
204 ds->sz = n_sz;
205 ds->len = 0;
206 ds->str[0] = 0;
207 }
208 }
209 }
210
211 /*
212 * Create a new string with a given size.
213 * Initialized to ""
214 */
215 Dstr *dStr_sized_new (int sz)
216 {
217 Dstr *ds;
218 if (sz < 2)
219 sz = 2;
220
221 ds = dNew(Dstr, 1);
222 ds->str = NULL;
223 dStr_resize(ds, sz + 1, 0); /* (sz + 1) for the extra '\0' */
224 return ds;
225 }
226
227 /*
228 * Return memory if there's too much allocated
229 */
230 void dStr_fit (Dstr *ds)
231 {
232 dStr_resize(ds, ds->len + 1, 1);
233 }
234
235 /*
236 * Insert a C string, at a given position, into a Dstr (providing length).
237 * Note: It also works with embedded nil characters.
238 */
239 void dStr_insert_l (Dstr *ds, int pos_0, const char *s, int l)
240 {
241 int n_sz;
242
243 if (ds && s && l && pos_0 >= 0 && pos_0 <= ds->len) {
244 for (n_sz = ds->sz; ds->len + l >= n_sz; n_sz *= 2);
245 if (n_sz > ds->sz) {
246 dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
247 }
248 if (pos_0 < ds->len)
249 memmove(ds->str+pos_0+l, ds->str+pos_0, ds->len-pos_0);
250 memcpy(ds->str+pos_0, s, l);
251 ds->len += l;
252 ds->str[ds->len] = 0;
253 }
254 }
255
256 /*
257 * Insert a C string, at a given position, into a Dstr
258 */
259 void dStr_insert (Dstr *ds, int pos_0, const char *s)
260 {
261 if (s)
262 dStr_insert_l(ds, pos_0, s, strlen(s));
263 }
264
265 /*
266 * Append a C string to a Dstr (providing length).
267 * Note: It also works with embedded nil characters.
268 */
269 void dStr_append_l (Dstr *ds, const char *s, int l)
270 {
271 dStr_insert_l(ds, ds->len, s, l);
272 }
273
274 /*
275 * Append a C string to a Dstr.
276 */
277 void dStr_append (Dstr *ds, const char *s)
278 {
279 dStr_append_l(ds, s, strlen(s));
280 }
281
282 /*
283 * Create a new string.
284 * Initialized to 's' or empty if 's == NULL'
285 */
286 Dstr *dStr_new (const char *s)
287 {
288 Dstr *ds = dStr_sized_new(0);
289 if (s && *s)
290 dStr_append(ds, s);
291 return ds;
292 }
293
294 /*
295 * Free a dillo string.
296 * if 'all' free everything, else free the structure only.
297 */
298 void dStr_free (Dstr *ds, int all)
299 {
300 if (ds) {
301 if (all)
302 dFree(ds->str);
303 dFree(ds);
304 }
305 }
306
307 /*
308 * Append one character.
309 */
310 void dStr_append_c (Dstr *ds, int c)
311 {
312 char cs[2];
313
314 if (ds) {
315 if (ds->sz > ds->len + 1) {
316 ds->str[ds->len++] = (Dstr_char_t)c;
317 ds->str[ds->len] = 0;
318 } else {
319 cs[0] = (Dstr_char_t)c;
320 cs[1] = 0;
321 dStr_append_l (ds, cs, 1);
322 }
323 }
324 }
325
326 /*
327 * Truncate a Dstr to be 'len' bytes long.
328 */
329 void dStr_truncate (Dstr *ds, int len)
330 {
331 if (ds && len < ds->len) {
332 ds->str[len] = 0;
333 ds->len = len;
334 }
335 }
336
337 /*
338 * Erase a substring.
339 */
340 void dStr_erase (Dstr *ds, int pos_0, int len)
341 {
342 if (ds && pos_0 >= 0 && len > 0 && pos_0 + len <= ds->len) {
343 memmove(ds->str + pos_0, ds->str + pos_0 + len, ds->len - pos_0 - len);
344 ds->len -= len;
345 ds->str[ds->len] = 0;
346 }
347 }
348
349 /*
350 * vsprintf-like function that appends.
351 * Used by: dStr_vsprintf(), dStr_sprintf() and dStr_sprintfa().
352 */
353 void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp)
354 {
355 int n, n_sz;
356
357 if (ds && format) {
358 va_list argp2; /* Needed in case of looping on non-32bit arch */
359 while (1) {
360 va_copy(argp2, argp);
361 n = vsnprintf(ds->str + ds->len, ds->sz - ds->len, format, argp2);
362 va_end(argp2);
363 if (n > -1 && n < ds->sz - ds->len) {
364 ds->len += n; /* Success! */
365 break;
366 } else if (n > -1) { /* glibc >= 2.1 */
367 n_sz = ds->len + n + 1;
368 } else { /* old glibc */
369 n_sz = ds->sz * 2;
370 }
371 dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
372 }
373 }
374 }
375
376 /*
377 * vsprintf-like function.
378 */
379 void dStr_vsprintf (Dstr *ds, const char *format, va_list argp)
380 {
381 if (ds) {
382 dStr_truncate(ds, 0);
383 dStr_vsprintfa(ds, format, argp);
384 }
385 }
386
387 /*
388 * Printf-like function
389 */
390 void dStr_sprintf (Dstr *ds, const char *format, ...)
391 {
392 va_list argp;
393
394 if (ds && format) {
395 va_start(argp, format);
396 dStr_vsprintf(ds, format, argp);
397 va_end(argp);
398 }
399 }
400
401 /*
402 * Printf-like function that appends.
403 */
404 void dStr_sprintfa (Dstr *ds, const char *format, ...)
405 {
406 va_list argp;
407
408 if (ds && format) {
409 va_start(argp, format);
410 dStr_vsprintfa(ds, format, argp);
411 va_end(argp);
412 }
413 }
414
415 /*
416 * Compare two dStrs.
417 */
418 int dStr_cmp(Dstr *ds1, Dstr *ds2)
419 {
420 int ret = 0;
421
422 if (ds1 && ds2)
423 ret = memcmp(ds1->str, ds2->str, MIN(ds1->len+1, ds2->len+1));
424 return ret;
425 }
426
427 /*
428 * Return a pointer to the first occurrence of needle in haystack.
429 */
430 char *dStr_memmem(Dstr *haystack, Dstr *needle)
431 {
432 int i;
433
434 if (needle && haystack) {
435 if (needle->len == 0)
436 return haystack->str;
437
438 for (i = 0; i <= (haystack->len - needle->len); i++) {
439 if (haystack->str[i] == needle->str[0] &&
440 !memcmp(haystack->str + i, needle->str, needle->len))
441 return haystack->str + i;
442 }
443 }
444 return NULL;
445 }
446
447 /*
448 * Return a printable representation of the provided Dstr, limited to a length
449 * of roughly maxlen.
450 *
451 * This is NOT threadsafe.
452 */
453 const char *dStr_printable(Dstr *in, int maxlen)
454 {
455 int i;
456 static const char *const HEX = "0123456789ABCDEF";
457 static Dstr *out = NULL;
458
459 if (in == NULL)
460 return NULL;
461
462 if (out)
463 dStr_truncate(out, 0);
464 else
465 out = dStr_sized_new(in->len);
466
467 for (i = 0; (i < in->len) && (out->len < maxlen); ++i) {
468 if (isprint(in->str[i]) || (in->str[i] == '\n')) {
469 dStr_append_c(out, in->str[i]);
470 } else {
471 dStr_append_l(out, "\\x", 2);
472 dStr_append_c(out, HEX[(in->str[i] >> 4) & 15]);
473 dStr_append_c(out, HEX[in->str[i] & 15]);
474 }
475 }
476 if (out->len >= maxlen)
477 dStr_append(out, "...");
478 return out->str;
479 }
480
481 /*
482 *- dList ---------------------------------------------------------------------
483 */
484
485 /*
486 * Create a new empty list
487 */
488 Dlist *dList_new(int size)
489 {
490 Dlist *l;
491 if (size <= 0)
492 return NULL;
493
494 l = dNew(Dlist, 1);
495 l->len = 0;
496 l->sz = size;
497 l->list = dNew(void*, l->sz);
498 return l;
499 }
500
501 /*
502 * Free a list (not its elements)
503 */
504 void dList_free (Dlist *lp)
505 {
506 if (!lp)
507 return;
508
509 dFree(lp->list);
510 dFree(lp);
511 }
512
513 /*
514 * Insert an element at a given position [0 based]
515 */
516 void dList_insert_pos (Dlist *lp, void *data, int pos0)
517 {
518 int i;
519
520 if (!lp || pos0 < 0 || pos0 > lp->len)
521 return;
522
523 if (lp->sz == lp->len) {
524 lp->sz *= 2;
525 lp->list = (void**) dRealloc (lp->list, lp->sz*sizeof(void*));
526 }
527 ++lp->len;
528
529 for (i = lp->len - 1; i > pos0; --i)
530 lp->list[i] = lp->list[i - 1];
531 lp->list[pos0] = data;
532 }
533
534 /*
535 * Append a data item to the list
536 */
537 void dList_append (Dlist *lp, void *data)
538 {
539 dList_insert_pos(lp, data, lp->len);
540 }
541
542 /*
543 * Prepend a data item to the list
544 */
545 void dList_prepend (Dlist *lp, void *data)
546 {
547 dList_insert_pos(lp, data, 0);
548 }
549
550 /*
551 * For completing the ADT.
552 */
553 int dList_length (Dlist *lp)
554 {
555 if (!lp)
556 return 0;
557 return lp->len;
558 }
559
560 /*
561 * Remove a data item without preserving order.
562 */
563 void dList_remove_fast (Dlist *lp, const void *data)
564 {
565 int i;
566
567 if (!lp)
568 return;
569
570 for (i = 0; i < lp->len; ++i) {
571 if (lp->list[i] == data) {
572 lp->list[i] = lp->list[--lp->len];
573 break;
574 }
575 }
576 }
577
578 /*
579 * Remove a data item preserving order.
580 */
581 void dList_remove (Dlist *lp, const void *data)
582 {
583 int i, j;
584
585 if (!lp)
586 return;
587
588 for (i = 0; i < lp->len; ++i) {
589 if (lp->list[i] == data) {
590 --lp->len;
591 for (j = i; j < lp->len; ++j)
592 lp->list[j] = lp->list[j + 1];
593 break;
594 }
595 }
596 }
597
598 /*
599 * Return the nth data item,
600 * NULL when not found or 'n0' is out of range
601 */
602 void *dList_nth_data (Dlist *lp, int n0)
603 {
604 if (!lp || n0 < 0 || n0 >= lp->len)
605 return NULL;
606 return lp->list[n0];
607 }
608
609 /*
610 * Return the found data item, or NULL if not present.
611 */
612 void *dList_find (Dlist *lp, const void *data)
613 {
614 int i = dList_find_idx(lp, data);
615 return (i >= 0) ? lp->list[i] : NULL;
616 }
617
618 /*
619 * Search a data item.
620 * Return value: its index if found, -1 if not present.
621 * (this is useful for a list of integers, for finding number zero).
622 */
623 int dList_find_idx (Dlist *lp, const void *data)
624 {
625 int i, ret = -1;
626
627 if (!lp)
628 return ret;
629
630 for (i = 0; i < lp->len; ++i) {
631 if (lp->list[i] == data) {
632 ret = i;
633 break;
634 }
635 }
636 return ret;
637 }
638
639 /*
640 * Search a data item using a custom function.
641 * func() is given the list item and the user data as parameters.
642 * Return: data item when found, NULL otherwise.
643 */
644 void *dList_find_custom (Dlist *lp, const void *data, dCompareFunc func)
645 {
646 int i;
647 void *ret = NULL;
648
649 if (!lp)
650 return ret;
651
652 for (i = 0; i < lp->len; ++i) {
653 if (func(lp->list[i], data) == 0) {
654 ret = lp->list[i];
655 break;
656 }
657 }
658 return ret;
659 }
660
661 /*
662 * QuickSort implementation.
663 * This allows for a simple compare function for all the ADT.
664 */
665 static void QuickSort(void **left, void **right, dCompareFunc compare)
666 {
667 void **p = left, **q = right, **t = left;
668
669 while (1) {
670 while (p != t && compare(*p, *t) < 0)
671 ++p;
672 while (q != t && compare(*q, *t) > 0)
673 --q;
674 if (p > q)
675 break;
676 if (p < q) {
677 void *tmp = *p;
678 *p = *q;
679 *q = tmp;
680 if (t == p)
681 t = q;
682 else if (t == q)
683 t = p;
684 }
685 if (++p > --q)
686 break;
687 }
688
689 if (left < q)
690 QuickSort(left, q, compare);
691 if (p < right)
692 QuickSort(p, right, compare);
693 }
694
695 /*
696 * Sort the list using a custom function
697 */
698 void dList_sort (Dlist *lp, dCompareFunc func)
699 {
700 if (lp && lp->len > 1) {
701 QuickSort(lp->list, lp->list + lp->len - 1, func);
702 }
703 }
704
705 /*
706 * Insert an element into a sorted list.
707 * The comparison function receives two list elements.
708 */
709 void dList_insert_sorted (Dlist *lp, void *data, dCompareFunc func)
710 {
711 int i, st, min, max;
712
713 if (lp) {
714 min = st = i = 0;
715 max = lp->len - 1;
716 while (min <= max) {
717 i = (min + max) / 2;
718 st = func(lp->list[i], data);
719 if (st < 0) {
720 min = i + 1;
721 } else if (st > 0) {
722 max = i - 1;
723 } else {
724 break;
725 }
726 }
727 dList_insert_pos(lp, data, (st >= 0) ? i : i+1);
728 }
729 }
730
731 /*
732 * Search a sorted list.
733 * Return the found data item, or NULL if not present.
734 * func() is given the list item and the user data as parameters.
735 */
736 void *dList_find_sorted (Dlist *lp, const void *data, dCompareFunc func)
737 {
738 int i, st, min, max;
739 void *ret = NULL;
740
741 if (lp && lp->len) {
742 min = 0;
743 max = lp->len - 1;
744 while (min <= max) {
745 i = (min + max) / 2;
746 st = func(lp->list[i], data);
747 if (st < 0) {
748 min = i + 1;
749 } else if (st > 0) {
750 max = i - 1;
751 } else {
752 ret = lp->list[i];
753 break;
754 }
755 }
756 }
757
758 return ret;
759 }
760
761 /*
762 *- Parse function ------------------------------------------------------------
763 */
764
765 /*
766 * Take a dillo rc line and return 'name' and 'value' pointers to it.
767 * Notes:
768 * - line is modified!
769 * - it skips blank lines and lines starting with '#'
770 *
771 * Return value: 1 on blank line or comment, 0 on successful value/pair,
772 * -1 otherwise.
773 */
774 int dParser_parse_rc_line(char **line, char **name, char **value)
775 {
776 char *eq, *p;
777 int len, ret = -1;
778
779 dReturn_val_if_fail(*line, ret);
780
781 *name = NULL;
782 *value = NULL;
783 dStrstrip(*line);
784 if (!*line[0] || *line[0] == '#') {
785 /* blank line or comment */
786 ret = 1;
787 } else if ((eq = strchr(*line, '='))) {
788 /* get name */
789 for (p = *line; *p && *p != '=' && !dIsspace(*p); ++p);
790 *p = 0;
791 *name = *line;
792
793 /* skip whitespace */
794 if (p < eq)
795 for (++p; dIsspace(*p); ++p);
796
797 /* get value */
798 if (p == eq) {
799 for (++p; dIsspace(*p); ++p);
800 len = strlen(p);
801 if (len >= 2 && *p == '"' && p[len-1] == '"') {
802 p[len-1] = 0;
803 ++p;
804 }
805 *value = p;
806 ret = 0;
807 }
808 }
809
810 return ret;
811 }
812
813 /*
814 *- Dlib messages -------------------------------------------------------------
815 */
816 void dLib_show_messages(bool_t show)
817 {
818 dLib_show_msg = show;
819 }
820
821 /*
822 *- Misc utility functions ----------------------------------------------------
823 */
824
825 /*
826 * Return the current working directory in a new string
827 */
828 char *dGetcwd ()
829 {
830 size_t size = 128;
831
832 while (1) {
833 char *buffer = dNew(char, size);
834 if (getcwd (buffer, size) == buffer)
835 return buffer;
836 dFree (buffer);
837 if (errno != ERANGE)
838 return 0;
839 size *= 2;
840 }
841 }
842
843 /*
844 * Return the home directory in a static string (don't free)
845 */
846 char *dGethomedir ()
847 {
848 static char *homedir = NULL;
849
850 if (!homedir) {
851 if (getenv("HOME")) {
852 homedir = dStrdup(getenv("HOME"));
853
854 } else if (getenv("HOMEDRIVE") && getenv("HOMEPATH")) {
855 homedir = dStrconcat(getenv("HOMEDRIVE"), getenv("HOMEPATH"), NULL);
856 } else {
857 DLIB_MSG("dGethomedir: $HOME not set, using '/'.\n");
858 homedir = dStrdup("/");
859 }
860 }
861 return homedir;
862 }
863
864 /*
865 * Get a line from a FILE stream.
866 * It handles backslash as line-continues character.
867 * Return value: read line on success, NULL on EOF.
868 */
869 char *dGetline (FILE *stream)
870 {
871 int ch;
872 Dstr *dstr;
873 char *line;
874
875 dReturn_val_if_fail (stream, 0);
876
877 dstr = dStr_sized_new(64);
878 while ((ch = fgetc(stream)) != EOF) {
879 if (ch == '\\') {
880 /* continue with the next line */
881 while ((ch = fgetc(stream)) != EOF && ch != '\n') ;
882 continue;
883 }
884 dStr_append_c(dstr, ch);
885 if (ch == '\n')
886 break;
887 }
888
889 line = (dstr->len) ? dstr->str : NULL;
890 dStr_free(dstr, (line) ? 0 : 1);
891 return line;
892 }
893
0 #ifndef __DLIB_H__
1 #define __DLIB_H__
2
3 #include <stdio.h> /* for FILE* */
4 #include <stddef.h> /* for size_t */
5 #include <stdarg.h> /* for va_list */
6 #include <string.h> /* for strerror */
7 #include <strings.h> /* for strcasecmp, strncasecmp (POSIX 2001) */
8
9 #include "d_size.h"
10
11 #ifdef __cplusplus
12 extern "C" {
13 #endif /* __cplusplus */
14
15 /*
16 *-- Common macros -----------------------------------------------------------
17 */
18 #ifndef FALSE
19 #define FALSE (0)
20 #endif
21
22 #ifndef TRUE
23 #define TRUE (!FALSE)
24 #endif
25
26 #undef MAX
27 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
28
29 #undef MIN
30 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
31
32 /* Handle signed char */
33 #define dIsspace(c) isspace((uchar_t)(c))
34 #define dIsalnum(c) isalnum((uchar_t)(c))
35
36 /*
37 *-- Casts -------------------------------------------------------------------
38 */
39 /* TODO: include a void* size test in configure.in */
40 /* (long) works for both 32bit and 64bit */
41 #define VOIDP2INT(p) ((long)(p))
42 #define INT2VOIDP(i) ((void*)((long)(i)))
43
44 /*
45 *-- Memory -------------------------------------------------------------------
46 */
47 #define dNew(type, count) \
48 ((type *) dMalloc ((unsigned) sizeof (type) * (count)))
49 #define dNew0(type, count) \
50 ((type *) dMalloc0 ((unsigned) sizeof (type) * (count)))
51
52 void *dMalloc (size_t size);
53 void *dRealloc (void *mem, size_t size);
54 void *dMalloc0 (size_t size);
55 void dFree (void *mem);
56
57 /*
58 *- Debug macros --------------------------------------------------------------
59 */
60 #define D_STMT_START do
61 #define D_STMT_END while (0)
62 #define dReturn_if(expr) \
63 D_STMT_START{ \
64 if (expr) { return; }; \
65 }D_STMT_END
66 #define dReturn_val_if(expr,val) \
67 D_STMT_START{ \
68 if (expr) { return val; }; \
69 }D_STMT_END
70 #define dReturn_if_fail(expr) \
71 D_STMT_START{ \
72 if (!(expr)) { return; }; \
73 }D_STMT_END
74 #define dReturn_val_if_fail(expr,val) \
75 D_STMT_START{ \
76 if (!(expr)) { return val; }; \
77 }D_STMT_END
78
79 /*
80 *- C strings -----------------------------------------------------------------
81 */
82 char *dStrdup(const char *s);
83 char *dStrndup(const char *s, size_t sz);
84 char *dStrconcat(const char *s1, ...);
85 char *dStrstrip(char *s);
86 char *dStrnfill(size_t len, char c);
87 char *dStrsep(char **orig, const char *delim);
88 char *dStristr(const char *haystack, const char *needle);
89
90 /* these are in POSIX 2001. Could be implemented if a port requires it */
91 #define dStrcasecmp strcasecmp
92 #define dStrncasecmp strncasecmp
93 #define dStrerror strerror
94
95 /*
96 *-- dStr ---------------------------------------------------------------------
97 */
98 #define Dstr_char_t char
99
100 typedef struct _dstr {
101 int sz; /* allocated size (private) */
102 int len;
103 Dstr_char_t *str;
104 } Dstr;
105
106 Dstr *dStr_new (const char *s);
107 Dstr *dStr_sized_new (int sz);
108 void dStr_fit (Dstr *ds);
109 void dStr_free (Dstr *ds, int all);
110 void dStr_append_c (Dstr *ds, int c);
111 void dStr_append (Dstr *ds, const char *s);
112 void dStr_append_l (Dstr *ds, const char *s, int l);
113 void dStr_insert (Dstr *ds, int pos_0, const char *s);
114 void dStr_insert_l (Dstr *ds, int pos_0, const char *s, int l);
115 void dStr_truncate (Dstr *ds, int len);
116 void dStr_erase (Dstr *ds, int pos_0, int len);
117 void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp);
118 void dStr_vsprintf (Dstr *ds, const char *format, va_list argp);
119 void dStr_sprintf (Dstr *ds, const char *format, ...);
120 void dStr_sprintfa (Dstr *ds, const char *format, ...);
121 int dStr_cmp(Dstr *ds1, Dstr *ds2);
122 char *dStr_memmem(Dstr *haystack, Dstr *needle);
123 const char *dStr_printable(Dstr *in, int maxlen);
124
125 /*
126 *-- dList --------------------------------------------------------------------
127 */
128 struct Dlist_ {
129 int sz; /* allocated size (private) */
130 int len;
131 void **list;
132 };
133
134 typedef struct Dlist_ Dlist;
135
136 /* dCompareFunc:
137 * Return: 0 if parameters are equal (for dList_find_custom).
138 * Return: 0 if equal, < 0 if (a < b), > 0 if (b < a) --for insert sorted.
139 *
140 * For finding a data node with an external key, the comparison function
141 * parameters are: first the data node, and then the key.
142 */
143 typedef int (*dCompareFunc) (const void *a, const void *b);
144
145
146 Dlist *dList_new(int size);
147 void dList_free (Dlist *lp);
148 void dList_append (Dlist *lp, void *data);
149 void dList_prepend (Dlist *lp, void *data);
150 void dList_insert_pos (Dlist *lp, void *data, int pos0);
151 int dList_length (Dlist *lp);
152 void dList_remove (Dlist *lp, const void *data);
153 void dList_remove_fast (Dlist *lp, const void *data);
154 void *dList_nth_data (Dlist *lp, int n0);
155 void *dList_find (Dlist *lp, const void *data);
156 int dList_find_idx (Dlist *lp, const void *data);
157 void *dList_find_custom (Dlist *lp, const void *data, dCompareFunc func);
158 void dList_sort (Dlist *lp, dCompareFunc func);
159 void dList_insert_sorted (Dlist *lp, void *data, dCompareFunc func);
160 void *dList_find_sorted (Dlist *lp, const void *data, dCompareFunc func);
161
162 /*
163 *- Parse function ------------------------------------------------------------
164 */
165 int dParser_parse_rc_line(char **line, char **name, char **value);
166
167 /*
168 *- Dlib messages -------------------------------------------------------------
169 */
170 void dLib_show_messages(bool_t show);
171
172 /*
173 *- Misc utility functions ----------------------------------------------------
174 */
175 char *dGetcwd ();
176 char *dGethomedir ();
177 char *dGetline (FILE *stream);
178
179
180 #ifdef __cplusplus
181 }
182 #endif /* __cplusplus */
183
184 #endif /* __DLIB_H__ */
185
0 Last review: August 04, 2009 --jcid
1
2
3 ----------------------------
4 Internal working for the CCC
5 ----------------------------
6
7
8 HTTP protocol
9 -------------
10
11
12 Query: |
13 .
14 1B --> 1B 1B --> 1B --> | -------------.
15 .----. .----. .----. . |
16 I |Capi| |http| | IO | | |
17 '----' '----' '----' . |
18 1F <-- 1F 1F <-- 1F | V
19 .
20 | [Server]
21 Answer: .
22
23 2B --> 2B 2B --> 2B | |
24 .----. .----. .----. . |
25 II |Capi| |Dpi | | IO | | |
26 '----' '----' '----' . |
27 2F <-- 2F 2F <-- 2F <-- | <------------'
28 .
29 |
30
31 * a_Capi_open_url() builds both the Answer and Query chains at
32 once (Answer first then Query), to ensure a uniform structure
33 that avoids complexity (e.g. race conditions).
34
35 * Http_get() sets a callback for the DNS hostname resolve.
36 Normally it comes later, but may also by issued immediately if
37 the hostname is cached.
38
39 * The socket FD is passed by means of OpSend by the http module
40 once the remote IP is known and the socket is connected.
41
42
43
44 Function calls for HTTP CCC
45 ---------------------------
46
47 a_Capi_open_url
48 if (reload)
49 Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO]
50 Capi OpStart 1B (query) [Capi] --> [http] --> [IO]
51 Http_get
52 a_Cache_open_url
53 if URL_E2EReload -> prepare reload
54 if cached
55 client enqueue
56 delayed process queue
57 else
58 Cache_entry_add
59 client enqueue
60
61 -//->
62 a_Http_dns_cb
63 Http_connect_socket
64 OpSend FD, BCK
65 OpSend FD, FWD
66 Http_send_query
67 a_Http_make_query_str
68 OpSend, BCK
69 IO_submit
70 a_IOwatch_add_fd (DIO_WRITE, ...)
71
72
73 Note about 'web' structures. They're created using a_Web_new().
74 The web.c module keeps a list of valid webs, so anytime you're
75 unsure of a weak reference to 'web', it can be checked with
76 a_Web_valid(web).
77
78
79
80 ------------
81 Dpi protocol
82 ------------
83
84
85 Query: |
86 .
87 1B --> 1B 1B --> 1B --> | -------------.
88 .----. .----. .----. . |
89 I |Capi| |Dpi | | IO | | |
90 '----' '----' '----' . |
91 1F <-- 1F 1F <-- 1F | V
92 .
93 | [Server]
94 .
95 Answer (same as HTTP): | |
96 . |
97 2B --> 2B 2B --> 2B | |
98 .----. .----. .----. . |
99 II |Capi| |Dpi | | IO | | |
100 '----' '----' '----' . |
101 2F <-- 2F 2F <-- 2F <-- | <------------'
102 .
103 |
104
105
106 CCC Construction:
107
108 a_Capi_open_url() calls a_Capi_dpi_send_cmd() when the URL
109 belongs to a dpi and it is not cached.
110
111 a_Capi_dpi_send_cmd() builds both the Answer and Query chains
112 at once (Answer first then Query), in the same way as HTTP does.
113 Note that the answer chain is the same for both, and the query
114 chain only differs in the module in the middle ([http] or [dpi]).
115
116
117 Function calls for DPI CCC
118 --------------------------
119
120 a_Capi_open_url
121 a_Capi_dpi_send_cmd
122 Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO]
123 Capi OpStart 1B (query) [Capi] --> [http] --> [IO]
124 a_Cache_open_url
125 [...]
126
127
128 Normal termination:
129
130 When the dpi server is done, it closes the FD, and OpEnd flows
131 from IO to Capi (answer branch). When in Capi, capi propagates
132 OpEnd to the query branch.
133
134 Abnormal termination:
135
136 The transfer may be aborted by a_Capi_conn_abort_by_url(). The
137 OpAbort is not yet standardized and has an ad-hoc implementation.
138 One idea is to have OpAbort always propagate BCK and then FWD and
139 to jump into the other chain when it gets to [Capi].
140
141
142 Debugging CCC
143 -------------
144
145 A simple way to "look" inside it, is to "#define VERBOSE 1" in
146 chain.c, and then to follow its work with a printed copy of the
147 diagrams in this document.
148
149 Each new data request generates a CCC, so if you want to debug,
150 it's good to refine the testcase to the minimum possible number
151 of connections.
152
00 June 2000, --Jcid
1 Last update: Oct 2004
1 Last update: Jul 09
22
33 -------
44 CACHE
1111 calls the cache or the dpi routines depending on the type of
1212 request.
1313
14 Every URL must be requested using a_Capi_open_url, no matter
15 if it is a http, file, dpi or whatever type of request. The capi
16 asks the dpi module for dpi URLs and the Cache for everything
17 else.
14 Every URL must be requested using a_Capi_open_url, which
15 sends the request to the cache if the data is cached, to dillo's
16 http module for http: URLs, and through dillo's DPI system for
17 other URLs.
1818
1919 Here we'll document non dpi requests.
20
21 The cache, at its turn, sends the requested-data from memory
22 (if cached), or opens a new network connection (if not cached).
23
24 This means that no mattering whether the answer comes from
25 memory or the net, the client requests it through the capi
26 wrapper, in a single uniform way.
2720
2821
2922 ----------------
3023 CACHE PHILOSOPHY
3124 ----------------
3225
33 Dillo's cache is very simple, every single resource that's
34 retrieved (URL) is kept in memory. NOTHING is saved. This is
35 mainly for three reasons:
26 Dillo's cache is very simple; every single resource that's
27 retrieved (URL) is kept in memory. NOTHING is saved to disk.
28 This is mainly for three reasons:
3629
3730 - Dillo encourages personal privacy and it assures there'll be
3831 no recorded tracks of the sites you visited.
4134 serve as caches.
4235
4336 - If you still want to have cached stuff, you can install an
44 external cache server (as WWWOFFLE), and benefit from it.
37 external cache server (such as WWWOFFLE), and benefit from it.
4538
4639
4740 ---------------
5043
5144 Currently, dillo's cache code is spread in different sources:
5245 mainly in cache.[ch], dicache.[ch] and it uses some other
53 functions from mime.c, Url.c and web.c.
46 functions from mime.c and web.cc.
5447
55 Cache.c is the principal source, and it also is the main
48 Cache.c is the principal source, and it also is the one
5649 responsible for processing cache-clients (held in a queue).
57 Dicache.c is the "decompressed image cache" and it holds the
58 original data and its corresponding decompressed RGB
59 representation (more on this subject in Images.txt).
50 Dicache.c is the interface to the decompressed RGB representations
51 of currently-displayed images held in DW's imgbuf.
6052
61 Url.c, mime.c and web.c are used for secondary tasks; as
53 mime.c and web.cc are used for secondary tasks such as
6254 assigning the right "viewer" or "decoder" for a given URL.
6355
6456
6658 A bit of history
6759 ----------------
6860
69 Some time ago, the cache functions, URL retrieving and
61 Some time ago, the cache functions, URL retrieval and
7062 external protocols were a whole mess of mixed code, and it was
7163 getting REALLY hard to fix, improve or extend the functionality.
7264 The main idea of this "layering" is to make code-portions as
7567
7668 An interesting part of the process is that, as resources are
7769 retrieved, the client (dillo in this case) doesn't know the
78 Content-Type of the resource at request-time. It only gets known
79 when the resource header is retrieved (think of http), and it
80 happens when the cache has the control so, the cache sets the
81 proper viewer for it! (unless the Callback function is specified
82 with the URL request).
70 Content-Type of the resource at request-time. It only becomes known
71 when the resource header is retrieved (think of http). This
72 happens when the cache has control, so the cache sets the
73 proper viewer for it (unless the Callback function was already
74 specified with the URL request).
8375
8476 You'll find a good example in http.c.
8577
86 Note: Files don't have a header, but the file handler inside
87 dillo tries to determine the Content-Type and sends it back in
88 HTTP form!
78 Note: All resources received by the cache have HTTP-style headers.
79 The file/data/ftp DPIs generate these headers when sending their
80 non-HTTP resources. Most importantly, a Content-Type header is
81 generated based on file extension or file contents.
8982
9083
9184 -------------
9285 Cache clients
9386 -------------
9487
95 Cache clients MUST use a_Cache_open_url to request an URL. The
88 Cache clients MUST use a_Capi_open_url to request an URL. The
9689 client structure and the callback-function prototype are defined,
9790 in cache.h, as follows:
9891
9992 struct _CacheClient {
100 gint Key; /* Primary Key for this client */
101 const char *Url; /* Pointer to a cache entry Url */
102 guchar *Buf; /* Pointer to cache-data */
103 guint BufSize; /* Valid size of cache-data */
93 int Key; /* Primary Key for this client */
94 const DilloUrl *Url; /* Pointer to a cache entry Url */
95 int Version; /* Dicache version of this Url (0 if not used) */
96 void *Buf; /* Pointer to cache-data */
97 uint_t BufSize; /* Valid size of cache-data */
10498 CA_Callback_t Callback; /* Client function */
10599 void *CbData; /* Client function data */
106100 void *Web; /* Pointer to the Web structure of our client */
123117 --------------------------
124118
125119 ииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииии
126 int a_Cache_open_url(const char *Url, CA_Callback_t Call, void *CbData)
120 int a_Cache_open_url(void *Web, CA_Callback_t Call, void *CbData)
127121
128 if Url is not cached
122 if Web->url is not cached
129123 Create a cache-entry for that URL
130124 Send client to cache queue
131 Initiate a new connection
132125 else
133126 Feed our client with cached data
134
135 ииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииии
136 ChainFunction_t a_Url_get_ccc_funct(const char *Url)
137
138 Scan the Url handlers for a handler that matches
139 If found
140 Return the CCC function for it
141 else
142 Return NULL
143
144 * Ex: If Url is an http request, a_Http_ccc is the matching
145 handler.
146127
147128 ииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииииии
148129
176157 to document it in more detail later (source is commented).
177158 Currently I have a drawing to understand it; hope the ASCII
178159 translation serves the same as the original.
179 If you're planning to understand the cache process troughly,
180 write me a note, just to assign a higher priority on further
181 improving of this doc.
160 If you're planning to understand the cache process thoroughly,
161 write me a note and I will assign higher priority to further
162 improvement of this doc.
182163 Hope this helps!
183164
184165
00 Jan 2002, Jörgen Viksell - jorgen.viksell@telia.com,
11 Jorge Arellano Cid --
2 Last update: April 2005, DarkSpirit
2 Last update: March 2010
33
44
55 ==================
66 Cookies in Dillo
77 ==================
88
9 The cookie support in Dillo aims to support cookies of the old
10 original Netscape style, as well as the kind specified in RFC 2109.
9 The current specification for cookies is RFC 6265
10 ( http://tools.ietf.org/html/rfc6265 ).
1111
12 Cookies are managed outside of dillo by the cookies.dpi. It uses
13 a cookies file in netscape format so it can be used with other
14 programs like for example wget.
12 Cookies are handled by a dpi (plugin) which shares them between your
13 instances of Dillo.
1514
16 Between sessions cookies are saved to ~/.dillo/cookies.txt, the
17 old ~/.dillo/cookies is read too but not updated. At the moment
18 the only enforcements on the amount of cookies to save to disk is
19 max 20 per domain.
15 Current cookie limits are: 20 per domain, and 1200 in total.
2016
21 There's also a file for controlling cookies: ~/.dillo/cookiesrc.
22 Dillo initially sets it to ignore (reject) all cookies, so if you
23 want to use cookies, change it to meet your needs.
24
25 If you don't want cookies at all, you have two options:
26
27 1.- Delete ~/.dillo/cookiesrc (or leave it just as dillo creates it).
28 2. Configure Dillo with ./configure --disable-cookies. Then all the
29 cookie stuff will be skipped at compilation.
30
31 Note: "--disable-cookies" absolutely eliminates cookie support,
32 no matter what "cookiesrc" says.
33
17 When the dpi exits, cookies that you have ACCEPTed are saved to
18 ~/.dillo/cookies.txt, and ACCEPT_SESSION cookies are forgotten.
19 The dpi normally exits after a period of inactivity, but you can force it to
20 exit with the command "dpidc stop".
3421
3522
3623 =====================
3724 Controlling cookies
3825 =====================
3926
40 There is a small and simple way to restrict urls from setting cookies
41 in Dillo. In the file ~/.dillo/cookiesrc You may specify rules
42 for different domains. The syntax looks something like this:
27 Out of the box, dillo rejects all cookies.
4328
29
30 If you want to accept certain cookies, you can specify rules for different
31 domains in the file ~/.dillo/cookiesrc. The syntax looks like:
32
33 #host action
4434 DEFAULT DENY
45 slashdot.org ACCEPT
35 fltk.org ACCEPT
4636 .host.com ACCEPT_SESSION
4737
48 The first line says that we should deny all cookies from all domains
49 by default.
50 The second one tells Dillo to save all cookies from slashdot.org
51 across sessions, until it expires.
52 And finally, the third says that all subdomains of host.com should be
53 allowed to set cookies. But these cookies will only be saved in
54 memory until you exit.
38 Line 0: Comment line begins with '#'.
39 Line 1: Deny all cookies from all domains not otherwise specified.
40 Line 2: Accept all cookies from fltk.org, and save them to
41 ~/.dillo/cookies.txt when the cookies dpi exits.
42 Line 3: Accept all cookies from all subdomains of host.com, but
43 do not save them when the dpi exits.
44
45
46 If you are positive that you will never want any cookies, you can
47 configure/compile Dillo without cookie support. The option is:
48 ./configure --disable-cookies
5549
5650
5751 ===================
7064 with more information than you have about yourself.
7165
7266 Some people may tell you this is "paranoid". But please, take my words
73 as those of someone that has written a web browser, a cookies implementation,
74 and that has deep understanding of HTTP (RFC-2068) and cookies (RFC-2965).
75
76 Non technical persons may like to read:
77 http://www.junkbusters.com/cookies.html
78 http://www.newsfactor.com/perl/story/16455.html (about user-spying)
67 as those of someone who has written a web browser, a cookies implementation,
68 and who has deep understanding of HTTP and cookies.
7969
8070 The dillo project is especially concerned about privacy and security
8171 issues. Our advice is to avoid cookies whenever possible and at most set
8272 ACCEPT_SESSION to specific, trusted sites. -- You have been warned.
8373
84
85 =========================
86 DPI-Dillo comunications
87 =========================
88
89 The cookies.dpi has the state of the cookies and is the only one
90 that reads and writes to the cookies.txt file. The different
91 running dillos must ask and send cookies to it.
92
93 To minimize communications between the dpi and dillo clients,
94 every different instance of dillo reads 'cookiesrc' and only ask
95 and send cookies for allowed sites.
96
97 The cookies.dpi also needs to read 'cookiesrc'. If a site is
98 changed to deny cookies in 'cookiesrc', the cookies dpi can
99 delete the cookies for that site from the 'cookies.txt' file the
100 next time it loads and writes that file.
101
102 All the work is implemented adding only three new dpi commands,
103 two really, and a new send bloking dpi command function.
104
105 When dillo wants the cookies for a certain site, it sends the
106 'get_cookie' dpi command to the cookies.dpi, and the dpi sends
107 the answer inside a 'get_cookies_answer' dpi command.
108
109 If an allowed site sends a cookie, dillo uses the 'set_cookies'
110 dpi command to let the cookies dpi store it.
111
112
113 ==============
114 Restrictions
115 ==============
116
117 Use "dpidc stop" before making changes to cookies.txt or any
118 cookie files or you can lose your changes when the running dpi
119 rewrites them. After the cookies dpi reloads the file all dillos
120 will use the new one.
121
122 If you change the 'cookiesrc' (previously calling "dpidc stop")
123 only newly opened dillos will use the changes.
124
125
126 Thats all folks!
2222
2323 Dillo can be viewed as the sum of five main parts:
2424
25 1.- Dillo Widget: A custom widget, gtk+ based, that holds the
26 neccesary data structures and mechanisms for graphical rendering.
25 1.- Dillo Widget: A custom widget, FLTK-based, that holds the
26 necessary data structures and mechanisms for graphical rendering.
2727 (Described in Dw*.txt, dw*.c files among the sources.)
2828
2929 2.- Dillo Cache: Integrated with a signal driven Input/Output
3030 engine that handles file descriptor activity, the cache acts as
3131 the main abstraction layer between rendering and networking.
3232 Every URL, whether cached or not, must be retrieved using
33 a_Cache_open_url (Described briefly in Cache.txt, source
34 contained in cache.c).
35 IO is described in IO.txt (recommended), source in IO/.
33 a_Capi_open_url (Described briefly in Cache.txt, source
34 contained in capi.c).
35 IO is described in IO.txt (recommended), source in src/IO/.
3636
3737 3.- The HTML parser: A streamed parser that joins the Dillo
3838 Widget and the Cache functionality to make browsing possible
39 (Described in HtmlParser.txt, source mainly inside html.c).
39 (Described in HtmlParser.txt, source mainly inside html.cc).
4040
4141 4.- Image processing code: The part that handles image
42 retrieving, decoding, caching and displaying. (Described in
43 Images.txt. Sources: image.c, dw_image.c, dicache.c, gif.c,
42 retrieval, decoding, caching and displaying. (Described in
43 Images.txt. Sources: image.c, dw/image.cc, dicache.c, gif.c,
4444 jpeg.c and png.c)
4545
4646 5.- The dpi framework: a gateway to interface the browser with
4747 external programs (Example: the bookmarks server plugin).
4848 Dpi spec: http://www.dillo.org/dpi1.html
49
49
5050
5151 -------------------------
5252 HOW IS THE PAGE RENDERED?
5454
5555 (A short description of the internal function calling process)
5656
57 When the user requests a new URL, a_Interface_entry_open_url
57 When the user requests a new URL, a_UIcmd_open_url
5858 is queried to do the job; it calls a_Nav_push (The highest level
5959 URL dispatcher); a_Nav_push updates current browsing history and
6060 calls Nav_open_url. Nav_open_url closes all open connections by
61 calling a_Interface_stop and a_Interface_stop, and then calls
62 a_Capi_open_url wich calls a_Cache_open_url (or the dpi module if
61 calling a_Bw_stop_clients, and then calls
62 a_Capi_open_url which calls a_Cache_open_url (or the dpi module if
6363 this gateway is used).
6464
65 If Cache_search hits (due to a cached url :), the client is
65 If Cache_entry_search hits (due to a cached url :), the client is
6666 fed with cached data, but if the URL isn't cached yet, a new CCC
67 (Concomitant Control Chain) is created and commited to fetch the
68 URL. Note that a_Cache_open_url will return the requested URL,
69 whether cached or not.
67 (Concomitant Control Chain) is created and committed to fetch the
68 URL.
7069
7170 The next CCC link is dynamically assigned by examining the
72 URL's protocol. It can be:
71 URL's protocol. It can be a_Http_ccc or a_Dpi_ccc.
7372
74 a_Http_ccc
75 a_File_ccc
76 a_About_ccc
77 a_Plugin_ccc (not implemented yet)
78
79
80 If we have a HTTP URL, a_Http_ccc will succeed, and the http
73 If we have an HTTP URL, a_Http_ccc will succeed, and the http
8174 module will be linked; it will create the proper HTTP query and
8275 link the IO module to submit and deliver the answer.
8376
84 Note that as the Content-type of the URL is not always known
77 Note that as the Content-Type of the URL is not always known
8578 in advance, the answering branch decides where to dispatch it to
8679 upon HTTP header arrival.
8780
9386 and the whole page is contructed in a streamed way.
9487 Somewhere in the middle of it, resize and repaint functions
9588 are activated and idle functions draw to screen what has been
96 processed.
89 processed.
9790
9891 (The process for images is described in Images.txt)
9992
00 Aug 2003, Jorge Arellano Cid,
11 Ferdi Franceschini --
2 Last update: Dec 2004
2 Last update: Nov 2009
33
44
55 ------
1313 dpi:
1414 generic term referring to dillo's plugin system (version1).
1515
16 dpi1:
16 dpi1:
1717 specific term for dillo's plugin spec version 1.
1818 at: http://www.dillo.org/dpi1.html
1919
3838 never run more than one instance of a server plugin at a time.
3939
4040 filter plugin:
41 Any program/script that can read or write to stdio. If you can write a
42 shell script you can write one of these (see examples at the end).
41 A dpi program that reads from stdin and writes to stdout, and that
42 exits after its task is done (they don't remain as server plugins).
4343 Warning, dpid will run multiple instances of filter plugins if requested.
44 This is safe if the plugin only writes to stdout which is what the filter
45 type dpis do at the moment.
4644
4745 -----------
4846 About dpid:
4947 -----------
5048
5149 * dpid is a program which manages dpi connections.
52 * dpid is a daemon that serves dillo using unix domain
53 sockets (UDS).
50 * dpid is a daemon that serves dillo using IDS sockets.
5451 * dpid launches dpi programs and arranges socket communication
5552 between the dpi program and dillo.
5653
5754 The concept and motivation is similar to that of inetd. The
58 plugin manager (dpid) listens for a service request on a Unix
59 domain socket and returns the socket name of a plugin that
60 handles the service. It also watches sockets of inactive plugins
61 and starts them when a connection is requested.
55 plugin manager (dpid) listens for a service request on a socket
56 and returns the socket/port pair of a plugin that handles the
57 service. It also watches sockets of inactive plugins and starts
58 them when a connection is requested.
6259
6360
6461 -----------------------------------------------------------
7168 * When having two or more running instances of Dillo, one
7269 should prevail, and take control of dpi managing (but all
7370 dillos carry the managing code).
74 * If the managing dillo exits, it must pass control to another
71 * If the managing-dillo exits, it must pass control to another
7572 instance, or leave it void if there's no other dillo running!
76 * The need to synchronise all the running instances of
73 * The need to synchronize all the running instances of
7774 dillo arises.
7875 * If the controlling instance finishes and quits, all the
7976 dpi-program PIDs are lost.
8279 * Forks can be expensive (Dillo had to fork its dpis).
8380 * When a managing dillo exits, the new one is no longer the
8481 parent of the forked dpis.
85 * If the Unix domain sockets for the dpis were to be named
82 * If Unix domain sockets for the dpis were to be named
8683 randomly, it gets very hard to recover their names if the
8784 controlling instance of dillo exits and another must "take
8885 over" the managing.
108105
109106 * Different implementations of the same service
110107 dpi programs ("dpis") are just an implementation of a
111 service. There's no problem in having more than one for the
112 same service.
108 service. There's no problem in implementing a different one
109 for the same service (e.g. downloads).
113110
114111 * Upgrading a service:
115112 to a new version or implementation without requiring
116 bringing down the dpid or patching dillo's core.
113 patching dillo's core or even bringing down the dpid.
117114
118115
119116 And finally, being aware that this design can support the
123120 ------------------------------------------------------------
124121 * "one demand/one response" man, preferences, ...
125122 * "resident while working" downloads, mp3, ...
126 * "resident until TERM signal" bookmarks, ...
127
123 * "resident until exit request" bookmarks, ...
124
128125 * "one client only" cd burner, ...
129126 * "one client per instance" man, ...
130127 * "multiple clients/one instance" downloads, cookies ...
135132 --------
136133 * Dpi programs go in: "EPREFIX/dillo/dpi" or "~/.dillo/dpi". The binaries
137134 are named <name>.dpi as "bookmarks.dpi" and <name>.filter.dpi as in
138 "hello.filter.dpi". The ".filter" plugins simply read and write to stdio
139 and can be implemented with a shell script easily.
135 "hello.filter.dpi". The ".filter" plugins simply read from stdin
136 and write to stdout.
140137 * Register/update/remove dpis from list of available dpis when a
141 <dpi cmd='register_all'> is received.
142 * dpid terminates when it receives a <dpi cmd='DpiBye'> command.
143 * dpis can be terminated with a <dpi cmd='DpiBye'> command.
138 'register_all' command is received.
139 * dpid terminates when it receives a 'DpiBye' command.
140 * dpis can be terminated with a 'DpiBye' command.
144141 * dpidc control program for dpid, currently allows register and stop.
145142
146143
150147
151148 These features are already designed, waiting for implementation:
152149
153 * How to register/update/remove/ individual dpis?
154 * How to kill dpis? (signals)
155
156 How:
157
158 A useful and flexible way is to have a "control program" for
159 dpid (it avoids having to find its PID among others).
160
161 Let's say:
162
163 dpidc [register | upgrade | stop | ...]
164
165 It can talk to a dpid UDS that serves for that (the same that
166 dillo would use). That way we may also have a dpidc dpi! :-)
167
168 Seriously, what I like from this approach is that it is very
169 flexible and can be implemented incrementally ("dpidc register"
170 is enough to start).
171
172 It also avoids the burden of having to periodically check the
173 dpis directory structure for changes).
174
175 It also lets shell scripts an easy way to do the "dirty" work
176 of installing dpis; as is required with distros' package
177 systems.
178
179 <note>
180 How do we tell a crashed dpi? That's the question.
181 We're thinking about using the "lease" concept (as in JINI).
182 </note>
150 * dpidc remove // May be not necessary after all...
183151
184152
185153 -----------------
192160 o both directories are scanned for the list of available plugins.
193161 ~/.dillo/dpi overrides system-wide dpis.
194162
195 o ~/.dillo/dpi_socket_dir is then checked for the name of the dpi socket
196 directory, if dpi_socket_dir does not exist it will be created.
197
198 o next it creates Unix domain sockets for the available plugins and
199 then listens for service requests on its own socket (dpid.srs)
163 o next it creates internet domain sockets for the available plugins and
164 then listens for service requests on its own socket,
200165 and for connections to the sockets of inactive plugins.
201166
202 o dpid returns the name of a plugin's socket when a client (dillo)
167 o dpid returns the port of a plugin's socket when a client (dillo)
203168 requests a service.
204169
205170 o if the requested plugin is a 'server' then
209174
210175 o if the requested plugin is a 'filter' then
211176 1) dpid accepts the connection
212 2) duplicates the connection on stdio
177 2) maps the socket fd to stdin/stdout (with dup2)
213178 3) forks and starts the plugin
214179 4) continues to watch the socket for new connections
215180
225190
226191 (I)
227192 .--- s1 s2 s3 ... sn
228 |
193 |
229194 [dpid] [dillo]
230195 |
231196 '--- srs
235200
236201 (II)
237202 .--- s1 s2 s3 ... sn
238 |
203 |
239204 [dpid] [dillo]
240205 | |
241206 '--- srs ------------------'
261226 .[dpid] | [dillo]
262227 . | | |
263228 . '--- srs '---------------'
264 .
229 .
265230 .............[dpi program]
266231
267232 when s3 has activity (incoming data), dpid forks the dpi
270235
271236 (V)
272237 .--- s1 s2 (s3) ... sn
273 |
238 |
274239 [dpid] [dillo]
275240 | |
276241 '--- srs .---------------'
284249 the dpi program exits, dpid resumes listening on the socket (s3).
285250
286251
287 -----------------------------------------------
288 How are the unix-domain-sockets for dpis named?
289 -----------------------------------------------
290
291 Let's say we have two users, "fred" and "joe".
292
293 When Fred's dillo starts its dpid, the dpid creates the
294 following directory (rwx------):
295
296 /tmp/fred-XXXXXX
297
298 using mkdtemp().
299
300 and saves that filename within "~/.dillo/dpi_socket_dir".
301
302 That way, another dillo instance of user Fred can easily find
303 the running dpid's service request socket at:
304
305 /tmp/fred-XXXXXX/dpid.srs
306
307 (because it is saved in "~/.dillo/dpi_socket_dir").
308
309 Now, we have a dpi directory per user, and its permissions are
310 locked so only the user has access, thus the following directory
311 tree structure should pose no problems:
312
313 /tmp/fred-XXXXXX/bookmarks
314 /downloads
315 /cookies
316 /ftp
317 ...
318 dpid.srs
319
320 If user Joe starts his dillo, the same happens for him:
321
322 /tmp/joe-XXXXXX/bookmarks
323 /downloads
324 /cookies
325 /ftp
326 ...
327 dpid.srs
328
329
330 What should dpid do at start time:
331
332 Check if both, ~/.dillo/dpi_socket_dir and its directory, exist
333 (it can also check the ownership and permissions).
334
335 If (both exist)
336 use them!
337 else
338 delete ~/.dillo/dpi_socket_dir
339 create another /tmp/<user>-XXXXXX directory
340 save the new directory name into ~/.dillo/dpi_socket_dir
341 (we could also add some paranoid level checks)
342
343 To some degree, this scheme solves the tmpnam issue, different
344 users of dillo at the same time, multiple dillo instances,
345 polluting /tmp (cosmetic), and reasonably accounts for an
346 eventual dillo or dpid crash.
347
348 It has worked very well so far!
349
350
351252 --------------------------------
352253 So, how do I make my own plugin?
353254 --------------------------------
354255
355 First, at least, read the "Developing a dillo plugin" section
356 of dpi1 spec! :-)
357
358 Note that the dpi1 spec may not be absolutely accurate, but the
359 main ideas remain.
360
361 Once you've got the concepts, contrast them with the drawings
362 in this document. Once it all makes sense, start playing with
363 hello.dpi, you can run it by starting dillo with
364 dillo dpi:/hello/
365 or entering
366 dpi:/hello/
367 as the url. Then try to understand how it works (use the drawings)
368 and finally look at its code.
369
370 Really, the order is not that important, what really matters is
371 to do it all.
372
373 Start modifying hello.dpi, and then some more. When you feel
374 like trying new things, review the code of the other plugins for
375 ideas.
376
377 The hardest part is to try to modify the dpi framework code
256 Maybe the simplest way to get started is to understand a few
257 concepts and then to use the hands-on method by using/modifying
258 the hello dpi. It's designed as an example to get developers
259 started.
260
261 ---------
262 Concepts:
263 ---------
264
265 * Dillo plugins work by communicating two processes: dillo
266 and the dpi.
267 * The underlying protocol (DPIP) has a uniform API which is
268 powerful enough for both blocking and nonblocking IO, and
269 filter or server dpis.
270 * The simplest example is one-request one-answer (for example
271 dillo asks for a URL and the dpi sends it). You'll find
272 this and more complex examples in hello.c
273
274 First, you should get familiar with the hello dpi as a user:
275
276 $dillo dpi:/hello/
277
278 Once you've played enough with it, start reading the well
279 commented code in hello.c and start making changes!
280
281
282 ---------------
283 Debugging a dpi
284 ---------------
285
286 The simplest way is to add printf() feedback using the MSG*
287 macros. You can start the dpid by hand on a terminal to force
288 messages to go there.
289
290 Sometimes more complex dpis need more than MSG*. In this case
291 you can use gdb like this.
292
293 1.- Add an sleep(20) statement just after the dpi starts.
294 2.- Start dillo and issue a request for your dpi. This will
295 get your dpi started.
296 3.- Standing in the dpi source directory:
297 ps aux|grep dpi
298 4.- Take note of the dpi's PID and start gdb, then:
299 (gdb) attach <PID>
300 5.- Continue from there...
301
302
303 ------------
304 Final Notes:
305 ------------
306
307 1.- If you already understand the hello dpi and want to try
308 something more advanced:
309
310 * bookmarks.c is a good example of a blocking server
311 * file.c is an advanced example of a server handling multiple
312 non-blocking connections with select().
313
314 2.- Multiple instances of a filter plugin may be run
315 concurrently, this could be a problem if your plugin records data
316 in a file, however it is safe if you simply write to stdout.
317 Alternatively you could write a 'server' plugin instead as they
318 are guaranteed not to run concurrently.
319
320 3.- The hardest part is to try to modify the dpi framework code
378321 inside dillo; you have been warned! It already supports a lot of
379322 functionality, but if you need to do some very custom stuff, try
380 extending the "chat" command.
381
382
383 ---------------------------------
384 Examples: Simple 'filter' plugins
385 ---------------------------------
386
387 For a quick and dirty introduction to dpis try the following shell scripts.
388
389 #!/bin/sh
390
391 read -d'>' dpip_tag # Read dillo's request
392
393 # Don't forget the empty line after the Content-type
394 cat <<EOF
395 <dpi cmd='start_send_page' url='dpi:/hi/hi.filter.dpi'>
396 Content-type: text
397
398 EOF
399
400 echo Hi
401
402 Of course you should use html in a real application (perl makes this easy).
403
404 A more useful example uses the "si" system info viewer:
405
406 #!/bin/sh
407 # si - System Information Viewer
408
409 read -d'>' dpip_tag
410
411 # We don't need to send the Content-type because "si --html" does this
412 # for us.
413 cat <<EOF
414 <dpi cmd='start_send_page' url='dpi:/si/si.dpi.filter'>
415 EOF
416
417 si --html
418
419 just make sure that you have si installed or you wont get far.
420
421 To try out the examples create two directories for the scripts under your home directory as follows:
422 mkdir -p ~/.dillo/dpi/hi
423 mkdir -p ~/.dillo/dpi/si
424
425 then create the scripts and put them in the dpi service directories so that you end up with
426 ~/.dillo/dpi/hi/hi.filter.dpi
427 ~/.dillo/dpi/si/si.filter.dpi
428
429 Don't forget to make them executable.
430
431 If dpid is already running register the new plugins with
432 dpidc register
433
434 You can now test them by entering
435 dpi:/hi/
436 or
437 dpi:/si/
438 as the url. Or simply passing the url to dillo on startup
439
440 dillo dpi:/si/
441
442
443 You can edit the files in place while dpid is running and reload them in
444 dillo to see the result, however if you change the file name or add a new
445 script you must run 'dpidc register'.
446
447 WARNING
448 Multiple instances of a filter plugin may be run concurrently, this could be a
449 problem if your plugin records data in a file, however it is safe if you simply
450 write to stdout. Alternatively you could write a 'server' plugin instead as
451 they are guaranteed not to run concurrently.
452 >>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<
453
323 extending the "chat" command, or asking in dillo-dev.
324
325
326
327 >>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<
328
0 Jan 2001, S.Geerken@ping.de
1 Last update: Dec 2004
0 Last update: Oct 2008
21
32 ================
43 Dw: Dillo Widget
54 ================
65
7 Dw is mainly the module for rendering HTML. It provides a framework
8 for widgets, based on the Gtk+ object framework, and is very similar
9 to Gtk+, so that experiences in using and extending Gtk+ help very
10 much in understanding Dw. There is some documentation at www.gtk.org
11 (and probably on your local harddisk, somewhere in /usr/doc/*gtk*),
12 you should especially have read the chapter "Writing Your Own
13 Widgets" in the tutorial.
6 Dw is the internal widget library for rendering HTML. It has excellent
7 documentation.
148
9 Just run "doxygen" and browse the html/ directory!
1510
16 Why Not Gtk+?
17 =============
18
19 There are two reasons for designing a new model instead of simply
20 using Gtk+ objects:
21
22 1. Most important, Gtk+ widgets are limited in size, because X
23 windows are so.
24 2. There are a few extensions which are due to the different needs
25 for HTML rendering compared to GUI's. (Of course, this could
26 have been solved by defining a new object derived from
27 GtkWidget.)
28
29
30 Notes On Naming
31 ===============
32
33 According to the naming standards, functions beginning with "a_Dw_"
34 may be used outside of Dw, while, as an extention, functions used
35 within Dw (e.g. p_Dw_widget_queue_resize) are prefixed with "p_Dw_".
36 [todo: This could be included in NC_design.txt.]
37
38 Non-static functions beginning with "Dw_" are only used between
39 GtkDwViewport and DwWidget (e.g. Dw_gtk_viewport_remove_dw), they
40 belong to the core of Dw. And, of course, functions only used within a
41 sub-module (e.g. a specific widget) start with "Dw_" and are static
42 (e.g. Dw_page_find_line_index).
43
44 Dw widgets and some other structures have the prefix "Dw", while Gtk+
45 widgets in Dw have the prefix "GtkDw", but functions of them begin
46 with "Dw_gtk_" or "a_Dw_gtk", respectively.
47
48
49 Basic Overview
50 ==============
51
52 Dw widgets are objects derived from DwWidget, which itself derives
53 from GtkObject. DwWidget is quite similar to GtkWidget, the main
54 difference is that Dw widgets are always windowless and that they are
55 presented in a viewport, so there is no need to limit the size. Much
56 of the functionality normally provided by the X server is simulated
57 by Dw.
58
59 The interface between Gtk+ and Dw is the Gtk+ widget GtkDwViewport,
60 which contains (at most) one top-level Dw widget.
61
62 A Few Definitions:
63
64 - world coordinates: coordinates relative to the upper left corner
65 of the whole scrolled area ("the world")
66 - viewport coordinates: coordinates relative to the upper left
67 corner of the visible area
68 - widget coordinates: coordinates relative to the upper left corner
69 of the widget
70
71 Dw widgets draw into the viewport window, and must adhere to
72 *viewport coordinates*: the "world" is only an abstract term, there
73 is no window for it. When GtkDwViewport processes expose events, they
74 are automatically delivered to the Dw widgets. Redrawing requests due
75 to scrolling of the viewport is done by the base object GtkLayout,
76 you will not find any code for this in Dw.
77
78 Mouse events also contain viewport coordinates. Dw will try to find
79 the right Dw widget to deliver the event to.
80
81 Resizing the GtkDwViewport will not resize the top-level Dw widget,
82 but the latter will get some hints, so that, e.g., the page widget
83 rewraps the lines at the appropriate width.
84
85 See DwWidget.txt for more details.
86
87
88 Embedding Gtk+ Widgets In Dw
89 ----------------------------
90 Dw Widgets may embed Gtk+ widgets, this is done by the Dw widget
91 DwEmbedGtk. For Gtk+, these embedded Gtk+ widgets are themselves
92 children of the GtkDwViewport, since Gtk+ does not care about Dw.
93
94 Of course, embedded Gtk+ widgets are again limited in size, but but
95 in position: GtkDwViewport is derived from GtkLayout which is exactly
96 designed for positioning widgets in an infinite scrolled area.
97
98
99 How To Get The Top-Level Dw Widget From A BrowserWindow
100 -------------------------------------------------------
101 The member "docwin" of BrowserWindow points on a GtkDwScrolledWindow,
102 which contains a GtkDwScrolledFrame, which contains a GtkDwViewport.
103 The member "child" of the latter points on the top-level Dw widget,
104 or may be NULL. The top-level Dw is (currently) a DwPage (HTML and
105 plain text documents) or a DwImage (images).
106
107 There is a function a_Dw_gtk_scrolled_window_get_dw for this.
108
109
110 Sizes
111 -----
112 A feature adapted from the old Dw are baselines. As well DwAllocation
113 as DwRequisition do not have a height member, but instead ascent and
114 descent, both positive or zero. (Originally I removed this, but there
115 will be a few widgets in future depending on this, e.g., math
116 formulas.)
117
118 Unlike in Gtk, sizes of zero are allowed. The upper limit for the
119 size of a widget is 2^31 (this will be enough to show the contents of
120 a small library in a web page).
121
122
123 Resizing
124 ========
125
126 From outside: When writing a new widget, you should implement the
127 signal "size_request". When the widget changes its size, it should
128 call p_Dw_widget_queue_resize, as in a_Dw_image_size. See "Incremental
129 Resizing" below for a way to increase the speed.
130
131 Even if the implementation of "size_request" gets quite expensive,
132 you do not have to check whether the size has changed, this is done
133 by a_Dw_widget_size_request.
134
135 Inside: q_Dw_widget_queue_resize will set the DW_NEEDS_RESIZE flag, a
136 further call of a_Dw_widget_size_request will only then emit the
137 "size_request" signal. Furthermore, mark_size_change and
138 mark_extremes_change are called (see below). After that, the resizing
139 is done in an idle loop, this prevents too many size requests. The
140 algorithm is quite simple: any widget with a child which needs
141 resizing, needs resizing, thus all parents up to top-level widget are
142 marked.
143
144 Incremental Resizing
145 ---------------------
146 A widget may calculate its size based on size calculations already
147 done before. In this case, a widget must exactly know the reasons, why
148 a call of size_request is necessary. To make use of this, a widget
149 must implement the following:
150
151 1. There is a member in DwWidget, called parent_ref, which is
152 totally under control of the parent widget (and so sometimes not
153 used at all). It is necessary to define how parent_ref is used
154 by a specific parent widget, and it has to be set to the correct
155 value whenever necessary.
156
157 2. The widget must implement mark_size_change and
158 mark_extremes_change, these methods are called in two cases:
159
160 a) directly after q_Dw_widget_queue_resize, with the argument
161 ref was passed to q_Dw_widget_queue_resize, and
162 b) if a child widget has called q_Dw_widget_queue_resize,
163 with the value of the parent_ref member of this child.
164
165 This way, a widget can exactly keep track on size changes, and so
166 implement resizing in a faster way. A good example on how to use this
167 is the DwPage widget, see DwPage.txt for details.
168
169
170 Anchors and Scrolling
171 =====================
172
173 Anchors
174 -------
175 todo: This section is out of sync with the actual code.
176
177 To set the anchor a page is viewed at, you can use one of the
178 following functions:
179
180 - void a_Dw_gtk_viewport_set_anchor (GtkDwViewport *viewport,
181 gchar *anchor)
182
183 Scroll directly to an anchor. The anchor does not need to exist
184 already, see below.
185
186 - void a_Dw_gtk_viewport_queue_anchor (GtkDwViewport *viewport,
187 gchar *anchor)
188
189 Set the anchor for the next top-level DwWidget (the next call
190 of a_Dw_gtk_viewport_add_dw).
191
192 There are wrappers, a_Dw_gtk_scrolled_window_queue_anchor and
193 a_Dw_gtk_scrolled_window_set_anchor.
194
195 After a_Dw_gtk_viewport_set_anchor has been called (indirectly by
196 Nav_open_url, or by a_Dw_gtk_viewport_add_dw), changes of anchor
197 positions (e.g., if widgets change there size or the anchor was not
198 known before) will correct the viewport adjustment (in function
199 p_Dw_gtk_viewport_update_anchor), but only as long as the user did not
200 change it directly. Look at Dw_gtk_scrolled_window_init for details
201 about the latter.
202
203 Use p_Dw_widget_set_anchor to add anchors to a widget, see
204 DwWidget.txt.
205
206 Scrolling
207 ---------
208 Here is an overview on more functions for scrolling:
209
210 - To scroll to a given position, there are two possibilities:
211 a_Dw_gtk_viewport_set_scrolling_position simply scrolls to this
212 position, while Dw_gtk_viewport_scroll_to has more facilities:
213 you specify a rectangle you want to see, and the way how it is
214 seen (at the border, centered, or just scroll as much as
215 necessary, that it is seen). If you have a widget, you can also
216 use Dw_widget_scroll_to. There is also a wrapper for
217 GtkDwScrolledWindow,
218 a_Dw_gtk_scrolled_window_set_scrolling_position, and two
219 functions for getting the position,
220 a_Dw_gtk_scrolled_window_get_scrolling_position_x, and
221 a_Dw_gtk_scrolled_window_get_scrolling_position_y.
222
223 - If you have a region, and want to display it, use
224 a_Dw_iterator_scroll_to. For example, the findtext module makes
225 use of it. There are equivalents for DwExtIterator and
226 DwWordIterator. See comments on and in the function for more
227 informations.
228
229 - If you just want to determine where some content is allocated,
230 represented by an iterator, you can use
231 a_Dw_iterator_get_allocation. There are equivalents for
232 DwExtIterator and DwWordIterator.
233
234
235 The Objects
236 ===========
237
238 This is the hierarchy of all objects of Dw:
239
240 (GtkObject)
241 +-DwWidget
242 | +----DwBullet
243 | +----DwContainer
244 | | `----DwPage
245 | +----DwEmbedGtk
246 | +----DwHruler
247 | `----DwImage
248 `----(GtkWidget)
249 `----(GtkContainer)
250 +----(GtkBin)
251 | +----(GtkScrolledWindow)
252 | | `----GtkDwScrolledWindow
253 | `----GtkDwScrolledFrame
254 `----(GtkLayout)
255 `----GtkDwViewport
256
257 Objects in parentheses are part of Gtk+, not of Dw.
258
259
260 DwBullet
261 --------
262 Simple widget used for unnumbered list (<ul>).
263
264
265 DwContainer
266 -----------
267 The base object for Dw widgets which contain other Dw widgets. As in
268 Gtk+, containers are responsible for storing the children, there is
269 no common data structure. There are a few signals:
270
271 - void add (DwContainer *container,
272 DwWidget *child);
273
274 Currently not used, but may be in future.
275
276 - void remove (DwContainer *container,
277 DwWidget *widget);
278
279 *Recognize* that a widget is destroyed, i.e., an implementation
280 should remove *the pointer* from the list or so, but not
281 destroy the child widget. It is called by Dw_widget_shutdown.
282
283 - void forall (DwContainer *container,
284 DwCallback callback,
285 gpointer callback_data);
286
287 Process callback for all children, in the form
288 (*callback)(child, callback_data).
289
290 The include_internals of the Gtk+ equivalent was not adapted,
291 since it is used for more sophisticated purposes not needed in
292 Dw.
293
294
295 DwEmbedGtk
296 ----------
297 This Dw widget is used to embed Gtk+ widgets into Dw container
298 widgets. The Gtk+ widget is set by a_Dw_embed_gtk_add_gtk, and can
299 simply be removed by destroying it.
300
301 If the DwEmbedGtk contains no Gtk+ widget, it always returns 0x0x0 as
302 size, so, for speed reasons, first add the Gtk+ widget into the
303 DwEmbedGtk, and then the DwEmbedGtk into the other Dw widget, as at
304 the end of Html_tag_open_input.
305
306
307 DwHruler
308 --------
309 Simple widget used for the <hr> tag.
310
311
312 DwImage
313 -------
314 Widget for displaying image. See DwImage.txt for details.
315
316
317 DwPage
318 ------
319 A widget for displaying texts. See DwPage.txt for details.
320
321
322 DwTable
323 -------
324 A container widget for rendering tables. See DwTable.txt for details.
325
326
327 DwWidget
328 --------
329 The base object for all Dw widgets. See DwWidget.txt for details.
330
331
332 GtkDwScrolledWindow
333 -------------------
334 Adds a few functionalities to GtkScrolledWindow: it creates the
335 GtkDwScrolledFrame and the GtkDwViewport, connects some signals, and
336 provides some wrappers for using the GtkDwViewport.
337
338
339 GtkDwScrolledFrame
340 ------------------
341 General purpose scrolled widget containing another scrolled widget,
342 adding a border and a focus frame. Furthermore, it processes key
343 presses and mouse drags (button 2, as in Gimp) to move the viewport.
344
345 There are two signals (except "set_scroll_adjustments"),
346 "user_hchanged" and "user_vchanged", which are emitted when the user
347 changed the viewport adjustments horizontally/vertically by using the
348 keys or button 2 dragging.
349
350
351 GtkDwViewport
352 -------------
353 The interface between Gtk+ and Dw. It is responsible for displaying
354 Dw Widgets and processing their events. It is derived from GtkLayout,
355 to make embedding Gtk+ widgets into Dw widgets simpler, see the
356 documentation of GtkLayout in the Gtk+ tutorial for details.
357
358 GtkDwViewport contains at most one top-level Dw Widget, if it exists.
359 The Gtk+ methods of GtkDwViewport are more or less mapped on the
360 methods of the DwWidget. In detail:
361
362 - Dw_gtk_viewport_size_allocate will call a_Dw_widget_set_width,
363 a_Dw_widget_set_ascent (with allocation->height) and
364 a_Dw_widget_set_descent (with zero as argument), and then allocate
365 the Dw widget at the size returned by a_Dw_widget_size_request.
366
367 - Dw_gtk_viewport_draw and Dw_gtk_viewport_expose will call
368 a_Dw_widget_draw, which will emit the "draw" signal.
369
370 - Handling of mouse events is mostly done in Dw_widget_mouse_event,
371 see DwWidget.txt for details. Note that the functions return
372 FALSE, if the event was not processed, so that they are delivered
373 to the parent widget(s) of the GtkDwViewport, this scheme e.g.
374 prevents dragging of the viewport (done by GtkScrolledFrame) when
375 pressing mouse button 2 on a link.
376
377 You may call gtk_container_set_border_width for a border around the
378 scrolled area.
+0
-198
doc/DwImage.txt less more
0 Jan 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 =======
4 DwImage
5 =======
6
7 A widget for displaying images and handling image maps.
8
9
10 Image Maps
11 ==========
12
13 Client Side Image Maps
14 ----------------------
15 You must first create a list of image maps: Allocate a DwImageMapList,
16 and initialize it by calling a_Dw_image_map_list_init. Adding a map is
17 done by a_Dw_image_map_list_add_map. a_Dw_image_map_list_add_shape
18 adds a shape to the last map. For the meaning of the link argument,
19 see Section "Signals".
20
21 Image maps are referred by a URL (not only by a name). But currently,
22 the image map list is stored in DilloHtmlLB and there is no
23 possibility to parse documents without rendering, so images can only
24 use maps defined in the same document.
25
26 To use a map in an image, call a_Dw_image_set_usemap with the image,
27 the map list, and the URL of the map. Passing the whole list makes it
28 possible to use maps parsed after the image is created.
29
30
31 Server Side Image Maps
32 ----------------------
33 To use images for server side image maps, you must call
34 a_Dw_image_set_ismap and the style must contain a valid link
35 element. See section "Signals" for more details.
36
37
38 Signals
39 =======
40
41 There are five signals, which can be connected to process actions with
42 links. All have at least three arguments:
43
44 - link is the link element of the DwStyle (server side image maps)
45 or DwImageMapShape (client side image maps). The value is an
46 integer, which is currently only used for hypertext links. But
47 generally, it depends on the signal callback how this value is
48 used.
49
50 - x and y are, when server side image maps are used, the relative
51 coordinates of the mouse pointer, otherwise always -1.
52
53 Note that, unlike by DwPage before, no cursors are set. Instead, the
54 signal callback function has to do this.
55
56 The signals:
57
58 - void link_entered (DwImage *image,
59 gint link, gint x, gint y)
60
61 Emitted when the link the mouse pointer is over has
62 changed. "Changed" includes link, x and y, so this signal is also
63 emitted each time the pointer is moved within a server side image
64 map. If the pointer is outside of a link, all arguments have the
65 value -1.
66
67
68 - void link_pressed (DwImage *image,
69 gint link, gint x, gint y,
70 GdkEventButton *event)
71
72 Emitted when the user presses a mouse button _inside_ a link,
73 this signal is never emitted with link = -1. You can use the
74 event to get information about the button, shift keys, etc.
75
76
77 - void link_released (DwImage *image,
78 gint link, gint x, gint y,
79 GdkEventButton *event)
80
81 - void link_clicked (DwImage *image,
82 gint link, gint x, gint y,
83 GdkEventButton *event)
84
85 Analogue to link_pressed.
86
87 - void void (*image_pressed) (DwImage *page,
88 GdkEventButton *event)
89
90 Emitted when the user presses the mouse button on an image which
91 has no related map. In some cases, it may be neccessary to
92 suppress event processing by a_Dw_widget_set_button_sensitive().
93
94
95 Future Extentions
96 =================
97
98 These are some ideas for a different design, which will solve several
99 problems (image transparency, memory usage when implementing a global
100 size factor):
101
102 1. Instead of a guchar array, a new data type, DilloImageBuffer,
103 should be used (DICacheEntry::ImageBuffer and DwImage::buffer). Any
104 access is done by function calls. Copying the lines (in
105 a_Image_write) is done by a_Image_buffer_copy_line, etc. The call to
106 Image_line becomes obsolete, since DilloImageBuffer will deal with
107 different types: indexed, RGB, gray, RGBA, gray-alpha(?). This may
108 be useful for a more efficient implementation of DilloImageBuffer.
109
110 2. The modules Png, Jpeg, Gif, Image and DICache deal with the
111 original image size (read by the decoders), while DwImage gets the
112 "viewed" size (original size, multiplied by the global image
113 scaling factor) from DilloImageBuffer.
114
115 3. For DwImage, there are further methods which replace the current
116 direct access. Note to worth:
117
118 - Scaled buffers are shared, reference counters are used. Copying
119 rows will automatically also affect the related scaled buffers.
120
121 - There are two methods for drawing, one called after expose events
122 (Dw_image_draw) and another called by a_Dw_image_draw_row. The
123 exact rules, how the buffer is scaled (this can, e.g., differ by a
124 pixel or so), is hidden by DilloImageBuffer.
125
126 - As noted above, all DwImage code is based on the viewed size.
127
128 4. The global image scaling factor is used in two places. The HTML
129 parser must apply it on the WIDTH and HEIGHT attributes of the
130 <IMG> tag and DilloImageBuffer must use it to scale the inherent
131 size of an image. There are two modes, which the user can switch
132 by a dillorc option:
133
134 (i) The original image data is preserved, and an additional scaled
135 buffer is created:
136
137 +-------+ +------------------+ additional
138 | Image | -- copy --> | DilloImageBuffer | --> scaled
139 +-------+ rows | (original size) | buffers
140 +------------------+
141 | ^
142 | |
143 scale requests
144 each row for scaled
145 | buffers
146 | |
147 v |
148 +------------------+
149 | DilloImageBuffer |
150 | (viewed size) |
151 +------------------+
152 ^
153 |
154 +---------+
155 | DwImage |
156 +---------+
157
158 (ii) The original data gets lost just after scaling:
159
160 +-------+ +------------------+
161 | Image | -- copy --> | DilloImageBuffer | --> scaled
162 +-------+ rows | (viewed size) | buffers
163 +------------------+
164 ^
165 |
166 +---------+
167 | DwImage |
168 +---------+
169
170 (ii) uses generally less memory, while in some cases leads to a
171 lower rendering quality, as in this example:
172
173 "foo.png" has a size of 100x100 pixels, the image scaling factor is
174 50%, and the following HTML sniplet is rendered:
175
176 <img src="foo.png">
177 <img src="foo.png" width=200 height=200>
178
179 The first image is displayed at a size of 50x50, the second at
180 100x100. (i) will, for the second, use the original buffer, but
181 (ii) will enlarge the buffer, which was scaled down before, so
182 resulting in a "pixelized" image.
183
184 5. Any implementation of DilloImageBuffer will handle transparency
185 independent of the background, i.e., the background colour will not
186 be used anymore. The first implementation may be based on GdkRGB
187 and (when needed) dithered clipping bitmaps. Better implementations
188 may then be developed in the future.
189
190 6. Background images: The modules Image and DICache do no longer
191 access the DwImage directly, all calls are replaced by an
192 Interface, which is then implemented by DwImage and an appropriate
193 structure for background images (part of DwStyle). The interface is
194 in C realized by a struct of function pointers, and a generic
195 pointer on DwImage, etc.
196
197 7. If someone really needs it, animated GIF's may be considered.
+0
-152
doc/DwPage.txt less more
0 Nov 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 ======
4 DwPage
5 ======
6
7 A widget for displaying texts. It is (currently) the main widget for
8 rendering HTML documents.
9
10
11 Signals
12 =======
13
14 DwPage defines the same signals as DwImage, except "image_pressed",
15 with the exception that the coordinates are always -1. See
16 DwImage.txt for more details.
17
18
19 Collapsing Spaces
20 =================
21
22 The idea behind this is that every text box has a specific vertical
23 space around and that they are combined to one space, according to
24 rules stated below. A rule is either a paragraph within a DwPage
25 widget, or a DwPage within a DwPage widget, in a single line; the
26 latter is used for indented boxes and list items.
27
28 The rules:
29
30 1. If a box is following another, the space between them is the
31 maximum of both box spaces:
32
33 +---------+
34 |/////////|
35 |/////////| +---------+
36 +---------+ |/////////|
37 | A | |/////////|
38 +---------+ +---------+
39 |/////////| | A |
40 |/////////| +---------+
41 +---------+ are combined like this: |/////////|
42 |XXXXXXXXX|
43 +---------+ +---------+
44 |\\\\\\\\\| | B |
45 +---------+ +---------+
46 | B | |\\\\\\\\\|
47 +---------+ +---------+
48 |\\\\\\\\\|
49 +---------+
50
51 2. a) If one box is the first box within another, the upper space
52 of these boxes collapse. b) The analogue is the case for the
53 last box:
54
55 +---------+ If B and C are put into A,
56 |/////////| the result is:
57 |/////////|
58 +---------+ +---------+ +---------+
59 | A | <--+-- |\\\\\\\\\| |/////////|
60 +---------+ ¦ +---------+ |XXXXXXXXX|
61 |/////////| | | B | +---------+
62 |/////////| | +---------+ | B |
63 +---------+ | |\\\\\\\\\| +---------+
64 | +---------+ |\\\\\\\\\|
65 | |\\\\\\\\\|
66 | +---------+ |\\\\\\\\\|
67 `-- |\\\\\\\\\| +---------+
68 |\\\\\\\\\| | C |
69 |\\\\\\\\\| +---------+
70 +---------+ |\\\\\\\\\|
71 | C | |XXXXXXXXX|
72 +---------+ |XXXXXXXXX|
73 |\\\\\\\\\| +---------+
74 |\\\\\\\\\|
75 |\\\\\\\\\|
76 +---------+
77
78 For achieving this, there are some features of DwPage:
79
80 - Consequent breaks are automatically combined, according to
81 rule 1. See the code of a_Dw_page_add_break for details.
82
83 - If a break is added as the first word of the DwPage within
84 another DwPage, collapsing according to rule 2a is done
85 automatically. See the code of a_Dw_page_add_break.
86
87 - To collapse spaces according to rule 2b,
88 a_Dw_page_hand_over_break must be called for the *inner*
89 widget. The HTML parser does this in Html_eventually_pop_dw.
90
91
92 Collapsing Margins
93 ==================
94
95 Collapsing margins, as defined in the CSS2 specification, are,
96 supported in addition to collapsing spaces. Also, spaces and margins
97 collapse themselves. I.e., the space between two paragraphs is the
98 maximum of the space calculated as described in "Collapsing Spaces"
99 and the space calculated according to the rules for collapsing margins.
100
101 (This is an intermediate hybrid state, collapsing spaces are used in
102 the current version of dillo, while I implemented collapsing margins
103 for CSS and integrated it already into the main trunk. For a pure
104 CSS-based dillo, collapsing spaces will not be needed anymore, and may
105 be removed for simplicity.)
106
107
108 Some Internals
109 ==============
110
111 There are two lists, words and lines. The word list is quite static;
112 only new words may be added. A word is either text, a widget, a break
113 or an anchor. Anchors are stored in the text, because it may be
114 necessary to correct the scroller positions at rewrapping.
115
116 Lines refer to the word list (first and last), they are completely
117 redundant, i.e., they can be rebuilt from the words. Lines can be
118 rewrapped either completely or partially (see "Incremental Resizing"
119 below). For the latter purpose, several values are accumulated in the
120 lines. See the file "dw_page.h" for details.
121
122
123 Incremental Resizing
124 --------------------
125 DwPage makes use of incremental resizing as described in Dw.txt,
126 section "Resizing". The parent_ref is, for children of a DwPage,
127 simply the number of the line.
128
129 Generally, there are three cases which may change the size of the
130 widget:
131
132 1. The available size of the widget has changed, e.g., because the
133 user has changed the size of the browser window. In this case,
134 it is necessary to rewrap all the lines.
135
136 2. A child widget has changed its size. In this case, only a rewrap
137 down from the line where this widget is located is necessary.
138
139 (This case is very important for tables. Tables are quite at the
140 bottom, so that a partial rewrap is relevant. Otherwise, tables
141 change their size quite often, so that this is necessary for a
142 fast, non-blocking rendering)
143
144 3. A word (or widget, break etc.) is added to the page. This makes
145 it possible to reuse the old size by simply adjusting the
146 current width and height, so no rewrapping is necessary.
147
148 The state of the size calculation is stored in wrap_ref within DwPage,
149 which has the value -1 if no rewrapping of lines necessary, or
150 otherwise the line from which a rewrap is necessary.
151
+0
-310
doc/DwStyle.txt less more
0 Apr 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 =======
4 DwStyle
5 =======
6
7 Styles of Dillo Widgets
8
9
10 Note
11 ====
12
13 DwStyle has derived from DwPageAttr, and its current structure is very
14 similar to it. In the future, there will be some changes and extensions.
15 Namely:
16
17 - image maps will be handled differently (done),
18 - margins, borders, paddings (done),
19 - background colors/images, and
20 - cursors and tooltips will perhaps move into DwStyle.
21
22 Furthermore, widgets will probably refer to different styles for
23 different states.
24
25
26 Overview
27 ========
28
29 DwStyle provides some resources and attributes for drawing widgets, as
30 well as for parts of a widget (e.g., DwPage uses DwStyle's for its
31 words). Creating a style is done by filling a DwStyle with the
32 attributes (except the ref_count), and calling Dw_style_new:
33
34 DwStyle style_attrs, *style;
35
36 style_attrs.foo = bar;
37 // etc.
38 style = a_Dw_style_new (&style_attrs, random_window);
39 // do something with style
40
41 After this, the attributes of style should not be changed anymore,
42 since styles are often shared between different widgets etc. (see
43 below). Most times, you simply copy the attributes of another style
44 and modify them:
45
46 style_attrs = *another_style;
47 style_attrs.foo = bar;
48 style = a_Dw_style_new (&style_attrs, random_window);
49
50 The font structure can be created by Dw_style_font_new, in a similar
51 way (the GdkFont in font_attrs will be ignored), and colors by
52 Dw_style_color_new, passing 0xrrggbb as an argument. Note that fonts
53 and colors are only intended to be used in conjunction with DwStyle.
54
55
56 Lengths and Percentages
57 =======================
58
59 DwStyleLength is a simple data type for lengths and percentages:
60
61 - A length refers to an absolute measurement. It is used to
62 represent the HTML type %Pixels; and the CSS type <length>.
63
64 For CSS lenghts, there are two units: (i) pixels and absolute
65 units, which have to be converted to pixels (a pixel is, unlike
66 in the CSS specification, treated as absolute unit), and (ii) the
67 relative units "em" and "ex" (see below).
68
69 - A percentage refers to a value relative to another value. It is
70 used for the HTML type %Length; (except %Pixels;), and the CSS
71 type <percentage>.
72
73 - A relative length can be used in lists of HTML MultiLengths.
74
75 Since many values in CSS may be either lengths or percentages, a
76 single type is very useful.
77
78 Useful macros and functions
79 ---------------------------
80 Macros for creating lengths:
81
82 DW_STYLE_CREATE_LENGTH (n) Returns a length of n pixels.
83
84 DW_STYLE_CREATE_EX_LENGTH (n) Returns a length of n times the
85 'x-height'
86
87 DW_STYLE_CREATE_EM_LENGTH (n) Returns a length of n times the
88 'font-size'
89
90 DW_STYLE_CREATE_PERCENTAGE (n) Returns a percentage, n is relative
91 to 1, not to 100.
92
93 DW_STYLE_CREATE_RELATIVE (n) Returns a relative length.
94
95 DW_STYLE_UNDEF_LENGTH Used to indicate unspecified sizes,
96 errors, and the end of a list of
97 lengths.
98
99 Furthermore, there are some functions in html.c:
100
101 DwStyleLength Html_parse_length (gchar *attr);
102
103 Returns a length or a percentage, or DW_STYLE_UNDEF_LENGTH in
104 case of an error.
105
106 DwStyleLength* Html_parse_multi_length (gchar *attr);
107
108 Returns a vector of lengths/percentages. The caller has to free
109 the result when it is not longer used.
110
111 Macros for examining lengths:
112
113 DW_STYLE_IS_LENGTH (l) Returns TRUE if l is a length.
114
115 DW_STYLE_IS_PERCENTAGE (l) Returns TRUE if l is a percentage.
116
117 DW_STYLE_IS_RELATIVE (l) Returns TRUE if l is a relative
118 length.
119
120 DW_STYLE_GET_LENGTH (l, f) Returns the value of a length in
121 pixels, as an integer. f is the
122 font, this is used if l is based on
123 font sizes.
124
125 DW_STYLE_GET_PERCENTAGE (l) Returns the value of a percentage,
126 relative to 1, as a float.
127
128 DW_STYLE_GET_RELATIVE (l) Returns the value of a relative
129 length, as a float.
130
131
132 Representation
133 --------------
134 Notes:
135
136 1. This is not part of the interface and may change! Use the
137 macros described above.
138 2. Negative numbers may not work yet.
139
140 DwStyleLength is represented by an integer (n is the number of bits of
141 an integer):
142
143 - Undefined lengths are represented by 0.
144
145 - Lenghts in pixel:
146
147 +---+ - - - +---+---+---+---+
148 | int value | 0 | 1 |
149 +---+ - - - +---+---+---+---+
150 n-1 3 2 1 0
151
152 - Lengths in in x-height:
153
154 +---+ - - - +---+---+---+---+
155 | real value | 0 | 1 | 1 |
156 +---+ - - - +---+---+---+---+
157 n-1 3 2 1 0
158
159 - Lengths in in font-size:
160
161 +---+ - - - +---+---+---+---+
162 | real value | 1 | 1 | 1 |
163 +---+ - - - +---+---+---+---+
164 n-1 3 2 1 0
165
166 - Percentages:
167
168 +---+ - - - +---+---+---+---+
169 | real value | 0 | 1 | 0 |
170 +---+ - - - +---+---+---+---+
171 n-1 3 2 1 0
172
173 - Relative lengths:
174
175 +---+ - - - +---+---+---+---+
176 | real value | 1 | 1 | 0 |
177 +---+ - - - +---+---+---+---+
178 n-1 3 2 1 0
179
180 A "real value" is a fixed point number consisting of (m is the number
181 of bits of the value, not the whole integer):
182
183 +---+ - - - +---+---+ - - - +---+
184 | integer part | rest |
185 +---+ - - - +---+---+ - - - +---+
186 m 16 15 0
187
188 For *internal* use, there are two converting macros,
189 DW_STYLE_REAL_TO_FLOAT and DW_STYLE_FLOAT_TO_REAL.
190
191
192 DwStyle Boxes
193 =============
194
195 The CSS Box Model
196 -----------------
197 For borders, margins etc., DwStyle uses the box model defined by
198 CSS2. DwStyle contains some members defining these attributes. A
199 widget must use these values for any calculation of sizes. There are
200 some helper functions (see dw_style.h). A DwStyle box looks quite
201 similar to a CSS box:
202
203
204 ,-- margin.left
205 | ,-- border.left
206 | | ,-- padding.left
207 |---+---+---|
208 +---------------------------------------+ ---
209 | | | margin.top
210 | +-------------------------------+ | -+-
211 | | Border | | | border.top
212 | | +-----------------------+ | | -+-
213 | | | Padding | | | | padding.top
214 new widget | | | +---------------+ | | | ---
215 allocation -->| | | | | | | |
216 | | | | Content | | | |
217 former widget ------------>| | | | |
218 allocation | | | +---------------+ | | | ---
219 | | | | | | | margin.bottom
220 | | +-----------------------+ | | -+-
221 | | | | | border.bottom
222 | +-------------------------------+ | -+-
223 | | | padding.bottom
224 +---------------------------------------+ ---
225 |---+---+---|
226 padding.right --' | |
227 border.right --' |
228 margin.right --'
229
230 Background colors
231 -----------------
232 The background color is stored in style->background_color, which be
233 NULL (the background color of the parent widget is shining through).
234
235 For toplevel widgets, this color is set as the background color of the
236 viewport, for other widgets, a filled rectangle is drawn, covering the
237 content and padding. (This is compliant with CSS2, the background
238 color of the toplevel element covers the whole canvas.)
239
240 Drawing
241 -------
242 There is a new function Dw_widget_draw_widget_box, which should be
243 called at the beginning of Dw_foo_draw. For parts referring to styles
244 (e.g., words in a page), Dw_widget_draw_box should be used.
245
246
247 Notes on Memory Management
248 ==========================
249
250 Memory management is done by reference counting, a_Dw_style_new
251 returns a pointer to DwStyle with an increased reference counter, so
252 you should care about calling Dw_style_unref if it is not used
253 anymore. You do *not* need to care about the reference counters of
254 fonts and styles.
255
256 In detail:
257
258 - a_Dw_style_ref is called in
259
260 * a_Dw_widget_set_style, to assign a style to a widget,
261
262 * a_Dw_page_add_text, a_Dw_page_add_widget,
263 a_Dw_page_add_anchor, to assign a style to a word,
264
265 * and Html_push_tag (often the reference counter is again
266 decreased shortly after this).
267
268 - a_Dw_unref_style is called in:
269
270 * Dw_page_destroy, Dw_widget_destroy, Html_cleanup_tag,
271 Html_pop_tag, Html_close,
272
273 * a_Dw_widget_set_style, Html_set_top_font (and several
274 Html_tag_open_... functions), these functions overwrite an
275 existing style.
276
277
278 HTML Stack
279 ==========
280
281 (This is not DwStyle specific, but may be useful if you are working on
282 the HTML parser.)
283
284 The topmost element of the HTML stack contains a (reference to a)
285 style which will be used to add the text to the current page. If you
286 use a style only for a short while (see Html_tag_open_frame for an
287 example), you may use it this way:
288
289 style_attrs = *html->stack[html->stack_top].style;
290 style_attrs.foo = bar;
291 style = a_Dw_style_new (&style_attrs, random_window);
292
293 Do not forget to unref it afterwards. A good choice for random_window
294 is html->bw->main_window->window.
295
296 In many cases, you want to set the style for the content of an element
297 (e.g., <A>). Then you must store it in the stack:
298
299 DwStyle style_attrs, *old_style;
300
301 old_style = html->stack[html->stack_top].style;
302 style_attrs = *old_style;
303 style_attrs.foo = bar;
304 html->stack[html->stack_top].style =
305 a_Dw_style_new (&style_attrs, random_window);
306 a_Dw_style_unref (old_style);
307
308 The macro HTML_SET_TOP_ATTR can be used for single attributes, for
309 changing more attributes, this code should be copied for efficiency.
+0
-205
doc/DwTable.txt less more
0 May 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 =======
4 DwTable
5 =======
6
7 A container widget for rendering tables.
8
9
10 The DwTable Widget
11 ==================
12
13 DwTable is a container widget for rendering tables. It aligns other
14 DwWidgets (normally DwPage), according to the following rules:
15
16 1. All columns have have the same width W, except:
17
18 - W is less than the minimal column width, or
19 - W is greater than the maximal column width.
20
21 Furthermore, W is
22
23 - less than all minimal widths of columns not having W as
24 width, and
25 - greater than all maximal widths of columns not having W as
26 width.
27
28 2. The table tries to use exactly the whole available width, except
29 if it is not possible, because the it is less/greater than the
30 minimal/maximal table width.
31
32 This is simple to implement for columns with COLSPAN == 1, using
33 a_Dw_get_extremes for getting the minimal and maximal widths. For
34 arbitrary COLSPAN values, an approach described in "Subtables" is
35 used to get optimal results (as described above) in most cases, while
36 the rendering remains fast.
37
38
39 Subtables
40 =========
41
42 A table is divided into subtables, which do not (in most cases) share
43 spanning cells, until single columns are left. Cells spanning the
44 whole width are removed before dividing further. Example:
45
46 +---+-------+---+
47 | A | B | C |
48 +---+-------+---+
49 | D | E |
50 +---+-------+---+
51 | F | G | H |
52 +---+-------+---+
53 ' ' ` `
54 ' ' ` `
55 +---+-------+ +---+
56 | A | B | | C |
57 +---+-------+ +---+
58 removed --> | D | | E |
59 +---+-------+ +---+
60 | F | G | | H |
61 +---+-------+ +---+
62 ' ' ` ` final
63 ' ' ` `
64 +---+ +-------+
65 | A | | B | <-.
66 +---+ +-------+ >- removed
67 | F | | G | <-'
68 +---+ +-------+
69 final ' ' ` `
70 ' ' ` `
71 [empty] [empty]
72 final final
73
74 There is a structure, DwTableSub, for holding all the information. It
75 is rebuilt when new cells are added. Do not confuse this with nested
76 tables, these are represented by the Dw widget hierarchy.
77
78 If table cells overlap horizontally, they are (virtually) divided. The
79 minimal and maximal widths are apportioned to the other columns
80 (resulting in a non optimal layout):
81
82 +-------+---+---+
83 | A | B | C |
84 +---+---+---+---+
85 | D | E |
86 +---+-----------+
87 ' ' ` `
88 ' ' ` `
89 +-------+ +---+---+
90 | A | | B | C |
91 +---+---+ +---+---+
92 | D |1/3| | 2/3 E |
93 | | E | | |
94 +---+---+ +-------+
95
96 Example for a non-optimal case
97 ------------------------------
98 The HTML document fragment
99
100 <table>
101 <tr>
102 <td colspan="2">Text
103 <td>LongText
104 <tr>
105 <td>Text
106 <td colspan="2">LongText
107 </table>
108
109 will result in:
110
111 | 0 | 1 | 2 |
112
113 +------------+----------+
114 | Text | LongText |
115 +------+-----+----------+
116 | Text | LongText |
117 +------+----------------+
118
119 The width of column 1 is determined by the half of the minimal width
120 of the LongText. An optimal rendering would be something like:
121
122 ,- 1
123 | 0 || 2 |
124
125 +-------+----------+
126 | Text | LongText |
127 +------++----------+
128 | Text | LongText |
129 +------+-----------+
130
131
132 Algorithms
133 ==========
134
135 Calculating extremes
136 --------------------
137 The extremes of all subtables are calculated by
138 Dw_table_sub_get_extremes and stored in DwTableSub:
139
140 minimal/maximal width (sub table) =
141 - for single column: maximum of all minimal/maximal widths
142 - otherwise: maximum of
143 1. all minimal/maximal widths of cells spanning
144 the whole width, and
145 2. the sum of the minimal/maximal widths of the
146 sub-subtables
147
148 In step 1, the width argument is used to adjust the maximum
149 and minimum width of the whole subtable and mark it as fixed.
150
151 todo: describe percentages.
152
153 Calculating column widths
154 -------------------------
155 The calculation is based on a fixed width, which is, at the top, the
156 width set by a_Dw_widget_set_width. This is corrected by the minimal and
157 maximal width of the whole table, and, if given, the width attribute
158 of the table. At each level, the available width is always between the
159 minimal and the maximal width of the subtable.
160
161 For single columns, the width is the passed fixed width. Otherwise:
162
163 1. Calculate relative widths, they effect the minimal and maximal
164 widths. (Temporally, not permanently!)
165
166 2. The sum of these corrected minima may be greater as the fixed
167 width of the subtable. In this case, decrease them again to
168 match exactly the fixed width, then use them as sub-subtable
169 fixed widths and finish. Otherwise, continue:
170
171 3. If the extremes of the spanning widths of the subtable are
172 greater than the sum of sub-subtables extremes, adjust the
173 extremes of sub-subtables which are not fixed, i.e., where no
174 width argument (either percentage or fixed) freezes the width.
175
176 4. Use an iteration on the subtables, to determine the column
177 widths, see Dw_table_sub_calc_col_widths for details.
178
179 5. After this, apply this recursively on all subtables and pass the
180 subtable width as fixed width.
181
182
183 Borders, Paddings, Spacing
184 ==========================
185
186 Currently, DwTable supports only the separated borders model (see CSS
187 specification). Borders, paddings, spacing is done by creating DwStyle
188 structures with values equivalent to following CSS:
189
190 TABLE {
191 border: outset <table-border>;
192 border-collapse: separate;
193 border-spacing: <table-cellspacing>
194 background-color: <table-bgcolor>
195 }
196
197 TD TH {
198 border: inset <table-border>;
199 padding: <table-cellspacing>
200 background-color: <td/th-bgcolor>
201 }
202
203 Here, <foo-bar> refers to the attribute bar of the tag foo. See
204 Html_open_table and Html_open_table_cell for more details.
+0
-339
doc/DwWidget.txt less more
0 Jan 2001, S.Geerken@ping.de
1 Last update: Dec 2004
2
3 ========
4 DwWidget
5 ========
6
7 The base object for all Dw widgets.
8
9
10 Structures
11 ==========
12
13 DwRectangle
14 -----------
15 A replacement for GdkRectangle, the only difference is the use of 32
16 instead of 16 bit integers.
17
18
19 DwAllocation, DwRequisition
20 ---------------------------
21 Similar to GtkAllocation and GtkRequisition. Instead of a height, you
22 have two members, ascent and descent.
23
24
25 DwExtremes
26 ----------
27 A structure containing the minimal and maximal width of a widget. See
28 get_extremes below for details.
29
30
31 DwWidget
32 --------
33 Some members you may use:
34
35 parent The parent widget. NULL for toplevel widgets.
36
37 flags See below.
38
39 style The style attached to the widget, this must
40 always be defined, but is not created
41 automatically. Instead, this has to be done
42 immediately after creating the widget
43 (e.g., in a_Web_dispatch_by_type). This style
44 contains attributes and resources for general
45 drawing. See DwStyle.txt for details.
46
47 These members should only be used within the "core" (GtkDw*, DwWidget
48 and DwEmbedGtk):
49
50 viewport The viewport containing the widget. Defined
51 for all widgets.
52
53 allocation,
54 requisition,
55 extremes,
56 user_requisition These are used to optimize several wrappers,
57 see below.
58
59 anchors_table See notes on achors.
60
61 Flags
62 -----
63 Flags can be set and unset with DW_WIDGET_SET_FLAGS and
64 DW_WIDGET_UNSET_FLAGS. For reading flags use the macros DW_WIDGET_...
65
66 DW_NEEDS_RESIZE
67 DW_EXTREMES_CHANGED Denotes that the widget must be resized. Used
68 only internally. See Dw.txt and the source
69 for more details.
70
71 DW_NEEDS_ALLOCATE Set to overide the optimation in
72 a_Dw_widget_size_allocate. Used only
73 internally.
74
75 DW_REALIZED Set when the widget is realized. Should be
76 used to prevent crashes in certain
77 situations.
78
79 Following flags describe characteristics of widgets and are typically
80 set in the init function:
81
82 DW_USES_HINTS A widget with this flag set has a complex way
83 to deal with sizes, and should
84
85 - implement the functions set_width,
86 set_ascent, set_descent, and
87 get_extremes, and
88 - deal completely with width and height
89 in widget->style.
90
91 Examples are DwPage and DwTable. Other
92 widgets, like DwImage and DwHRuler, are much
93 simpler and don't have to set this flag. For
94 these widgets, much of the size calculation
95 is done by the parent widget.
96
97 This flag is unset by default.
98
99 DW_HAS_CONTENT If this flag is set, more space is reserved
100 for the widget in some circumstances. E.g.,
101 if an image has a width of 100%, it makes
102 sense to use more space within a table cell,
103 as compared to a horizontal ruler, which does
104 not "have a content".
105
106 This flag is set by default.
107
108
109 Signal Prototypes
110 =================
111
112 - void size_request (DwWidget *widget,
113 DwRequisition *requisition);
114
115 Similar to Gtk.
116
117 void get_extremes (DwWidget *widget,
118 DwExtremes *extremes);
119
120 Return the maximal and minimal width of the widget, equivalent
121 to the requisition width after calling set_width with zero and
122 infinitive, respectively. This is important for fast table
123 rendering. Simple widgets do not have to implement this; the
124 default is the requisition width for both values.
125
126 - void size_allocate (DwWidget *widget,
127 DwAllocation *allocation);
128
129 Similar in Gtk. Note: allocation has world coordinates.
130
131 - void set_width (DwWidget *widget,
132 guint32 width);
133
134 - void set_height (DwWidget *widget,
135 guint32 height);
136
137 These are hints by the caller, which *may* influence the size
138 returned by size_request. The implementation should call
139 Dw_widget_queue_resize if necessary. In most cases, these
140 signals do not have to be implemented. Currently, only the
141 DwPage widget uses this to determine the width for rewrapping
142 text (note that the resulting width returned by
143 Dw_page_size_request may be _bigger_) and relative sizes of the
144 children.
145
146 - void draw (DwWidget *widget,
147 DwRectangle *area,
148 GdkEventExpose *event);
149
150 Draw the widget's content in the specified area. It may either
151 be caused by an expose event, or by an internal drawing request
152 (e.g., followed by resizing of widgets). In the first case, you
153 get the *original* expose event as third argument. In the
154 latter, event is NULL. The area argument has widget
155 coordinates. A DwContainer is responsible for drawing its
156 children.
157
158 (Since DwWidget's are always windowless, there was no need for
159 two signals, "draw" and "expose_event".)
160
161 - void realize (DwWidget *widget);
162
163 Create all necessary X resources. Called when either the
164 viewport (top-level widgets) or, respectively, the parent Dw
165 widget is realized, or an widget is added to an already
166 realized Dw widget/viewport.
167
168 - void unrealize (DwWidget *widget);
169
170 Remove created X resources.
171
172 - gint button_press_event (DwWidget *widget,
173 guint32 x,
174 guint32 y,
175 GdkEventButton *event);
176
177 This signal is emitted when the user presses a mouse button in
178 a DwWidget. x and y are the coordinates relative to the origin
179 of the widget, event is the *original* event, which may, e.g.,
180 be used to determine the number of the pressed button, the state
181 of the shift keys, etc. The implementation of this signal
182 should return TRUE, if the event has been processed, otherwise
183 FALSE.
184
185 A DwContainer is *not* responsible for delivering button press
186 events to its children. Instead, Dw first emits the
187 button_press_event signal for the most inner widgets and
188 continues this for the parents, until TRUE is returned.
189
190 - gint button_release_event (DwWidget *widget,
191 guint32 x,
192 guint32 y,
193 GdkEventButton *event);
194
195 Compare button_press_event.
196
197 - gint motion_notify_event (DwWidget *widget,
198 guint32 x,
199 guint32 y,
200 GdkEventMotion *event);
201
202 Compare button_press_event. event may be NULL when the call was
203 caused by something different than a "real" motion notify event.
204 E.g., either when widgets are moved (not yet implemented), or the
205 viewport.
206
207 - gint enter_notify_event (DwWidget *widget,
208 DwWidget *last_widget,
209 GdkEventMotion *event);
210
211 These "events" are simulated based on motion nofify events.
212 event is the *original* event (may also be NULL in some cases).
213 last_widget is the widget in which the pointer was in before.
214
215 - gint leave_notify_event (DwWidget *widget,
216 DwWidget *next_widget,
217 GdkEventMotion *event);
218
219 Compare enter_notify_event. next_widget is the widget the
220 pointer is now in.
221
222
223 Useful Internal Functions and Macros
224 ====================================
225
226 - gint Dw_widget_intersect (DwWidget *widget,
227 DwRectangle *area,
228 DwRectangle *intersection);
229
230 Calculates the intersection of widget->allocation and area,
231 returned in intersection (in widget coordinates!). Typically
232 used by containers when drawing their children. Returns whether
233 intersection is not empty.
234
235 - gint32 Dw_widget_x_viewport_to_world (DwWidget *widget,
236 gint16 viewport_x);
237
238 - gint32 Dw_widget_y_viewport_to_world (DwWidget *widget,
239 gint16 viewport_y);
240
241 - gint16 Dw_widget_x_world_to_viewport (DwWidget *widget,
242 gint32 world_x);
243
244 - gint16 Dw_widget_y_world_to_viewport (DwWidget *widget,
245 gint32 world_y);
246
247 These functions convert between world and viewport coordinates.
248
249 - void Dw_widget_queue_draw (DwWidget *widget);
250
251 - void Dw_widget_queue_draw_area (DwWidget *widget,
252 gint32 x,
253 gint32 y,
254 guint32 width,
255 guint32 height);
256
257 - void Dw_widget_queue_clear (DwWidget *widget);
258
259 - void Dw_widget_queue_clear_area (DwWidget *widget,
260 gint32 x,
261 gint32 y,
262 guint32 width,
263 guint32 height);
264
265 Equivalents to the Gtk+ functions. They (currently) result in a
266 call of gtk_widget_xxx_area with the viewport as first
267 argument. x and y are widget coordinates.
268
269 - void Dw_widget_queue_resize (DwWidget *widget,
270 gint ref,
271 gboolean extremes_changed);
272
273 Similar to gtk_widget_queue_resize. Call this function when the
274 widget has changed its size. The next call to
275 Dw_xxx_size_request should then return the new size.
276
277 See Dw.txt for explanation on how to use the ref argument,
278 extremes_changed specifies whether the extremes have changed
279 (the latter is often not the case for an implementations of
280 set_{width|ascent|descent}).
281
282 - void Dw_widget_set_anchor (DwWidget *widget,
283 gchar *name,
284 int pos);
285
286 Add an anchor to a widget. The name will not be copied, it has
287 to be stored elsewhere (DwPage e.g. stores it in the DwPageWord
288 structure).
289
290 - void a_Dw_widget_set_cursor (DwWidget *widget,
291 GdkCursor *cursor)
292
293 Set the cursor for a DwWidget. cursor has to be stored
294 elsewhere, it is not copied (and not destroyed). If cursor is
295 NULL, the cursor of the parent widget is used.
296
297 (This will probably be changed in the future and replaced by a
298 common style mechanism.)
299
300 - DW_WIDGET_WINDOW (widget)
301
302 Returns the window a widget should draw into.
303
304
305 External Functions
306 ==================
307
308 - void a_Dw_widget_set_usize (DwWidget *widget,
309 guint32 width,
310 guint32 ascent,
311 guint32 descent);
312
313 Override the "desired" size of a widget. Further calls of
314 a_Dw_widget_request_size will return these values, except those
315 specified as -1. A possible use shows Html_add_widget in
316 html.c.
317
318 (This will probably be removed. Instead DwStyle should be used.)
319
320
321 - void a_Dw_widget_set_bg_color (DwWidget *widget,
322 gint32 color);
323
324 Set the background color of a widget. This works currently only
325 for the top-level widget. In this case, the background color of
326 the GtkDwViewport is changed. In future, background colors for
327 all widgets will be needed, e.g., for table cells (will be
328 DwPage's), this will (probably) be based on filled rectangles.
329
330 - void a_Dw_widget_scroll_to (DwWidget *widget,
331 int pos)
332
333 Scroll viewport to pos (vertical widget coordinate).
334
335 There are furthermore wrappers for the signals, in some cases they
336 are optimized and/or provide further functionality. In (almost) no
337 case should you emit the signals directly. See dw_widget.c for more
338 details.
00 October 2001, --Jcid
1 Last update: Dec 2004
1 Last update: Jul 2009
22
33 ---------------
44 THE HTML PARSER
1010 behaviour while working:
1111
1212 typedef enum {
13 DILLO_HTML_PARSE_MODE_INIT,
13 DILLO_HTML_PARSE_MODE_INIT = 0,
1414 DILLO_HTML_PARSE_MODE_STASH,
1515 DILLO_HTML_PARSE_MODE_STASH_AND_BODY,
1616 DILLO_HTML_PARSE_MODE_BODY,
2121
2222 The parser works upon a token-grained basis, i.e., the data
2323 stream is parsed into tokens and the parser is fed with them. The
24 process is simple: whenever the cache has new data, it gets
24 process is simple: whenever the cache has new data, it is
2525 passed to Html_write, which groups data into tokens and calls the
26 appropriate functions for the token type (TAG, SPACE or WORD).
26 appropriate functions for the token type (tag, space, or word).
2727
2828 Note: when in DILLO_HTML_PARSE_MODE_VERBATIM, the parser
29 doesn't try to split the data stream into tokens anymore, it
29 doesn't try to split the data stream into tokens anymore; it
3030 simply collects until the closing tag.
3131
3232 ------
6464 As it's a common mistake for human authors to mistype or
6565 forget one of the quote marks of an attribute value; the
6666 parser solves the problem with a look-ahead technique
67 (otherwise the parser could skip significative amounts of
68 well written HTML).
67 (otherwise the parser could skip significant amounts of
68 properly-written HTML).
6969
7070
7171
7272 * WORD --> Html_process_word
7373
74 A word is anything that doesn't start with SPACE, and that's
74 A word is anything that doesn't start with SPACE, that's
7575 outside of a tag, up to the first SPACE or tag start.
7676
7777 SPACE = ' ' | \n | \r | \t | \f | \v
7878
7979
8080 -----------------
81 THE PARSING STACK
81 THE PARSING STACK
8282 -----------------
8383
8484 The parsing state of the document is kept in a stack:
8585
86 struct _DilloHtml {
86 class DilloHtml {
8787 [...]
88 DilloHtmlState *stack;
89 gint stack_top; /* Index to the top of the stack [0 based] */
90 gint stack_max;
88 lout::misc::SimpleVector<DilloHtmlState> *stack;
9189 [...]
9290 };
9391
9492 struct _DilloHtmlState {
95 char *tag;
96 DwStyle *style, *table_cell_style;
97 DilloHtmlParseMode parse_mode;
98 DilloHtmlTableMode table_mode;
99 gint list_level;
100 gint list_number;
101 DwWidget *page, *table;
102 gint32 current_bg_color;
93 CssPropertyList *table_cell_props;
94 DilloHtmlParseMode parse_mode;
95 DilloHtmlTableMode table_mode;
96 bool cell_text_align_set;
97 DilloHtmlListMode list_type;
98 int list_number;
99
100 /* TagInfo index for the tag that's being processed */
101 int tag_idx;
102
103 dw::core::Widget *textblock, *table;
104
105 /* This is used to align list items (especially in enumerated lists) */
106 dw::core::Widget *ref_list_item;
107
108 /* This is used for list items etc; if it is set to TRUE, breaks
109 have to be "handed over" (see Html_add_indented and
110 Html_eventually_pop_dw). */
111 bool hand_over_break;
103112 };
104
105113
106114 Basically, when a TAG is processed, a new state is pushed into
107115 the 'stack' and its 'style' is set to reflect the desired
7272
7373 Closing the TCP connection:
7474 The four packet-sending closing sequence of the TCP protocol.
75
75
7676 In a WAN context, every single item of this list has an
7777 associated delay that is non deterministic and often measured in
7878 seconds. If we add several connections per browsed page (each one
9595 packets that belong to other connections may be arriving,
9696 and have to wait for service.
9797
98 Web browsers handle many small transactions,
98 Web browsers handle many small transactions,
9999 if waiting times are not overlapped
100100 the latency perceived by the user can be very annoying.
101101
218218 what it was doing at DNS-request time. The interesting thing is
219219 that the call-back happens in the main thread, while the child
220220 thread simply exits when done. This is implemented through a
221 server-channel design.
221 server-channel design.
222222
223223
224224 The server channel
251251 and set to non-blocking I/O. When the DNS server has resolved the
252252 name, the socket connection process begins by calling connect(2);
253253 {We use the Unix convention of identifying the manual section
254 where the concept is described, in this case
254 where the concept is described, in this case
255255 section 2 (system calls).}
256256 which returns immediately with an EINPROGRESS error.
257257
310310 ----------------------
311311 Closing the connection
312312 ----------------------
313
313
314314 Closing a TCP connection requires four data segments, not an
315315 impressive amount but twice the round trip time, which can be
316316 substantial. When data retrieval finishes, socket closing is
369369 'ExtData' MAY be provided.
370370
371371 'Status', 'FD' and 'GioCh' are set by I/O engine internal
372 routines.
372 routines.
373373
374374 When there is new data in the file descriptor, 'IO_callback'
375375 gets called (by glib). Only after the I/O engine finishes
382382 The 'Buf' and 'BufSize' fields of the request structure
383383 provide the transfer buffer for each operation. This buffer must
384384 be set by the client (to increase performance by avoiding copying
385 data).
385 data).
386386
387387 On reads, the client specifies the amount and where to place
388388 the retrieved data; on writes, it specifies the amount and source
0 February 2001, --Jcid
0 January 2009, --Jcid
11
22 ------
33 IMAGES
2222 one has the whole data already decoded.
2323
2424 * Regardless of the RGB-data feeding method, the decoded data is
25 passed to the widget (DwImage) and drawn by GdkRGB in a streamed
26 way.
27 Note that INDEXED images are also decoded into RGB format
28 before sending them to GdkRGB.
25 passed to the widget (DwImage) and drawn in a streamed way.
26 Note that INDEXED images are also decoded into RGB format.
27
28 Html_tag_open_img // IMG element processing
29 Html_add_new_image // Read attributes, create image, add to HTML page
30 a_Image_new // Create a 'DilloImage' data structure, to coordinate
31 // decoded image-data transfer to an 'Imgbuf'.
32 Html_add_widget // Adds the dw::Image to the page
33 Html_load_image // Tells cache to retrieve image
2934
3035
3136 ---------------------
3237 Fetching from the net
3338 ---------------------
3439
35 * a_Cache_open_url initiates the resource request, and when
40 * a_Capi_open_url initiates the resource request, and when
3641 finally the answer arrives, the HTTP header is examined for MIME
3742 type and either the GIF or PNG or JPEG decoder is set to handle
3843 the incoming data stream.
39 Decoding functions: a_Gif_image, a_Jpeg_image and a_Png_image.
44
45 Decoding functions:
46 a_Gif_callback, a_Jpeg_callback and a_Png_callback.
4047
4148 * The decoding function calls the following dicache methods as
4249 the data is processed (listed in order):
4451 a_Dicache_set_parms
4552 a_Dicache_set_cmap (only for indexed-GIF images)
4653 a_Dicache_write
54 a_Dicache_new_scan (MAY be called here or after set_cmap)
4755 a_Dicache_close
56
4857
4958 * The dicache methods call the necessary functions to connect
5059 with the widget code. This is done by calling image.c functions:
5261 a_Image_set_parms
5362 a_Image_set_cmap
5463 a_Image_write
64 a_Image_new_scan
5565 a_Image_close
56
57 * The functions in image.c make the required a_Dw_image_...
58 calls.
66
67 * The functions in image.c make the required Dw calls.
5968
6069
6170 -------------------------
6271 Fetching from the dicache
6372 -------------------------
6473
65 * a_Cache_open_url tests the dicache for the image, and directly
66 enqueues a cache client for it, without asking the network for
67 the data. When the client queue is processed (a bit later), the
68 decoder is selected based on the cache entry type.
74 * a_Capi_open_url() tests the cache for the image, and the cache,
75 via a_Cache_open_url(), enqueues a client for it, without asking
76 the network for the data. When the client queue is processed (a
77 bit later), Dicache_image() is set as the callback.
6978
70 * When the decoder is called, it tests the dicache for the image;
71 if the image is found, then it sets a_Dicache_callback as the
72 handling function and gets out of the way (no image decoding is
73 needed).
79 * When Dicache_image() is called, it sets the proper image data
80 decoder (RGB) and its data structure based on the entry's Type.
81 Then it substitutes itself with a_Dicache_callback() as the
82 handling function, and gets out of the way.
7483
75 * Later on, the DwImage buffer is set to reference the
76 dicache-entry's buffer and the rest of the functions calls is
77 driven by a_Dicache_callback.
84 * Thenceforth the rest of the functions calls is driven by
85 a_Dicache_callback().
7886
7987
8088 -----------
8189 Misc. notes
8290 -----------
8391
84 * Repeated images generate new cache clients, but only one of
85 them (the first) is handled with a_Dicache_* functions, the rest
86 is done with a_Dicache_callback..
92 * Repeated images generate new cache clients, but they may share
93 the imgbuf.
94 Note: Currently there's no proper support for transparent
95 images (i.e. decode to RGBA), but most of the time they render
96 the background color OK. This is: when first loaded, repeated
97 images share a background color, but when cached they render
98 correctly ;-). There's no point in trying to fix this because the
99 correct solution is to decode to RGBA and let the toolkit (FLTK)
100 handle the transparency.
87101
88 * The cache-client callback is set when the Content-type of the
89 image is got. It can be: a_Png_image, a_Gif_image or a_Jpeg_image
90 Those are called 'decoders' because their main function is to
91 translate the original data into RGB format.
102 * The first cache-client callback (Dicache_image()) is set when
103 the Content-type of the image is got.
92104
93 * Later on, the decoder can substitute itself, if it finds the
94 image has been already decoded, with a_Dicache_callback function.
95 This avoids decoding it twice.
105 * Later on, when there's a shared imgbuf, the dicache's logic
106 avoids decoding it multiple times and reuses what's already done.
96107
97108 * The dicache-entry and the Image structure hold bit arrays that
98 represent which rows had been decoded.
109 represent which rows have been decoded.
99110
100111 * The image processing can be found in the following sources:
101112
102 - image.[ch]
113 - image.{cc,hh}
103114 - dicache.[ch]
104115 - gif.[ch], png.[ch], jpeg.[ch]
105 - dw_image.[ch]
116 - dw/image.{cc,hh}
106117
107 * Bear in mind that there are three data structures for image
118 * Bear in mind that there are four data structures for image
108119 code:
109120
110 - DilloImage (image.h)
111 - DwImage (dw_image.h)
121 - DilloImage (image.hh)
112122 - DICacheEntry (dicache.h)
123 - dw::Image (class Image in dw/image.hh)
124 - core::Imgbuf (imgbuf.hh)
113125
114
0 Aug 2004, S.Geerken@ping.de
1
2 =============
3 Image Buffers
4 =============
5
6 General
7 =======
8
9 Image buffers depend on the platform (see DwRender.txt), but have a
10 general, platform independant interface, which is described in this
11 section. The next section describes the Gdk version of Imgbuf.
12
13 The structure ImgBuf will become part of the image processing, between
14 image data decoding and the widget DwImage. Its purposes are
15
16 1. storing the image data,
17 2. handling scaled versions of this buffer, and
18 3. drawing.
19
20 The latter must be done independently from the window.
21
22 Storing Image Data
23 ------------------
24 Imgbuf supports five image types, which are listed in the table
25 below. The representation defines, how the colors are stored within
26 the data, which is passed to a_Imgbuf_copy_row().
27
28 | bytes per |
29 type | pixel | representation
30 ---------------+-----------+-------------------------
31 RGB | 3 | red, green, blue
32 RGBA | 4 | red, green, blue, alpha
33 gray | 1 | gray value
34 indexed | 1 | index to colormap
35 indexed alpha | 1 | index to colormap
36
37 The last two types need a colormap, which is set by
38 a_Imgbuf_set_cmap(), which must be called before
39 a_Imgbuf_copy_row(). This function expects the colors as 32 bit
40 unsigned integers, which have the format 0xrrbbgg (for indexed
41 images), or 0xaarrggbb (for indexed alpha), respectively.
42
43 Scaling
44 -------
45 The buffer with the original size, which was created by
46 a_Imgbuf_new(), is called root buffer. Imgbuf provides the ability to
47 scale buffers. Generally, both root buffers, as well as scaled
48 buffers, may be shared, memory management is done by reference
49 counters.
50
51 Via a_Imgbuf_get_scaled_buf(), you can retrieve a scaled buffer. The
52 way, how this function works in detail, is described in the code, but
53 generally, something like this works always, in an efficient way:
54
55 old_buf = cur_buf;
56 cur_buf = a_Imgbuf_get_scaled_buf(old_buf, with, height);
57 a_Imgbuf_unref (old_buf);
58
59 Old_buf may both be a root buffer, or a scaled buffer.
60
61 (As an exception, there should always be a reference on the root
62 buffer, since scaled buffers cannot exist without the root buffer, but
63 on the other side, do not hold references on it. So, if in the example
64 above, old_buf would be a root buffer, and there would, at the
65 beginning, only be one reference on it, new_buf would also be
66 destroyed, along with old_buf. Therefore, an external reference must
67 be added to the root buffer, which is in dillo done within the dicache
68 module.)
69
70 The root buffer keeps a list of all children, and all operations
71 operating on the image data (a_Imgbuf_copy_row() and
72 a_Imgbuf_set_cmap()) are delegated to the scaled buffers, when
73 processed, and inherited, when a new scaled buffer is created. This
74 means, that they must only be performed for the root buffer.
75
76 Drawing
77 -------
78 There are two situations, when drawing is necessary:
79
80 1. To react on expose events, the function a_Imgbuf_draw() can be
81 used. Notice that the exact signature of this function is
82 platform dependant.
83
84 2. When a row has been copied, it has to be drawn. To determine the
85 area, which has to be drawn, the function
86 a_Imgbuf_get_row_area() should be used. In dillo, the dicache
87 module will first call a_Img_copy_row(), and then call
88 a_Dw_image_draw_row() for the images connected to this image
89 buffer. a_Dw_image_draw_row() will then call
90 p_Dw_widget_queue_draw(), with an area determined by
91 a_Imgbuf_get_row_area().
92
93
94 The Gdk Implementation
95 ======================
96
97 The Gdk implementation is used by the Gtk+ platform. [... todo]
98
99
100 Global Scalers
101 ==============
102
103 In some cases, there is a context, where images have to be scaled
104 often, by a relatively constant factor. For example, the preview
105 window (GtkDwPreview) draws images via the Imgbuf draw functions, but
106 uses scaled buffers. Scaling such a buffer each time it is needed,
107 causes huge performance losses. On the other hand, if the preview
108 window would keep these scaled buffers (e.g. by lazy mapping of the
109 original buffer to the scaled buffer), the scaled buffers get never
110 freed, since the view is not told about, when the original buffer is
111 not needed anymore. (n.b., that currently, the scaled buffers are
112 destroyed, when the original buffer is destroyed, but this may change,
113 and even this would leave "zombies" in this mapping structure, where
114 the values refer to dead pointers).
115
116 It is sufficient, that references on the scaled buffers are referred
117 somehow, so that they do not get destroyed between different
118 usages. The caller (in this case the preview) simply requests a scaled
119 buffer, but the Imgbuf returns this from the list of already scaled
120 buffers.
121
122 These references are hold by special structures, which are called
123 "scalers". There are two types of scalers, local scalers, which are
124 bound to image buffers, and global scalers, which refer to multiple
125 scalers.
126
127 What happens in different situations:
128
129 - The caller (e.g. the preview) requests a scaled buffer. For this,
130 it uses a special method, which also passes the global image
131 scaler, which was created before (for the preview, there is a 1-1
132 association). The Imgbuf uses this global image scaler, to
133 identify the caller, and keeps a list of them. If this global
134 scaler is not yet in the list, it is added, and a local scaler is
135 created.
136
137
138
139 -
140
141 There are three images in the page, i1a, i1b, and i2. I1a and i1b
142 refer to the same image recource, represented by the root image buffer
143 iba, which original size is 200 x 200. I1a is displayed in original
144 size, while i1b is displayed at 100 x 100. I2 refers to an other
145 recource, ibb, which has the size 300 x 300. I2 is shown in original
146 size.
147
148
149 :DwRenderLayout ------------------- :DwPage ----------.
150 / \ |
151 ,----' `----. ,------ i1a:DwImage --+
152 / \ | |
153 view1:GtkDwViewport view2:GtkDwPreview | ,---- i1b:DwImage --|
154 | | | |
155 ,------------------------------' | | ,-- i2: DwImage --'
156 | | | |
157 | ,-------------------------------------' | |
158 | | ,--------------------------------' |
159 | | | ,----'
160 | | | |
161 | V | V
162 | iba:Imgbuf | ibb:Imgbuf -- 30x30
163 | | | V | ^
164 | | +- 100x100 ,- 20x20 ,- 10x10 | |
165 | | | | ^ | ^ | |
166 | | `----------+----|---' | `--. ,--'
167 | | ,--------------' | | |
168 | | | ,------------------' | |
169 | | | | | |
170 | lca:ImgbufLSc lcb:ImgbufLSc
171 | (factor 1/10) (factor 1/10)
172 | \ /
173 | `-----------. ,-------------------'
174 | \ /
175 `------------------> scl:ImgbufGSc
176 (factor 1/10)
0 dist_doc_DATA = user_help.html
1 man_MANS = dillo.1
02 EXTRA_DIST = \
1 Cache.txt \
2 Cookies.txt \
3 Dillo.txt \
4 Dw.txt \
5 DwImage.txt \
6 DwPage.txt \
7 DwStyle.txt \
8 DwTable.txt \
9 DwWidget.txt \
10 HtmlParser.txt \
11 IO.txt \
12 Images.txt \
13 NC_design.txt \
14 Selection.txt \
15 Dpid.txt \
16 README
3 $(man_MANS) \
4 index.doc \
5 lout.doc \
6 dw-map.doc \
7 dw-overview.doc \
8 dw-usage.doc \
9 dw-layout-views.doc \
10 dw-layout-widgets.doc \
11 dw-widget-sizes.doc \
12 dw-changes.doc \
13 dw-images-and-backgrounds.doc \
14 fltk-problems.doc \
15 rounding-errors.doc \
16 uml-legend.doc \
17 dw-example-screenshot.png \
18 dw-viewport-without-scrollbar.png \
19 dw-viewport-with-scrollbar.png \
20 dw-size-of-widget.png \
21 dw-style-box-model.png \
22 dw-style-length-absolute.png \
23 dw-style-length-percentage.png \
24 dw-style-length-relative.png \
25 dw-textblock-collapsing-spaces-1-1.png \
26 dw-textblock-collapsing-spaces-1-2.png \
27 dw-textblock-collapsing-spaces-2-1.png \
28 dw-textblock-collapsing-spaces-2-2.png \
29 Cache.txt \
30 Cookies.txt \
31 Dillo.txt \
32 Dw.txt \
33 HtmlParser.txt \
34 IO.txt \
35 Images.txt \
36 Imgbuf.txt \
37 NC_design.txt \
38 Selection.txt \
39 Dpid.txt \
40 README
+0
-331
doc/Makefile.in less more
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15 srcdir = @srcdir@
16 top_srcdir = @top_srcdir@
17 VPATH = @srcdir@
18 pkgdatadir = $(datadir)/@PACKAGE@
19 pkglibdir = $(libdir)/@PACKAGE@
20 pkgincludedir = $(includedir)/@PACKAGE@
21 top_builddir = ..
22 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
23 INSTALL = @INSTALL@
24 install_sh_DATA = $(install_sh) -c -m 644
25 install_sh_PROGRAM = $(install_sh) -c
26 install_sh_SCRIPT = $(install_sh) -c
27 INSTALL_HEADER = $(INSTALL_DATA)
28 transform = $(program_transform_name)
29 NORMAL_INSTALL = :
30 PRE_INSTALL = :
31 POST_INSTALL = :
32 NORMAL_UNINSTALL = :
33 PRE_UNINSTALL = :
34 POST_UNINSTALL = :
35 build_triplet = @build@
36 host_triplet = @host@
37 target_triplet = @target@
38 subdir = doc
39 DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in
40 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
41 am__aclocal_m4_deps = $(top_srcdir)/configure.in
42 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
43 $(ACLOCAL_M4)
44 mkinstalldirs = $(install_sh) -d
45 CONFIG_HEADER = $(top_builddir)/config.h
46 CONFIG_CLEAN_FILES =
47 SOURCES =
48 DIST_SOURCES =
49 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
50 ACLOCAL = @ACLOCAL@
51 AMDEP_FALSE = @AMDEP_FALSE@
52 AMDEP_TRUE = @AMDEP_TRUE@
53 AMTAR = @AMTAR@
54 AUTOCONF = @AUTOCONF@
55 AUTOHEADER = @AUTOHEADER@
56 AUTOMAKE = @AUTOMAKE@
57 AWK = @AWK@
58 CC = @CC@
59 CCDEPMODE = @CCDEPMODE@
60 CFLAGS = @CFLAGS@
61 CPP = @CPP@
62 CPPFLAGS = @CPPFLAGS@
63 CXX = @CXX@
64 CXXDEPMODE = @CXXDEPMODE@
65 CXXFLAGS = @CXXFLAGS@
66 CYGPATH_W = @CYGPATH_W@
67 DEFS = @DEFS@
68 DEPDIR = @DEPDIR@
69 DLGUI_FALSE = @DLGUI_FALSE@
70 DLGUI_TRUE = @DLGUI_TRUE@
71 ECHO_C = @ECHO_C@
72 ECHO_N = @ECHO_N@
73 ECHO_T = @ECHO_T@
74 EGREP = @EGREP@
75 EXEEXT = @EXEEXT@
76 GLIB_CFLAGS = @GLIB_CFLAGS@
77 GLIB_CONFIG = @GLIB_CONFIG@
78 GLIB_LIBS = @GLIB_LIBS@
79 GTK_CFLAGS = @GTK_CFLAGS@
80 GTK_CONFIG = @GTK_CONFIG@
81 GTK_LIBS = @GTK_LIBS@
82 INSTALL_DATA = @INSTALL_DATA@
83 INSTALL_PROGRAM = @INSTALL_PROGRAM@
84 INSTALL_SCRIPT = @INSTALL_SCRIPT@
85 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
86 LDFLAGS = @LDFLAGS@
87 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
88 LIBFLTK_LIBS = @LIBFLTK_LIBS@
89 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
90 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
91 LIBJPEG_LIBS = @LIBJPEG_LIBS@
92 LIBOBJS = @LIBOBJS@
93 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
94 LIBPNG_LIBS = @LIBPNG_LIBS@
95 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
96 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
97 LIBS = @LIBS@
98 LIBSSL_LIBS = @LIBSSL_LIBS@
99 LIBZ_LIBS = @LIBZ_LIBS@
100 LTLIBOBJS = @LTLIBOBJS@
101 MAKEINFO = @MAKEINFO@
102 OBJEXT = @OBJEXT@
103 PACKAGE = @PACKAGE@
104 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
105 PACKAGE_NAME = @PACKAGE_NAME@
106 PACKAGE_STRING = @PACKAGE_STRING@
107 PACKAGE_TARNAME = @PACKAGE_TARNAME@
108 PACKAGE_VERSION = @PACKAGE_VERSION@
109 PATH_SEPARATOR = @PATH_SEPARATOR@
110 RANLIB = @RANLIB@
111 SET_MAKE = @SET_MAKE@
112 SHELL = @SHELL@
113 STRIP = @STRIP@
114 VERSION = @VERSION@
115 ac_ct_CC = @ac_ct_CC@
116 ac_ct_CXX = @ac_ct_CXX@
117 ac_ct_RANLIB = @ac_ct_RANLIB@
118 ac_ct_STRIP = @ac_ct_STRIP@
119 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
120 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
121 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
122 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
123 am__include = @am__include@
124 am__leading_dot = @am__leading_dot@
125 am__quote = @am__quote@
126 am__tar = @am__tar@
127 am__untar = @am__untar@
128 bindir = @bindir@
129 build = @build@
130 build_alias = @build_alias@
131 build_cpu = @build_cpu@
132 build_os = @build_os@
133 build_vendor = @build_vendor@
134 datadir = @datadir@
135 exec_prefix = @exec_prefix@
136 host = @host@
137 host_alias = @host_alias@
138 host_cpu = @host_cpu@
139 host_os = @host_os@
140 host_vendor = @host_vendor@
141 includedir = @includedir@
142 infodir = @infodir@
143 install_sh = @install_sh@
144 libdir = @libdir@
145 libexecdir = @libexecdir@
146 localstatedir = @localstatedir@
147 mandir = @mandir@
148 mkdir_p = @mkdir_p@
149 oldincludedir = @oldincludedir@
150 prefix = @prefix@
151 program_transform_name = @program_transform_name@
152 sbindir = @sbindir@
153 sharedstatedir = @sharedstatedir@
154 sysconfdir = @sysconfdir@
155 target = @target@
156 target_alias = @target_alias@
157 target_cpu = @target_cpu@
158 target_os = @target_os@
159 target_vendor = @target_vendor@
160 EXTRA_DIST = \
161 Cache.txt \
162 Cookies.txt \
163 Dillo.txt \
164 Dw.txt \
165 DwImage.txt \
166 DwPage.txt \
167 DwStyle.txt \
168 DwTable.txt \
169 DwWidget.txt \
170 HtmlParser.txt \
171 IO.txt \
172 Images.txt \
173 NC_design.txt \
174 Selection.txt \
175 Dpid.txt \
176 README
177
178 all: all-am
179
180 .SUFFIXES:
181 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
182 @for dep in $?; do \
183 case '$(am__configure_deps)' in \
184 *$$dep*) \
185 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
186 && exit 0; \
187 exit 1;; \
188 esac; \
189 done; \
190 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \
191 cd $(top_srcdir) && \
192 $(AUTOMAKE) --gnu doc/Makefile
193 .PRECIOUS: Makefile
194 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
195 @case '$?' in \
196 *config.status*) \
197 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
198 *) \
199 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
200 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
201 esac;
202
203 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
204 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
205
206 $(top_srcdir)/configure: $(am__configure_deps)
207 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
208 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
209 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
210 uninstall-info-am:
211 tags: TAGS
212 TAGS:
213
214 ctags: CTAGS
215 CTAGS:
216
217
218 distdir: $(DISTFILES)
219 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
220 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
221 list='$(DISTFILES)'; for file in $$list; do \
222 case $$file in \
223 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
224 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
225 esac; \
226 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
227 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
228 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
229 dir="/$$dir"; \
230 $(mkdir_p) "$(distdir)$$dir"; \
231 else \
232 dir=''; \
233 fi; \
234 if test -d $$d/$$file; then \
235 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
236 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
237 fi; \
238 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
239 else \
240 test -f $(distdir)/$$file \
241 || cp -p $$d/$$file $(distdir)/$$file \
242 || exit 1; \
243 fi; \
244 done
245 check-am: all-am
246 check: check-am
247 all-am: Makefile
248 installdirs:
249 install: install-am
250 install-exec: install-exec-am
251 install-data: install-data-am
252 uninstall: uninstall-am
253
254 install-am: all-am
255 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
256
257 installcheck: installcheck-am
258 install-strip:
259 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
260 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
261 `test -z '$(STRIP)' || \
262 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
263 mostlyclean-generic:
264
265 clean-generic:
266
267 distclean-generic:
268 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
269
270 maintainer-clean-generic:
271 @echo "This command is intended for maintainers to use"
272 @echo "it deletes files that may require special tools to rebuild."
273 clean: clean-am
274
275 clean-am: clean-generic mostlyclean-am
276
277 distclean: distclean-am
278 -rm -f Makefile
279 distclean-am: clean-am distclean-generic
280
281 dvi: dvi-am
282
283 dvi-am:
284
285 html: html-am
286
287 info: info-am
288
289 info-am:
290
291 install-data-am:
292
293 install-exec-am:
294
295 install-info: install-info-am
296
297 install-man:
298
299 installcheck-am:
300
301 maintainer-clean: maintainer-clean-am
302 -rm -f Makefile
303 maintainer-clean-am: distclean-am maintainer-clean-generic
304
305 mostlyclean: mostlyclean-am
306
307 mostlyclean-am: mostlyclean-generic
308
309 pdf: pdf-am
310
311 pdf-am:
312
313 ps: ps-am
314
315 ps-am:
316
317 uninstall-am: uninstall-info-am
318
319 .PHONY: all all-am check check-am clean clean-generic distclean \
320 distclean-generic distdir dvi dvi-am html html-am info info-am \
321 install install-am install-data install-data-am install-exec \
322 install-exec-am install-info install-info-am install-man \
323 install-strip installcheck installcheck-am installdirs \
324 maintainer-clean maintainer-clean-generic mostlyclean \
325 mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am \
326 uninstall-info-am
327
328 # Tell versions [3.59,3.63) of GNU make to not export all variables.
329 # Otherwise a system limit (for SysV at least) may be exceeded.
330 .NOEXPORT:
00
11 _________________________________________________________________
2
2
33 Naming&Coding design
44 _________________________________________________________________
5
5
66 Dillo's code is divided into modules. For instance: bookmark, cache,
77 dicache, gif.
8
9 Lets think of a module named "menu", then:
8
9 Let's think of a module named "menu", then:
1010 * Every internal routine of the module, should start with "Menu_"
1111 prefix.
1212 * "Menu_" prefixed functions are not meant to be called from outside
1414 * If the function is to be exported to other modules (i.e. it will
1515 be called from the outside), it should be wrapped with an "a_"
1616 prefix.
17
17
1818 For instance: if the function name is "Menu_create", then it's an
1919 internal function, but if we need to call it from the outside, then it
2020 should be renamed to "a_Menu_create".
21
21
2222 Why the "a_" prefix?
2323 Because of historical reasons.
24 And it reads better "a_Menu_create" than "d_Menu_create" cause the
24 And "a_Menu_create" reads better than "d_Menu_create" because the
2525 first one suggests "a Menu create" function!
26
26
2727 Another way of understanding this is thinking of "a_" prefixed
2828 functions as Dillo's internal library, and the rest ("Menu_" prefixed
2929 in our example) as a private module-library.
30
31 Indentation: Source code must be indented with 3 blank spaces, no Tabs.
30
31 Indentation:
32
33 Source code must be indented with 3 blank spaces, no Tabs.
3234 Why?
3335 Because different editors expand or treat tabs in several ways; 8
3436 spaces being the most common, but that makes code really wide and
3537 we'll try to keep it within the 80 columns bounds (printer friendly).
36
38
39 You can use: indent -kr -sc -i3 -bad -nbbo -nut -l79 myfile.c
40
3741 Function commenting:
38
42
3943 Every single function of the module should start with a short comment
40 that explains it's purpose; three lines must be enough, but if you
44 that explains its purpose; three lines must be enough, but if you
4145 think it requires more, enlarge it.
4246
43 /*
44 * Try finding the url in the cache. If it hits, send the contents
45 * to the caller. If it misses, set up a new connection.
46 */
47 int a_Cache_open_url(const char *url, void *Data)
48 {
49 ...
50 ...
51 ...
52 }
47 /*
48 * Try finding the url in the cache. If it hits, send the contents
49 * to the caller. If it misses, set up a new connection.
50 */
51 int a_Cache_open_url(const char *url, void *Data)
52 {
53 ...
54 ...
55 ...
56 }
5357
54 We also have the BUG: and todo: tags.
58 We also have the BUG:, TODO:, and WORKAROUND: tags.
5559 Use them within source code comments to spot hidden issues. For
5660 instance:
5761
58 /* BUG: this counter is not accurate */
59 ++i;
62 /* BUG: this counter is not accurate */
63 ++i;
6064
61 /* todo: get color from the right place */
62 a = color;
65 /* TODO: get color from the right place */
66 a = color;
67
68 /* WORKAROUND: the canonical way of doing it doesn't work yet. */
69 ++a; ++a; ++a;
70
71 Function length:
72
73 Let's try to keep functions within the 45 lines boundary. This eases
74 code reading, following, understanding and maintenance.
75
76 Functions with a single exit:
77
78 It's much easier to follow and maintain functions with a single exit
79 point at the bottom (instead of multiple returns). The exception to
80 the rule are calls like dReturn_if_fail() at its head.
81
82 dlib functions:
83
84 * Dillo uses dlib extensively in its C sources. Before starting
85 to code something new, a good starting point is to check what
86 this library has to offer (check dlib/dlib.h).
87 * Memory management must be done using dNew, dNew0, dMalloc, dFree
88 and their relatives.
89 * For debugging purposes and error catching (not for normal flow):
90 dReturn_if_fail, dReturn_val_if_fail etc. are encouraged.
91 * The MSG macro is extensively used to output additional information
92 to the calling terminal.
93
6394 _________________________________________________________________
64
95
96 C++
97
98 Source code in C++ should follow the same rules with these exceptions:
99
100 * Class method names are camel-cased and start with lowercase
101 e.g. appendInputMultipart
102 * Classes and types start uppercased
103 e.g. class DilloHtmlReceiver
104 * Class methods don't need to prefix its module name
105 e.g. links->get()
106
107 We also try to keep the C++ relatively simple. Dillo does use
108 inheritance and templates, but that's about all.
109
110 _________________________________________________________________
111
65112 What do we get with this?
66
113
67114 * A clear module API for Dillo; every function prefixed "a_" is to
68115 be used outside the module.
69 * A way for identifying where the function came from (the
116 * A way to identify where the function came from (the
70117 capitalized word is the module name).
71 * An inner ADT (Abstract data type) for the module. That can be
118 * An inner ADT (Abstract data type) for the module that can be
72119 isolated, tested and replaced independently.
73120 * A two stage instance for bug-fixing. You can change the exported
74121 function algorithms while someone else fixes the internal
75122 module-ADT!
76123 * A coding standard ;)
77124 _________________________________________________________________
78
125
79126 Naming&Coding design by Jorge Arellano Cid
0 README: Last update Jul 2009
01
1 OK, here we are:
2 These documents cover dillo's internals.
3 For user help, see http://www.dillo.org/dillo2-help.html
24
5 --------------------------------------------------------------------------
6
7 These documents need a review.
8 *.txt were current with Dillo1, but many have since become more or
9 less out-of-date.
10 *.doc are doxygen source for the Dillo Widget (dw) component, and
11 were written for Dillo2.
12
13 They will give you an overview of what's going on, but take them
14 with a pinch of salt.
15
16 Of course I'd like to have *.txt as doxygen files too!
17 If somebody wants to make this conversion, please let me know
18 to assign higher priority to updating these docs.
19
20 --
21 Jorge.-
322
423 --------------------------------------------------------------------------
524 FILE DESCRIPTION STATE
1231 Images.txt Image handling and processing Current
1332 HtmlParser.txt A versatile parser Current
1433 Dw.txt The New Dillo Widget (Overview) Current
15 DwWidget.txt The base object of Dw Current
16 DwImage.txt Dillo Widget image handling Incomplete
17 DwPage.txt Dillo Widget page (shortly) Incomplete
18 DwStyle.txt Styles of Dillo Widgets Pending
19 DwTable.txt Tables in dillo Current
34 Imgbuf.txt Image buffers Pending
2035 Selection.txt Selections, and link activation Current (?)
2136 Cookies.txt Explains how to enable cookies Current
2237 Dpid.txt Dillo plugin daemon Current
2338 --------------------------------------------------------------------------
24 [This documents cover dillo's internal working. They're NOT a user manual]
25 --------------------------------------------------------------------------
2639
2740
28 * Ah!, there's a small program (srch) within the src/ dir. It searches
41 * BTW, there's a small program (srch) within the src/ dir. It searches
2942 tokens within the whole code (*.[ch]). It has proven very useful.
3043 Ex: ./srch a_Image_write
3144 ./srch todo:
3245
33 * Please submit your patches with 'diff -pru'.
46 * Please submit your patches with 'hg diff'.
3447
3548
36 Happy coding!
49 Happy coding!
3750 --Jcid
2525 transferred to extended iterators (DwExtIterator), see below for more
2626 details. All event handling functions have the same signature, the
2727 arguments in detail are:
28
28
2929 - DwIterator *it the iterator pointing on the item under
3030 the mouse pointer,
3131 - gint char_pos the exact (character) position within
0 .TH dillo 1 "August 5, 2010" "version 2.2" "USER COMMANDS"
1 .SH NAME
2 dillo \- web browser
3 .SH SYNOPSIS
4 .B dillo
5 .RI [ OPTION ]...
6 .RB [ \-\- ]
7 .RI [ URL | FILE ]...
8 .SH DESCRIPTION
9 .PP
10 Dillo is a lightweight graphical web browser that aims to be secure.
11 It handles HTTP internally, and FILE, FTP, and
12 DATA URIs are handled through a plugin system (dpi). In addition,
13 .I INSECURE
14 HTTPS support can be enabled. Both FTP and Dillo's download manager use the
15 .BR wget (1)
16 downloader.
17 .PP
18 Dillo displays HTML, text, PNG, JPEG, and GIF files.
19 It handles cookies, basic authentication, proxying, and some CSS.
20 .PP
21 Framesets are displayed as links to frames, and there is currently
22 no support for javascript or video.
23 .SH OPTIONS
24 .TP
25 \fB\-f\fR, \fB\-\-fullwindow\fR
26 Start in full window mode: hide address bar, navigation buttons, menu, and
27 status bar.
28 .TP
29 \fB\-g\fR, \fB\-\-geometry \fIGEO\fR
30 Set initial window position where \fIGEO\fR is
31 \fIW\fBx\fIH\fR[{\fB+\-\fR}\fIX\fR{\fB+\-\fR}\fIY\fR].
32 .TP
33 \fB\-h\fR, \fB\-\-help\fR
34 Display this help text and exit.
35 .TP
36 \fB\-l\fR, \fB\-\-local\fR
37 Don't load images for these URL(s).
38 .TP
39 \fB\-v\fR, \fB\-\-version\fR
40 Display version info and exit.
41 .TP
42 \fB\-x\fR, \fB\-\-xid \fIXID\fR
43 Open first Dillo window in an existing window whose window ID is \fIXID\fR.
44 .SH EXIT STATUS
45 .TP
46 .B 0
47 No error.
48 .TP
49 .B 1
50 Internal error.
51 .TP
52 .B 2
53 Error in command line arguments.
54 .SH ENVIRONMENT
55 .TP
56 .BR "HOME " "(or " "HOMEDRIVE " "and " "HOMEPATH " "on Cygwin)"
57 User's home directory.
58 .TP
59 .B http_proxy
60 URL of proxy to send HTTP traffic through.
61 .SH FILES
62 .TP
63 .I dpid
64 Dillo plugin daemon
65 .TP
66 .I dpidc
67 Control program for dpid.
68 .TP
69 .I ~/.dillo/bm.txt
70 User bookmarks
71 .TP
72 .I ~/.dillo/certs/
73 Saved certificates for HTTPS.
74 .TP
75 .I ~/.dillo/cookies.txt
76 Stored cookies
77 .TP
78 .I ~/.dillo/cookiesrc
79 Cookie settings
80 .TP
81 .I ~/.dillo/dillorc
82 Configuration file.
83 .TP
84 .I ~/.dillo/dpid_comm_keys
85 Keys used in dpi daemon communication.
86 .TP
87 .I ~/.dillo/dpidrc
88 Contains name of directory containing dpis, and associates
89 dpi files with protocols.
90 .TP
91 .I ~/.dillo/keysrc
92 Keybindings.
93 .TP
94 .I ~/.dillo/style.css
95 User style sheet
96 .SH SEE ALSO
97 .BR wget (1)
98 .PP
99 Dillo website:
100 .B http://www.dillo.org
0 /** \page dw-changes Changes to the GTK+-based Release Version
1
2 <h2>Changes in Dw</h2>
3
4 Related to the FLTK port, there have been many changes, this is a
5 (hopefully complete) list:
6
7 <ul>
8 <li> Rendering abstraction, read \ref dw-overview and \ref dw-layout-views
9 for details. Some important changes:
10
11 <ul>
12 <li> The underlying platform (e.g. the UI toolkit) is fully abstract,
13 there are several platform independent structures replacing
14 GTK+ structures, e.g. dw::core::Event.
15
16 <li> The central class managing the widget tree is not anymore
17 GtkDwViewport, but dw::core::Layout.
18
19 <li> Drawing is done via dw::core::View, a pointer is passed to
20 dw::core::Widget::draw.
21
22 <li> The distinction between viewport coordinates and canvas
23 coordinates (formerly world coordinates) has been mostly
24 removed. (Only for views, it sometimes plays a role, see
25 \ref dw-layout-views).
26 </ul>
27
28 <li> Cursors have been moved to dw::core::style, see
29 dw::core::style::Style::cursor. dw::core::Widget::setCursor is now
30 protected (and so only called by widget implementations).
31
32 <li> World coordinates are now called canvas coordinates.
33
34 <li> There is now a distinction between dw::core::style::StyleAttrs and
35 dw::core::style::Style.
36
37 <li> There is no base class for container widgets anymore. The former
38 DwContainer::for_all has been removed, instead this functionality
39 is now done via iterators (dw::core::Widget::iterator,
40 dw::core::Iterator).
41
42 <li> DwPage is now called dw::Textblock, and DwAlignedPage
43 dw::AlignedTextblock.
44
45 <li> dw::Textblock, all sub classes of it, and dw::Table do not read
46 "limit_text_width" from the preferences, but get it as an argument.
47 (May change again.)
48
49 <li> dw::Table has been rewritten.
50
51 <li> Instead of border_spacing in the old DwStyle, there are two attributes,
52 dw::core::style::Style::hBorderSpacing and
53 dw::core::style::Style::vBorderSpacing, since CSS allowes to specify
54 two values. Without CSS, both attributes should have the same value.
55
56 <li> Images are handled differently, see \ref dw-images-and-backgrounds.
57
58 <li> Embedded UI widgets (formerly GtkWidget's) are handled differently,
59 see dw::core::ui.
60
61 <li> DwButton has been removed, instead, embedded UI widgets are used. See
62 dw::core::ui and dw::core::ui::ComplexButtonResource.
63 </ul>
64
65 Dw is now written C++, the transition should be obvious. All "Dw"
66 prefixes have been removed, instead, namespaces are used now:
67
68 <ul>
69 <li>dw::core contains the core,
70 <li>dw::core::style styles,
71 <li>dw::core::ui embedded UI resources,
72 <li>dw::fltk classes related to FLTK, and
73 <li>::dw the widgets.
74 </ul>
75
76 <h2>Documentation</h2>
77
78 The old documentation has been moved to:
79
80 <table>
81 <tr><th colspan="2">Old <th>New
82 <tr><td rowspan="2">Dw.txt
83 <td>general part <td>\ref dw-overview, \ref dw-usage,
84 \ref dw-layout-widgets,
85 \ref dw-widget-sizes
86 <tr><td>remarks on specific widgets <td>respective source files: dw::Bullet,
87 dw::core::ui::Embed
88 <tr><td rowspan="2">DwImage.txt
89 <td>signals <td>dw::core::Layout::LinkReceiver
90 <tr><td>rest <td>dw::Image,
91 \ref dw-images-and-backgrounds
92 <tr><td colspan="2">Imgbuf.txt <td>dw::core::Imgbuf,
93 \ref dw-images-and-backgrounds
94 <tr><td colspan="2">DwPage.txt <td>dw::Textblock
95 <tr><td colspan="2">DwRender.txt <td>\ref dw-overview, \ref dw-layout-views,
96 dw::core::ui
97 <tr><td colspan="2">DwStyle.txt <td>dw::core::style
98 <tr><td colspan="2">DwTable.txt <td>dw::Table
99 <tr><td colspan="2">DwWidget.txt <td>dw::core::Widget, \ref dw-layout-widgets,
100 \ref dw-widget-sizes
101 <tr><td colspan="2">Selection.txt <td>dw::core::SelectionState
102 </table>
103
104 */
0 /** \page dw-images-and-backgrounds Images and Backgrounds in Dw
1
2 <h2>General</h2>
3
4 Representation of the image data is delegated to dw::core::Imgbuf, see
5 there for details. Drawing is delegated to dw::core::View
6 (dw::core::View::drawImgbuf).
7
8 Since dw::core::Imgbuf provides memory management based on reference
9 counting, there may be an 1-to-n relation from image renderers (image
10 widgets or backgrounds, see below) and dw::core::Imgbuf. Since
11 dw::core::Imgbuf does not know about renderers, but just provides
12 rendering functionality, the caller must (typically after calling
13 dw::core::Imgbuf::copyRow) notify all renderers connected to the
14 buffer.
15
16
17 <h2>Images</h2>
18
19 This is the simplest renderer, displaying an image. For each row to be
20 drawn,
21
22 <ol>
23 <li> first dw::core::Imgbuf::copyRow, and then
24 <li> for each dw::Image, dw::Image::drawRow must be called, with the same
25 argument (no scaling is necessary).
26 </ol>
27
28 dw::Image automatically scales the dw::core::Imgbuf, the root buffer
29 should be passed to dw::Image::setBuffer.
30
31 \see dw::Image for more details.
32
33 <h2>Future Extensions</h2>
34
35 (This is not implemented yet.) Rendering itself (image widgets and
36 background) will be abstracted, by a new interface
37 dw::core::ImageRenderer. In the current code for image decoding, this
38 interface will replace references to dw::Image, which implements
39 dw::core::ImageRenderer, in most cases.
40
41 <h2>Backgrounds</h2>
42
43 (This is based on future extensions described above.) Since background
44 are style resources, they are associated with
45 dw::core::style::Style. For backgrounds, another level is needed,
46 because of the 1-to-n relation from dw::core::style::Style to
47 dw::core::Widget:
48
49 \dot
50 digraph G {
51 node [shape=record, fontname=Helvetica, fontsize=10];
52 edge [arrowhead="open", arrowtail="none", labelfontname=Helvetica,
53 labelfontsize=10, color="#404040", labelfontcolor="#000080"];
54 fontname=Helvetica; fontsize=10;
55
56 "background renderer (as a part of style)" -> "image buffer" [headlabel="*",
57 taillabel="1"];
58 "widget (or a part of it)" -> "background renderer (as a part of style)"
59 [headlabel="*", taillabel="1"];
60 }
61 \enddot
62
63 Unlike dw::Image, dw::core::style::BgRenderer is not associated with a
64 certain rectangle on the canvas. Instead, widgets, or parts of widgets
65 take this role. This is generally represented by an implementation of
66 the interface dw::core::style::BgAllocation, which is implemented by
67 dw::core::Widget, but also by all parts of widget implementation,
68 which may have an own background image.
69
70 The following diagram gives a total overview:
71
72 \dot
73 digraph G {
74 node [shape=record, fontname=Helvetica, fontsize=10];
75 edge [arrowhead="open", arrowtail="none", labelfontname=Helvetica,
76 labelfontsize=10, color="#404040", labelfontcolor="#000080"];
77 fontname=Helvetica; fontsize=10;
78
79 "DICache Entry";
80
81 subgraph cluster_dw_images {
82 style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
83 label="Dw Images";
84
85 ImageRenderer [URL="\ref dw::core::ImageRenderer", color="#ff8080"];
86 Imgbuf [URL="\ref dw::core::Imgbuf", color="#ff8080"];
87 }
88
89 subgraph cluster_widgets {
90 style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
91 label="Widgets";
92
93 Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
94 Textblock [URL="\ref dw::Textblock"];
95 "Textblock::Word" [URL="\ref dw::Textblock::Word"];
96 Table [URL="\ref dw::Table"];
97 "Table::Row" [URL="\ref dw::Table::Row"];
98 Image [URL="\ref dw::Image"];
99 }
100
101 subgraph cluster_style {
102 style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
103 label="dw::core::style";
104
105 Style [URL="\ref dw::core::style::Style"];
106 BgRenderer [URL="\ref dw::core::style::BgRenderer"];
107 BgAllocation [URL="\ref dw::core::style::BgAllocation", color="#ff8080"];
108 }
109
110 "DICache Entry" -> ImageRenderer [headlabel="*", taillabel="1"];
111 "DICache Entry" -> Imgbuf [headlabel="1", taillabel="1"];
112
113 BgRenderer -> Imgbuf [headlabel="1", taillabel="*"];
114 BgRenderer -> BgAllocation [headlabel="*", taillabel="1"];
115 ImageRenderer -> BgRenderer [arrowhead="none", arrowtail="empty",
116 style="dashed"];
117 ImageRenderer -> Image [arrowhead="none", arrowtail="empty",
118 style="dashed"];
119
120 Style -> BgRenderer [headlabel="0..1", taillabel="1"];
121
122 Widget -> Textblock [arrowhead="none", arrowtail="empty"];
123 Textblock -> "Textblock::Word" [headlabel="*", taillabel="1"];
124 Widget -> Table [arrowhead="none", arrowtail="empty"];
125 Table -> "Table::Row" [headlabel="*", taillabel="1"];
126 Widget -> Image [arrowhead="none", arrowtail="empty"];
127
128 BgAllocation -> Widget [arrowhead="none", arrowtail="empty",
129 style="dashed"];
130 BgAllocation -> "Textblock::Word" [arrowhead="none", arrowtail="empty",
131 style="dashed"];
132 BgAllocation -> "Table::Row" [arrowhead="none", arrowtail="empty",
133 style="dashed"];
134 }
135 \enddot
136
137 <center>[\ref uml-legend "legend"]</center>
138
139
140 <h2>Integration into dillo</h2>
141
142 \todo Add some references.
143
144
145 */
0 /** \page dw-layout-views Layout and Views
1
2 Rendering of Dw is done in a way resembling the model-view pattern, at
3 least formally. Actually, the counterpart of the model, the layout
4 (dw::core::Layout), does a bit more than a typical model, namely the
5 layouting (delegated to the widget tree, see \ref dw-layout-widgets),
6 and the view does a bit less than a typical view, i.e. only the actual
7 drawing.
8
9 Additionally, there is a structure representing common properties of
10 the platform. A platform is typically related to the underlying UI
11 toolkit, but other uses may be thought of.
12
13 This design helps to archieve two important goals:
14
15 <ul>
16 <li> Abstraction of the actual drawing, by different implementations
17 of dw::core::View.
18
19 <li> It makes portability simple.
20 </ul>
21
22
23 <h2>Viewports</h2>
24
25 Although the design implies that the usage of viewports should be
26 fully transparent to the layout module, this cannot be fully achieved,
27 for the following reasons:
28
29 <ul>
30 <li> Some features, which are used on the level of dw::core::Widget,
31 e.g. anchors, refer to scrolling positions.
32
33 <li> Size hints (see \ref dw-layout-widgets) depend on the viewport
34 sizes, e.g. when the user changes the window size, and so also
35 the size of a viewport, the text within should be rewrapped.
36 </ul>
37
38 Therefore, dw::core::Layout keeps track of the viewport size, the
39 viewport position, and even the thickness of the scrollbars, they are
40 relevant, see below for more details.
41 If a viewport is not used, however, the size is not defined.
42
43 Whether a given dw::core::View implementation is a viewport or not, is
44 defined by the return value of dw::core::View::usesViewport. If this
45 method returns false, the following methods need not to be implemented
46 at all:
47
48 <ul>
49 <li> dw::core::View::getHScrollbarThickness,
50 <li> dw::core::View::getVScrollbarThickness,
51 <li> dw::core::View::scrollTo, and
52 <li> dw::core::View::setViewportSize.
53 </ul>
54
55 <h3>Scrolling Positions</h3>
56
57 The scrolling position is the canvas position at the upper left corner
58 of the viewport. Views using viewports must
59
60 <ol>
61 <li> change this value on request (dw::core::View::scrollTo), and
62 <li> tell other changes to the layout, e.g. caused by user events
63 (dw::core::Layout::scrollPosChanged).
64 </ol>
65
66 Applications of scrolling positions (anchors, test search etc.) are
67 handled by the layout, in a way fully transparent to the view.
68
69 <h3>Scrollbars</h3>
70
71 A feature of the viewport size model are scrollbars. There may be a
72 vertical scrollbar and a horizontal scrollbar, displaying the
73 relationship between canvas and viewport height or width,
74 respectively. If they are not needed, they are hidden, to save screen
75 space.
76
77 Since scrollbars decrease the usable space of a view, dw::core::Layout
78 must know how much space they take. The view returns, via
79 dw::core::View::getHScrollbarThickness and
80 dw::core::View::getVScrollbarThickness, how thick they will be, when
81 visible.
82
83 Viewport sizes, which denote the size of the viewport widgets, include
84 scrollbar thicknesses. When referring to the viewport \em excluding
85 the scrollbars space, we will call it "usable viewport size", this is
86 the area, which is used to display the canvas.
87
88 <h2>Drawing</h2>
89
90 A view must implement several drawing methods, which work on the whole
91 canvas. If it is necessary to convert them (e.g. into
92 dw::fltk::FltkViewport), this is done in a way fully transparent to
93 dw::core::Widget and dw::core::Layout, instead, this is done by the
94 view implementation.
95
96 There exist following situations:
97
98 <ul>
99 <li> A view gets an expose event: It will delegate this to the
100 layout (dw::core::Layout::draw), which will then pass it to the
101 widgets (dw::core::Widget::draw), with the view as a parameter.
102 Eventually, the widgets will call drawing methods of the view.
103
104 <li> A widget requests a redraw: In this case, the widget will
105 delegate this to the layout (dw::core::Layout::queueDraw), which
106 delegates it to the view (dw::core::View::queueDraw).
107 Typically, the view will queue these requests for efficiency.
108
109 <li> A widget requests a resize: This case is described below, in short,
110 dw::core::View::queueDrawTotal is called for the view.
111 </ul>
112
113 If the draw method of a widget is implemented in a way that it may
114 draw outside of the widget's allocation, it should draw into a
115 <i>clipping view.</i> A clipping view is a view related to the actual
116 view, which guarantees that the parts drawn outside are discarded. At
117 the end, the clipping view is merged into the actual view. Sample
118 code:
119
120 \code
121 void Foo::draw (dw::core::View *view, dw::core::Rectangle *area)
122 {
123 // 1. Create a clipping view.
124 dw::core::View clipView =
125 view->getClippingView (allocation.x, allocation.y,
126 allocation.width, getHeight ());
127
128 // 2. Draw into clip_view
129 clipView->doSomeDrawing (...);
130
131 // 3. Draw the children, they receive the clipping view as argument.
132 dw::core::Rectangle *childArea
133 for (<all relevant children>) {
134 if (child->intersects (area, &childArea))
135 child->draw (clipView, childArea);
136 }
137
138 // 4. Merge
139 view->mergeClippingView (clipView);
140 }
141 \endcode
142
143 A drawing process is always embedded into calls of
144 dw::core::View::startDrawing and dw::core::View::finishDrawing. An
145 implementation of this may e.g. use backing pixmaps, to prevent
146 flickering.
147
148
149 <h2>Sizes</h2>
150
151 In the simplest case, the view does not have any influence on
152 the canvas size, so it is told about changes of the
153 canvas size by a call to dw::core::View::setCanvasSize. This happens
154 in the following situations:
155
156 <ul>
157 <li> dw::core::Layout::addWidget,
158 <li> dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget),
159 and
160 <li> dw::core::Layout::queueResize (called by
161 dw::core::Widget::queueResize, when a widget itself requests a size
162 change).
163 </ul>
164
165 <h3>Viewports</h3>
166
167 There are two cases where the viewport size changes:
168
169 <ul>
170 <li> As an reaction on a user event, e.g. when the user changes the
171 window size. In this case, the view delegates this
172 change to the layout, by calling
173 dw::core::Layout::viewportSizeChanged.
174
175 <li> The viewport size may also depend on the visibility of UI
176 widgets, which depend on the world size, e.g scrollbars,
177 generally called "viewport markers". This is described in a separate
178 section.
179 </ul>
180
181 After the creation of the layout, the viewport size is undefined. When
182 a view is attached to a layout, and this view can already specify
183 its viewport size, it may call
184 dw::core::Layout::viewportSizeChanged within the implementation of
185 dw::core::Layout::setLayout. If not, it may do this as soon as the
186 viewport size is known.
187
188 Generally, the scrollbars have to be considered. If e.g. an HTML page
189 is rather small, it looks like this:
190
191 \image html dw-viewport-without-scrollbar.png
192
193 If some more data is retrieved, so that the height exceeds the
194 viewport size, the text has to be rewrapped, since the available width
195 gets smaller, due to the vertical scrollbar:
196
197 \image html dw-viewport-with-scrollbar.png
198
199 Notice the different line breaks.
200
201 This means circular dependencies between these different sizes:
202
203 <ol>
204 <li> Whether the scrollbars are visible or not, determines the
205 usable space of the viewport.
206
207 <li> From the usable space of the viewport, the size hints for the
208 toplevel are calculated.
209
210 <li> The size hints for the toplevel widgets may have an effect on its
211 size, which is actually the canvas size.
212
213 <li> The canvas size determines the visibility of the scrollbarss.
214 </ol>
215
216 To make an implementation simpler, we simplify the model:
217
218 <ol>
219 <li> For the calls to dw::core::Widget::setAscent and
220 dw::core::Widget::setDescent, we will always exclude the
221 horizontal scrollbar thickness (i.e. assume the horizontal
222 scrollbar is used, although the visibility is determined correctly).
223
224 <li> For the calls to dw::core::Widget::setWidth, we will calculate
225 the usable viewport width, but with the general assumption, that
226 the widget generally gets higher.
227 </ol>
228
229 This results in the following rules:
230
231 <ol>
232 <li> Send always (when it changes) dw::core::Layout::viewportHeight
233 minus the maximal value of dw::core::View::getHScrollbarThickness as
234 argument to dw::core::Widget::setAscent, and 0 as argument to
235 dw::core::Widget::setDescent.
236
237 <li> There is a flag, dw::core::Layout::canvasHeightGreater, which is set
238 to false in the following cases:
239
240 <ul>
241 <li> dw::core::Layout::addWidget,
242 <li> dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget),
243 and
244 <li> dw::core::Layout::viewportSizeChanged.
245 </ul>
246
247 Whenever the canvas size is calculated (dw::core::Layout::resizeIdle),
248 and dw::core::Layout::canvasHeightGreater is false, a test is made,
249 whether the widget has in the meantime grown that high, that the second
250 argument should be set to true (i.e. the vertical scrollbar gets visible).
251 As soon as and dw::core::Layout::canvasHeightGreater is true, no such test
252 is done anymore.
253 </ol>
254
255 */
0 /** \page dw-layout-widgets Layout and Widgets
1
2 Both, the layouting and the drawing is delegated to a tree of
3 widgets. A widget represents a given part of the document, e.g. a text
4 block, a table, or an image. Widgets may be nested, so layouting and
5 drawing may be delegated by one widget to its child widgets.
6
7 Where to define the borders of a widget, whether to combine different
8 widgets to one, or to split one widget into multiple ones, should be
9 considered based on different concerns:
10
11 <ul>
12 <li> First, there are some restrictions of Dw:
13
14 <ul>
15 <li> The allocation (this is the space a widget allocates at
16 a time) of a dillo widget is always rectangular, and
17 <li> the allocation of a child widget must be a within the allocation
18 of the parent widget.
19 </ul>
20
21 <li> Since some widgets are already rather complex, an important goal
22 is to keep the implementation of the widget simple.
23
24 <li> Furthermore, the granularity should not be too fine, because of the
25 overhead each single widget adds.
26 </ul>
27
28 For CSS, there will be a document tree on top of Dw, this will be
29 flexible enough when mapping the document structure on the widget
30 structure, so you should not have the document structure in mind.
31
32 <h2>Sizes</h2>
33
34 \ref dw-widget-sizes
35
36
37 <h2>Styles</h2>
38
39 Each widget is assigned a style, see dw::core::style for more
40 informations.
41
42
43 <h2>Iterators</h2>
44
45 Widgets must implement dw::core::Widget::iterator. There are several
46 common iterators:
47
48 <ul>
49 <li>dw::core::EmptyIterator, and
50 <li>dw::core::TextIterator.
51 </ul>
52
53 Both hide the constructor, use the \em create method.
54
55 These simple iterators only iterate through one widget, it does not
56 have to iterate recursively through child widgets. Instead, the type
57 dw::core::Content::WIDGET is returned, and the next call of
58 dw::core::Iterator::next will return the piece of contents \em after
59 (not within) this child widget.
60
61 This makes implementation much simpler, for recursive iteration, there
62 is dw::core::DeepIterator.
63
64
65 <h2>Anchors and Scrolling</h2>
66
67 \todo This section is not implemented yet, after the implementation,
68 the documentation should be reviewed.
69
70 Here is a description, what is to be done for a widget
71 implementation. How to jump to anchors, set scrolling positions
72 etc. is described in \ref dw-usage.
73
74 <h3>Anchors</h3>
75
76 Anchors are position markers, which are identified by a name, which is
77 unique in the widget tree. The widget must care about anchors in three
78 different situations:
79
80 <ol>
81 <li> Adding an anchor is inititiated by a specific widget method, e.g.
82 dw::Textblock::addAnchor. Here, dw::core::Widget::addAnchor must be
83 called,
84
85 <li> Whenever the position of an anchor is changed,
86 dw::core::Widget::changeAnchor is called (typically, this is done
87 in the implementation of dw::core::Widget::sizeAllocateImpl).
88
89 <li> When a widget is destroyed, the anchor must be removed, by calling
90 dw::core::Widget::removeAnchor.
91 </ol>
92
93 All these methods are delegated to dw::core::Layout, which manages
94 anchors centrally. If the anchor in question has been set to jump to,
95 the viewport position is automatically adjusted, see \ref
96 dw-usage.
97
98
99 <h2>Drawing</h2>
100
101 In two cases, a widget has to be drawn:
102
103 <ol>
104 <li> as a reaction on an expose event,
105 <li> if the widget content has changed and it needs to be redrawn.
106 </ol>
107
108 In both cases, drawing is done by the implementation of
109 dw::core::Widget::draw, which draws into the view.
110
111
112 Each view provides some primitive methods for drawing, most should be
113 obvious. Note that the views do not know anything about dillo
114 widgets, and so coordinates have to be passed as canvas coordinates.
115
116 A widget may only draw in its own allocation. If this cannot be
117 achieved, a <i>clipping view</i> can be used, this is described in
118 \ref dw-layout-views. Generally, drawing should then look like:
119
120 \code
121 void Foo::draw (dw::core::View *view, dw::core::Rectangle *area)
122 {
123 // 1. Create a clipping view.
124 dw::core::View clipView =
125 view->getClippingView (allocation.x, allocation.y,
126 allocation.width, getHeight ());
127
128 // 2. Draw into clip_view
129 clipView->doSomeDrawing (...);
130
131 // 3. Draw the children, they receive the clipping view as argument.
132 dw::core::Rectangle *childArea
133 for (<all relevant children>) {
134 if (child->intersects (area, &childArea))
135 child->draw (clipView, childArea);
136 }
137
138 // 4. Merge
139 view->mergeClippingView (clipView);
140 }
141 \endcode
142
143 Clipping views are expensive, so they should be avoided when possible.
144
145 The second argument to dw::core::Widget::draw is the region, which has
146 to be drawn. This may (but needs not) be used for optimization.
147
148 If a widget contains child widgets, it must explicitly draw these
149 children (see also code example above). For this, there is the useful
150 method dw::core::Widget::intersects, which returns, which area of the
151 child must be drawn.
152
153 <h3>Explicit Redrawing</h3>
154
155 If a widget changes its contents, so that it must be redrawn, it must
156 call dw::core::Widget::queueDrawArea or
157 dw::core::Widget::queueDraw. The first variant expects a region within
158 the widget, the second will cause the whole widget to be redrawn. This
159 will cause an asynchronous call of dw::core::Widget::draw.
160
161 If only the size changes, a call to dw::core::Widget::queueResize is
162 sufficient, this will also queue a complete redraw (see \ref
163 dw-widget-sizes.)
164
165
166 <h2>Mouse Events</h2>
167
168 A widget may process mouse events. The view (\ref dw-layout-views)
169 passes mouse events to the layout, which then passes them to the
170 widgets. There are two kinds of mouse events:
171
172 <ul>
173 <li>events returning bool, and
174 <li>events returning nothing (void).
175 </ul>
176
177 The first group consists of:
178
179 <ul>
180 <li>dw::core::Widget::buttonPressImpl,
181 <li>dw::core::Widget::buttonReleaseImpl, and
182 <li>dw::core::Widget::motionNotifyImpl.
183 </ul>
184
185 For these events, a widget returns a boolean value, which denotes,
186 whether the widget has processed this event (true) or not (false). In
187 the latter case, the event is delegated according to the following
188 rules:
189
190 <ol>
191 <li> First, this event is passed to the bottom-most widget, in which
192 allocation the mouse position is in.
193 <li> If the widget does not process this event (returning false), it is
194 passed to the parent, and so on.
195 <li> The final result (whether \em any widget has processed this event) is
196 returned to the view.
197 </ol>
198
199 The view may return this to the UI toolkit, which then interprets this
200 in a similar way (whether the viewport, a UI widget, has processed
201 this event).
202
203 These events return nothing:
204
205 <ul>
206 <li>dw::core::Widget::enterNotifyImpl and
207 <li>dw::core::Widget::leaveNotifyImpl.
208 </ul>
209
210 since they are bound to a widget.
211
212 When processing mouse events, the layout always deals with two
213 widgets: the widget, the mouse pointer was in, when the previous mouse
214 event was processed, (below called the "old widget") and the widget,
215 in which the mouse pointer is now ("new widget").
216
217 The following paths are calculated:
218
219 <ol>
220 <li> the path from the old widget to the nearest common ancestor of the old
221 and the new widget, and
222 <li> the path from this ancestor to the new widget.
223 </ol>
224
225 For the widgets along these paths, dw::core::Widget::enterNotifyImpl
226 and dw::core::Widget::leaveNotifyImpl are called.
227
228 <h3>Signals</h3>
229
230 If a caller outside of the widget is interested in these events, he
231 can connect a dw::core::Layout::LinkReceiver. For those events with a
232 boolean return value, the results of the signal emission is regarded,
233 i.e. the delegation of an event to the parent of the widget can be
234 stopped by a signal receiver returning true, even if the widget method
235 returns false.
236
237 First, the widget method is called, then (in any case) the signal is
238 emitted.
239
240 <h3>Selection</h3>
241
242 If your widget has selectable contents, it should delegate the events
243 to dw::core::SelectionState (dw::core::Layout::selectionState).
244
245
246 <h2>Miscellaneous</h2>
247
248 <h3>Cursors</h3>
249
250 Each widget has a cursor, which is set by
251 dw::core::Widget::setCursor. If a cursor is assigned to a part of a
252 widget, this widget must process mouse events, and call
253 dw::core::Widget::setCursor explicitly.
254
255 (This will change, cursors should become part of
256 dw::core::style::Style.)
257
258 <h3>Background</h3>
259
260 Backgrounds are part of styles
261 (dw::core::style::Style::backgroundColor). If a widget assigns
262 background colors to parts of a widget (as dw::Table does for rows),
263 it must call dw::core::Widget::setBgColor for the children inside this
264 part.
265
266 */
0 /** \page dw-map Dillo Widget Documentation Map
1
2 This maps includes special documentations as well as longer comments
3 in the sources. Arrows denote references between the documents.
4
5 \dot
6 digraph G {
7 rankdir=LR;
8 node [shape=record, fontname=Helvetica, fontsize=8];
9 fontname=Helvetica; fontsize=8;
10
11 dw_overview [label="Dillo Widget Overview", URL="\ref dw-overview"];
12 dw_usage [label="Dillo Widget Usage", URL="\ref dw-usage"];
13 dw_layout_views [label="Layout and Views", URL="\ref dw-layout-views"];
14 dw_layout_widgets [label="Layout and Widgets",
15 URL="\ref dw-layout-widgets"];
16 dw_widget_sizes [label="Sizes of Dillo Widgets",
17 URL="\ref dw-widget-sizes"];
18 dw_changes [label="Changes to the GTK+-based Release Version",
19 URL="\ref dw-changes"];
20 dw_images_and_backgrounds [label="Images and Backgrounds in Dw",
21 URL="\ref dw-images-and-backgrounds"];
22 dw_Image [label="dw::Image", URL="\ref dw::Image"];
23 dw_core_Imgbuf [label="dw::core::Imgbuf", URL="\ref dw::core::Imgbuf"];
24 dw_core_SelectionState [label="dw::core::SelectionState",
25 URL="\ref dw::core::SelectionState"];
26 dw_core_style [label="dw::core::style", URL="\ref dw::core::style"];
27 dw_Table [label="dw::Table", URL="\ref dw::Table"];
28 dw_Textblock [label="dw::Textblock", URL="\ref dw::Textblock"];
29 dw_core_ui [label="dw::core::ui", URL="\ref dw::core::ui"];
30
31 dw_overview -> dw_changes;
32 dw_overview -> dw_usage;
33 dw_overview -> dw_core_style;
34 dw_overview -> dw_core_ui;
35 dw_overview -> dw_images_and_backgrounds;
36 dw_overview -> dw_layout_widgets;
37 dw_overview -> dw_widget_sizes;
38 dw_overview -> dw_layout_views;
39
40 dw_usage -> dw_Table;
41 dw_usage -> dw_Textblock;
42 dw_usage -> dw_core_style;
43 dw_usage -> dw_core_ui;
44 dw_usage -> dw_images_and_backgrounds;
45
46 dw_layout_widgets -> dw_widget_sizes;
47 dw_layout_widgets -> dw_core_SelectionState;
48
49 dw_widget_sizes -> dw_Table;
50 dw_widget_sizes -> dw_Textblock;
51
52 dw_images_and_backgrounds -> dw_core_Imgbuf;
53 dw_images_and_backgrounds -> dw_Image;
54
55 dw_core_style -> dw_Textblock;
56 }
57 \enddot
58 */
0 /** \page dw-overview Dillo Widget Overview
1
2 Note: If you are already familiar with the Gtk+-based version of Dw,
3 read \ref dw-changes.
4
5
6 The module Dw (Dillo Widget) is responsible for the low-level rendering of
7 all resources, e.g. images, plain text, and HTML pages (this is the
8 most complex type). Dw is \em not responsible for parsing HTML, or
9 decoding image data. Furthermore, the document tree, which is planned
10 for CSS, is neither a part of Dw, instead, it is a new module on top
11 of Dw.
12
13 The rendering, as done by Dw, is split into two phases:
14
15 <ul>
16 <li> the \em layouting, this means calculating the exact positions of
17 words, lines, etc. (in pixel position), and
18 <li> the \em drawing, i.e. making the result of the layouting visible
19 on the screen.
20 </ul>
21
22 The result of the layouting allocates an area, which is called
23 \em canvas.
24
25 <h2>Structure</h2>
26
27 The whole Dw module can be split into the following parts:
28
29 \dot
30 digraph G {
31 node [shape=record, fontname=Helvetica, fontsize=10];
32 edge [arrowhead="open", fontname=Helvetica, fontsize=10,
33 labelfontname=Helvetica, labelfontsize=10,
34 color="#404040", labelfontcolor="#000080"];
35
36 subgraph cluster_core {
37 style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
38 label="Platform independent core";
39
40 Layout [URL="\ref dw::core::Layout"];
41 Platform [URL="\ref dw::core::Platform", color="#ff8080"];
42 View [URL="\ref dw::core::View", color="#ff8080"];
43 Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
44 }
45
46 subgraph cluster_fltk {
47 style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
48 label="FLTK specific part (as an\nexample for the platform specific\n\
49 implementations)";
50
51 subgraph cluster_fltkcore {
52 style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
53 label="FLTK core";
54
55 FltkPlatform [URL="\ref dw::fltk::FltkPlatform"];
56 FltkView [URL="\ref dw::fltk::FltkView", color="#ff8080"];
57 }
58
59 FltkViewport [URL="\ref dw::fltk::FltkViewport"];
60 FltkPreview [URL="\ref dw::fltk::FltkPreview"];
61 }
62
63 subgraph cluster_widgets {
64 style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
65 label="Platform independent widgets";
66
67 Textblock [URL="\ref dw::Textblock"];
68 AlignedTextblock [URL="\ref dw::AlignedTextblock", color="#a0a0a0"];
69 Table [URL="\ref dw::Table"];
70 Image [URL="\ref dw::Image"];
71 etc1 [label="..."];
72 etc2 [label="..."];
73 }
74
75 Layout -> Platform [headlabel="1", taillabel="1"];
76 Layout -> View [headlabel="*", taillabel="1"];
77
78 Layout -> Widget [headlabel="1", taillabel="1", label="topLevel"];
79 Widget -> Widget [headlabel="*", taillabel="1", label="children"];
80
81 Widget -> Textblock [arrowhead="none", arrowtail="empty"];
82 Widget -> Table [arrowhead="none", arrowtail="empty"];
83 Widget -> Image [arrowhead="none", arrowtail="empty"];
84 Widget -> etc1 [arrowhead="none", arrowtail="empty"];
85 Textblock -> AlignedTextblock [arrowhead="none", arrowtail="empty"];
86 AlignedTextblock -> etc2 [arrowhead="none", arrowtail="empty"];
87
88 Platform -> FltkPlatform [arrowhead="none", arrowtail="empty",
89 style="dashed"];
90 FltkPlatform -> FltkView [headlabel="*", taillabel="1"];
91
92 View -> FltkView [arrowhead="none", arrowtail="empty"];
93 FltkView -> FltkViewport [arrowhead="none", arrowtail="empty",
94 style="dashed"];
95 FltkView -> FltkPreview [arrowhead="none", arrowtail="empty",
96 style="dashed"];
97 }
98 \enddot
99
100 <center>[\ref uml-legend "legend"]</center>
101
102 \em Platform means in most cases the underlying UI toolkit
103 (e.g. FLTK). A layout is bound to a specific platform, but multiple
104 platforms may be handled in one program.
105
106 A short overview:
107
108 <ul>
109 <li> dw::core::Layout is the central class, it manages the widgets and the
110 view, and provides delegation methods for the platform.
111
112 <li> The layouting is done by a tree of widgets (details are described in
113 \ref dw-layout-widgets), also the drawing, which is finally delegated
114 to the view.
115
116 <li> The view (implementation of dw::core::View) provides primitive methods
117 for drawing, but also have an influence on
118 the canvas size (via size hints). See \ref dw-layout-views for details.
119
120 <li> Some platform dependencies are handled by implementations
121 of dw::core::Platform.
122 </ul>
123
124
125 <h3>Header Files</h3>
126
127 The structures mentioned above can be found in the following header
128 files:
129
130 <ul>
131 <li> Anything from the Dw core in core.hh. Do not include the single files.
132
133 <li> The single widgets can be found in the respective header files, e.g.
134 image.hh for dw::Image.
135
136 <li> The core of the FLTK implementation is defined in fltkcore.hh. This
137 includes dw::fltk::FltkPlatform, dw::fltk::FltkView, but not the concrete
138 view implementations.
139
140 <li> The views can be found in single header files, e.g fltkviewport.hh for
141 dw::fltk::FltkViewport.
142 </ul>
143
144
145 <h2>Further Documentations</h2>
146
147 A complete map can be found at \ref dw-map.
148
149 <ul>
150 <li> For learning, how to use Dw, read \ref dw-usage and related documents,
151 dw::core::style, dw::core::ui and \ref dw-images-and-backgrounds.
152 <li> Advanced topics are described in \ref dw-layout-widgets,
153 \ref dw-widget-sizes and \ref dw-layout-views.
154 </ul>
155
156 */
Binary diff not shown
Binary diff not shown
0 /** \page dw-usage Dillo Widget Usage
1
2 This document describes the usage of Dw, without going too much into
3 detail.
4
5
6 <h2>Getting Started</h2>
7
8 In this section, a small runnable example is described, based on the
9 FLTK implementation.
10
11 As described in \ref dw-overview, the following objects are needed:
12
13 <ul>
14 <li> dw::core::Layout,
15 <li> an implementation of dw::core::Platform (we will use
16 dw::fltk::FltkPlatform),
17 <li> at least one implementation of dw::core::View (dw::fltk::FltkViewport),
18 and
19 <li> some widgets (for this example, only a simple dw::Textblock).
20 </ul>
21
22 First of all, the necessary \#include's:
23
24 \code
25 #include <FL/Fl_Window.H>
26 #include <FL/Fl.H>
27
28 #include "dw/core.hh"
29 #include "dw/fltkcore.hh"
30 #include "dw/fltkviewport.hh"
31 #include "dw/textblock.hh"
32 \endcode
33
34 Everything is put into one function:
35
36 \code
37 int main(int argc, char **argv)
38 {
39 \endcode
40
41 As the first object, the platform is instantiated:
42
43 \code
44 dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform ();
45 \endcode
46
47 Then, the layout is created, with the platform attached:
48
49 \code
50 dw::core::Layout *layout = new dw::core::Layout (platform);
51 \endcode
52
53 For the view, we first need a FLTK window:
54
55 \code
56 Fl_Window *window = new Fl_Window(200, 300, "Dw Example");
57 window->begin();
58 \endcode
59
60 After this, we can create a viewport, and attach it to the layout:
61
62 \code
63 dw::fltk::FltkViewport *viewport =
64 new dw::fltk::FltkViewport (0, 0, 200, 300);
65 layout->attachView (viewport);
66 \endcode
67
68 Each widget needs a style (dw::core::style::Style, see dw::core::style),
69 so we construct it here. For this, we need to fill a
70 dw::core::style::StyleAttrs structure with values, and call
71 dw::core::style::Style::create (latter is done further below):
72
73 \code
74 dw::core::style::StyleAttrs styleAttrs;
75 styleAttrs.initValues ();
76 styleAttrs.margin.setVal (5);
77 \endcode
78
79 dw::core::style::StyleAttrs::initValues sets several default
80 values. The last line sets a margin of 5 pixels. Next, we need a
81 font. Fonts are created in a similar way, first, the attributes are
82 defined:
83
84 \code
85 dw::core::style::FontAttrs fontAttrs;
86 fontAttrs.name = "Bitstream Charter";
87 fontAttrs.size = 14;
88 fontAttrs.weight = 400;
89 fontAttrs.style = dw::core::style::FONT_STYLE_NORMAL;
90 fontAttrs.letterSpacing = 0;
91 fontAttrs.fontVariant = dw::core::style::FONT_VARIANT_NORMAL;
92 \endcode
93
94 Now, the font can be created:
95
96 \code
97 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
98 \endcode
99
100 As the last attributes, the background and forground colors are
101 defined, here dw::core::style::Color::createSimple must be called:
102
103 \code
104 styleAttrs.color =
105 dw::core::style::Color::create (layout, 0x000000);
106 styleAttrs.backgroundColor =
107 dw::core::style::Color::create (layout, 0xffffff);
108 \endcode
109
110 Finally, the style for the widget is created:
111
112 \code
113 dw::core::style::Style *widgetStyle =
114 dw::core::style::Style::create (layout, &styleAttrs);
115 \endcode
116
117 Now, we create a widget, assign a style to it, and set it as the
118 toplevel widget of the layout:
119
120 \code
121 dw::Textblock *textblock = new dw::Textblock (false);
122 textblock->setStyle (widgetStyle);
123 layout->setWidget (textblock);
124 \endcode
125
126 The style is not needed anymore (a reference is added in
127 dw::core::Widget::setStyle), so it should be unreferred:
128
129 \code
130 widgetStyle->unref();
131 \endcode
132
133 Now, some text should be added to the textblock. For this, we first
134 need another style. \em styleAttrs can still be used for this. We set
135 the margin to 0, and the background color to "transparent":
136
137 \code
138 styleAttrs.margin.setVal (0);
139 styleAttrs.backgroundColor = NULL;
140
141 dw::core::style::Style *wordStyle =
142 dw::core::style::Style::create (layout, &styleAttrs);
143 \endcode
144
145 This loop adds some paragraphs:
146
147 \code
148 for(int i = 1; i <= 10; i++) {
149 char buf[4];
150 sprintf(buf, "%d.", i);
151
152 char *words[] = { "This", "is", "the", buf, "paragraph.",
153 "Here", "comes", "some", "more", "text",
154 "to", "demonstrate", "word", "wrapping.",
155 NULL };
156
157 for(int j = 0; words[j]; j++) {
158 textblock->addText(strdup(words[j]), wordStyle);
159 \endcode
160
161 Notice the \em strdup, dw::Textblock::addText will feel responsible
162 for the string, and free the text at the end. (This has been done to
163 avoid some overhead in the HTML parser.)
164
165 The rest is simple, it also includes spaces (which also have styles):
166
167 \code
168 textblock->addSpace(wordStyle);
169 }
170 \endcode
171
172 Finally, a paragraph break is added, which is 10 pixels high:
173
174 \code
175 textblock->addParbreak(10, wordStyle);
176 }
177 \endcode
178
179 Again, this style should be unreferred:
180
181 \code
182 wordStyle->unref();
183 \endcode
184
185 After adding text, this method should always be called (for faster
186 adding large text blocks):
187
188 \code
189 textblock->flush ();
190 \endcode
191
192 Some FLTK stuff to finally show the window:
193
194 \code
195 window->resizable(viewport);
196 window->show();
197 int errorCode = Fl::run();
198 \endcode
199
200 For cleaning up, it is sufficient to destroy the layout:
201
202 \code
203 delete layout;
204 \endcode
205
206 And the rest
207
208 \code
209 return errorCode;
210 }
211 \endcode
212
213 If you compile and start the program, you should see the following:
214
215 \image html dw-example-screenshot.png
216
217 Try to scroll, or to resize the window, you will see, that everything
218 is done automatically.
219
220 Of course, creating new widgets, adding text to widgets etc. can also
221 be done while the program is running, i.e. after fltk::run has been
222 called, within timeouts, idles, I/O functions etc. Notice that Dw is
223 not thread safe, so that everything should be done within one thread.
224
225 With the exception, that you have to call dw::Textblock::flush,
226 everything gets immediately visible, within reasonable times; Dw has
227 been optimized for frequent updates.
228
229
230 <h2>List of all Widgets</h2>
231
232 These widgets are used within dillo:
233
234 <ul>
235 <li>dw::core::ui::Embed
236 <li>dw::AlignedTextblock
237 <li>dw::Bullet
238 <li>dw::Ruler
239 <li>dw::Image
240 <li>dw::ListItem
241 <li>dw::Table
242 <li>dw::TableCell
243 <li>dw::Textblock
244 </ul>
245
246 If you want to create a new widget, refer to \ref dw-layout-widgets.
247
248
249 <h2>List of Views</h2>
250
251 There are three dw::core::View implementations for FLTK:
252
253 <ul>
254 <li> dw::fltk::FltkViewport implements a viewport, which is used in the
255 example above.
256
257 <li> dw::fltk::FltkPreview implements a preview window, together with
258 dw::fltk::FltkPreviewButton, it is possible to have a scaled down
259 overview of the whole canvas.
260
261 <li> dw::fltk::FltkFlatView is a "flat" view, i.e. it does not support
262 scrolling. It is used for HTML buttons, see
263 dw::fltk::ui::FltkComplexButtonResource and especially
264 dw::fltk::ui::FltkComplexButtonResource::createNewWidget for details.
265 </ul>
266
267 More informations about views in general can be found in \ref
268 dw-layout-views.
269
270
271 <h2>Iterators</h2>
272
273 For examining generally the contents of widgets, there are iterators
274 (dw::core::Iterator), created by the method
275 dw::core::Widget::iterator (see there for more details).
276
277 These simple iterators only iterate through one widget, and return
278 child widgets as dw::core::Content::WIDGET. The next call of
279 dw::core::Iterator::next will return the piece of contents \em after
280 (not within) this child widget.
281
282 If you want to iterate through the whole widget trees, there are two
283 possibilities:
284
285 <ol>
286 <li> Use a recursive function. Of course, with this approach, you are
287 limited by the program flow.
288
289 <li> Maintain a stack of iterators, so you can freely pass this stack
290 around. This is already implemented, as dw::core::DeepIterator.
291 </ol>
292
293 As an example, dw::core::SelectionState represents the selected region
294 as two instances of dw::core::DeepIterator.
295
296
297 <h2>Finding Text</h2>
298
299 See dw::core::Layout::findtextState and dw::core::FindtextState
300 (details in the latter). There are delegation methods:
301
302 <ul>
303 <li> dw::core::Layout::search and
304 <li> dw::core::Layout::resetSearch.
305 </ul>
306
307
308 <h2>Anchors and Scrolling</h2>
309
310 In some cases, it is necessary to scroll to a given position, or to
311 an anchor, programmatically.
312
313 <h3>Anchors</h3>
314
315 Anchors are defined by widgets, e.g. dw::Textblock defines them, when
316 dw::Textblock::addAnchor is called. To jump to a specific anchor
317 within the current widget tree, use dw::core::Layout::setAnchor.
318
319 This can be done immediately after assignig a toplevel widget, even
320 when the anchor has not yet been defined. The layout will remember the
321 anchor, and jump to the respective position, as soon as possible. Even
322 if the anchor position changes (e.g., when an anchor is moved
323 downwards, since some space is needed for an image in the text above),
324 the position is corrected.
325
326 As soon as the user scrolls the viewport, this correction is not done
327 anymore. If in dillo, the user request a page with an anchor, which is
328 quite at the bottom of the page, he may be get interested in the text
329 at the beginning of the page, and so scrolling down. If then, after
330 the anchor has been read and added to the dw::Textblock, this anchor
331 would be jumped at, the user would become confused.
332
333 The anchor is dismissed, too, when the toplevel widget is removed
334 again.
335
336 \todo Currently, anchors only define vertical positions.
337
338 <h3>Scrolling</h3>
339
340 To scroll to a given position, use the method
341 dw::core::Layout::scrollTo. It expects several parameters:
342
343 <ul>
344 <li>a horizontal adjustment parameter, defined by dw::core::HPosition,
345 <li>a vertical adjustment parameter, defined by dw::core::VPosition, and
346 <li>a rectangle (\em x, \em y, \em width and \em heigh) of the region
347 to be adjusted.
348 </ul>
349
350 If you just want to move the canvas coordinate (\em x, \em y) into the
351 upper left corner of the viewport, you can call:
352
353 \code
354 dw::core::Layout *layout;
355 // ...
356 layout->scrollTo(dw::core::HPOS_LEFT, dw::core::VPOS_TOP, 0, 0, 0, 0);
357 \endcode
358
359 By using dw::core::HPOS_NO_CHANGE or dw::core::VPOS_NO_CHANGE, you can
360 change only one dimension. dw::core::HPOS_INTO_VIEW and
361 dw::core::VPOS_INTO_VIEW will cause the viewport to move as much as
362 necessary, that the region is visible in the viewport (this is
363 e.g. used for finding text).
364
365
366 <h2>Further Documentations</h2>
367
368 <ul>
369 <li> dw::core::style
370 <li> dw::core::ui
371 <li> \ref dw-images-and-backgrounds
372 </ul>
373
374 */
0 /** \page dw-widget-sizes Sizes of Dillo Widgets
1
2 <h2>Allocation</h2>
3
4 Each widget has an \em allocation at a given time, this includes
5
6 <ul>
7 <li> the position (\em x, \em y) relative to the upper left corner of the
8 canvas, and
9 <li> the size (\em width, \em ascent, \em descent).
10 </ul>
11
12 The \em canvas is the whole area available for the widgets, in most
13 cases, only a part is seen in a viewport. The allocation of the toplevel widget is exactly the allocation of the canvas, i.e.
14
15 <ul>
16 <li> the position of the toplevel widget is always (0, 0), and
17 <li> the canvas size is defined by the size of the toplevel widget.
18 </ul>
19
20 The size of a widget is not simply defined by the width and the
21 height, instead, widgets may have a base line, and so are vertically
22 divided into an ascender (which height is called \em ascent), and a
23 descender (which height is called \em descent). The total height is so
24 the sum of \em ascent and \em descent.
25
26 Sizes of zero are allowed. The upper limit for the size of a widget is
27 defined by the limits of the C++ type \em int.
28
29 \image html dw-size-of-widget.png Allocation of a Widget
30
31 In the example in the image, the widget has the following allocation:
32
33 <ul>
34 <li>\em x = 50
35 <li>\em y = 50
36 <li>\em width = 150
37 <li>\em ascent = 150
38 <li>\em descent = 100
39 </ul>
40
41 The current allocation of a widget is hold in
42 dw::core::Widget::allocation. It can be set from outside by
43 calling dw::core::Widget::sizeAllocate. This is a concrete method,
44 which will call dw::core::Widget::sizeAllocateImpl (see code of
45 dw::core::Widget::sizeAllocate for details).
46
47 For trivial widgets (like dw::Bullet),
48 dw::core::Widget::sizeAllocateImpl does not need to be
49 implemented. For more complex widgets, the implementation should call
50 dw::core::Widget::sizeAllocate (not
51 dw::core::Widget::sizeAllocateImpl) on all child widgets, with
52 appropriate child allocations. dw::core::Widget::allocation should not
53 be changed here, this is already done in
54 dw::core::Widget::sizeAllocate.
55
56 <h2>Requisitions</h2>
57
58 A widget may prefer a given size for the allocation. This size, the
59 \em requisition, should be returned by the method
60 dw::core::Widget::sizeRequestImpl. In the simplest case, this is
61 independent of the context, e.g. for an
62 image. dw::Image::sizeRequestImpl returns the following size:
63
64 <ul>
65 <li> If no buffer has yet been assigned (see dw::Image for more details),
66 the size necessary for the alternative text is returned. If no
67 alternative text has been set, zero is returned.
68
69 <li> If a buffer has been assigned (by dw::Image::setBuffer), the root
70 size is returned (i.e. the original size of the image to display).
71 </ul>
72
73 This is a bit simplified, dw::Image::sizeRequestImpl should also deal
74 with margins, borders and paddings, see dw::core::style.
75
76 From the outside, dw::Image::sizeRequest should be called, which does
77 a bit of optimization. Notice that in dw::Image::sizeRequestImpl, no
78 optimization like lazy evaluation is necessary, this is already done
79 in dw::Image::sizeRequest.
80
81 A widget, which has children, will likely call dw::Image::sizeRequest
82 on its children, to calculate the total requisition.
83
84 The caller (this is either the dw::core::Layout, or the parent
85 widget), may, but also may not consider the requisition. Instead, a
86 widget must deal with any allocation. (For example, dw::Image scales
87 the image buffer when allocated at another size.)
88
89 <h2>Size Hints</h2>
90
91 Some widgets do not have an inherent size, but depend on the context,
92 e.g. the viewport size. These widgets should adhere to <i>size hints</i>,
93 i.e. implement the methods dw::core::Widget::setWidth,
94 dw::core::Widget::setAscent and dw::core::Widget::setDescent. The values
95 passed to the callees are
96
97 <ul>
98 <li> the viewport size (ascent is the heigt here, while descent is 0) for
99 the toplevel widget, and
100 <li> determined by the parent for its child widgets.
101 </ul>
102
103 Generally, the values should define the available space for the
104 widget.
105
106 A widget, which depends on size hints, should call
107 dw::core::Widget::queueResize, when apropriate.
108
109 \todo There should be a definition of "available space".
110
111 <h2>Width Extremes</h2>
112
113 dw::Table uses width extremes for fast calculation of column
114 widths. The structure dw::core::Extremes represents the minimal and
115 maximal width of a widget, as defined by:
116
117 <ul>
118 <li> the minimal width is the smallest width, at which a widget can still
119 display contents, and
120 <li> the maximal width is the largest width, above which increasing the width
121 does not make any sense.
122 </ul>
123
124 Especially the latter is vaguely defined, here are some examples:
125
126 <ul>
127 <li> For those widgets, which do not depend on size hints, the minimal and
128 the maximal width is the inherent width (the one returned by
129 dw::core::Widget::sizeRequest).
130
131 <li> For a textblock, the minimal width is the width of the widest
132 (unbreakable) word, the maximal width is the width of the total
133 paragraph (stretching a paragraph further would only waste space).
134 Actually, the implementation of dw::Textblock::getExtremesImpl is
135 a bit more complex.
136
137 <li> dw::Table is an example, where the width extremes are calculated
138 from the width extremes of the children.
139 </ul>
140
141 Handling width extremes is similar to handling requisitions, a widget
142 must implement dw::core::Widget::getExtremesImpl, but a caller will
143 use dw::core::Widget::getExtremes.
144
145
146 <h2>Resizing</h2>
147
148 When the widget changes its size (requisition), it should call
149 dw::core::Widget::queueResize. The next call of
150 dw::core::Widget::sizeRequestImpl should then return the new
151 size. See dw::Image::setBuffer as an example.
152
153 Interna are described in the code of dw::core::Widget::queueResize.
154
155 <h3>Incremental Resizing</h3>
156
157 A widget may calculate its size based on size calculations already
158 done before. In this case, a widget must exactly know the reasons, why
159 a call of dw::core::Widget::sizeRequestImpl is necessary. To make use
160 of this, a widget must implement the following:
161
162 <ol>
163 <li> There is a member dw::core::Widget::parentRef, which is
164 totally under control of the parent widget (and so sometimes not
165 used at all). It is necessary to define how parentRef is used
166 by a specific parent widget, and it has to be set to the correct
167 value whenever necessary.
168
169 <li> The widget must implement dw::core::Widget::markSizeChange and
170 dw::core::Widget::markExtremesChange, these methods are called in
171 two cases:
172
173 <ol>
174 <li> directly after dw::core::Widget::queueResize, with the argument
175 ref was passed to dw::core::Widget::queueResize, and
176 <li> if a child widget has called dw::core::Widget::queueResize,
177 with the value of the parent_ref member of this child.
178 </ol>
179 </ol>
180
181 This way, a widget can exactly keep track on size changes, and so
182 implement resizing in a faster way. A good example on how to use this
183 is dw::Textblock.
184
185 */
0 /** \page fltk-problems Problems with FLTK
1
2 <h2>dw::fltk::FltkViewport</h2>
3
4 Current problems:
5
6 <ul>
7 <li> How should dw::fltk::FltkViewport::cancelQueueDraw be implemented?
8
9 <li> If the value of a scrollbar is changed by the program, not the user,
10 the callback seems not to be called. Can this be assured?
11
12 <li> The same for dw::fltk::FltkViewport::layout?
13
14 <li> Also, the problems with the widgets seems to work. Also sure?
15
16 <li> When drawing, clipping of 32 bit values is not working properly.
17
18 <li> The item group within a selection widget (menu) should not be selectable.
19 </ul>
20
21
22 <h2>dw::fltk::FltkPlatform</h2>
23
24 <ul>
25 <li> There is the problem, that fltk::font always returns a font, the
26 required one, or a replacements. The latter is not wanted in all
27 cases, e.g. when several fonts are tested. Perhaps, this could be
28 solved by searching in the font list. <i>[This was true of fltk2.
29 What is the state of font handling now with fltk-1.3?]</i>
30
31 <li> Distinction between italics and oblique would be nice
32 (dw::fltk::FltkFont::FltkFont).
33 </ul>
34
35
36 <h2>dw::fltk::ui::FltkCheckButtonResource</h2>
37
38 Groups of Fl_Radio_Button must be added to one Fl_Group, which is
39 not possible in this context. There are two alternatives:
40
41 <ol>
42 <li>there is a more flexible way to group radio buttons, or
43 <li>radio buttons are not grouped, instead, grouping (especially
44 unchecking other buttons) is done by the application.
45 </ol>
46
47 (This is mostly solved.)
48
49 <h2>dw::fltk::FltkImgbuf</h2>
50
51 Alpha transparency should be best abstracted by FLTK itself. If not,
52 perhaps different implementations for different window systems could
53 be used. Then, it is for X necessary to use GCs with clipping masks.
54
55
56 <h2>dw::fltk::ui::ComplexButton</h2>
57
58 Unfortunately, FLTK does not provide a button with Fl_Group as parent, so
59 that children may be added to the button. dw::fltk::ui::ComplexButton does
60 exactly this, and is, in an ugly way, a modified copy of the FLTK
61 button.
62
63 It would be nice, if this is merged with the standard FLTK
64 button. Furthermore, setting the type is strange.
65
66 If the files do not compile, it may be useful to create a new one from
67 the FLTK source:
68
69 <ol>
70 <li> Copy Fl_Button.H from FLTK to dw/fltkcomplexbutton.hh and
71 src/Button.cxx to dw/fltkcomplexbutton.cc.
72
73 <li> In both files, rename "Button" to "ComplexButton". Automatic replacing
74 should work.
75
76 <li> Apply the changes below.
77 </ol>
78
79 The following changes should be applied manually.
80
81 <h3>Changes in fltkcomplexbutton.hh</h3>
82
83 First of all, the \#define's for avoiding multiple includes:
84
85 \code
86 -#ifndef fltk_ComplexButton_h // fltk_Button_h formerly
87 -#define fltk_ComplexButton_h
88 +#ifndef __FLTK_COMPLEX_BUTTON_HH__
89 +#define __FLTK_COMPLEX_BUTTON_HH__
90 \endcode
91
92 at the beginning and
93
94 \code
95 -#endif
96 +#endif // __FLTK_COMPLEX_BUTTON_HH__
97 \endcode
98
99 at the end. Then, the namespace is changed:
100
101 \code
102 -namespace fltk {
103 +namespace dw {
104 +namespace fltk {
105 +namespace ui {
106 \endcode
107
108 at the beginning and
109
110 \code
111 -}
112 +} // namespace ui
113 +} // namespace fltk
114 +} // namespace dw
115 \endcode
116
117 at the end. Most important, the base class is changed:
118
119 \code
120 -#include "FL/Fl_Widget.H"
121 +#include <FL/Fl_Group.H>
122 \endcode
123
124 and
125
126 \code
127 -class FL_API ComplexButton : public Fl_Widget {
128 +class ComplexButton: public Fl_Group
129 +{
130 \endcode
131
132 Finally, for dw::fltk::ui::ComplexButton::default_style, there is a
133 namespace conflict:
134
135 \code
136 - static NamedStyle* default_style;
137 + static ::fltk::NamedStyle* default_style;
138 \endcode
139
140 <h3>Changes in fltkcomplexbutton.cc</h3>
141
142 First, \#include's:
143
144 \code
145
146 #include <FL/Fl.H>
147 -#include <FL/ComplexButton.h> // <FL/Fl_Button.H> formerly
148 #include <FL/Fl_Group.H>
149 #include <FL/Fl_Window.H>
150 +
151 +#include "fltkcomplexbutton.hh"
152 \endcode
153
154 Second, namespaces:
155
156 \code
157 +using namespace dw::fltk::ui;
158 \endcode
159
160 Since the base class is now Fl_Group, the constructor must be changed:
161
162 \code
163 -ComplexButton::ComplexButton(int x,int y,int w,int h, const char *l) : Fl_Widget(x,y,w,h,l) {
164 +ComplexButton::ComplexButton(int x,int y,int w,int h, const char *l) :
165 + Fl_Group(x,y,w,h,l)
166 +{
167 \endcode
168
169 Finally, the button must draw its children (end of
170 dw::fltk::ui::ComplexButton::draw()):
171
172 \code
173 +
174 + for (int i = children () - 1; i >= 0; i--)
175 + draw_child (*child (i));
176 }
177 \endcode
178
179 */
0 /** \mainpage
1
2 <h2>Overview</h2>
3
4 This is a list of documents to start with:
5
6 <ul>
7 <li> \ref lout
8 <li> \ref dw-overview (map at \ref dw-map)
9 </ul>
10
11 Currently, a document \ref fltk-problems is maintained, ideally, it
12 will be removed soon.
13
14 <h2>Historical</h2>
15
16 <h3>Replacements for GTK+ and GLib</h3>
17
18 There are several classes etc., which are used for tasks formerly (in the GTK+
19 version of dillo) achieved by GtkObject (in 1.2.x, this is part of Gtk+) and
20 GLib. For an overview on all this, take a look at \ref lout.
21
22 GtkObject is replaced by the following:
23
24 <ul>
25 <li> object::Object is a common base class for many classes used dillo. In
26 the namespace ::object, there are also some more common classes and
27 interfaces.
28
29 <li> A sub class of object::Object is identity::IdentifiableObject, which
30 allows to determine the class at run-time (equivalent to GTK_CHECK_CAST
31 in GtkObject).
32
33 <li> For signals, there is the namespace ::signal.
34 </ul>
35
36 Hash tables, linked lists etc. can be found in the ::container namespace,
37 several useful macros from GLib have been implemented as inline functions
38 in the ::misc namespace.
39
40 As an alternative to the macros defined in list.h, there is also a template
41 class, misc::SimpleVector, which does the same.
42
43 <h3>Changes in Dw</h3>
44
45 If you have been familiar with Dw before, take a look at \ref dw-changes.
46
47 */
0 /** \page lout Lots of Useful Tools
1
2 In the "lout" directory, there are some common base functionality for
3 C++. Most is described as doxygen comments, this text gives an
4 overview.
5
6 <h2>Common Base Class</h2>
7
8 Many classes are derived from object::Object, which defines some
9 general methods. See there for more information.
10
11 For the case, that you need primitive C++ types, there are some
12 wrappers:
13
14 <table>
15 <tr><th>C++ Type <th>Wrapper Class
16 <tr><td>void* <td>object::Pointer
17 <tr><td>specific pointer <td>object::TypedPointer (template class)
18 <tr><td>int <td>object::Integer
19 <tr><td>const char* <td>object::ConstString
20 <tr><td>char* <td>object::String
21 </table>
22
23
24 <h2>Containers</h2>
25
26 In the namespace ::container, several container classes are defined,
27 which all deal with instances of object::Object.
28
29 <h3>Untyped Containers</h3>
30
31 In container::untyped, there are the following containers:
32
33 <ul>
34 <li>container::untyped::Vector, a dynamically increases array,
35 <li>container::untyped::List, a linked list,
36 <li>container::untyped::HashTable, a hash table, and
37 <li>container::untyped::Stack, a stack.
38 </ul>
39
40 All provide specific methods, but since they have a common base class,
41 container::untyped::Collection, they all provide iterators, by the
42 method container::untyped::Collection::iterator.
43
44 <h3>Typed Containers</h3>
45
46 container::typed provides wrappers for the container classes defined
47 in container::untyped, which are more type safe, by using C++
48 templates.
49
50
51 <h2>Signals</h2>
52
53 For how to connect objects at run-time (to reduce dependencies), take a
54 look at the ::signal namespace.
55
56 There is also a base class signal::ObservedObject, which implements
57 signals for deletion.
58
59
60 <h2>Debugging</h2>
61
62 In debug.hh, there are some some useful macros for debugging messages,
63 see the file for mor informations.
64
65
66 <h2>Identifying Classes at Runtime</h2>
67
68 If the class of an object must be identified at runtime,
69 identity::IdentifiableObject should be used as the base class, see
70 there for more details.
71
72
73 <h2>Miscellaneous</h2>
74
75 The ::misc namespace provides several miscellaneous stuff:
76
77 <ul>
78 <li> In some contexts, it is necessary to compare objects
79 (less/greater), for this, also misc::Comparable must be
80 implemented. For example., container::untyped::Vector::sort and
81 container::typed::Vector::sort cast the elements to
82 misc::Comparable. This can be mixed with object::Object.
83 <li> misc::SimpleVector, a simple, template based vector class (not
84 depending on object::Object),
85 <li> misc::StringBuffer, class for fast concatenation of a large number
86 of strings,
87 <li> misc::BitSet implements a bitset.
88 <li> useful (template) functions (misc::min, misc::max), and
89 <li> some functions useful for runtime checks (misc::assert,
90 misc::assertNotReached).
91 </ul>
92
93 */
0 /** \page rounding-errors How to Avoid Rounding Errors
1
2 (Probably, this is a standard algorithm, so if someone knows the name,
3 drop me a note.)
4
5 If something like
6
7 \f[y_i = {x_i a \over b}\f]
8
9 is to be calculated, and all numbers are integers, a naive
10 implementation would result in something, for which
11
12 \f[\sum y_i \ne {(\sum x_i) a \over b}\f]
13
14 because of rounding errors, due to the integer division. This can be
15 avoided by transforming the formula into
16
17 \f[y_i = {(\sum_{j=0}^{j=i} x_j) a \over b} - \sum_{j=0}^{j=i} y_j\f]
18
19 Of corse, when all \f$y_i\f$ are calculated in a sequence,
20 \f$\sum_{j=0}^{j=i} x_j\f$ and \f$\sum_{j=0}^{j=i} y_j\f$ can be
21 accumulated in the same loop.
22
23 */
0 /** \page uml-legend UML Legend
1
2 This page describes the notation for several diagrams used in the
3 documentation, which is a slight variation of UML.
4
5
6 <h2>Classes</h2>
7
8 Classes are represented by boxes, containing there names:
9
10 \dot
11 digraph G {
12 node [shape=record, fontname=Helvetica, fontsize=10];
13 fontname=Helvetica; fontsize=8;
14 "Concrete Class";
15 "Abstract Class" [color="#a0a0a0"];
16 Interface [color="#ff8080"];
17 }
18 \enddot
19
20 (In most cases, the attributes and operations are left away, for
21 better readibility. Just click on it, to get to the detailed
22 description.)
23
24 Of course, in C++, there are no interfaces, but here, we call a class,
25 which has only virtual abstract methods, and so does not provide any
26 functionality, an interface.
27
28 Templates get a yellow background color:
29
30 \dot
31 digraph G {
32 node [shape=record, fontname=Helvetica, fontsize=10,
33 fillcolor="#ffffc0", style="filled"];
34 fontname=Helvetica; fontsize=8;
35 "Concrete Class Template";
36 "Abstract Class Template" [color="#a0a0a0"];
37 "Interface Template" [color="#ff8080"];
38 }
39 \enddot
40
41
42 <h2>Objects</h2>
43
44 In some cases, an examle for a concrete constellation of objects is
45 shown. An object is represented by a box containing a name and the
46 class, separated by a colon.
47
48 \dot
49 digraph G {
50 node [shape=record, fontname=Helvetica, fontsize=10];
51 edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
52 color="#404040", labelfontcolor="#000080"];
53 fontname=Helvetica; fontsize=10;
54
55 "x: A" -> "y1: B";
56 "x: A" -> "y2: B";
57 }
58 \enddot
59
60 The names (\em x, \em y, and \em z) are only meant within the context
61 of the diagram, there needs not to be a relation to the actual names
62 in the program. They should be unique within the diagram.
63
64 Classes and objects may be mixed in one diagram.
65
66
67 <h2>Associations</h2>
68
69 \dot
70 digraph G {
71 node [shape=record, fontname=Helvetica, fontsize=10];
72 edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
73 color="#404040", labelfontcolor="#000080",
74 fontname=Helvetica, fontsize=10, fontcolor="#000080"];
75 fontname=Helvetica; fontsize=10;
76 A -> B [headlabel="*", taillabel="1", label="x"];
77 }
78 \enddot
79
80 In this example, one instance of A refers to an arbitrary number of B
81 instances (denoted by the "*"), and each instance of B is referred by
82 exactly one ("1") A. The label \em x is the name of the association,
83 in most cases the name of the field, e.g. A::x.
84
85 Possible other values for the \em multiplicity:
86
87 <ul>
88 <li> a concrete number, in most cases "1",
89 <li> a range, e.g. "0..1",
90 <li> "*", denoting an arbitrary number.
91 </ul>
92
93
94 <h2>Implementations and Inheritance</h2>
95
96 \dot
97 digraph G {
98 node [shape=record, fontname=Helvetica, fontsize=10];
99 edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
100 labelfontsize=10, color="#404040", labelfontcolor="#000080"];
101 fontname=Helvetica; fontsize=10;
102 A[color="#ff8080"];
103 B[color="#ff8080"];
104 C;
105 D;
106 A -> B;
107 A -> C [style="dashed"];
108 C -> D;
109 }
110 \enddot
111
112 In this example,
113
114 <ul>
115 <li> the interface B extends the interface A,
116 <li> the class C implements the interface A, and
117 <li> the class D extends the class C.
118 </ul>
119
120
121 <h2>Template Instantiations</h2>
122
123 Template instantiations are shown as own classes/interfaces, the
124 instantiation by the template is shown by a yellow dashed arrow:
125
126 \dot
127 digraph G {
128 node [shape=record, fontname=Helvetica, fontsize=10];
129 edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
130 labelfontsize=10, color="#404040", labelfontcolor="#000080"];
131 fontname=Helvetica; fontsize=10;
132
133 A[color="#ff8080"];
134 B[color="#ff8080"];
135 C[color="#ff8080", fillcolor="#ffffc0", style="filled"];
136 C_A[color="#ff8080", label="C \<A\>"];
137 C_B[color="#ff8080", label="C \<A\>"];
138 D;
139
140 C -> C_A [arrowhead="open", arrowtail="none", style="dashed",
141 color="#808000"];
142 C -> C_B [arrowhead="open", arrowtail="none", style="dashed",
143 color="#808000"];
144 A -> C_A;
145 B -> C_B;
146 C_A -> D [style="dashed"];
147 }
148 \enddot
149
150 In this example, the interface template C uses the template argument
151 as super interface.
152
153
154 <h2>Packages</h2>
155
156 Packages are presented by dashed rectangles:
157
158 \dot
159 digraph G {
160 node [shape=record, fontname=Helvetica, fontsize=10];
161 edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
162 labelfontsize=10, color="#404040", labelfontcolor="#000080"];
163 fontname=Helvetica; fontsize=10;
164
165 subgraph cluster_1 {
166 style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
167 label="package 1";
168
169 A;
170 B [color="#a0a0a0"];
171 }
172
173 subgraph cluster_2 {
174 style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
175 label="package 2";
176
177 C;
178 D [color="#a0a0a0"];
179 E
180 }
181
182 A -> C;
183 B -> D;
184 D -> E;
185 E -> A [arrowhead="open", arrowtail="none"];
186 }
187 \enddot
188
189 Packages may be nested.
190
191 */
0 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
1 <html>
2
3 <head>
4 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
5 <title> Dillo Web Browser ::
6
7 Help for New Users
8
9 </title>
10 </head>
11
12 <body text="#0f0f0f" link="#2020e0" vlink="#403090" bgcolor="#607cf8">
13
14 <table width="100%" bgcolor="#8080f0" cellspacing="4" cellpadding="5"
15 border="0o">
16 <tr><td><big><font color="#302080">Welcome to Dillo 3.0</font></big>
17 </table>
18 <p>
19
20 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
21 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
22 <h4><font color="green">Basics:</font></h4>
23 </td></tr>
24
25 <tr><td WIDTH="100%" bgcolor="#70a0c0">
26 <ul>
27 <li>You can tell a link from plain content by the hand-shaped cursor.
28
29 <li> Besides browsing the web, Dillo also has basic file browsing
30 capabilities included. So, entering <code>file:</code> in your
31 Dillo URL window will give you the contents of your current
32 working directory, and <code>file:~</code> entered in the
33 same place will point your Dillo browser right to your home
34 directory...
35
36 <li> Dillo, at this stage of development, is not ready
37 to render pages that use <b><font color="#5040a0">frames</font></b>.
38 Nevertheless, it comes with a tiny handler (lynx/w3m-like) that will
39 let you choose which frame to visit, one by one.
40
41 <li> Dillo has <b><font color="#5040a0">context
42 sensitive menus</font></b> using the right mouse button
43 (available on pages, links, images, forms, the Back
44 and Forward buttons, and the bug meter).
45
46 <li> Some of the functions in Dillo are handled by independent
47 processes. For instance, downloads come through
48 <em><a href="http://www.gnu.org/software/wget/">wget</a></em>.
49 If Dillo exits, the downloads continue (more details
50 below).
51
52
53 </ul>
54 </td></tr>
55 </table>
56
57 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
58 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
59 <h4><font color="green">Usage:</font></h4>
60 </td></tr>
61
62 <tr><td BGCOLOR="#70a0c0">
63 <ul>
64 <li> You can scroll around your Dillo main window using
65 CTRL+{PgUp|PgDwn|Home|End} or using the mouse middle button
66 or mouse wheel. If nothing happens when keys are pressed, try
67 <b> <font color="#5040a0">focusing</font></b> your Dillo main
68 window first by clicking it (not on any link!:)).
69
70 <li>You can use the space key as PgDn, and {'b' | 'B'} as PgUp.</li>
71
72 <li> Similarly, you can use "<b>,</b>" and "<b>.</b>" as shortcuts for
73 forward and backward buttons (mnemonic: those
74 keys are usually labeled "<" and ">").
75
76 <li> <b>Configuration:</b> If you want to change Dillo's
77 appearance or behaviour, look at the options in your
78 <b><font color="#5040a0">dillorc</font></b>
79 file (if you don't have a copy in your ~/.dillo/ directory, get it
80 <a href='http://www.dillo.org/dillorc'>here</a>).
81
82 <li> Clicking the "Reload" button always requests an end-to-end reload
83 of the page currently viewed, but it will *not* reload embedded
84 images during this process.
85
86 <li> Dialogs can be closed with the ESC key<br>
87 (ESC also hides the findbar and control panels).
88
89 <li> If you want to try a different control panel, right-click over the
90 tools button and select one that suits your needs, then make
91 it the default in your <code>dillorc</code> file.
92
93 <li> The whole window area can be used to display the page (ESC key).
94
95 </ul>
96 </td></tr>
97 </table>
98
99 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
100 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
101 <h4><font color="green">Downloads:</font></h4>
102 </td></tr>
103
104 <tr><td BGCOLOR="#70a0c0">
105 <p>
106 Downloads are made using
107 <b><font color="#5040a0">
108 <a href="http://www.gnu.org/software/wget/">wget</a></font></b>
109 with a <a href="http://www.fltk.org">FLTK</a>-based GUI wrapper, through
110 the Dillo plugin (dpi) framework.
111 If you close the browser window, downloads will continue.
112 <p>
113 </td></tr>
114 </table>
115
116 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
117 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
118 <h4><font color="green">Find text:</font></h4>
119 </td></tr>
120
121 <tr><td bgcolor="#70a0c0">
122 <p>
123 This one is very useful;
124 it can be found in the right-mouse-button menu<br>
125 Find text is tuned for speed so don't hesitate to use it even for minimal
126 searches.
127 <p>
128 <u>Semantics:</u>
129 <ul>
130 <li> You can search for substrings, words and sentences. </li>
131 <li> To find a substring or word, just enter its text. </li>
132 <li> To find a left-aligned substring, prepend it with a space. </li>
133 <li> To find a right-aligned substring, append a space to it.</li>
134 <li> To find full words only, prepend and append spaces to them. </li>
135 <li> To find a sentence, enter the words and remember that
136 the above rules apply for every word in it. </li>
137 </ul>
138 <p>
139 Dillo will scroll the page and highlight found text!
140 <p>
141 <small>Default shortcut: [CTRL]+"F".</small>
142
143 </td></tr>
144 </table>
145
146 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
147 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
148 <h4><font color="green">Copy&amp;Paste:</font></h4>
149 </td></tr>
150
151 <tr><td bgcolor="#70a0c0">
152 <p>
153 Just hold down the left mouse button and move to select the
154 area to copy. To paste, go to the target application and
155 press the middle mouse button.
156 <p>
157 If you want to select more than one screen, hold the mouse button
158 down and scroll with PgUp, PgDn or the arrow keys.
159 <P>
160 If you want to paste an URL into Dillo, do it on the "clear-URL"
161 button (the "X" next to the location bar).
162 <p>
163 Note: If it doesn't work, please try again. There's a bug lurking there.
164 </td></tr>
165 </table>
166
167 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
168 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
169 <h4><font color="green">Navigation history:</font></h4>
170 </td></tr>
171
172 <tr><td bgcolor="#70a0c0">
173 <p>
174 Currently, navigation history supports the navigation-stack model; just
175 right-click on the Back or Forward buttons and they will pop up!
176 <p> <u>Remember:</u>
177 <ul>
178 <li> These history menus are relative to the current page. </li>
179 <li> They show the page-title but the status bar shows the URL. </li>
180 <li> Left-click jumps to the selected item. </li>
181 <li> Middle-click opens the item in a new browser tab/window. </li>
182 </ul>
183 </td></tr>
184 </table>
185
186 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
187 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
188 <h4><font color="green">Cookies:</font></h4>
189 </td></tr>
190
191 <tr><td bgcolor="#70a0c0">
192 <p>
193 Due to privacy concerns, cookies are disabled by default.
194 That is, if you just compile and use dillo, it will reject
195 every single cookie sent to it!
196 <p>
197 If you want to enable cookies in dillo, please read
198 <a href="Cookies.txt">Cookies.txt</a>. It's very easy --
199 just a matter of setting up a <code>cookiesrc</code> file).
200 </td></tr>
201 </table>
202
203 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
204 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
205 <h4><font color="green">Tabs:</font></h4>
206 </td></tr>
207
208 <tr><td bgcolor="#70a0c0">
209 <p>
210 Dillo has tabbed browsing. Just middle click to open a link or submit a
211 form in a new tab. It will be automatically focused. If you want to
212 customize this behaviour, adjust these dillorc options:
213 <ul>
214 <li><code>middle_click_opens_new_tab</code>
215 <li><code>focus_new_tab</code>
216 </ul>
217 Press SHIFT to temporarily reverse the focusing behaviour.
218 <p>
219 You can <b><font color="#5040a0">close</font></b>
220 a tab with middle-click on its label (the default),
221 or with right-click by setting this dillorc option:
222 <ul><li><code>right_click_closes_tab</code></ul>
223 </td></tr>
224 </table>
225
226 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
227 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
228 <h4><font color="green">Images-off mode:</font></h4>
229 </td></tr>
230 <tr><td bgcolor="#70a0c0">
231 <p>
232 You can browse without images now:
233 <ul>
234 <li>There is an option in the Tools menu to disable automatic image loading.
235 An image's alt text (or <code>[IMG]</code> placeholder) will appear in the
236 page.
237 <li>If you want to load an individual image, left click on its text.
238 <li>You can set "no images" as the default mode in dillorc.
239 </ul>
240 </td></tr>
241 </table>
242
243 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
244 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
245 <h4><font color="green">Bookmarks:</font></h4>
246 </td></tr>
247
248 <tr><td bgcolor="#70a0c0">
249 <p>
250 Bookmarks are handled by the Dillo plugin
251 (<a href='http://www.dillo.org/dpi1.html'>dpi</a>) framework.
252 This should be transparent to the end user. Please note that:
253 <ul>
254 <li>It is a good idea to keep a backup of your
255 <code>~/.dillo/bm.txt</code>
256 <li>The server will stay alive after closing dillo.
257 <li>You can stop the server by sending it a KILL signal. Dillo
258 will automatically restart it when it is needed.
259 <li>If you don't have root access, install <em>dpid</em> and
260 the <em>dpis</em>
261 inside your <code>~/.dillo/</code> directory as explained
262 in the README file.
263 </ul>
264 </td></tr>
265 </table>
266
267 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
268 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
269 <h4><font color="green">Bug Meter:</font></h4>
270 </td></tr>
271 <tr><td bgcolor="#70a0c0">
272 <p>
273 Dillo's <em><a href='http://www.dillo.org/help/bug_meter.html'>Bug
274 meter</a></em> shows the
275 number of <b><font color="#5040a0">detected bugs</font></b> inside the
276 page. The bugs are caught at parsing time, so the
277 error messages also show the line where they occur and provide a
278 <b><font color="#5040a0">hint</font></b> of what was expected instead!
279 <p>
280 The primary purpose of the bug meter is to
281 <b><font color="#5040a0">help</font></b> webmasters and page
282 authors to polish the contents of their sites with a view to making
283 them standards-compliant.
284 <p>
285 The Bug meter is located at the lower right corner of
286 Dillo. Left-click to see the messages, right-click for a menu.
287 </td></tr>
288 </table>
289
290 <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
291 <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
292 <h4><font color="green">Keyboard shortcuts:</font></h4>
293 </td></tr>
294 <tr><td bgcolor="#70a0c0">
295 <table border=1 width="100%">
296 <tr><th>Shortcut <th>Mnemonic <th>Function
297 <tr><td>Ctrl-L <td>Location <td>enter a new URL
298 <tr><td>Ctrl-F <td>Find <td>find text
299 <tr><td>Ctrl-S <td>Search <td>search the web
300 <tr><td>Ctrl-R <td>Reload <td>reload current page
301 <tr><td>Ctrl-N <td>New <td>New browser window
302 <tr><td>Ctrl-T <td>Tab <td>New tab
303 <tr><td>Ctrl-W <td>Window <td>quit tab/window
304 <tr><td>Ctrl-O <td>Open <td>Open file
305 <tr><td>Ctrl-B <td>Bookmarks <td>view bookmarks
306 <tr><td>Ctrl-Q <td>Quit <td>Quit dillo
307 <tr><td>Back or "<b>,</b>" <td>< <td>previous page
308 <tr><td>Shift-Back or "<b>.</b>" <td>> <td>next page
309 <tr><td>Alt-F <td>File <td>file menu
310 <tr><td>Ctrl-TabKey <td>TabKey <td>Next tab
311 <tr><td>Shift-TabKey <td>TabKey <td>Previous tab
312 <tr><td>Esc <td>escape <td>close dialog,
313 close findbar,<br>
314 Hide/show control panels
315 </table>
316 <p>
317 You can <b><font color="#5040a0">change</font></b> the bindings using a
318 <code>~/.dillo/<a href="http://www.dillo.org/keysrc">keysrc</a></code>
319 file.
320 </td></tr>
321 </table>
322
323
324 </body>
325 </html>
326
0 AM_CFLAGS = @GLIB_CFLAGS@
1 AM_CXXFLAGS = @GLIB_CFLAGS@
0 AM_CPPFLAGS = \
1 -I$(top_srcdir)
22
33 bookmarksdir = $(libdir)/dillo/dpi/bookmarks
44 downloadsdir = $(libdir)/dillo/dpi/downloads
55 ftpdir = $(libdir)/dillo/dpi/ftp
66 httpsdir = $(libdir)/dillo/dpi/https
77 hellodir = $(libdir)/dillo/dpi/hello
8 vsourcedir = $(libdir)/dillo/dpi/vsource
89 filedir = $(libdir)/dillo/dpi/file
910 cookiesdir = $(libdir)/dillo/dpi/cookies
1011 datauridir = $(libdir)/dillo/dpi/datauri
1314 ftp_PROGRAMS = ftp.filter.dpi
1415 https_PROGRAMS = https.filter.dpi
1516 hello_PROGRAMS = hello.filter.dpi
17 vsource_PROGRAMS = vsource.filter.dpi
1618 file_PROGRAMS = file.dpi
1719 cookies_PROGRAMS = cookies.dpi
1820 datauri_PROGRAMS = datauri.filter.dpi
1921
20 bookmarks_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
21 if DLGUI
22 downloads_dpi_LDADD = @GLIB_LIBS@ @LIBFLTK_LIBS@ ../dpip/libDpip.a
23 else
24 downloads_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
25 endif
26 ftp_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
27 https_filter_dpi_LDADD = @GLIB_LIBS@ @LIBSSL_LIBS@ ../dpip/libDpip.a
28 hello_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
29 file_dpi_LDADD = @GLIB_LIBS@ @LIBPTHREAD_LIBS@ ../dpip/libDpip.a
30 cookies_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
31 datauri_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
22 bookmarks_dpi_LDADD = \
23 $(top_builddir)/dpip/libDpip.a \
24 $(top_builddir)/dlib/libDlib.a
25 downloads_dpi_LDADD = @LIBFLTK_LIBS@ \
26 $(top_builddir)/dpip/libDpip.a \
27 $(top_builddir)/dlib/libDlib.a
28 ftp_filter_dpi_LDADD = \
29 $(top_builddir)/dpip/libDpip.a \
30 $(top_builddir)/dlib/libDlib.a
31 https_filter_dpi_LDADD = @LIBSSL_LIBS@ \
32 $(top_builddir)/dpip/libDpip.a \
33 $(top_builddir)/dlib/libDlib.a
34 hello_filter_dpi_LDADD = \
35 $(top_builddir)/dpip/libDpip.a \
36 $(top_builddir)/dlib/libDlib.a
37 vsource_filter_dpi_LDADD = \
38 $(top_builddir)/dpip/libDpip.a \
39 $(top_builddir)/dlib/libDlib.a
40 file_dpi_LDADD = \
41 $(top_builddir)/dpip/libDpip.a \
42 $(top_builddir)/dlib/libDlib.a
43 cookies_dpi_LDADD = \
44 $(top_builddir)/dpip/libDpip.a \
45 $(top_builddir)/dlib/libDlib.a
46 datauri_filter_dpi_LDADD = \
47 $(top_builddir)/dpip/libDpip.a \
48 $(top_builddir)/dlib/libDlib.a
3249
33 file_dpi_LDFLAGS = @LIBPTHREAD_LDFLAGS@
50 downloads_dpi_CXXFLAGS = @LIBFLTK_CXXFLAGS@
3451
3552 bookmarks_dpi_SOURCES = bookmarks.c dpiutil.c dpiutil.h
36 if DLGUI
3753 downloads_dpi_SOURCES = downloads.cc dpiutil.c dpiutil.h
38 else
39 downloads_dpi_SOURCES = downloads-old.c dpiutil.c dpiutil.h
40 endif
4154 ftp_filter_dpi_SOURCES = ftp.c dpiutil.c dpiutil.h
4255 https_filter_dpi_SOURCES = https.c dpiutil.c dpiutil.h
4356 hello_filter_dpi_SOURCES = hello.c dpiutil.c dpiutil.h
57 vsource_filter_dpi_SOURCES = vsource.c dpiutil.c dpiutil.h
4458 file_dpi_SOURCES = file.c dpiutil.c dpiutil.h
4559 cookies_dpi_SOURCES = cookies.c dpiutil.c dpiutil.h
4660 datauri_filter_dpi_SOURCES = datauri.c dpiutil.c dpiutil.h
+0
-744
dpi/Makefile.in less more
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 SOURCES = $(bookmarks_dpi_SOURCES) $(cookies_dpi_SOURCES) $(datauri_filter_dpi_SOURCES) $(downloads_dpi_SOURCES) $(file_dpi_SOURCES) $(ftp_filter_dpi_SOURCES) $(hello_filter_dpi_SOURCES) $(https_filter_dpi_SOURCES)
17
18 srcdir = @srcdir@
19 top_srcdir = @top_srcdir@
20 VPATH = @srcdir@
21 pkgdatadir = $(datadir)/@PACKAGE@
22 pkglibdir = $(libdir)/@PACKAGE@
23 pkgincludedir = $(includedir)/@PACKAGE@
24 top_builddir = ..
25 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
26 INSTALL = @INSTALL@
27 install_sh_DATA = $(install_sh) -c -m 644
28 install_sh_PROGRAM = $(install_sh) -c
29 install_sh_SCRIPT = $(install_sh) -c
30 INSTALL_HEADER = $(INSTALL_DATA)
31 transform = $(program_transform_name)
32 NORMAL_INSTALL = :
33 PRE_INSTALL = :
34 POST_INSTALL = :
35 NORMAL_UNINSTALL = :
36 PRE_UNINSTALL = :
37 POST_UNINSTALL = :
38 build_triplet = @build@
39 host_triplet = @host@
40 target_triplet = @target@
41 bookmarks_PROGRAMS = bookmarks.dpi$(EXEEXT)
42 downloads_PROGRAMS = downloads.dpi$(EXEEXT)
43 ftp_PROGRAMS = ftp.filter.dpi$(EXEEXT)
44 https_PROGRAMS = https.filter.dpi$(EXEEXT)
45 hello_PROGRAMS = hello.filter.dpi$(EXEEXT)
46 file_PROGRAMS = file.dpi$(EXEEXT)
47 cookies_PROGRAMS = cookies.dpi$(EXEEXT)
48 datauri_PROGRAMS = datauri.filter.dpi$(EXEEXT)
49 subdir = dpi
50 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
51 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
52 am__aclocal_m4_deps = $(top_srcdir)/configure.in
53 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
54 $(ACLOCAL_M4)
55 mkinstalldirs = $(install_sh) -d
56 CONFIG_HEADER = $(top_builddir)/config.h
57 CONFIG_CLEAN_FILES =
58 am__installdirs = "$(DESTDIR)$(bookmarksdir)" \
59 "$(DESTDIR)$(cookiesdir)" "$(DESTDIR)$(datauridir)" \
60 "$(DESTDIR)$(downloadsdir)" "$(DESTDIR)$(filedir)" \
61 "$(DESTDIR)$(ftpdir)" "$(DESTDIR)$(hellodir)" \
62 "$(DESTDIR)$(httpsdir)"
63 bookmarksPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
64 cookiesPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
65 datauriPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
66 downloadsPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
67 filePROGRAMS_INSTALL = $(INSTALL_PROGRAM)
68 ftpPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
69 helloPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
70 httpsPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
71 PROGRAMS = $(bookmarks_PROGRAMS) $(cookies_PROGRAMS) \
72 $(datauri_PROGRAMS) $(downloads_PROGRAMS) $(file_PROGRAMS) \
73 $(ftp_PROGRAMS) $(hello_PROGRAMS) $(https_PROGRAMS)
74 am_bookmarks_dpi_OBJECTS = bookmarks.$(OBJEXT) dpiutil.$(OBJEXT)
75 bookmarks_dpi_OBJECTS = $(am_bookmarks_dpi_OBJECTS)
76 bookmarks_dpi_DEPENDENCIES = ../dpip/libDpip.a
77 am_cookies_dpi_OBJECTS = cookies.$(OBJEXT) dpiutil.$(OBJEXT)
78 cookies_dpi_OBJECTS = $(am_cookies_dpi_OBJECTS)
79 cookies_dpi_DEPENDENCIES = ../dpip/libDpip.a
80 am_datauri_filter_dpi_OBJECTS = datauri.$(OBJEXT) dpiutil.$(OBJEXT)
81 datauri_filter_dpi_OBJECTS = $(am_datauri_filter_dpi_OBJECTS)
82 datauri_filter_dpi_DEPENDENCIES = ../dpip/libDpip.a
83 am__downloads_dpi_SOURCES_DIST = downloads-old.c dpiutil.c dpiutil.h \
84 downloads.cc
85 @DLGUI_FALSE@am_downloads_dpi_OBJECTS = downloads-old.$(OBJEXT) \
86 @DLGUI_FALSE@ dpiutil.$(OBJEXT)
87 @DLGUI_TRUE@am_downloads_dpi_OBJECTS = downloads.$(OBJEXT) \
88 @DLGUI_TRUE@ dpiutil.$(OBJEXT)
89 downloads_dpi_OBJECTS = $(am_downloads_dpi_OBJECTS)
90 @DLGUI_FALSE@downloads_dpi_DEPENDENCIES = ../dpip/libDpip.a
91 @DLGUI_TRUE@downloads_dpi_DEPENDENCIES = ../dpip/libDpip.a
92 am_file_dpi_OBJECTS = file.$(OBJEXT) dpiutil.$(OBJEXT)
93 file_dpi_OBJECTS = $(am_file_dpi_OBJECTS)
94 file_dpi_DEPENDENCIES = ../dpip/libDpip.a
95 am_ftp_filter_dpi_OBJECTS = ftp.$(OBJEXT) dpiutil.$(OBJEXT)
96 ftp_filter_dpi_OBJECTS = $(am_ftp_filter_dpi_OBJECTS)
97 ftp_filter_dpi_DEPENDENCIES = ../dpip/libDpip.a
98 am_hello_filter_dpi_OBJECTS = hello.$(OBJEXT) dpiutil.$(OBJEXT)
99 hello_filter_dpi_OBJECTS = $(am_hello_filter_dpi_OBJECTS)
100 hello_filter_dpi_DEPENDENCIES = ../dpip/libDpip.a
101 am_https_filter_dpi_OBJECTS = https.$(OBJEXT) dpiutil.$(OBJEXT)
102 https_filter_dpi_OBJECTS = $(am_https_filter_dpi_OBJECTS)
103 https_filter_dpi_DEPENDENCIES = ../dpip/libDpip.a
104 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
105 depcomp = $(SHELL) $(top_srcdir)/depcomp
106 am__depfiles_maybe = depfiles
107 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
108 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
109 CCLD = $(CC)
110 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
111 CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
112 $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
113 CXXLD = $(CXX)
114 CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
115 -o $@
116 SOURCES = $(bookmarks_dpi_SOURCES) $(cookies_dpi_SOURCES) \
117 $(datauri_filter_dpi_SOURCES) $(downloads_dpi_SOURCES) \
118 $(file_dpi_SOURCES) $(ftp_filter_dpi_SOURCES) \
119 $(hello_filter_dpi_SOURCES) $(https_filter_dpi_SOURCES)
120 DIST_SOURCES = $(bookmarks_dpi_SOURCES) $(cookies_dpi_SOURCES) \
121 $(datauri_filter_dpi_SOURCES) \
122 $(am__downloads_dpi_SOURCES_DIST) $(file_dpi_SOURCES) \
123 $(ftp_filter_dpi_SOURCES) $(hello_filter_dpi_SOURCES) \
124 $(https_filter_dpi_SOURCES)
125 ETAGS = etags
126 CTAGS = ctags
127 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
128 ACLOCAL = @ACLOCAL@
129 AMDEP_FALSE = @AMDEP_FALSE@
130 AMDEP_TRUE = @AMDEP_TRUE@
131 AMTAR = @AMTAR@
132 AUTOCONF = @AUTOCONF@
133 AUTOHEADER = @AUTOHEADER@
134 AUTOMAKE = @AUTOMAKE@
135 AWK = @AWK@
136 CC = @CC@
137 CCDEPMODE = @CCDEPMODE@
138 CFLAGS = @CFLAGS@
139 CPP = @CPP@
140 CPPFLAGS = @CPPFLAGS@
141 CXX = @CXX@
142 CXXDEPMODE = @CXXDEPMODE@
143 CXXFLAGS = @CXXFLAGS@
144 CYGPATH_W = @CYGPATH_W@
145 DEFS = @DEFS@
146 DEPDIR = @DEPDIR@
147 DLGUI_FALSE = @DLGUI_FALSE@
148 DLGUI_TRUE = @DLGUI_TRUE@
149 ECHO_C = @ECHO_C@
150 ECHO_N = @ECHO_N@
151 ECHO_T = @ECHO_T@
152 EGREP = @EGREP@
153 EXEEXT = @EXEEXT@
154 GLIB_CFLAGS = @GLIB_CFLAGS@
155 GLIB_CONFIG = @GLIB_CONFIG@
156 GLIB_LIBS = @GLIB_LIBS@
157 GTK_CFLAGS = @GTK_CFLAGS@
158 GTK_CONFIG = @GTK_CONFIG@
159 GTK_LIBS = @GTK_LIBS@
160 INSTALL_DATA = @INSTALL_DATA@
161 INSTALL_PROGRAM = @INSTALL_PROGRAM@
162 INSTALL_SCRIPT = @INSTALL_SCRIPT@
163 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
164 LDFLAGS = @LDFLAGS@
165 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
166 LIBFLTK_LIBS = @LIBFLTK_LIBS@
167 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
168 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
169 LIBJPEG_LIBS = @LIBJPEG_LIBS@
170 LIBOBJS = @LIBOBJS@
171 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
172 LIBPNG_LIBS = @LIBPNG_LIBS@
173 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
174 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
175 LIBS = @LIBS@
176 LIBSSL_LIBS = @LIBSSL_LIBS@
177 LIBZ_LIBS = @LIBZ_LIBS@
178 LTLIBOBJS = @LTLIBOBJS@
179 MAKEINFO = @MAKEINFO@
180 OBJEXT = @OBJEXT@
181 PACKAGE = @PACKAGE@
182 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
183 PACKAGE_NAME = @PACKAGE_NAME@
184 PACKAGE_STRING = @PACKAGE_STRING@
185 PACKAGE_TARNAME = @PACKAGE_TARNAME@
186 PACKAGE_VERSION = @PACKAGE_VERSION@
187 PATH_SEPARATOR = @PATH_SEPARATOR@
188 RANLIB = @RANLIB@
189 SET_MAKE = @SET_MAKE@
190 SHELL = @SHELL@
191 STRIP = @STRIP@
192 VERSION = @VERSION@
193 ac_ct_CC = @ac_ct_CC@
194 ac_ct_CXX = @ac_ct_CXX@
195 ac_ct_RANLIB = @ac_ct_RANLIB@
196 ac_ct_STRIP = @ac_ct_STRIP@
197 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
198 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
199 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
200 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
201 am__include = @am__include@
202 am__leading_dot = @am__leading_dot@
203 am__quote = @am__quote@
204 am__tar = @am__tar@
205 am__untar = @am__untar@
206 bindir = @bindir@
207 build = @build@
208 build_alias = @build_alias@
209 build_cpu = @build_cpu@
210 build_os = @build_os@
211 build_vendor = @build_vendor@
212 datadir = @datadir@
213 exec_prefix = @exec_prefix@
214 host = @host@
215 host_alias = @host_alias@
216 host_cpu = @host_cpu@
217 host_os = @host_os@
218 host_vendor = @host_vendor@
219 includedir = @includedir@
220 infodir = @infodir@
221 install_sh = @install_sh@
222 libdir = @libdir@
223 libexecdir = @libexecdir@
224 localstatedir = @localstatedir@
225 mandir = @mandir@
226 mkdir_p = @mkdir_p@
227 oldincludedir = @oldincludedir@
228 prefix = @prefix@
229 program_transform_name = @program_transform_name@
230 sbindir = @sbindir@
231 sharedstatedir = @sharedstatedir@
232 sysconfdir = @sysconfdir@
233 target = @target@
234 target_alias = @target_alias@
235 target_cpu = @target_cpu@
236 target_os = @target_os@
237 target_vendor = @target_vendor@
238 AM_CFLAGS = @GLIB_CFLAGS@
239 AM_CXXFLAGS = @GLIB_CFLAGS@
240 bookmarksdir = $(libdir)/dillo/dpi/bookmarks
241 downloadsdir = $(libdir)/dillo/dpi/downloads
242 ftpdir = $(libdir)/dillo/dpi/ftp
243 httpsdir = $(libdir)/dillo/dpi/https
244 hellodir = $(libdir)/dillo/dpi/hello
245 filedir = $(libdir)/dillo/dpi/file
246 cookiesdir = $(libdir)/dillo/dpi/cookies
247 datauridir = $(libdir)/dillo/dpi/datauri
248 bookmarks_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
249 @DLGUI_FALSE@downloads_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
250 @DLGUI_TRUE@downloads_dpi_LDADD = @GLIB_LIBS@ @LIBFLTK_LIBS@ ../dpip/libDpip.a
251 ftp_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
252 https_filter_dpi_LDADD = @GLIB_LIBS@ @LIBSSL_LIBS@ ../dpip/libDpip.a
253 hello_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
254 file_dpi_LDADD = @GLIB_LIBS@ @LIBPTHREAD_LIBS@ ../dpip/libDpip.a
255 cookies_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
256 datauri_filter_dpi_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
257 file_dpi_LDFLAGS = @LIBPTHREAD_LDFLAGS@
258 bookmarks_dpi_SOURCES = bookmarks.c dpiutil.c dpiutil.h
259 @DLGUI_FALSE@downloads_dpi_SOURCES = downloads-old.c dpiutil.c dpiutil.h
260 @DLGUI_TRUE@downloads_dpi_SOURCES = downloads.cc dpiutil.c dpiutil.h
261 ftp_filter_dpi_SOURCES = ftp.c dpiutil.c dpiutil.h
262 https_filter_dpi_SOURCES = https.c dpiutil.c dpiutil.h
263 hello_filter_dpi_SOURCES = hello.c dpiutil.c dpiutil.h
264 file_dpi_SOURCES = file.c dpiutil.c dpiutil.h
265 cookies_dpi_SOURCES = cookies.c dpiutil.c dpiutil.h
266 datauri_filter_dpi_SOURCES = datauri.c dpiutil.c dpiutil.h
267 all: all-am
268
269 .SUFFIXES:
270 .SUFFIXES: .c .cc .o .obj
271 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
272 @for dep in $?; do \
273 case '$(am__configure_deps)' in \
274 *$$dep*) \
275 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
276 && exit 0; \
277 exit 1;; \
278 esac; \
279 done; \
280 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu dpi/Makefile'; \
281 cd $(top_srcdir) && \
282 $(AUTOMAKE) --gnu dpi/Makefile
283 .PRECIOUS: Makefile
284 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
285 @case '$?' in \
286 *config.status*) \
287 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
288 *) \
289 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
290 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
291 esac;
292
293 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
294 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
295
296 $(top_srcdir)/configure: $(am__configure_deps)
297 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
298 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
299 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
300 install-bookmarksPROGRAMS: $(bookmarks_PROGRAMS)
301 @$(NORMAL_INSTALL)
302 test -z "$(bookmarksdir)" || $(mkdir_p) "$(DESTDIR)$(bookmarksdir)"
303 @list='$(bookmarks_PROGRAMS)'; for p in $$list; do \
304 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
305 if test -f $$p \
306 ; then \
307 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
308 echo " $(INSTALL_PROGRAM_ENV) $(bookmarksPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bookmarksdir)/$$f'"; \
309 $(INSTALL_PROGRAM_ENV) $(bookmarksPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bookmarksdir)/$$f" || exit 1; \
310 else :; fi; \
311 done
312
313 uninstall-bookmarksPROGRAMS:
314 @$(NORMAL_UNINSTALL)
315 @list='$(bookmarks_PROGRAMS)'; for p in $$list; do \
316 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
317 echo " rm -f '$(DESTDIR)$(bookmarksdir)/$$f'"; \
318 rm -f "$(DESTDIR)$(bookmarksdir)/$$f"; \
319 done
320
321 clean-bookmarksPROGRAMS:
322 -test -z "$(bookmarks_PROGRAMS)" || rm -f $(bookmarks_PROGRAMS)
323 install-cookiesPROGRAMS: $(cookies_PROGRAMS)
324 @$(NORMAL_INSTALL)
325 test -z "$(cookiesdir)" || $(mkdir_p) "$(DESTDIR)$(cookiesdir)"
326 @list='$(cookies_PROGRAMS)'; for p in $$list; do \
327 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
328 if test -f $$p \
329 ; then \
330 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
331 echo " $(INSTALL_PROGRAM_ENV) $(cookiesPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(cookiesdir)/$$f'"; \
332 $(INSTALL_PROGRAM_ENV) $(cookiesPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(cookiesdir)/$$f" || exit 1; \
333 else :; fi; \
334 done
335
336 uninstall-cookiesPROGRAMS:
337 @$(NORMAL_UNINSTALL)
338 @list='$(cookies_PROGRAMS)'; for p in $$list; do \
339 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
340 echo " rm -f '$(DESTDIR)$(cookiesdir)/$$f'"; \
341 rm -f "$(DESTDIR)$(cookiesdir)/$$f"; \
342 done
343
344 clean-cookiesPROGRAMS:
345 -test -z "$(cookies_PROGRAMS)" || rm -f $(cookies_PROGRAMS)
346 install-datauriPROGRAMS: $(datauri_PROGRAMS)
347 @$(NORMAL_INSTALL)
348 test -z "$(datauridir)" || $(mkdir_p) "$(DESTDIR)$(datauridir)"
349 @list='$(datauri_PROGRAMS)'; for p in $$list; do \
350 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
351 if test -f $$p \
352 ; then \
353 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
354 echo " $(INSTALL_PROGRAM_ENV) $(datauriPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(datauridir)/$$f'"; \
355 $(INSTALL_PROGRAM_ENV) $(datauriPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(datauridir)/$$f" || exit 1; \
356 else :; fi; \
357 done
358
359 uninstall-datauriPROGRAMS:
360 @$(NORMAL_UNINSTALL)
361 @list='$(datauri_PROGRAMS)'; for p in $$list; do \
362 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
363 echo " rm -f '$(DESTDIR)$(datauridir)/$$f'"; \
364 rm -f "$(DESTDIR)$(datauridir)/$$f"; \
365 done
366
367 clean-datauriPROGRAMS:
368 -test -z "$(datauri_PROGRAMS)" || rm -f $(datauri_PROGRAMS)
369 install-downloadsPROGRAMS: $(downloads_PROGRAMS)
370 @$(NORMAL_INSTALL)
371 test -z "$(downloadsdir)" || $(mkdir_p) "$(DESTDIR)$(downloadsdir)"
372 @list='$(downloads_PROGRAMS)'; for p in $$list; do \
373 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
374 if test -f $$p \
375 ; then \
376 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
377 echo " $(INSTALL_PROGRAM_ENV) $(downloadsPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(downloadsdir)/$$f'"; \
378 $(INSTALL_PROGRAM_ENV) $(downloadsPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(downloadsdir)/$$f" || exit 1; \
379 else :; fi; \
380 done
381
382 uninstall-downloadsPROGRAMS:
383 @$(NORMAL_UNINSTALL)
384 @list='$(downloads_PROGRAMS)'; for p in $$list; do \
385 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
386 echo " rm -f '$(DESTDIR)$(downloadsdir)/$$f'"; \
387 rm -f "$(DESTDIR)$(downloadsdir)/$$f"; \
388 done
389
390 clean-downloadsPROGRAMS:
391 -test -z "$(downloads_PROGRAMS)" || rm -f $(downloads_PROGRAMS)
392 install-filePROGRAMS: $(file_PROGRAMS)
393 @$(NORMAL_INSTALL)
394 test -z "$(filedir)" || $(mkdir_p) "$(DESTDIR)$(filedir)"
395 @list='$(file_PROGRAMS)'; for p in $$list; do \
396 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
397 if test -f $$p \
398 ; then \
399 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
400 echo " $(INSTALL_PROGRAM_ENV) $(filePROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(filedir)/$$f'"; \
401 $(INSTALL_PROGRAM_ENV) $(filePROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(filedir)/$$f" || exit 1; \
402 else :; fi; \
403 done
404
405 uninstall-filePROGRAMS:
406 @$(NORMAL_UNINSTALL)
407 @list='$(file_PROGRAMS)'; for p in $$list; do \
408 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
409 echo " rm -f '$(DESTDIR)$(filedir)/$$f'"; \
410 rm -f "$(DESTDIR)$(filedir)/$$f"; \
411 done
412
413 clean-filePROGRAMS:
414 -test -z "$(file_PROGRAMS)" || rm -f $(file_PROGRAMS)
415 install-ftpPROGRAMS: $(ftp_PROGRAMS)
416 @$(NORMAL_INSTALL)
417 test -z "$(ftpdir)" || $(mkdir_p) "$(DESTDIR)$(ftpdir)"
418 @list='$(ftp_PROGRAMS)'; for p in $$list; do \
419 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
420 if test -f $$p \
421 ; then \
422 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
423 echo " $(INSTALL_PROGRAM_ENV) $(ftpPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(ftpdir)/$$f'"; \
424 $(INSTALL_PROGRAM_ENV) $(ftpPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(ftpdir)/$$f" || exit 1; \
425 else :; fi; \
426 done
427
428 uninstall-ftpPROGRAMS:
429 @$(NORMAL_UNINSTALL)
430 @list='$(ftp_PROGRAMS)'; for p in $$list; do \
431 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
432 echo " rm -f '$(DESTDIR)$(ftpdir)/$$f'"; \
433 rm -f "$(DESTDIR)$(ftpdir)/$$f"; \
434 done
435
436 clean-ftpPROGRAMS:
437 -test -z "$(ftp_PROGRAMS)" || rm -f $(ftp_PROGRAMS)
438 install-helloPROGRAMS: $(hello_PROGRAMS)
439 @$(NORMAL_INSTALL)
440 test -z "$(hellodir)" || $(mkdir_p) "$(DESTDIR)$(hellodir)"
441 @list='$(hello_PROGRAMS)'; for p in $$list; do \
442 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
443 if test -f $$p \
444 ; then \
445 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
446 echo " $(INSTALL_PROGRAM_ENV) $(helloPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(hellodir)/$$f'"; \
447 $(INSTALL_PROGRAM_ENV) $(helloPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(hellodir)/$$f" || exit 1; \
448 else :; fi; \
449 done
450
451 uninstall-helloPROGRAMS:
452 @$(NORMAL_UNINSTALL)
453 @list='$(hello_PROGRAMS)'; for p in $$list; do \
454 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
455 echo " rm -f '$(DESTDIR)$(hellodir)/$$f'"; \
456 rm -f "$(DESTDIR)$(hellodir)/$$f"; \
457 done
458
459 clean-helloPROGRAMS:
460 -test -z "$(hello_PROGRAMS)" || rm -f $(hello_PROGRAMS)
461 install-httpsPROGRAMS: $(https_PROGRAMS)
462 @$(NORMAL_INSTALL)
463 test -z "$(httpsdir)" || $(mkdir_p) "$(DESTDIR)$(httpsdir)"
464 @list='$(https_PROGRAMS)'; for p in $$list; do \
465 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
466 if test -f $$p \
467 ; then \
468 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
469 echo " $(INSTALL_PROGRAM_ENV) $(httpsPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(httpsdir)/$$f'"; \
470 $(INSTALL_PROGRAM_ENV) $(httpsPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(httpsdir)/$$f" || exit 1; \
471 else :; fi; \
472 done
473
474 uninstall-httpsPROGRAMS:
475 @$(NORMAL_UNINSTALL)
476 @list='$(https_PROGRAMS)'; for p in $$list; do \
477 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
478 echo " rm -f '$(DESTDIR)$(httpsdir)/$$f'"; \
479 rm -f "$(DESTDIR)$(httpsdir)/$$f"; \
480 done
481
482 clean-httpsPROGRAMS:
483 -test -z "$(https_PROGRAMS)" || rm -f $(https_PROGRAMS)
484 bookmarks.dpi$(EXEEXT): $(bookmarks_dpi_OBJECTS) $(bookmarks_dpi_DEPENDENCIES)
485 @rm -f bookmarks.dpi$(EXEEXT)
486 $(LINK) $(bookmarks_dpi_LDFLAGS) $(bookmarks_dpi_OBJECTS) $(bookmarks_dpi_LDADD) $(LIBS)
487 cookies.dpi$(EXEEXT): $(cookies_dpi_OBJECTS) $(cookies_dpi_DEPENDENCIES)
488 @rm -f cookies.dpi$(EXEEXT)
489 $(LINK) $(cookies_dpi_LDFLAGS) $(cookies_dpi_OBJECTS) $(cookies_dpi_LDADD) $(LIBS)
490 datauri.filter.dpi$(EXEEXT): $(datauri_filter_dpi_OBJECTS) $(datauri_filter_dpi_DEPENDENCIES)
491 @rm -f datauri.filter.dpi$(EXEEXT)
492 $(LINK) $(datauri_filter_dpi_LDFLAGS) $(datauri_filter_dpi_OBJECTS) $(datauri_filter_dpi_LDADD) $(LIBS)
493 downloads.dpi$(EXEEXT): $(downloads_dpi_OBJECTS) $(downloads_dpi_DEPENDENCIES)
494 @rm -f downloads.dpi$(EXEEXT)
495 $(CXXLINK) $(downloads_dpi_LDFLAGS) $(downloads_dpi_OBJECTS) $(downloads_dpi_LDADD) $(LIBS)
496 file.dpi$(EXEEXT): $(file_dpi_OBJECTS) $(file_dpi_DEPENDENCIES)
497 @rm -f file.dpi$(EXEEXT)
498 $(LINK) $(file_dpi_LDFLAGS) $(file_dpi_OBJECTS) $(file_dpi_LDADD) $(LIBS)
499 ftp.filter.dpi$(EXEEXT): $(ftp_filter_dpi_OBJECTS) $(ftp_filter_dpi_DEPENDENCIES)
500 @rm -f ftp.filter.dpi$(EXEEXT)
501 $(LINK) $(ftp_filter_dpi_LDFLAGS) $(ftp_filter_dpi_OBJECTS) $(ftp_filter_dpi_LDADD) $(LIBS)
502 hello.filter.dpi$(EXEEXT): $(hello_filter_dpi_OBJECTS) $(hello_filter_dpi_DEPENDENCIES)
503 @rm -f hello.filter.dpi$(EXEEXT)
504 $(LINK) $(hello_filter_dpi_LDFLAGS) $(hello_filter_dpi_OBJECTS) $(hello_filter_dpi_LDADD) $(LIBS)
505 https.filter.dpi$(EXEEXT): $(https_filter_dpi_OBJECTS) $(https_filter_dpi_DEPENDENCIES)
506 @rm -f https.filter.dpi$(EXEEXT)
507 $(LINK) $(https_filter_dpi_LDFLAGS) $(https_filter_dpi_OBJECTS) $(https_filter_dpi_LDADD) $(LIBS)
508
509 mostlyclean-compile:
510 -rm -f *.$(OBJEXT)
511
512 distclean-compile:
513 -rm -f *.tab.c
514
515 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bookmarks.Po@am__quote@
516 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cookies.Po@am__quote@
517 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/datauri.Po@am__quote@
518 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/downloads-old.Po@am__quote@
519 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/downloads.Po@am__quote@
520 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpiutil.Po@am__quote@
521 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Po@am__quote@
522 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ftp.Po@am__quote@
523 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hello.Po@am__quote@
524 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/https.Po@am__quote@
525
526 .c.o:
527 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
528 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
529 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
530 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
531 @am__fastdepCC_FALSE@ $(COMPILE) -c $<
532
533 .c.obj:
534 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
535 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
536 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
537 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
538 @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
539
540 .cc.o:
541 @am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
542 @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
543 @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
544 @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
545 @am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
546
547 .cc.obj:
548 @am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
549 @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
550 @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
551 @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
552 @am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
553 uninstall-info-am:
554
555 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
556 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
557 unique=`for i in $$list; do \
558 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
559 done | \
560 $(AWK) ' { files[$$0] = 1; } \
561 END { for (i in files) print i; }'`; \
562 mkid -fID $$unique
563 tags: TAGS
564
565 TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
566 $(TAGS_FILES) $(LISP)
567 tags=; \
568 here=`pwd`; \
569 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
570 unique=`for i in $$list; do \
571 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
572 done | \
573 $(AWK) ' { files[$$0] = 1; } \
574 END { for (i in files) print i; }'`; \
575 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
576 test -n "$$unique" || unique=$$empty_fix; \
577 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
578 $$tags $$unique; \
579 fi
580 ctags: CTAGS
581 CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
582 $(TAGS_FILES) $(LISP)
583 tags=; \
584 here=`pwd`; \
585 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
586 unique=`for i in $$list; do \
587 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
588 done | \
589 $(AWK) ' { files[$$0] = 1; } \
590 END { for (i in files) print i; }'`; \
591 test -z "$(CTAGS_ARGS)$$tags$$unique" \
592 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
593 $$tags $$unique
594
595 GTAGS:
596 here=`$(am__cd) $(top_builddir) && pwd` \
597 && cd $(top_srcdir) \
598 && gtags -i $(GTAGS_ARGS) $$here
599
600 distclean-tags:
601 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
602
603 distdir: $(DISTFILES)
604 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
605 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
606 list='$(DISTFILES)'; for file in $$list; do \
607 case $$file in \
608 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
609 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
610 esac; \
611 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
612 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
613 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
614 dir="/$$dir"; \
615 $(mkdir_p) "$(distdir)$$dir"; \
616 else \
617 dir=''; \
618 fi; \
619 if test -d $$d/$$file; then \
620 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
621 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
622 fi; \
623 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
624 else \
625 test -f $(distdir)/$$file \
626 || cp -p $$d/$$file $(distdir)/$$file \
627 || exit 1; \
628 fi; \
629 done
630 check-am: all-am
631 check: check-am
632 all-am: Makefile $(PROGRAMS)
633 installdirs:
634 for dir in "$(DESTDIR)$(bookmarksdir)" "$(DESTDIR)$(cookiesdir)" "$(DESTDIR)$(datauridir)" "$(DESTDIR)$(downloadsdir)" "$(DESTDIR)$(filedir)" "$(DESTDIR)$(ftpdir)" "$(DESTDIR)$(hellodir)" "$(DESTDIR)$(httpsdir)"; do \
635 test -z "$$dir" || $(mkdir_p) "$$dir"; \
636 done
637 install: install-am
638 install-exec: install-exec-am
639 install-data: install-data-am
640 uninstall: uninstall-am
641
642 install-am: all-am
643 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
644
645 installcheck: installcheck-am
646 install-strip:
647 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
648 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
649 `test -z '$(STRIP)' || \
650 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
651 mostlyclean-generic:
652
653 clean-generic:
654
655 distclean-generic:
656 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
657
658 maintainer-clean-generic:
659 @echo "This command is intended for maintainers to use"
660 @echo "it deletes files that may require special tools to rebuild."
661 clean: clean-am
662
663 clean-am: clean-bookmarksPROGRAMS clean-cookiesPROGRAMS \
664 clean-datauriPROGRAMS clean-downloadsPROGRAMS \
665 clean-filePROGRAMS clean-ftpPROGRAMS clean-generic \
666 clean-helloPROGRAMS clean-httpsPROGRAMS mostlyclean-am
667
668 distclean: distclean-am
669 -rm -rf ./$(DEPDIR)
670 -rm -f Makefile
671 distclean-am: clean-am distclean-compile distclean-generic \
672 distclean-tags
673
674 dvi: dvi-am
675
676 dvi-am:
677
678 html: html-am
679
680 info: info-am
681
682 info-am:
683
684 install-data-am: install-bookmarksPROGRAMS install-cookiesPROGRAMS \
685 install-datauriPROGRAMS install-downloadsPROGRAMS \
686 install-filePROGRAMS install-ftpPROGRAMS install-helloPROGRAMS \
687 install-httpsPROGRAMS
688
689 install-exec-am:
690
691 install-info: install-info-am
692
693 install-man:
694
695 installcheck-am:
696
697 maintainer-clean: maintainer-clean-am
698 -rm -rf ./$(DEPDIR)
699 -rm -f Makefile
700 maintainer-clean-am: distclean-am maintainer-clean-generic
701
702 mostlyclean: mostlyclean-am
703
704 mostlyclean-am: mostlyclean-compile mostlyclean-generic
705
706 pdf: pdf-am
707
708 pdf-am:
709
710 ps: ps-am
711
712 ps-am:
713
714 uninstall-am: uninstall-bookmarksPROGRAMS uninstall-cookiesPROGRAMS \
715 uninstall-datauriPROGRAMS uninstall-downloadsPROGRAMS \
716 uninstall-filePROGRAMS uninstall-ftpPROGRAMS \
717 uninstall-helloPROGRAMS uninstall-httpsPROGRAMS \
718 uninstall-info-am
719
720 .PHONY: CTAGS GTAGS all all-am check check-am clean \
721 clean-bookmarksPROGRAMS clean-cookiesPROGRAMS \
722 clean-datauriPROGRAMS clean-downloadsPROGRAMS \
723 clean-filePROGRAMS clean-ftpPROGRAMS clean-generic \
724 clean-helloPROGRAMS clean-httpsPROGRAMS ctags distclean \
725 distclean-compile distclean-generic distclean-tags distdir dvi \
726 dvi-am html html-am info info-am install install-am \
727 install-bookmarksPROGRAMS install-cookiesPROGRAMS install-data \
728 install-data-am install-datauriPROGRAMS \
729 install-downloadsPROGRAMS install-exec install-exec-am \
730 install-filePROGRAMS install-ftpPROGRAMS install-helloPROGRAMS \
731 install-httpsPROGRAMS install-info install-info-am install-man \
732 install-strip installcheck installcheck-am installdirs \
733 maintainer-clean maintainer-clean-generic mostlyclean \
734 mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
735 tags uninstall uninstall-am uninstall-bookmarksPROGRAMS \
736 uninstall-cookiesPROGRAMS uninstall-datauriPROGRAMS \
737 uninstall-downloadsPROGRAMS uninstall-filePROGRAMS \
738 uninstall-ftpPROGRAMS uninstall-helloPROGRAMS \
739 uninstall-httpsPROGRAMS uninstall-info-am
740
741 # Tell versions [3.59,3.63) of GNU make to not export all variables.
742 # Otherwise a system limit (for SysV at least) may be exceeded.
743 .NOEXPORT:
22 *
33 * NOTE: this code illustrates how to make a dpi-program.
44 *
5 * Copyright 2002-2006 Jorge Arellano Cid <jcid@dillo.org>
5 * Copyright 2002-2007 Jorge Arellano Cid <jcid@dillo.org>
66 *
77 * This program is free software; you can redistribute it and/or modify
88 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
9 * the Free Software Foundation; either version 3 of the License, or
1010 * (at your option) any later version.
1111 *
1212 */
1313
14 /* Todo: this server is not assembling the received packets.
14 /* TODO: this server is not assembling the received packets.
1515 * This means it currently expects dillo to send full dpi tags
1616 * within the socket; if that fails, everything stops.
1717 * This is not hard to fix, mainly is a matter of expecting the
3636 #include "../dpip/dpip.h"
3737 #include "dpiutil.h"
3838
39 #include <glib.h>
40
41 /* This one is tricky, some sources state it should include the byte
42 * for the terminating NULL, and others say it shouldn't. */
43 # define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
44 + strlen ((ptr)->sun_path))
39
40 /*
41 * Debugging macros
42 */
43 #define _MSG(...)
44 #define MSG(...) printf("[bookmarks dpi]: " __VA_ARGS__)
4545
4646 #define DOCTYPE \
4747 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
5353 * - ' is escaped as %27 in URLs and saved escaped.
5454 */
5555 typedef struct {
56 gint key;
57 gint section;
58 gchar *url;
59 gchar *title;
56 int key;
57 int section;
58 char *url;
59 char *title;
6060 } BmRec;
6161
6262 typedef struct {
63 gint section;
64 gchar *title;
65
66 gint o_sec; /* private, for normalization */
63 int section;
64 char *title;
65
66 int o_sec; /* private, for normalization */
6767 } BmSec;
6868
6969
7373 static char *Header = "Content-type: text/html\n\n";
7474 static char *BmFile = NULL;
7575 static time_t BmFileTimeStamp = 0;
76 static GSList *B_bms = NULL;
77 static gint bm_key = 0;
78
79 static GSList *B_secs = NULL;
80 static gint sec_key = 0;
76 static Dlist *B_bms = NULL;
77 static int bm_key = 0;
78
79 static Dlist *B_secs = NULL;
80 static int sec_key = 0;
8181
8282 static int MODIFY_PAGE_NUM = 1;
8383
8989
9090 /* -- HTML templates ------------------------------------------------------- */
9191
92 char *mainpage_header =
92 static const char *mainpage_header =
9393 DOCTYPE
9494 "<html>\n"
9595 "<head>\n"
9696 "<title>Bookmarks</title>\n"
9797 "</head>\n"
98 "<body bgcolor='#778899' link='black' vlink='brown'>\n"
98 "<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
9999 "<table border='1' cellpadding='0' width='100%'>\n"
100100 " <tr><td>\n"
101101 " <table width='100%' bgcolor='#b4b4b4'>\n"
102102 " <tr>\n"
103 " <td>&nbsp;Bookmarks::</td>\n"
104 " <td width='100%' align='right'>\n"
103 " <td> Bookmarks :: </td>\n"
104 " <td align='right'>\n"
105105 " [<a href='dpi:/bm/modify'>modify</a>]\n"
106106 " </td></tr>\n"
107107 " </table></td></tr>\n"
108108 "</table>\n"
109109 "<br>\n";
110110
111 char *modifypage_header =
111 static const char *modifypage_header =
112112 DOCTYPE
113113 "<html>\n"
114114 "<head>\n"
115115 "<title>Bookmarks</title>\n"
116116 "</head>\n"
117 "<body bgcolor='#778899' link='black' vlink='brown'>\n"
117 "<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
118118 "<table border='1' cellpadding='0' width='100%'>\n"
119119 " <tr><td>\n"
120120 " <table width='100%' bgcolor='#b4b4b4'>\n"
121121 " <tr>\n"
122 " <td>&nbsp;Bookmarks :: modify</td></tr>\n"
122 " <td> Bookmarks :: modify</td>\n"
123 " <td align='right'>\n"
124 " [<a href='dpi:/bm/'>cancel</a>]\n"
125 " </td>\n"
126 " </tr>\n"
123127 " </table></td></tr> \n"
124128 "</table> \n"
125129 "\n"
126 "<form>\n"
130 "<form action='modify'>\n"
127131 "<table width='100%' border='1' cellpadding='0'>\n"
128 " <tr><td>\n"
129 " <table width='100%' bgcolor='teal'>\n"
130 " <tr>\n"
131 " <td><b>Select&nbsp;an&nbsp;operation&nbsp;</b></td>\n"
132 " <td><select name='operation'>\n"
133 " <option value='none' selected>--\n"
134 " <option value='delete'>Delete\n"
135 " <option value='move'>Move\n"
136 " <option value='modify'>Modify\n"
137 " <option value='add_sec'>Add Section\n"
138 " <option value='add_url'>Add URL\n"
139 " </select></td>\n"
140 " <td><b>,&nbsp;mark&nbsp;its&nbsp;operands,&nbsp;and&nbsp;</b></td>\n"
141 " <td><input type='submit' name='submit' value='submit.'></td>\n"
142 " <td width='100%'></td>\n"
143 " </tr>\n"
144 " </table></td></tr>\n"
132 " <tr style='background-color: teal'>\n"
133 " <td>\n"
134 " <b>Select an operation</b>\n"
135 " <select name='operation'>\n"
136 " <option value='none' selected>--\n"
137 " <option value='delete'>Delete\n"
138 " <option value='move'>Move\n"
139 " <option value='modify'>Modify\n"
140 " <option value='add_sec'>Add Section\n"
141 " <option value='add_url'>Add URL\n"
142 " </select>\n"
143 " <b>, mark its operands, and</b>\n"
144 " <input type='submit' name='submit' value='submit.'>\n"
145 " </td>\n"
146 " </tr>\n"
145147 "</table>\n";
146148
147 char *mainpage_sections_header =
149 static const char *mainpage_sections_header =
148150 "<table border='1' cellpadding='0' cellspacing='20' width='100%'>\n"
149151 " <tr valign='top'>\n"
150152 " <td>\n"
153155 " <table width='100%' bgcolor='#b4b4b4'>\n"
154156 " <tr><td><small>Sections:</small></td></tr></table></td></tr>\n";
155157
156 char *modifypage_sections_header =
158 static const char *modifypage_sections_header =
157159 "<table border='1' cellpadding='0' cellspacing='20' width='100%'>\n"
158160 " <tr valign='top'>\n"
159161 " <td>\n"
162164 " <table width='100%' bgcolor='#b4b4b4'>\n"
163165 " <tr><td><small>Sections:</small></td></tr></table></td></tr>\n";
164166
165 char *mainpage_sections_item =
167 static const char *mainpage_sections_item =
166168 " <tr><td align='center'>\n"
167169 " <a href='#s%d'>%s</a></td></tr>\n";
168170
169 char *modifypage_sections_item =
171 static const char *modifypage_sections_item =
170172 " <tr><td>\n"
171 " <table width='100%%' bgcolor='#b4b4b4'cellspacing='0' cellpadding='0'>\n"
173 " <table width='100%%'>\n"
172174 " <tr align='center'>"
173 " <td width='1%%'><input type='checkbox' name='s%d'></td>\n"
174 " <td><a href='#s%d'>%s</a></td></tr></table></td></tr>\n";
175
176 char *mainpage_sections_footer =
175 " <td><input type='checkbox' name='s%d'></td>\n"
176 " <td width='100%%'><a href='#s%d'>%s</a></td></tr></table></td></tr>\n";
177
178 static const char *mainpage_sections_footer =
177179 " </table>\n";
178180
179 char *modifypage_sections_footer =
181 static const char *modifypage_sections_footer =
180182 " </table>\n";
181183
182 char *mainpage_middle1 =
184 static const char *mainpage_middle1 =
183185 " </td>\n"
184186 " <td width='100%'>\n";
185187
186 char *modifypage_middle1 =
188 static const char *modifypage_middle1 =
187189 " </td>\n"
188190 " <td width='100%'>\n";
189191
190 char *mainpage_section_card_header =
192 static const char *mainpage_section_card_header =
191193 " <a name='s%d'></a>\n"
192194 " <table bgcolor='#bfbfbf' width='100%%' cellspacing='2'>\n"
193195 " <tr>\n"
195197 " &nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;&nbsp;</b></font></td>\n"
196198 " <td bgcolor='white' width='100%%'>&nbsp;</td></tr>\n";
197199
198 char *modifypage_section_card_header =
200 static const char *modifypage_section_card_header =
199201 " <a name='s%d'></a>\n"
200202 " <table bgcolor='#bfbfbf' width='100%%' cellspacing='2'>\n"
201203 " <tr>\n"
203205 " &nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;&nbsp;</b></font></td>\n"
204206 " <td bgcolor='white' width='100%%'>&nbsp;</td></tr>\n";
205207
206 char *mainpage_section_card_item =
208 static const char *mainpage_section_card_item =
207209 " <tr><td colspan='2'>\n"
208210 " <a href='%s'>%s</a> </td></tr>\n";
209211
210 char *modifypage_section_card_item =
212 static const char *modifypage_section_card_item =
211213 " <tr>\n"
212214 " <td colspan='2'><input type='checkbox' name='url%d'>\n"
213215 " <a href='%s'>%s</a></td></tr>\n";
214216
215 char *mainpage_section_card_footer =
217 static const char *mainpage_section_card_footer =
216218 " </table>\n"
217219 " <hr>\n";
218220
219 char *modifypage_section_card_footer =
221 static const char *modifypage_section_card_footer =
220222 " </table>\n"
221223 " <hr>\n";
222224
223 char *mainpage_footer =
225 static const char *mainpage_footer =
224226 " </td>\n"
225227 " </tr>\n"
226228 "</table>\n"
227229 "</body>\n"
228230 "</html>\n";
229231
230 char *modifypage_footer =
232 static const char *modifypage_footer =
231233 " </td>\n"
232234 " </tr>\n"
233235 "</table>\n"
236238 "</html>\n";
237239
238240 /* ------------------------------------------------------------------------- */
239 char *modifypage_add_section_page =
241 static const char *modifypage_add_section_page =
240242 DOCTYPE
241243 "<html>\n"
242244 "<head>\n"
243245 "<title>Bookmarks</title>\n"
244246 "</head>\n"
245 "<body bgcolor='#778899' link='black' vlink='brown'>\n"
247 "<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
246248 "<table border='1' cellpadding='0' width='100%'>\n"
247249 " <tr><td colspan='2'>\n"
248250 " <table bgcolor='#b4b4b4' width='100%'>\n"
249 " <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: add section\n"
250 " </td></tr></table></td></tr>\n"
251 " <tr>\n"
252 " <td bgcolor='#b4b4b4'>\n"
253 " Modify bookmarks :: add section\n"
254 " </td>\n"
255 " <td align='right'>\n"
256 " [<a href='dpi:/bm/'>cancel</a>]\n"
257 " </td>\n"
258 " </tr>\n"
259 " </table></td></tr>\n"
251260 "</table>\n"
252261 "<br>\n"
253 "<form>\n"
262 "<form action='modify'>\n"
254263 " <input type='hidden' name='operation' value='add_section'>\n"
255264 "<table border='1' width='100%'>\n"
256265 " <tr>\n"
276285 "\n";
277286
278287 /* ------------------------------------------------------------------------- */
279 char *modifypage_update_header =
288 static const char *modifypage_update_header =
280289 DOCTYPE
281290 "<html>\n"
282291 "<head>\n"
283292 "<title>Bookmarks</title>\n"
284293 "</head>\n"
285 "<body bgcolor='#778899' link='black' vlink='brown'>\n"
294 "<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
286295 "<table border='1' cellpadding='0' width='100%'>\n"
287296 " <tr><td colspan='2'>\n"
288297 " <table bgcolor='#b4b4b4' width='100%'>\n"
289 " <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: update\n"
290 " </td></tr></table></td></tr>\n"
298 " <tr><td bgcolor='#b4b4b4'> Modify bookmarks :: update\n"
299 " </td>\n"
300 " <td align='right'>\n"
301 " [<a href='dpi:/bm/'>cancel</a>]\n"
302 " </td>\n"
303 " </tr>\n"
304 " </table></td></tr>\n"
291305 "</table>\n"
292306 "<br>\n"
293 "<form>\n"
307 "<form action='modify'>\n"
294308 "<input type='hidden' name='operation' value='modify2'>\n";
295309
296 char *modifypage_update_title =
310 static const char *modifypage_update_title =
297311 "<table border='1' width='100%%'>\n"
298312 " <tr>\n"
299313 " <td bgcolor='olive'><b>%s</b></td>\n"
300314 " <td bgcolor='white' width='100%%'></td></tr>\n"
301315 "</table>\n";
302316
303 char *modifypage_update_item_header =
317 static const char *modifypage_update_item_header =
304318 "<table width='100%' cellpadding='10'>\n";
305319
306 char *modifypage_update_item =
320 static const char *modifypage_update_item =
307321 "<tr><td>\n"
308322 " <table width='100%%' bgcolor='teal'>\n"
309323 " <tr>\n"
316330 " </table>\n"
317331 " </td></tr>\n";
318332
319 char *modifypage_update_item2 =
333 static const char *modifypage_update_item2 =
320334 "<tr><td>\n"
321335 " <table width='100%%' bgcolor='teal'>\n"
322336 " <tr>\n"
326340 " </table>\n"
327341 " </td></tr>\n";
328342
329 char *modifypage_update_item_footer =
343 static const char *modifypage_update_item_footer =
330344 "</table>\n";
331345
332 char *modifypage_update_footer =
346 static const char *modifypage_update_footer =
333347 "<table width='100%' cellpadding='4' border='0'>\n"
334348 "<tr><td bgcolor='#a0a0a0'>\n"
335349 " <input type='submit' name='submit' value='submit.'></td></tr>\n"
339353 "</html>\n";
340354
341355 /* ------------------------------------------------------------------------- */
342 char *modifypage_add_url =
356 static const char *modifypage_add_url =
343357 DOCTYPE
344358 "<html>\n"
345359 "<head>\n"
346360 "<title>Bookmarks</title>\n"
347361 "</head>\n"
348 "<body bgcolor='#778899' link='black' vlink='brown'>\n"
362 "<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
349363 "<table border='1' cellpadding='0' width='100%'>\n"
350364 " <tr><td colspan='2'>\n"
351365 " <table bgcolor='#b4b4b4' width='100%'>\n"
352 " <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: add url\n"
353 " </td></tr></table></td></tr>\n"
366 " <tr><td bgcolor='#b4b4b4'> Modify bookmarks :: add url\n"
367 " </td>\n"
368 " <td align='right'>\n"
369 " [<a href='dpi:/bm/'>cancel</a>]\n"
370 " </td>\n"
371 " </tr>\n"
372 " </table></td></tr>\n"
354373 "</table>\n"
355374 "<br>\n"
356 "<form>\n"
375 "<form action='modify'>\n"
357376 "<input type='hidden' name='operation' value='add_url2'>\n"
358377 "<table border='1' width='100%'>\n"
359378 " <tr>\n"
395414 if (str[i] == ' ')
396415 ++n;
397416
398 new_str = g_new(char, strlen(str) + 6*n + 1);
417 new_str = dNew(char, strlen(str) + 6*n + 1);
399418 new_str[0] = 0;
400419
401420 for (i = 0, j = 0; str[i]; ++i) {
422441 if (*e == '+') {
423442 *p = ' ';
424443 } else if (*e == '%') {
425 if (g_strncasecmp(e, "%0D%0A", 6) == 0) {
444 if (dStrncasecmp(e, "%0D%0A", 6) == 0) {
426445 *p = '\n';
427446 e += 5;
428447 } else {
438457 }
439458
440459 /*
441 * Read a line of text up to the newline character, store it into a newly
442 * allocated string and return it.
443 */
444 static char *Get_line(FILE *stream)
445 {
446 guint i, size = 64;
447 int ch;
448 char *buf;
449
450 buf = g_new(char, size);
451
452 for (i = 0; (ch = fgetc(stream)) != EOF; ++i) {
453 if (i + 1 == size) {
454 size *= 2;
455 buf = g_realloc(buf, size);
456 }
457 if ((buf[i] = ch) == '\n' && ++i)
458 break;
459 }
460 buf[i] = 0;
461
462 if (i > 0) {
463 buf = g_realloc(buf, i + 1);
464 } else {
465 g_free(buf);
466 buf = NULL;
467 }
468 return buf;
469 }
470
471 /*
472460 * Send a short message to dillo's status bar.
473461 */
474 static int Bmsrv_dpi_send_status_msg(SockHandler *sh, char *str)
462 static int Bmsrv_dpi_send_status_msg(Dsh *sh, char *str)
475463 {
476464 int st;
477465 char *d_cmd;
478466
479467 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_status_message", str);
480 st = sock_handler_write_str(sh, d_cmd, 1);
481 g_free(d_cmd);
468 st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
469 dFree(d_cmd);
482470 return st;
483471 }
484472
486474 /*
487475 * Compare function for searching a bookmark by its key
488476 */
489 static gint Bms_key_cmp(gconstpointer node, gconstpointer key)
490 {
491 return ( GPOINTER_TO_INT(key) != ((BmRec *)node)->key );
477 static int Bms_node_by_key_cmp(const void *node, const void *key)
478 {
479 return ((BmRec *)node)->key - VOIDP2INT(key);
492480 }
493481
494482 /*
495483 * Compare function for searching a bookmark by section
496484 */
497 static gint Bms_bysec_cmp(gconstpointer node, gconstpointer key)
498 {
499 return ( GPOINTER_TO_INT(key) != ((BmRec *)node)->section );
485 static int Bms_node_by_section_cmp(const void *node, const void *key)
486 {
487 return ((BmRec *)node)->section - VOIDP2INT(key);
500488 }
501489
502490 /*
503491 * Compare function for searching a section by its number
504492 */
505 static gint Bms_sec_cmp(gconstpointer node, gconstpointer key)
506 {
507 return ( GPOINTER_TO_INT(key) != ((BmSec *)node)->section );
493 static int Bms_sec_by_number_cmp(const void *node, const void *key)
494 {
495 return ((BmSec *)node)->section - VOIDP2INT(key);
508496 }
509497
510498 /*
511499 * Return the Bm record by key
512500 */
513 static BmRec *Bms_get(gint key)
514 {
515 GSList *list;
516
517 list = g_slist_find_custom(B_bms, GINT_TO_POINTER(key),
518 Bms_key_cmp);
519 return (list) ? list->data : NULL;
501 static BmRec *Bms_get(int key)
502 {
503 return dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
520504 }
521505
522506 /*
523507 * Return the Section record by key
524508 */
525 static BmSec *Bms_get_sec(gint key)
526 {
527 GSList *list;
528
529 list = g_slist_find_custom(B_secs, GINT_TO_POINTER(key),
530 Bms_sec_cmp);
531 return (list) ? list->data : NULL;
509 static BmSec *Bms_get_sec(int key)
510 {
511 return dList_find_custom(B_secs, INT2VOIDP(key), Bms_sec_by_number_cmp);
532512 }
533513
534514 /*
535515 * Add a bookmark
536516 */
537 static void Bms_add(gint section, char *url, char *title)
538 {
539 BmRec *node;
540
541 node = g_new(BmRec, 1);
542 node->key = ++bm_key;
543 node->section = section;
544 node->url = Escape_uri_str(url, "'");
545 node->title = Escape_html_str(title);
546 B_bms = g_slist_append(B_bms, node);
517 static void Bms_add(int section, char *url, char *title)
518 {
519 BmRec *bm_node;
520
521 bm_node = dNew(BmRec, 1);
522 bm_node->key = ++bm_key;
523 bm_node->section = section;
524 bm_node->url = Escape_uri_str(url, "'");
525 bm_node->title = Escape_html_str(title);
526 dList_append(B_bms, bm_node);
547527 }
548528
549529 /*
551531 */
552532 static void Bms_sec_add(char *title)
553533 {
554 BmSec *node;
555
556 node = g_new(BmSec, 1);
557 node->section = sec_key++;
558 node->title = Escape_html_str(title);
559 B_secs = g_slist_append(B_secs, node);
534 BmSec *sec_node;
535
536 sec_node = dNew(BmSec, 1);
537 sec_node->section = sec_key++;
538 sec_node->title = Escape_html_str(title);
539 dList_append(B_secs, sec_node);
560540 }
561541
562542 /*
563543 * Delete a bookmark by its key
564544 */
565 static void Bms_del(gint key)
566 {
567 GSList *list;
568 BmRec *node;
569
570 list = g_slist_find_custom(B_bms, GINT_TO_POINTER(key),
571 Bms_key_cmp);
572 if (list) {
573 node = list->data;
574 g_free(node->title);
575 g_free(node->url);
576 g_free(list->data);
577 B_bms = g_slist_remove(B_bms, list->data);
578 }
579 if (B_bms == NULL)
545 static void Bms_del(int key)
546 {
547 BmRec *bm_node;
548
549 bm_node = dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
550 if (bm_node) {
551 dList_remove(B_bms, bm_node);
552 dFree(bm_node->title);
553 dFree(bm_node->url);
554 dFree(bm_node);
555 }
556 if (dList_length(B_bms) == 0)
580557 bm_key = 0;
581558 }
582559
583560 /*
584561 * Delete a section and its bookmarks by section number
585562 */
586 static void Bms_sec_del(gint section)
587 {
588 GSList *list;
563 static void Bms_sec_del(int section)
564 {
589565 BmSec *sec_node;
590566 BmRec *bm_node;
591567
592 list = g_slist_find_custom(B_secs, GINT_TO_POINTER(section),
593 Bms_sec_cmp);
594 if (list) {
595 sec_node = list->data;
596 g_free(sec_node->title);
597 g_free(list->data);
598 B_secs = g_slist_remove(B_secs, list->data);
568 sec_node = dList_find_custom(B_secs, INT2VOIDP(section),
569 Bms_sec_by_number_cmp);
570 if (sec_node) {
571 dList_remove(B_secs, sec_node);
572 dFree(sec_node->title);
573 dFree(sec_node);
599574
600575 /* iterate B_bms and remove those that match the section */
601 while ((list = g_slist_find_custom(B_bms, GINT_TO_POINTER(section),
602 Bms_bysec_cmp))) {
603 bm_node = list->data;
576 while ((bm_node = dList_find_custom(B_bms, INT2VOIDP(section),
577 Bms_node_by_section_cmp))) {
604578 Bms_del(bm_node->key);
605579 }
606580 }
607 if (B_secs == NULL)
581 if (dList_length(B_secs) == 0)
608582 sec_key = 0;
609583 }
610584
611585 /*
612586 * Move a bookmark to another section
613587 */
614 static void Bms_move(gint key, gint target_section)
615 {
616 GSList *list;
617
618 list = g_slist_find_custom(B_bms, GINT_TO_POINTER(key), Bms_key_cmp);
619 if (list) {
620 BmRec *node = list->data;
621 node->section = target_section;
588 static void Bms_move(int key, int target_section)
589 {
590 BmRec *bm_node;
591
592 bm_node = dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
593 if (bm_node) {
594 bm_node->section = target_section;
622595 }
623596 }
624597
625598 /*
626599 * Update a bookmark title by key
627600 */
628 static void Bms_update_title(gint key, gchar *n_title)
629 {
630 GSList *list;
631
632 list = g_slist_find_custom(B_bms, GINT_TO_POINTER(key), Bms_key_cmp);
633 if (list) {
634 BmRec *node = list->data;
635 g_free(node->title);
636 node->title = Escape_html_str(n_title);
601 static void Bms_update_title(int key, char *n_title)
602 {
603 BmRec *bm_node;
604
605 bm_node = dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
606 if (bm_node) {
607 dFree(bm_node->title);
608 bm_node->title = Escape_html_str(n_title);
637609 }
638610 }
639611
640612 /*
641613 * Update a section title by key
642614 */
643 static void Bms_update_sec_title(gint key, gchar *n_title)
644 {
645 GSList *list;
646
647 list = g_slist_find_custom(B_secs, GINT_TO_POINTER(key),
648 Bms_sec_cmp);
649 if (list) {
650 BmSec *node = list->data;
651 g_free(node->title);
652 node->title = Escape_html_str(n_title);
615 static void Bms_update_sec_title(int key, char *n_title)
616 {
617 BmSec *sec_node;
618
619 sec_node = dList_find_custom(B_secs, INT2VOIDP(key), Bms_sec_by_number_cmp);
620 if (sec_node) {
621 dFree(sec_node->title);
622 sec_node->title = Escape_html_str(n_title);
653623 }
654624 }
655625
662632 BmSec *sec_node;
663633
664634 /* free B_bms */
665 while (B_bms) {
666 bm_node = B_bms->data;
635 while ((bm_node = dList_nth_data(B_bms, 0))) {
667636 Bms_del(bm_node->key);
668637 }
669638 /* free B_secs */
670 while (B_secs) {
671 sec_node = B_secs->data;
639 while ((sec_node = dList_nth_data(B_secs, 0))) {
672640 Bms_sec_del(sec_node->section);
673641 }
674642 }
680648 {
681649 BmRec *bm_node;
682650 BmSec *sec_node;
683 GSList *list1, *list2;
684 gint n;
651 int i, j;
685652
686653 /* we need at least one section */
687 if (!B_secs)
654 if (dList_length(B_secs) == 0)
688655 Bms_sec_add("Unclassified");
689656
690657 /* make correlative section numbers */
691 n = 0;
692 for (list1 = B_secs; list1; list1 = list1->next) {
693 sec_node = list1->data;
658 for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
694659 sec_node->o_sec = sec_node->section;
695 sec_node->section = n++;
660 sec_node->section = i;
696661 }
697662
698663 /* iterate B_secs and make the changes in B_bms */
699 for (list1 = B_secs; list1; list1 = list1->next) {
700 sec_node = list1->data;
664 for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
701665 if (sec_node->section != sec_node->o_sec) {
702666 /* update section numbers */
703 for (list2 = B_bms; list2; list2 = list2->next) {
704 bm_node = list2->data;
667 for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
705668 if (bm_node->section == sec_node->o_sec)
706669 bm_node->section = sec_node->section;
707670 }
716679 */
717680 static void Bms_check_import(void)
718681 {
719 gchar *OldBmFile;
682 char *OldBmFile;
720683 char *cmd1 =
721684 "echo \":s0: Unclassified\" > %s";
722685 char *cmd2 =
723686 "grep -i \"href\" %s | "
724687 "sed -e 's/<li><A HREF=\"/s0 /' -e 's/\">/ /' -e 's/<.*$//' >> %s";
725 GString *gstr = g_string_new("");
688 Dstr *dstr = dStr_new("");
689 int rc;
726690
727691
728692 if (access(BmFile, F_OK) != 0) {
729 OldBmFile = g_strconcat(g_get_home_dir(),
730 "/", ".dillo/bookmarks.html", NULL);
693 OldBmFile = dStrconcat(dGethomedir(), "/.dillo/bookmarks.html", NULL);
731694 if (access(OldBmFile, F_OK) == 0) {
732 g_string_sprintf(gstr, cmd1, BmFile);
733 system(gstr->str);
734 g_string_sprintf(gstr, cmd2, OldBmFile, BmFile);
735 system(gstr->str);
736 g_string_free(gstr, TRUE);
737 g_free(OldBmFile);
695 dStr_sprintf(dstr, cmd1, BmFile);
696 rc = system(dstr->str);
697 if (rc == 127) {
698 MSG("Bookmarks: /bin/sh could not be executed\n");
699 } else if (rc == -1) {
700 MSG("Bookmarks: process creation failure: %s\n",
701 dStrerror(errno));
702 }
703 dStr_sprintf(dstr, cmd2, OldBmFile, BmFile);
704 rc = system(dstr->str);
705 if (rc == 127) {
706 MSG("Bookmarks: /bin/sh could not be executed\n");
707 } else if (rc == -1) {
708 MSG("Bookmarks: process creation failure: %s\n",
709 dStrerror(errno));
710 }
711
712 dStr_free(dstr, TRUE);
713 dFree(OldBmFile);
738714 }
739715 }
740716 }
759735 }
760736
761737 /* load bm file into memory */
762 while ((buf = Get_line(BmTxt)) != NULL) {
738 while ((buf = dGetline(BmTxt)) != NULL) {
763739 if (buf[0] == 's') {
764740 /* get section, url and title */
765741 section = strtol(buf + 1, NULL, 10);
770746 p = strchr(p, '\n'); *p = 0;
771747 u_title = Unescape_html_str(title);
772748 Bms_add(section, url, u_title);
773 g_free(u_title);
749 dFree(u_title);
774750
775751 } else if (buf[0] == ':' && buf[1] == 's') {
776752 /* section = strtol(buf + 2, NULL, 10); */
780756 Bms_sec_add(title);
781757
782758 } else {
783 g_print("Syntax error in bookmarks file:\n %s", buf);
784 }
785 g_free(buf);
759 MSG("Syntax error in bookmarks file:\n %s", buf);
760 }
761 dFree(buf);
786762 }
787763 fclose(BmTxt);
788764
810786 TimeStamp.st_mtime = 0;
811787 }
812788
813 if (!BmFileTimeStamp || !B_bms || !B_secs ||
789 if (!BmFileTimeStamp || !dList_length(B_bms) || !dList_length(B_secs) ||
814790 BmFileTimeStamp < TimeStamp.st_mtime) {
815791 Bms_load();
816792 st = 1;
829805 FILE *BmTxt;
830806 BmRec *bm_node;
831807 BmSec *sec_node;
832 GSList *list, *list2;
833808 struct stat BmStat;
834 gchar *u_title;
835 GString *gstr = g_string_new("");
809 char *u_title;
810 int i, j;
811 Dstr *dstr = dStr_new("");
836812
837813 /* make a safety backup */
838814 if (stat(BmFile, &BmStat) == 0 && BmStat.st_size > 256) {
839 gchar *BmFileBak = g_strconcat(BmFile, ".bak", NULL);
815 char *BmFileBak = dStrconcat(BmFile, ".bak", NULL);
840816 rename(BmFile, BmFileBak);
841 g_free(BmFileBak);
817 dFree(BmFileBak);
842818 }
843819
844820 /* open bm file */
851827 Bms_normalize();
852828
853829 /* save sections */
854 for (list = B_secs; list; list = list->next) {
855 sec_node = list->data;
830 for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
856831 u_title = Unescape_html_str(sec_node->title);
857 g_string_sprintf(gstr, ":s%d: %s\n", sec_node->section, u_title);
858 fwrite(gstr->str, (size_t)gstr->len, 1, BmTxt);
859 g_free(u_title);
832 dStr_sprintf(dstr, ":s%d: %s\n", sec_node->section, u_title);
833 fwrite(dstr->str, (size_t)dstr->len, 1, BmTxt);
834 dFree(u_title);
860835 }
861836
862837 /* save bookmarks (section url title) */
863 for (list = B_secs; list; list = list->next) {
864 sec_node = list->data;
865 for (list2 = B_bms; list2; list2 = list2->next) {
866 bm_node = list2->data;
838 for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
839 for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
867840 if (bm_node->section == sec_node->section) {
868841 u_title = Unescape_html_str(bm_node->title);
869 g_string_sprintf(gstr, "s%d %s %s\n",
870 bm_node->section, bm_node->url, u_title);
871 fwrite(gstr->str, (size_t)gstr->len, 1, BmTxt);
872 g_free(u_title);
842 dStr_sprintf(dstr, "s%d %s %s\n",
843 bm_node->section, bm_node->url, u_title);
844 fwrite(dstr->str, (size_t)dstr->len, 1, BmTxt);
845 dFree(u_title);
873846 }
874847 }
875848 }
876849
877 g_string_free(gstr, TRUE);
850 dStr_free(dstr, TRUE);
878851 fclose(BmTxt);
879852
880853 /* keep track of the timestamp */
889862 /*
890863 * Add a new bookmark to DB :)
891864 */
892 static int Bmsrv_add_bm(SockHandler *sh, char *url, char *title)
865 static int Bmsrv_add_bm(Dsh *sh, char *url, char *title)
893866 {
894867 char *u_title;
895868 char *msg="Added bookmark!";
898871 /* Add in memory */
899872 u_title = Unescape_html_str(title);
900873 Bms_add(section, url, u_title);
901 g_free(u_title);
874 dFree(u_title);
902875
903876 /* Write to file */
904877 Bms_save();
914887 /*
915888 * Count how many sections and urls were marked in a request
916889 */
917 static void Bmsrv_count_urls_and_sections(char *url, gint *n_sec, gint *n_url)
890 static void Bmsrv_count_urls_and_sections(char *url, int *n_sec, int *n_url)
918891 {
919892 char *p, *q;
920893 int i;
937910 * Send a dpi reload request
938911 * Return code: { 0:OK, 1:Abort, 2:Close }
939912 */
940 static int Bmsrv_send_reload_request(SockHandler *sh, char *url)
941 {
942 gint st;
913 static int Bmsrv_send_reload_request(Dsh *sh, char *url)
914 {
915 int st;
943916 char *d_cmd;
944917
945918 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "reload_request", url);
946 st = sock_handler_write_str(sh, d_cmd, 1) ? 1 : 0;
947 g_free(d_cmd);
919 st = a_Dpip_dsh_write_str(sh, 1, d_cmd) ? 1 : 0;
920 dFree(d_cmd);
948921 return st;
949922 }
950923
952925 * Send the HTML for the modify page
953926 * Return code: { 0:OK, 1:Abort, 2:Close }
954927 */
955 static int Bmsrv_send_modify_page(SockHandler *sh)
956 {
957 static GString *gstr = NULL;
958 gchar *l_title;
959 GSList *list1, *list2;
928 static int Bmsrv_send_modify_page(Dsh *sh)
929 {
930 static Dstr *dstr = NULL;
931 char *l_title;
960932 BmSec *sec_node;
961933 BmRec *bm_node;
962
963 if (!gstr)
964 gstr = g_string_new("");
934 int i, j;
935
936 if (!dstr)
937 dstr = dStr_new("");
965938
966939 /* send modify page header */
967 if (sock_handler_write_str(sh, modifypage_header, 0))
940 if (a_Dpip_dsh_write_str(sh, 0, modifypage_header))
968941 return 1;
969942
970943 /* write sections header */
971 if (sock_handler_write_str(sh, modifypage_sections_header, 0))
944 if (a_Dpip_dsh_write_str(sh, 0, modifypage_sections_header))
972945 return 1;
973946 /* write sections */
974 for (list1 = B_secs; list1; list1 = list1->next) {
975 sec_node = list1->data;
976 g_string_sprintf(gstr, modifypage_sections_item,
977 sec_node->section, sec_node->section, sec_node->title);
978 if (sock_handler_write_str(sh, gstr->str, 0))
947 for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
948 dStr_sprintf(dstr, modifypage_sections_item,
949 sec_node->section, sec_node->section, sec_node->title);
950 if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
979951 return 1;
980952 }
981953 /* write sections footer */
982 if (sock_handler_write_str(sh, modifypage_sections_footer, 0))
954 if (a_Dpip_dsh_write_str(sh, 0, modifypage_sections_footer))
983955 return 1;
984956
985957 /* send page middle */
986 if (sock_handler_write_str(sh, modifypage_middle1, 0))
958 if (a_Dpip_dsh_write_str(sh, 0, modifypage_middle1))
987959 return 1;
988960
989961 /* send bookmark cards */
990 for (list1 = B_secs; list1; list1 = list1->next) {
991 sec_node = list1->data;
992
962 for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
993963 /* send card header */
994964 l_title = make_one_line_str(sec_node->title);
995 g_string_sprintf(gstr, modifypage_section_card_header,
996 sec_node->section, l_title);
997 g_free(l_title);
998 if (sock_handler_write_str(sh, gstr->str, 0))
965 dStr_sprintf(dstr, modifypage_section_card_header,
966 sec_node->section, l_title);
967 dFree(l_title);
968 if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
999969 return 1;
1000970
1001971 /* send section's bookmarks */
1002 for (list2 = B_bms; list2; list2 = list2->next) {
1003 bm_node = list2->data;
972 for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
1004973 if (bm_node->section == sec_node->section) {
1005 g_string_sprintf(gstr, modifypage_section_card_item,
1006 bm_node->key, bm_node->url, bm_node->title);
1007 if (sock_handler_write_str(sh, gstr->str, 0))
974 dStr_sprintf(dstr, modifypage_section_card_item,
975 bm_node->key, bm_node->url, bm_node->title);
976 if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
1008977 return 1;
1009978 }
1010979 }
1011980
1012981 /* send card footer */
1013 if (sock_handler_write_str(sh, modifypage_section_card_footer, 0))
982 if (a_Dpip_dsh_write_str(sh, 0, modifypage_section_card_footer))
1014983 return 1;
1015984 }
1016985
1017986 /* finish page */
1018 if (sock_handler_write_str(sh, modifypage_footer, 1))
987 if (a_Dpip_dsh_write_str(sh, 1, modifypage_footer))
1019988 return 1;
1020989
1021990 return 2;
1025994 * Send the HTML for the modify page for "add section"
1026995 * Return code: { 0:OK, 1:Abort, 2:Close }
1027996 */
1028 static int Bmsrv_send_modify_page_add_section(SockHandler *sh)
997 static int Bmsrv_send_modify_page_add_section(Dsh *sh)
1029998 {
1030999 /* send modify page2 */
1031 if (sock_handler_write_str(sh, modifypage_add_section_page, 1))
1000 if (a_Dpip_dsh_write_str(sh, 1, modifypage_add_section_page))
10321001 return 1;
10331002
10341003 return 2;
10381007 * Send the HTML for the modify page for "add url"
10391008 * Return code: { 0:OK, 1:Abort, 2:Close }
10401009 */
1041 static int Bmsrv_send_modify_page_add_url(SockHandler *sh)
1042 {
1043 if (sock_handler_write_str(sh, modifypage_add_url, 1))
1010 static int Bmsrv_send_modify_page_add_url(Dsh *sh)
1011 {
1012 if (a_Dpip_dsh_write_str(sh, 1, modifypage_add_url))
10441013 return 1;
10451014 return 2;
10461015 }
10521021 * - send the modify page for the marked urls and sections
10531022 * Return code: { 0:OK, 1:Abort, 2:Close }
10541023 */
1055 static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
1024 static int Bmsrv_send_modify_update(Dsh *sh, char *url)
10561025 {
10571026 static char *url1 = NULL;
1058 static GString *gstr = NULL;
1027 static Dstr *dstr = NULL;
10591028 char *p, *q;
10601029 int i, key, n_sec, n_url;
10611030 BmRec *bm_node;
10631032
10641033 /* bookmarks were loaded before */
10651034
1066 if (!gstr)
1067 gstr = g_string_new("");
1035 if (!dstr)
1036 dstr = dStr_new("");
10681037
10691038 if (sh == NULL) {
10701039 /* just copy url */
1071 g_free(url1);
1072 url1 = g_strdup(url);
1040 dFree(url1);
1041 url1 = dStrdup(url);
10731042 return 0;
10741043 }
10751044
10761045 /* send HTML here */
1077 if (sock_handler_write_str(sh, modifypage_update_header, 0))
1046 if (a_Dpip_dsh_write_str(sh, 0, modifypage_update_header))
10781047 return 1;
10791048
10801049 /* Count number of marked urls and sections */
10811050 Bmsrv_count_urls_and_sections(url1, &n_sec, &n_url);
10821051
10831052 if (n_sec) {
1084 g_string_sprintf(gstr, modifypage_update_title, "Update&nbsp;sections:");
1085 sock_handler_write_str(sh, gstr->str, 0);
1086 sock_handler_write_str(sh, modifypage_update_item_header, 0);
1053 dStr_sprintf(dstr, modifypage_update_title, "Update&nbsp;sections:");
1054 a_Dpip_dsh_write_str(sh, 0, dstr->str);
1055 a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_header);
10871056 /* send items here */
10881057 p = strchr(url1, '?');
10891058 for (q = p; (q = strstr(q, "&s")); ++q) {
10911060 if (q[2+i] == '=') {
10921061 key = strtol(q + 2, NULL, 10);
10931062 if ((sec_node = Bms_get_sec(key))) {
1094 g_string_sprintf(gstr, modifypage_update_item2,
1095 sec_node->section, sec_node->title);
1096 sock_handler_write_str(sh, gstr->str, 0);
1063 dStr_sprintf(dstr, modifypage_update_item2,
1064 sec_node->section, sec_node->title);
1065 a_Dpip_dsh_write_str(sh, 0, dstr->str);
10971066 }
10981067 }
10991068 }
1100 sock_handler_write_str(sh, modifypage_update_item_footer, 0);
1069 a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_footer);
11011070 }
11021071
11031072 if (n_url) {
1104 g_string_sprintf(gstr, modifypage_update_title, "Update&nbsp;titles:");
1105 sock_handler_write_str(sh, gstr->str, 0);
1106 sock_handler_write_str(sh, modifypage_update_item_header, 0);
1073 dStr_sprintf(dstr, modifypage_update_title, "Update&nbsp;titles:");
1074 a_Dpip_dsh_write_str(sh, 0, dstr->str);
1075 a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_header);
11071076 /* send items here */
11081077 p = strchr(url1, '?');
11091078 for (q = p; (q = strstr(q, "&url")); ++q) {
11111080 if (q[4+i] == '=') {
11121081 key = strtol(q + 4, NULL, 10);
11131082 bm_node = Bms_get(key);
1114 g_string_sprintf(gstr, modifypage_update_item,
1115 bm_node->key, bm_node->title, bm_node->url);
1116 sock_handler_write_str(sh, gstr->str, 0);
1083 dStr_sprintf(dstr, modifypage_update_item,
1084 bm_node->key, bm_node->title, bm_node->url);
1085 a_Dpip_dsh_write_str(sh, 0, dstr->str);
11171086 }
11181087 }
1119 sock_handler_write_str(sh, modifypage_update_item_footer, 0);
1120 }
1121
1122 sock_handler_write_str(sh, modifypage_update_footer, 1);
1088 a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_footer);
1089 }
1090
1091 a_Dpip_dsh_write_str(sh, 1, modifypage_update_footer);
11231092
11241093 return 2;
11251094 }
11281097 * Make the modify-page and send it back
11291098 * Return code: { 0:OK, 1:Abort, 2:Close }
11301099 */
1131 static int Bmsrv_send_modify_answer(SockHandler *sh, char *url)
1100 static int Bmsrv_send_modify_answer(Dsh *sh, char *url)
11321101 {
11331102 char *d_cmd;
11341103 int st;
11351104
11361105 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
1137 st = sock_handler_write_str(sh, d_cmd, 1);
1138 g_free(d_cmd);
1106 st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
1107 dFree(d_cmd);
11391108 if (st != 0)
11401109 return 1;
11411110
11421111 /* Send HTTP header */
1143 if (sock_handler_write(sh, Header, strlen(Header), 0) != 0) {
1112 if (a_Dpip_dsh_write_str(sh, 0, Header) != 0) {
11441113 return 1;
11451114 }
11461115
11651134 * Parse a delete bms request, delete them, and update bm file.
11661135 * Return code: { 0:OK, 1:Abort }
11671136 */
1168 static int Bmsrv_modify_delete(SockHandler *sh, char *url)
1169 {
1170 gchar *p;
1171 gint nb, ns, key;
1137 static int Bmsrv_modify_delete(char *url)
1138 {
1139 char *p;
1140 int nb, ns, key;
11721141
11731142 /* bookmarks were loaded before */
11741143
12111180 * Parse a move urls request, move and update bm file.
12121181 * Return code: { 0:OK, 1:Abort }
12131182 */
1214 static int Bmsrv_modify_move(SockHandler *sh, char *url)
1183 static int Bmsrv_modify_move(char *url)
12151184 {
12161185 char *p;
12171186 int n, section = 0, key;
12501219 * Parse a modify request: update urls and sections, then save.
12511220 * Return code: { 0:OK, 1:Abort }
12521221 */
1253 static int Bmsrv_modify_update(SockHandler *sh, char *url)
1222 static int Bmsrv_modify_update(char *url)
12541223 {
12551224 char *p, *q, *title;
12561225 int i, key;
12651234 /* we have a title/key to change */
12661235 key = strtol(p + 1, NULL, 10);
12671236 if ((q = strchr(p + 1, '&')))
1268 title = g_strndup(p + 2 + i, (guint)(q - (p + 2 + i)));
1237 title = dStrndup(p + 2 + i, (uint_t)(q - (p + 2 + i)));
12691238 else
1270 title = g_strdup(p + 2 + i);
1239 title = dStrdup(p + 2 + i);
12711240
12721241 Unencode_str(title);
12731242 Bms_update_sec_title(key, title);
1274 g_free(title);
1243 dFree(title);
12751244 }
12761245 }
12771246 }
12841253 /* we have a title/key to change */
12851254 key = strtol(p + 5, NULL, 10);
12861255 if ((q = strchr(p + 5, '&')))
1287 title = g_strndup(p + 6 + i, (guint)(q - (p + 6 + i)));
1256 title = dStrndup(p + 6 + i, (uint_t)(q - (p + 6 + i)));
12881257 else
1289 title = g_strdup(p + 6 + i);
1258 title = dStrdup(p + 6 + i);
12901259
12911260 Unencode_str(title);
12921261 Bms_update_title(key, title);
1293 g_free(title);
1262 dFree(title);
12941263 }
12951264 }
12961265 }
13051274 * Parse an "add section" request, and update the bm file.
13061275 * Return code: { 0:OK, 1:Abort }
13071276 */
1308 static int Bmsrv_modify_add_section(SockHandler *sh, char *url)
1277 static int Bmsrv_modify_add_section(char *url)
13091278 {
13101279 char *p, *title = NULL;
13111280
13131282
13141283 /* get new section's title */
13151284 if ((p = strstr(url, "&title="))) {
1316 title = g_strdup (p + 7);
1285 title = dStrdup (p + 7);
13171286 if ((p = strchr(title, '&')))
13181287 *p = 0;
13191288 Unencode_str(title);
13211290 return 1;
13221291
13231292 Bms_sec_add(title);
1324 g_free(title);
1293 dFree(title);
13251294
13261295 /* Write new bookmarks file */
13271296 Bms_save();
13331302 * Parse an "add url" request, and update the bm file.
13341303 * Return code: { 0:OK, 1:Abort }
13351304 */
1336 static int Bmsrv_modify_add_url(SockHandler *sh, char *s_url)
1305 static int Bmsrv_modify_add_url(Dsh *sh, char *s_url)
13371306 {
13381307 char *p, *q, *title, *u_title, *url;
13391308 int i;
1340 static gint section = 0;
1309 static int section = 0;
13411310
13421311 /* bookmarks were loaded before */
13431312
13551324 !(q = strstr(s_url, "&url=")))
13561325 return 1;
13571326
1358 title = g_strdup (p + 7);
1327 title = dStrdup (p + 7);
13591328 if ((p = strchr(title, '&')))
13601329 *p = 0;
1361 url = g_strdup (q + 5);
1330 url = dStrdup (q + 5);
13621331 if ((p = strchr(url, '&')))
13631332 *p = 0;
13641333 if (strlen(title) && strlen(url)) {
13661335 Unencode_str(url);
13671336 u_title = Unescape_html_str(title);
13681337 Bms_add(section, url, u_title);
1369 g_free(u_title);
1370 }
1371 g_free(title);
1372 g_free(url);
1338 dFree(u_title);
1339 }
1340 dFree(title);
1341 dFree(url);
13731342 section = 0;
13741343
1375 /* todo: we should send an "Bookmark added" message, but the
1344 /* TODO: we should send an "Bookmark added" message, but the
13761345 msg-after-HTML functionallity is still pending, not hard though. */
13771346
13781347 /* Write new bookmarks file */
13861355 * when it's wrong.
13871356 * Return code: { 0:OK, 2:Close }
13881357 */
1389 static int Bmsrv_check_modify_request(SockHandler *sh, char *url)
1358 static int Bmsrv_check_modify_request(Dsh *sh, char *url)
13901359 {
13911360 char *p, *msg;
13921361 int n_sec, n_url;
14511420 * Parse a and process a modify request.
14521421 * Return code: { 0:OK, 1:Abort, 2:Close }
14531422 */
1454 static int Bmsrv_process_modify_request(SockHandler *sh, char *url)
1423 static int Bmsrv_process_modify_request(Dsh *sh, char *url)
14551424 {
14561425 /* check the provided parameters */
14571426 if (Bmsrv_check_modify_request(sh, url) != 0)
14581427 return 2;
14591428
14601429 if (strstr(url, "operation=delete&")) {
1461 if (Bmsrv_modify_delete(sh, url) == 1)
1430 if (Bmsrv_modify_delete(url) == 1)
14621431 return 1;
14631432 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
14641433 return 1;
14651434
14661435 } else if (strstr(url, "operation=move&")) {
1467 if (Bmsrv_modify_move(sh, url) == 1)
1436 if (Bmsrv_modify_move(url) == 1)
14681437 return 1;
14691438 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
14701439 return 1;
14771446 return 1;
14781447
14791448 } else if (strstr(url, "operation=modify2&")) {
1480 if (Bmsrv_modify_update(sh, url) == 1)
1449 if (Bmsrv_modify_update(url) == 1)
14811450 return 1;
14821451 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
14831452 return 1;
14891458 return 1;
14901459
14911460 } else if (strstr(url, "operation=add_section&")) {
1492 if (Bmsrv_modify_add_section(sh, url) == 1)
1461 if (Bmsrv_modify_add_section(url) == 1)
14931462 return 1;
14941463 if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
14951464 return 1;
15171486 /*
15181487 * Send the current bookmarks page (in HTML)
15191488 */
1520 static int send_bm_page(SockHandler *sh)
1521 {
1522 static GString *gstr = NULL;
1523 gchar *l_title;
1524 GSList *list1, *list2;
1489 static int send_bm_page(Dsh *sh)
1490 {
1491 static Dstr *dstr = NULL;
1492 char *l_title;
15251493 BmSec *sec_node;
15261494 BmRec *bm_node;
1527
1528 if (!gstr)
1529 gstr = g_string_new("");
1530
1531 if (sock_handler_write_str(sh, mainpage_header, 0))
1495 int i, j;
1496
1497 if (!dstr)
1498 dstr = dStr_new("");
1499
1500 if (a_Dpip_dsh_write_str(sh, 0, mainpage_header))
15321501 return 1;
15331502
15341503 /* write sections header */
1535 if (sock_handler_write_str(sh, mainpage_sections_header, 0))
1504 if (a_Dpip_dsh_write_str(sh, 0, mainpage_sections_header))
15361505 return 1;
15371506 /* write sections */
1538 for (list1 = B_secs; list1; list1 = list1->next) {
1539 sec_node = list1->data;
1540 g_string_sprintf(gstr, mainpage_sections_item,
1541 sec_node->section, sec_node->title);
1542 if (sock_handler_write_str(sh, gstr->str, 0))
1507 for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
1508 dStr_sprintf(dstr, mainpage_sections_item,
1509 sec_node->section, sec_node->title);
1510 if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
15431511 return 1;
15441512 }
15451513 /* write sections footer */
1546 if (sock_handler_write_str(sh, mainpage_sections_footer, 0))
1514 if (a_Dpip_dsh_write_str(sh, 0, mainpage_sections_footer))
15471515 return 1;
15481516
15491517 /* send page middle */
1550 if (sock_handler_write_str(sh, mainpage_middle1, 0))
1518 if (a_Dpip_dsh_write_str(sh, 0, mainpage_middle1))
15511519 return 1;
15521520
15531521 /* send bookmark cards */
1554 for (list1 = B_secs; list1; list1 = list1->next) {
1555 sec_node = list1->data;
1556
1522 for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
15571523 /* send card header */
15581524 l_title = make_one_line_str(sec_node->title);
1559 g_string_sprintf(gstr, mainpage_section_card_header,
1560 sec_node->section, l_title);
1561 g_free(l_title);
1562 if (sock_handler_write_str(sh, gstr->str, 0))
1525 dStr_sprintf(dstr, mainpage_section_card_header,
1526 sec_node->section, l_title);
1527 dFree(l_title);
1528 if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
15631529 return 1;
15641530
15651531 /* send section's bookmarks */
1566 for (list2 = B_bms; list2; list2 = list2->next) {
1567 bm_node = list2->data;
1532 for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
15681533 if (bm_node->section == sec_node->section) {
1569 g_string_sprintf(gstr, mainpage_section_card_item,
1570 bm_node->url, bm_node->title);
1571 if (sock_handler_write_str(sh, gstr->str, 0))
1534 dStr_sprintf(dstr, mainpage_section_card_item,
1535 bm_node->url, bm_node->title);
1536 if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
15721537 return 1;
15731538 }
15741539 }
15751540
15761541 /* send card footer */
1577 if (sock_handler_write_str(sh, mainpage_section_card_footer, 0))
1542 if (a_Dpip_dsh_write_str(sh, 0, mainpage_section_card_footer))
15781543 return 1;
15791544 }
15801545
15811546 /* finish page */
1582 if (sock_handler_write_str(sh, mainpage_footer, 1))
1547 if (a_Dpip_dsh_write_str(sh, 1, mainpage_footer))
15831548 return 1;
15841549
15851550 return 0;
15901555
15911556 /*
15921557 * Parse a data stream (dpi protocol)
1593 * Note: Buf is a zero terminated string
1558 * Note: Buf is a dpip token (zero terminated string)
15941559 * Return code: { 0:OK, 1:Abort, 2:Close }
15951560 */
1596 static int Bmsrv_parse_buf(SockHandler *sh, char *Buf, size_t BufSize)
1561 static int Bmsrv_parse_token(Dsh *sh, char *Buf)
15971562 {
15981563 static char *msg1=NULL, *msg2=NULL, *msg3=NULL;
1599 char *p, *cmd, *d_cmd, *url, *title, *msg;
1564 char *cmd, *d_cmd, *url, *title, *msg;
1565 size_t BufSize;
16001566 int st;
16011567
16021568 if (!msg1) {
16061572 msg3 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Ok, send it");
16071573 }
16081574
1609 if (!(p = strchr(Buf, '>'))) {
1610 /* Haven't got a full tag */
1611 g_print("Haven't got a full tag!\n");
1612 return 1;
1613 }
1614
1615 cmd = a_Dpip_get_attr(Buf, BufSize, "cmd");
1575 if (sh->mode & DPIP_RAW) {
1576 MSG("ERROR: Unhandled DPIP_RAW mode!\n");
1577 return 1;
1578 }
1579
1580 BufSize = strlen(Buf);
1581 cmd = a_Dpip_get_attr_l(Buf, BufSize, "cmd");
16161582
16171583 if (cmd && strcmp(cmd, "chat") == 0) {
1618 g_free(cmd);
1619 msg = a_Dpip_get_attr(Buf, BufSize, "msg");
1584 dFree(cmd);
1585 msg = a_Dpip_get_attr_l(Buf, BufSize, "msg");
16201586 if (*msg == 'H') {
16211587 /* "Hi server" */
1622 if (sock_handler_write_str(sh, msg1, 1))
1588 if (a_Dpip_dsh_write_str(sh, 1, msg1))
16231589 return 1;
16241590 } else if (*msg == 'I') {
16251591 /* "I want to set abookmark" */
1626 if (sock_handler_write_str(sh, msg2, 1))
1592 if (a_Dpip_dsh_write_str(sh, 1, msg2))
16271593 return 1;
16281594 } else if (*msg == 'S') {
16291595 /* "Sure" */
1630 if (sock_handler_write_str(sh, msg3, 1))
1596 if (a_Dpip_dsh_write_str(sh, 1, msg3))
16311597 return 1;
16321598 }
1633 g_free(msg);
1599 dFree(msg);
16341600 return 0;
16351601 }
16361602
16381604 Bms_cond_load();
16391605
16401606 if (cmd && strcmp(cmd, "DpiBye") == 0) {
1641 g_print("bookmarks dpi (pid %d): Got DpiBye.\n", (gint)getpid());
1607 MSG("(pid %d): Got DpiBye.\n", (int)getpid());
16421608 exit(0);
16431609
16441610 } else if (cmd && strcmp(cmd, "add_bookmark") == 0) {
1645 g_free(cmd);
1646 url = a_Dpip_get_attr(Buf, BufSize, "url");
1647 title = a_Dpip_get_attr(Buf, BufSize, "title");
1611 dFree(cmd);
1612 url = a_Dpip_get_attr_l(Buf, BufSize, "url");
1613 title = a_Dpip_get_attr_l(Buf, BufSize, "title");
16481614 if (strlen(title) == 0) {
1649 g_free(title);
1650 title = g_strdup("(Untitled)");
1615 dFree(title);
1616 title = dStrdup("(Untitled)");
16511617 }
16521618 if (url && title)
16531619 Bmsrv_add_bm(sh, url, title);
1654 g_free(url);
1655 g_free(title);
1620 dFree(url);
1621 dFree(title);
16561622 return 2;
16571623
16581624 } else if (cmd && strcmp(cmd, "open_url") == 0) {
1659 g_free(cmd);
1660 url = a_Dpip_get_attr(Buf, BufSize, "url");
1625 dFree(cmd);
1626 url = a_Dpip_get_attr_l(Buf, BufSize, "url");
16611627
16621628 if (strcmp(url, "dpi:/bm/modify") == 0) {
16631629 st = Bmsrv_send_modify_answer(sh, url);
1630 dFree(url);
16641631 return st;
16651632
16661633 } else if (strncmp(url, "dpi:/bm/modify?", 15) == 0) {
16671634 /* process request */
16681635 st = Bmsrv_process_modify_request(sh, url);
1636 dFree(url);
16691637 return st;
16701638 }
16711639
16721640
16731641 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
1674 st = sock_handler_write_str(sh, d_cmd, 1);
1675 g_free(d_cmd);
1642 dFree(url);
1643 st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
1644 dFree(d_cmd);
16761645 if (st != 0)
16771646 return 1;
16781647
16791648 /* Send HTTP header */
1680 if (sock_handler_write(sh, Header, strlen(Header), 1) != 0) {
1649 if (a_Dpip_dsh_write_str(sh, 1, Header) != 0) {
16811650 return 1;
16821651 }
16831652
16851654 if (st != 0) {
16861655 char *err =
16871656 DOCTYPE
1688 "<HTML><body> Error on the bookmarks server...</body></html>";
1689 if (sock_handler_write(sh, err, strlen(err), 1) != 0) {
1657 "<HTML><body id='dillo_bm'> Error on the bookmarks server..."
1658 " </body></html>";
1659 if (a_Dpip_dsh_write_str(sh, 1, err) != 0) {
16901660 return 1;
16911661 }
16921662 }
17181688 /*
17191689 * -- MAIN -------------------------------------------------------------------
17201690 */
1721 int main (void) {
1691 int main(void) {
17221692 struct sockaddr_un spun;
1723 int temp_sock_descriptor;
1724 int address_size;
1725 char *buf;
1726 int code;
1727 SockHandler *sh;
1693 int sock_fd, code;
1694 socklen_t address_size;
1695 char *tok;
1696 Dsh *sh;
17281697
17291698 /* Arrange the cleanup function for terminations via exit() */
17301699 atexit(cleanup);
17371706 if (signal (SIGTERM, termination_handler) == SIG_IGN)
17381707 signal (SIGTERM, SIG_IGN);
17391708
1740 BmFile = g_strconcat(g_get_home_dir(), "/", ".dillo/bm.txt", NULL);
1741
1742 g_print("bookmarks.dpi (v.13): accepting connections...\n");
1743
1709 /* We may receive SIGPIPE (e.g. socket is closed early by our client) */
1710 signal(SIGPIPE, SIG_IGN);
1711
1712 /* Initialize local data */
1713 B_bms = dList_new(512);
1714 B_secs = dList_new(32);
1715 BmFile = dStrconcat(dGethomedir(), "/.dillo/bm.txt", NULL);
17441716 /* some OSes may need this... */
17451717 address_size = sizeof(struct sockaddr_un);
17461718
1719 MSG("(v.13): accepting connections...\n");
1720
17471721 while (1) {
1748 temp_sock_descriptor =
1749 accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
1750 if (temp_sock_descriptor == -1) {
1722 sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
1723 if (sock_fd == -1) {
17511724 perror("[accept]");
17521725 exit(1);
17531726 }
17541727
1755 /* create the SockHandler structure */
1756 sh = sock_handler_new(temp_sock_descriptor,temp_sock_descriptor,8*1024);
1728 /* create the Dsh structure */
1729 sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
1730
1731 /* Authenticate our client... */
1732 if (!(tok = a_Dpip_dsh_read_token(sh, 1)) ||
1733 a_Dpip_check_auth(tok) < 0) {
1734 MSG("can't authenticate request: %s\n", dStrerror(errno));
1735 a_Dpip_dsh_close(sh);
1736 exit(1);
1737 }
1738 dFree(tok);
17571739
17581740 while (1) {
17591741 code = 1;
1760 if ((buf = sock_handler_read(sh)) != NULL) {
1742 if ((tok = a_Dpip_dsh_read_token(sh, 1)) != NULL) {
17611743 /* Let's see what we fished... */
1762 code = Bmsrv_parse_buf(sh, buf, strlen(buf));
1744 code = Bmsrv_parse_token(sh, tok);
17631745 }
1764 if (code == 1)
1765 exit(1);
1766 else if (code == 2)
1746 dFree(tok);
1747
1748 if (code != 0) {
1749 /* socket is not operative (e.g. closed by client) */
17671750 break;
1768 }
1769
1770 sock_handler_close(sh);
1771 sock_handler_free(sh);
1751 }
1752 }
1753
1754 a_Dpip_dsh_close(sh);
1755 a_Dpip_dsh_free(sh);
17721756
17731757 }/*while*/
17741758 }
33 *
44 * Copyright 2001 Lars Clausen <lrclause@cs.uiuc.edu>
55 * Jörgen Viksell <jorgen.viksell@telia.com>
6 * Copyright 2002-2005 Jorge Arellano Cid <jcid@dillo.org>
6 * Copyright 2002-2007 Jorge Arellano Cid <jcid@dillo.org>
77 *
88 * This program is free software; you can redistribute it and/or modify
99 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
10 * the Free Software Foundation; either version 3 of the License, or
1111 * (at your option) any later version.
1212 *
1313 */
1414
15 /* Handling of cookies takes place here.
16 * This implementation aims to follow RFC 2965:
17 * http://www.cis.ohio-state.edu/cs/Services/rfc/rfc-text/rfc2965.txt
18 */
19
20 /* Todo: this server is not assembling the received packets.
21 * This means it currently expects dillo to send full dpi tags
22 * within the socket; if that fails, everything stops.
23 * This is not hard to fix, mainly is a matter of expecting the
24 * final '>' of a tag.
15 /* The current standard for cookies is RFC 6265.
16 *
17 * Info from 2009 on cookies in the wild:
18 * http://www.ietf.org/mail-archive/web/http-state/current/msg00078.html
19 * And dates specifically:
20 * http://www.ietf.org/mail-archive/web/http-state/current/msg00128.html
2521 */
2622
2723 #ifdef DISABLE_COOKIES
3834 #include <sys/socket.h>
3935 #include <sys/stat.h>
4036 #include <sys/un.h>
37 #include <netinet/in.h>
4138 #include <fcntl.h>
4239 #include <unistd.h>
4340 #include <errno.h>
5350 #include "../dpip/dpip.h"
5451
5552
56 #include <glib.h>
57
58 /* This one is tricky, some sources state it should include the byte
59 * for the terminating NULL, and others say it shouldn't. */
60 # define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
61 + strlen ((ptr)->sun_path))
62
63 /* Debugging macros
64 */
65 #define _MSG(fmt...)
66 #define MSG(fmt...) g_print("[cookies dpi]: " fmt)
67
53 /*
54 * Debugging macros
55 */
56 #define _MSG(...)
57 #define MSG(...) printf("[cookies dpi]: " __VA_ARGS__)
6858
6959 /*
7060 * a_List_add()
7464 * list size --to make it faster)
7565 */
7666 #define a_List_add(list,num_items,alloc_step) \
77 if ( !list ) { \
78 list = g_malloc(alloc_step * sizeof((*list))); \
67 if (!list) { \
68 list = dMalloc(alloc_step * sizeof((*list))); \
7969 } \
80 if ( num_items >= alloc_step ){ \
70 if (num_items >= alloc_step){ \
8171 while ( num_items >= alloc_step ) \
8272 alloc_step <<= 1; \
83 list = g_realloc(list, alloc_step * sizeof((*list))); \
73 list = dRealloc(list, alloc_step * sizeof((*list))); \
8474 }
8575
8676 /* The maximum length of a line in the cookie file */
8777 #define LINE_MAXLEN 4096
78
79 #define MAX_DOMAIN_COOKIES 20
80 #define MAX_TOTAL_COOKIES 1200
8881
8982 typedef enum {
9083 COOKIE_ACCEPT,
9386 } CookieControlAction;
9487
9588 typedef struct {
89 char *domain;
9690 CookieControlAction action;
91 } CookieControl;
92
93 typedef struct {
9794 char *domain;
98 } CookieControl;
95 Dlist *cookies;
96 } DomainNode;
9997
10098 typedef struct {
10199 char *name;
103101 char *domain;
104102 char *path;
105103 time_t expires_at;
106 guint version;
107 char *comment;
108 char *comment_url;
109 gboolean secure;
110 gboolean session_only;
111 GList *ports;
104 bool_t host_only;
105 bool_t secure;
106 bool_t session_only;
107 long last_used;
112108 } CookieData_t;
113109
110 typedef struct {
111 Dsh *sh;
112 int status;
113 } ClientInfo;
114
114115 /*
115116 * Local data
116117 */
117118
118 /* Hashtable indexed by domain, each value is a set of cookies for
119 * that domain. */
120 static GHashTable *cookies;
119 static Dlist *all_cookies;
120
121 /* List of DomainNode. Each node holds a domain and its list of cookies */
122 static Dlist *domains;
121123
122124 /* Variables for access control */
123125 static CookieControl *ccontrol = NULL;
125127 static int num_ccontrol_max = 1;
126128 static CookieControlAction default_action = COOKIE_DENY;
127129
128 static gboolean disabled;
130 static long cookies_use_counter = 0;
131 static bool_t disabled;
129132 static FILE *file_stream;
130 static char *cookies_txt_header_str =
133 static const char *const cookies_txt_header_str =
131134 "# HTTP Cookie File\n"
132 "# http://www.netscape.com/newsref/std/cookie_spec.html\n"
133 "# This is a generated file! Do not edit.\n\n";
134
135 "# This is a generated file! Do not edit.\n"
136 "# [domain subdomains path secure expiry_time name value]\n\n";
137
138 /* The epoch is Jan 1, 1970. When there is difficulty in representing future
139 * dates, use the (by far) most likely last representable time in Jan 19, 2038.
140 */
141 static struct tm cookies_epoch_tm = {0, 0, 0, 1, 0, 70, 0, 0, 0, 0, 0};
142 static time_t cookies_epoch_time, cookies_future_time;
135143
136144 /*
137145 * Forward declarations
138146 */
139147
140 static gchar *d_strsep(char **orig, const char *delim);
141 static FILE *Cookies_fopen(const char *file, gchar *init_str);
142148 static CookieControlAction Cookies_control_check_domain(const char *domain);
143149 static int Cookie_control_init(void);
144 static void Cookies_parse_ports(gint url_port, CookieData_t *cookie,
145 const char *port_str);
146 static char *Cookies_build_ports_str(CookieData_t *cookie);
147 static char *Cookies_strip_path(const char *path);
148150 static void Cookies_add_cookie(CookieData_t *cookie);
149 static void Cookies_remove_cookie(CookieData_t *cookie);
150 static gint Cookies_equals(gconstpointer a, gconstpointer b);
151
152 /*
153 * strsep implementation
154 */
155 gchar *d_strsep(char **orig, const char *delim)
156 {
157 gchar *str, *p;
158
159 if (!(str = *orig))
160 return NULL;
161
162 p = strpbrk(str, delim);
163 if (p) {
164 *p++ = 0;
165 *orig = p;
166 } else {
167 *orig = NULL;
168 }
169 return str;
151 static int Cookies_cmp(const void *a, const void *b);
152
153 /*
154 * Compare function for searching a domain node
155 */
156 static int Domain_node_cmp(const void *v1, const void *v2)
157 {
158 const DomainNode *n1 = v1, *n2 = v2;
159
160 return dStrcasecmp(n1->domain, n2->domain);
161 }
162
163 /*
164 * Compare function for searching a domain node by domain
165 */
166 static int Domain_node_by_domain_cmp(const void *v1, const void *v2)
167 {
168 const DomainNode *node = v1;
169 const char *domain = v2;
170
171 return dStrcasecmp(node->domain, domain);
172 }
173
174 /*
175 * Delete node. This will not free any cookies that might be in node->cookies.
176 */
177 static void Cookies_delete_node(DomainNode *node)
178 {
179 dList_remove(domains, node);
180 dFree(node->domain);
181 dList_free(node->cookies);
182 dFree(node);
170183 }
171184
172185 /*
173186 * Return a file pointer. If the file doesn't exist, try to create it,
174187 * with the optional 'init_str' as its content.
175188 */
176 static FILE *Cookies_fopen(const char *filename, gchar *init_str)
189 static FILE *Cookies_fopen(const char *filename, const char *mode,
190 const char *init_str)
177191 {
178192 FILE *F_in;
179 int fd;
180
181 if ((F_in = fopen(filename, "r+")) == NULL) {
193 int fd, rc;
194
195 if ((F_in = fopen(filename, mode)) == NULL) {
182196 /* Create the file */
183197 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
184198 if (fd != -1) {
185 if (init_str)
186 write(fd, init_str, strlen(init_str));
199 if (init_str) {
200 rc = write(fd, init_str, strlen(init_str));
201 if (rc == -1) {
202 MSG("Cookies: Could not write initial string to file %s: %s\n",
203 filename, dStrerror(errno));
204 }
205 }
187206 close(fd);
188207
189208 MSG("Created file: %s\n", filename);
190 F_in = Cookies_fopen(filename, NULL);
209 F_in = fopen(filename, mode);
191210 } else {
192211 MSG("Could not create file: %s!\n", filename);
193212 }
194213 }
195214
196 /* set close on exec */
197 fcntl(fileno(F_in), F_SETFD, FD_CLOEXEC | fcntl(fileno(F_in), F_GETFD));
215 if (F_in) {
216 /* set close on exec */
217 fcntl(fileno(F_in), F_SETFD, FD_CLOEXEC | fcntl(fileno(F_in), F_GETFD));
218 }
198219
199220 return F_in;
200221 }
201222
202223 static void Cookies_free_cookie(CookieData_t *cookie)
203224 {
204 g_free(cookie->name);
205 g_free(cookie->value);
206 g_free(cookie->domain);
207 g_free(cookie->path);
208 g_free(cookie->comment);
209 g_free(cookie->comment_url);
210 g_list_free(cookie->ports);
211 g_free(cookie);
225 dFree(cookie->name);
226 dFree(cookie->value);
227 dFree(cookie->domain);
228 dFree(cookie->path);
229 dFree(cookie);
230 }
231
232 static void Cookies_tm_init(struct tm *tm)
233 {
234 tm->tm_sec = cookies_epoch_tm.tm_sec;
235 tm->tm_min = cookies_epoch_tm.tm_min;
236 tm->tm_hour = cookies_epoch_tm.tm_hour;
237 tm->tm_mday = cookies_epoch_tm.tm_mday;
238 tm->tm_mon = cookies_epoch_tm.tm_mon;
239 tm->tm_year = cookies_epoch_tm.tm_year;
240 tm->tm_isdst = cookies_epoch_tm.tm_isdst;
241 }
242
243 /*
244 * Read in cookies from 'stream' (cookies.txt)
245 */
246 static void Cookies_load_cookies(FILE *stream)
247 {
248 char line[LINE_MAXLEN];
249
250 all_cookies = dList_new(32);
251 domains = dList_new(32);
252
253 /* Get all lines in the file */
254 while (!feof(stream)) {
255 line[0] = '\0';
256 if ((fgets(line, LINE_MAXLEN, stream) == NULL) && ferror(stream)) {
257 MSG("Error while reading from cookies.txt: %s\n", dStrerror(errno));
258 break; /* bail out */
259 }
260
261 /* Remove leading and trailing whitespaces */
262 dStrstrip(line);
263
264 if ((line[0] != '\0') && (line[0] != '#')) {
265 /*
266 * Split the row into pieces using a tab as the delimiter.
267 * pieces[0] The domain name
268 * pieces[1] TRUE/FALSE: is the domain a suffix, or a full domain?
269 * pieces[2] The path
270 * pieces[3] TRUE/FALSE: is the cookie for secure use only?
271 * pieces[4] Timestamp of expire date
272 * pieces[5] Name of the cookie
273 * pieces[6] Value of the cookie
274 */
275 CookieControlAction action;
276 char *piece;
277 char *line_marker = line;
278 CookieData_t *cookie = dNew0(CookieData_t, 1);
279
280 cookie->session_only = FALSE;
281 cookie->domain = dStrdup(dStrsep(&line_marker, "\t"));
282 piece = dStrsep(&line_marker, "\t");
283 if (piece != NULL && piece[0] == 'F')
284 cookie->host_only = TRUE;
285 cookie->path = dStrdup(dStrsep(&line_marker, "\t"));
286 piece = dStrsep(&line_marker, "\t");
287 if (piece != NULL && piece[0] == 'T')
288 cookie->secure = TRUE;
289 piece = dStrsep(&line_marker, "\t");
290 if (piece != NULL) {
291 /* There is some problem with simply putting the maximum value
292 * into tm.tm_sec (although a value close to it works).
293 */
294 long seconds = strtol(piece, NULL, 10);
295 struct tm tm;
296 Cookies_tm_init(&tm);
297 tm.tm_min += seconds / 60;
298 tm.tm_sec += seconds % 60;
299 cookie->expires_at = mktime(&tm);
300 } else {
301 cookie->expires_at = (time_t) -1;
302 }
303 cookie->name = dStrdup(dStrsep(&line_marker, "\t"));
304 cookie->value = dStrdup(line_marker ? line_marker : "");
305
306 if (!cookie->domain || cookie->domain[0] == '\0' ||
307 !cookie->path || cookie->path[0] != '/' ||
308 !cookie->name || !cookie->value) {
309 MSG("Malformed line in cookies.txt file!\n");
310 Cookies_free_cookie(cookie);
311 continue;
312 }
313
314 action = Cookies_control_check_domain(cookie->domain);
315 if (action == COOKIE_DENY) {
316 Cookies_free_cookie(cookie);
317 continue;
318 } else if (action == COOKIE_ACCEPT_SESSION) {
319 cookie->session_only = TRUE;
320 }
321
322 /* Save cookie in memory */
323 Cookies_add_cookie(cookie);
324 }
325 }
326 MSG("Cookies loaded: %d.\n", dList_length(all_cookies));
212327 }
213328
214329 /*
215330 * Initialize the cookies module
216 * (The 'disabled' variable is writable only within Cookies_init)
217 */
218 void Cookies_init()
219 {
220 CookieData_t *cookie;
331 * (The 'disabled' variable is writeable only within Cookies_init)
332 */
333 static void Cookies_init()
334 {
221335 char *filename;
222 char line[LINE_MAXLEN];
223336 #ifndef HAVE_LOCKF
224337 struct flock lck;
225338 #endif
226 FILE *old_cookies_file_stream;
339 struct tm future_tm = {7, 14, 3, 19, 0, 138, 0, 0, 0, 0, 0};
227340
228341 /* Default setting */
229342 disabled = TRUE;
343
344 cookies_epoch_time = mktime(&cookies_epoch_tm);
345 cookies_future_time = mktime(&future_tm);
230346
231347 /* Read and parse the cookie control file (cookiesrc) */
232348 if (Cookie_control_init() != 0) {
235351 }
236352
237353 /* Get a stream for the cookies file */
238 filename = g_strconcat(g_get_home_dir(), "/.dillo/cookies.txt", NULL);
239 file_stream = Cookies_fopen(filename, cookies_txt_header_str);
240
241 g_free(filename);
354 filename = dStrconcat(dGethomedir(), "/.dillo/cookies.txt", NULL);
355 file_stream = Cookies_fopen(filename, "r+", cookies_txt_header_str);
356
357 dFree(filename);
242358
243359 if (!file_stream) {
244 MSG("ERROR: Can't open ~/.dillo/cookies.txt, disabling cookies\n");
360 MSG("ERROR: Can't open ~/.dillo/cookies.txt; disabling cookies\n");
245361 return;
246362 }
247363
257373 disabled = (fcntl(fileno(file_stream), F_SETLK, &lck) == -1);
258374 #endif
259375 if (disabled) {
260 MSG("The cookies file has a file lock: disabling cookies!\n");
376 MSG("The cookies file has a file lock; disabling cookies!\n");
261377 fclose(file_stream);
262378 return;
263379 }
264
265 MSG("Enabling cookies as from cookiesrc...\n");
266
267 cookies = g_hash_table_new(g_str_hash, g_str_equal);
268
269 /* Get all lines in the file */
270 while (!feof(file_stream)) {
271 line[0] = '\0';
272 fgets(line, LINE_MAXLEN, file_stream);
273
274 /* Remove leading and trailing whitespaces */
275 g_strstrip(line);
276
277 if ((line[0] != '\0') && (line[0] != '#')) {
278 /* Would use g_strsplit, but it doesn't give empty trailing pieces.
279 */
280 /* Split the row into pieces using a tab as the delimiter.
281 * pieces[0] The domain name
282 * pieces[1] TRUE/FALSE: is the domain a suffix, or a full domain?
283 * pieces[2] The path
284 * pieces[3] Is the cookie unsecure or secure (TRUE/FALSE)
285 * pieces[4] Timestamp of expire date
286 * pieces[5] Name of the cookie
287 * pieces[6] Value of the cookie
288 */
289 CookieControlAction action;
290 char *piece;
291 char *line_marker = line;
292
293 cookie = g_new0(CookieData_t, 1);
294
295 cookie->session_only = FALSE;
296 cookie->version = 0;
297 cookie->domain = g_strdup(d_strsep(&line_marker, "\t"));
298 d_strsep(&line_marker, "\t"); /* we use domain always as sufix */
299 cookie->path = g_strdup(d_strsep(&line_marker, "\t"));
300 piece = d_strsep(&line_marker, "\t");
301 if (piece != NULL && piece[0] == 'T')
302 cookie->secure = TRUE;
303 piece = d_strsep(&line_marker, "\t");
304 if (piece != NULL)
305 cookie->expires_at = (time_t) strtol(piece, NULL, 10);
306 cookie->name = g_strdup(d_strsep(&line_marker, "\t"));
307 cookie->value = g_strdup(d_strsep(&line_marker, "\t"));
308
309 if (!cookie->domain || cookie->domain[0] == '\0' ||
310 !cookie->path || cookie->path[0] != '/' ||
311 !cookie->name || cookie->name[0] == '\0' ||
312 !cookie->value) {
313 MSG("Malformed line in cookies.txt file!\n");
314 Cookies_free_cookie(cookie);
315 continue;
316 }
317
318 action = Cookies_control_check_domain(cookie->domain);
319 if (action == COOKIE_DENY) {
320 Cookies_free_cookie(cookie);
321 continue;
322 } else if (action == COOKIE_ACCEPT_SESSION) {
323 cookie->session_only = TRUE;
324 }
325
326 /* Save cookie in memory */
327 Cookies_add_cookie(cookie);
328 }
329 }
330
331 filename = g_strconcat(g_get_home_dir(), "/.dillo/cookies", NULL);
332 if ((old_cookies_file_stream = fopen(filename, "r")) != NULL) {
333 g_free(filename);
334 MSG("WARNING: Reading old cookies file ~/.dillo/cookies too\n");
335
336 /* Get all lines in the file */
337 while (!feof(old_cookies_file_stream)) {
338 line[0] = '\0';
339 fgets(line, LINE_MAXLEN, old_cookies_file_stream);
340
341 /* Remove leading and trailing whitespaces */
342 g_strstrip(line);
343
344 if (line[0] != '\0') {
345 /* Would use g_strsplit, but it doesn't give empty trailing pieces.
346 */
347 /* Split the row into pieces using a tab as the delimiter.
348 * pieces[0] The version this cookie was set as (0 / 1)
349 * pieces[1] The domain name
350 * pieces[2] A comma separated list of accepted ports
351 * pieces[3] The path
352 * pieces[4] Is the cookie unsecure or secure (0 / 1)
353 * pieces[5] Timestamp of expire date
354 * pieces[6] Name of the cookie
355 * pieces[7] Value of the cookie
356 * pieces[8] Comment
357 * pieces[9] Comment url
358 */
359 CookieControlAction action;
360 char *piece;
361 char *line_marker = line;
362
363 cookie = g_new0(CookieData_t, 1);
364
365 cookie->session_only = FALSE;
366 piece = d_strsep(&line_marker, "\t");
367 if (piece != NULL)
368 cookie->version = strtol(piece, NULL, 10);
369 cookie->domain = g_strdup(d_strsep(&line_marker, "\t"));
370 Cookies_parse_ports(0, cookie, d_strsep(&line_marker, "\t"));
371 cookie->path = g_strdup(d_strsep(&line_marker, "\t"));
372 piece = d_strsep(&line_marker, "\t");
373 if (piece != NULL && piece[0] == '1')
374 cookie->secure = TRUE;
375 piece = d_strsep(&line_marker, "\t");
376 if (piece != NULL)
377 cookie->expires_at = (time_t) strtol(piece, NULL, 10);
378 cookie->name = g_strdup(d_strsep(&line_marker, "\t"));
379 cookie->value = g_strdup(d_strsep(&line_marker, "\t"));
380 cookie->comment = g_strdup(d_strsep(&line_marker, "\t"));
381 cookie->comment_url = g_strdup(d_strsep(&line_marker, "\t"));
382
383 if (!cookie->domain || cookie->domain[0] == '\0' ||
384 !cookie->path || cookie->path[0] != '/' ||
385 !cookie->name || cookie->name[0] == '\0' ||
386 !cookie->value) {
387 MSG("Malformed line in cookies file!\n");
388 Cookies_free_cookie(cookie);
389 continue;
390 }
391
392 action = Cookies_control_check_domain(cookie->domain);
393 if (action == COOKIE_DENY) {
394 Cookies_free_cookie(cookie);
395 continue;
396 } else if (action == COOKIE_ACCEPT_SESSION) {
397 cookie->session_only = TRUE;
398 }
399
400 /* Save cookie in memory */
401 Cookies_add_cookie(cookie);
402 }
403 }
404 fclose(old_cookies_file_stream);
405 } else {
406 g_free(filename);
407 }
408
409 }
410
411 /*
412 * Save the cookies and remove them from the hash table
413 */
414 static gboolean Cookies_freeall_cb(gpointer key,
415 gpointer value,
416 gpointer data)
417 {
380 MSG("Enabling cookies as per cookiesrc...\n");
381
382 Cookies_load_cookies(file_stream);
383 }
384
385 /*
386 * Flush cookies to disk and free all the memory allocated.
387 */
388 static void Cookies_save_and_free()
389 {
390 int i, fd, saved = 0;
391 DomainNode *node;
418392 CookieData_t *cookie;
419 GList *domain_cookies = value;
420 /* char *ports_str; */
421
422 for (; domain_cookies; domain_cookies = g_list_next(domain_cookies)) {
423 cookie = domain_cookies->data;
424
425 if (!cookie->session_only) {
426 /* ports_str = Cookies_build_ports_str(cookie); */
427 fprintf(file_stream, "%s\tTRUE\t%s\t%s\t%ld\t%s\t%s\n",
428 cookie->domain,
429 cookie->path,
430 cookie->secure ? "TRUE" : "FALSE",
431 (long)cookie->expires_at,
432 cookie->name,
433 cookie->value);
434 /* g_free(ports_str); */
435 }
436
437 Cookies_free_cookie(cookie);
438 }
439 g_list_free(value);
440 g_free(key);
441
442 /* Return TRUE to tell GLIB to free this key from the hash table */
443 return TRUE;
444 }
445
446 /*
447 * Flush cookies to disk and free all the memory allocated.
448 */
449 void Cookies_freeall()
450 {
451 int fd;
393 time_t now;
394
452395 #ifndef HAVE_LOCKF
453396 struct flock lck;
454397 #endif
456399 if (disabled)
457400 return;
458401
402 now = time(NULL);
403
459404 rewind(file_stream);
460405 fd = fileno(file_stream);
461 ftruncate(fd, 0);
462 fprintf(file_stream, cookies_txt_header_str);
463
464 g_hash_table_foreach_remove(cookies, Cookies_freeall_cb, NULL);
406 if (ftruncate(fd, 0) == -1)
407 MSG("Cookies: Truncate file stream failed: %s\n", dStrerror(errno));
408 fprintf(file_stream, "%s", cookies_txt_header_str);
409
410 /* Iterate cookies per domain, saving and freeing */
411 while ((node = dList_nth_data(domains, 0))) {
412 for (i = 0; (cookie = dList_nth_data(node->cookies, i)); ++i) {
413 if (!cookie->session_only && difftime(cookie->expires_at, now) > 0) {
414 int len;
415 char buf[LINE_MAXLEN];
416
417 len = snprintf(buf, LINE_MAXLEN, "%s\t%s\t%s\t%s\t%ld\t%s\t%s\n",
418 cookie->domain,
419 cookie->host_only ? "FALSE" : "TRUE",
420 cookie->path,
421 cookie->secure ? "TRUE" : "FALSE",
422 (long) difftime(cookie->expires_at,
423 cookies_epoch_time),
424 cookie->name,
425 cookie->value);
426 if (len < LINE_MAXLEN) {
427 fprintf(file_stream, "%s", buf);
428 saved++;
429 } else {
430 MSG("Not saving overly long cookie for %s.\n", cookie->domain);
431 }
432 }
433 Cookies_free_cookie(cookie);
434 }
435 Cookies_delete_node(node);
436 }
437 dList_free(domains);
438 dList_free(all_cookies);
465439
466440 #ifdef HAVE_LOCKF
467441 lockf(fd, F_ULOCK, 0);
474448 fcntl(fileno(file_stream), F_SETLKW, &lck);
475449 #endif
476450 fclose(file_stream);
477 }
478
479 static char *months[] =
480 { "",
481 "Jan", "Feb", "Mar",
482 "Apr", "May", "Jun",
483 "Jul", "Aug", "Sep",
484 "Oct", "Nov", "Dec"
485 };
486
487 /*
488 * Take a months name and return a number between 1-12.
489 * E.g. 'April' -> 4
451
452 MSG("Cookies saved: %d.\n", saved);
453 }
454
455 /*
456 * Take a month's name and return a number between 0-11.
457 * E.g. 'April' -> 3
490458 */
491459 static int Cookies_get_month(const char *month_name)
492460 {
461 static const char *const months[] =
462 { "Jan", "Feb", "Mar",
463 "Apr", "May", "Jun",
464 "Jul", "Aug", "Sep",
465 "Oct", "Nov", "Dec"
466 };
493467 int i;
494468
495 for (i = 1; i <= 12; i++) {
496 if (!g_strncasecmp(months[i], month_name, 3))
469 for (i = 0; i < 12; i++) {
470 if (!dStrncasecmp(months[i], month_name, 3))
497471 return i;
498472 }
499 return 0;
500 }
501
502 /*
503 * Return a local timestamp from a GMT date string
504 * Accept: RFC-1123 | RFC-850 | ANSI asctime | Old Netscape format.
473 return -1;
474 }
475
476 /*
477 * Accept: RFC-1123 | RFC-850 | ANSI asctime | Old Netscape format date string.
505478 *
506479 * Wdy, DD-Mon-YY HH:MM:SS GMT
507480 * Wdy, DD-Mon-YYYY HH:MM:SS GMT
510483 * Tue May 21 13:46:22 1991\n
511484 * Tue May 21 13:46:22 1991
512485 *
513 * (return 0 on malformed date string syntax)
514 */
515 static time_t Cookies_create_timestamp(const char *expires)
516 {
517 time_t ret;
518 int day, month, year, hour, minutes, seconds;
519 gchar *cp;
520 gchar *E_msg =
521 "Expire date is malformed!\n"
522 " (should be RFC-1123 | RFC-850 | ANSI asctime)\n"
523 " Ignoring cookie: ";
524
525 cp = strchr(expires, ',');
526 if (!cp && (strlen(expires) == 24 || strlen(expires) == 25)) {
486 * Let's add:
487 * Mon Jan 11 08:00:00 2010 GMT
488 *
489 * Return a pointer to a struct tm, or NULL on error.
490 *
491 * NOTE that the RFC wants user agents to be more flexible in what
492 * they accept. For now, let's hack in special cases when they're encountered.
493 * Why? Because this function is currently understandable, and I don't want to
494 * abandon that (or at best decrease that -- see section 5.1.1) until there
495 * is known to be good reason.
496 */
497 static struct tm *Cookies_parse_date(const char *date)
498 {
499 struct tm *tm;
500 char *cp = strchr(date, ',');
501
502 if (!cp && strlen(date)>20 && date[13] == ':' && date[16] == ':') {
527503 /* Looks like ANSI asctime format... */
528 cp = (gchar *)expires;
529 day = strtol(cp + 8, NULL, 10); /* day */
530 month = Cookies_get_month(cp + 4); /* month */
531 year = strtol(cp + 20, NULL, 10); /* year */
532 hour = strtol(cp + 11, NULL, 10); /* hour */
533 minutes = strtol(cp + 14, NULL, 10); /* minutes */
534 seconds = strtol(cp + 17, NULL, 10); /* seconds */
535
536 } else if (cp && (cp - expires == 3 || cp - expires > 5) &&
504 tm = dNew0(struct tm, 1);
505
506 cp = (char *)date;
507 tm->tm_mon = Cookies_get_month(cp + 4);
508 tm->tm_mday = strtol(cp + 8, NULL, 10);
509 tm->tm_hour = strtol(cp + 11, NULL, 10);
510 tm->tm_min = strtol(cp + 14, NULL, 10);
511 tm->tm_sec = strtol(cp + 17, NULL, 10);
512 tm->tm_year = strtol(cp + 20, NULL, 10) - 1900;
513
514 } else if (cp && (cp - date == 3 || cp - date > 5) &&
537515 (strlen(cp) == 24 || strlen(cp) == 26)) {
538516 /* RFC-1123 | RFC-850 format | Old Netscape format */
539 day = strtol(cp + 2, NULL, 10);
540 month = Cookies_get_month(cp + 5);
541 year = strtol(cp + 9, &cp, 10);
542 /* todo: tricky, because two digits for year IS ambiguous! */
543 year += (year < 70) ? 2000 : ((year < 100) ? 1900 : 0);
544 hour = strtol(cp + 1, NULL, 10);
545 minutes = strtol(cp + 4, NULL, 10);
546 seconds = strtol(cp + 7, NULL, 10);
517 tm = dNew0(struct tm, 1);
518
519 tm->tm_mday = strtol(cp + 2, NULL, 10);
520 tm->tm_mon = Cookies_get_month(cp + 5);
521 tm->tm_year = strtol(cp + 9, &cp, 10);
522 /* tm_year is the number of years since 1900 */
523 if (tm->tm_year < 70)
524 tm->tm_year += 100;
525 else if (tm->tm_year > 100)
526 tm->tm_year -= 1900;
527 tm->tm_hour = strtol(cp + 1, NULL, 10);
528 tm->tm_min = strtol(cp + 4, NULL, 10);
529 tm->tm_sec = strtol(cp + 7, NULL, 10);
547530
548531 } else {
549 MSG("%s%s\n", E_msg, expires);
550 return (time_t) 0;
551 }
552
553 /* Error checks --this may be overkill */
554 if (!(day > 0 && day < 32 && month > 0 && month < 13 && year > 1970 &&
555 hour >= 0 && hour < 24 && minutes >= 0 && minutes < 60 &&
556 seconds >= 0 && seconds < 60)) {
557 MSG("%s%s\n", E_msg, expires);
558 return (time_t) 0;
559 }
560
561 /* Calculate local timestamp.
562 * [stolen from Lynx... (http://lynx.browser.org)] */
563 month -= 3;
564 if (month < 0) {
565 month += 12;
566 year--;
567 }
568
569 day += (year - 1968) * 1461 / 4;
570 day += ((((month * 153) + 2) / 5) - 672);
571 ret = (time_t)((day * 60 * 60 * 24) +
572 (hour * 60 * 60) +
573 (minutes * 60) +
574 seconds);
575
576 MSG("Expires in %ld seconds, at %s",
577 (long)ret - time(NULL), ctime(&ret));
578
532 tm = NULL;
533 MSG("In date \"%s\", format not understood.\n", date);
534 }
535
536 /* Error checks. This may be overkill. */
537 if (tm &&
538 !(tm->tm_mday > 0 && tm->tm_mday < 32 && tm->tm_mon >= 0 &&
539 tm->tm_mon < 12 && tm->tm_year >= 70 && tm->tm_hour >= 0 &&
540 tm->tm_hour < 24 && tm->tm_min >= 0 && tm->tm_min < 60 &&
541 tm->tm_sec >= 0 && tm->tm_sec < 60)) {
542 MSG("Date \"%s\" values not in range.\n", date);
543 dFree(tm);
544 tm = NULL;
545 }
546
547 return tm;
548 }
549
550 /*
551 * Find the least recently used cookie among those in the provided list.
552 */
553 static CookieData_t *Cookies_get_LRU(Dlist *cookies)
554 {
555 int i, n = dList_length(cookies);
556 CookieData_t *lru = dList_nth_data(cookies, 0);
557
558 for (i = 1; i < n; i++) {
559 CookieData_t *curr = dList_nth_data(cookies, i);
560
561 if (curr->last_used < lru->last_used)
562 lru = curr;
563 }
564 return lru;
565 }
566
567 /*
568 * Delete expired cookies.
569 * If node is given, only check those cookies.
570 * Note that nodes can disappear if all of their cookies were expired.
571 *
572 * Return the number of cookies that were expired.
573 */
574 static int Cookies_rm_expired_cookies(DomainNode *node)
575 {
576 Dlist *cookies = node ? node->cookies : all_cookies;
577 int removed = 0;
578 int i = 0, n = dList_length(cookies);
579 time_t now = time(NULL);
580
581 while (i < n) {
582 CookieData_t *c = dList_nth_data(cookies, i);
583
584 if (difftime(c->expires_at, now) < 0) {
585 DomainNode *currnode = node ? node :
586 dList_find_sorted(domains, c->domain, Domain_node_by_domain_cmp);
587 dList_remove(currnode->cookies, c);
588 if (dList_length(currnode->cookies) == 0)
589 Cookies_delete_node(currnode);
590 dList_remove_fast(all_cookies, c);
591 Cookies_free_cookie(c);
592 n--;
593 removed++;
594 } else {
595 i++;
596 }
597 }
598 return removed;
599 }
600
601 /*
602 * There are too many cookies. Choose one to remove and delete.
603 * If node is given, select from among its cookies only.
604 */
605 static void Cookies_too_many(DomainNode *node)
606 {
607 CookieData_t *lru = Cookies_get_LRU(node ? node->cookies : all_cookies);
608
609 MSG("Too many cookies!\n"
610 "Removing LRU cookie for \'%s\': \'%s=%s\'\n", lru->domain,
611 lru->name, lru->value);
612 if (!node)
613 node = dList_find_sorted(domains, lru->domain,Domain_node_by_domain_cmp);
614
615 dList_remove(node->cookies, lru);
616 dList_remove_fast(all_cookies, lru);
617 Cookies_free_cookie(lru);
618 if (dList_length(node->cookies) == 0)
619 Cookies_delete_node(node);
620 }
621
622 static void Cookies_add_cookie(CookieData_t *cookie)
623 {
624 Dlist *domain_cookies;
625 CookieData_t *c;
626 DomainNode *node;
627
628 node = dList_find_sorted(domains, cookie->domain,Domain_node_by_domain_cmp);
629 domain_cookies = (node) ? node->cookies : NULL;
630
631 if (domain_cookies) {
632 /* Remove any cookies with the same name, path, and host-only values. */
633 while ((c = dList_find_custom(domain_cookies, cookie, Cookies_cmp))) {
634 dList_remove(domain_cookies, c);
635 dList_remove_fast(all_cookies, c);
636 Cookies_free_cookie(c);
637 }
638 }
639
640 if ((cookie->expires_at == (time_t) -1) ||
641 (difftime(cookie->expires_at, time(NULL)) <= 0)) {
642 /*
643 * Don't add an expired cookie. Whether expiring now == expired, exactly,
644 * is arguable, but we definitely do not want to add a Max-Age=0 cookie.
645 */
646 _MSG("Goodbye, cookie %s=%s d:%s p:%s\n", cookie->name,
647 cookie->value, cookie->domain, cookie->path);
648 Cookies_free_cookie(cookie);
649 } else {
650 if (domain_cookies && dList_length(domain_cookies) >=MAX_DOMAIN_COOKIES){
651 int removed = Cookies_rm_expired_cookies(node);
652
653 if (removed == 0) {
654 Cookies_too_many(node);
655 } else if (removed >= MAX_DOMAIN_COOKIES) {
656 /* So many were removed that the node might have been deleted. */
657 node = dList_find_sorted(domains, cookie->domain,
658 Domain_node_by_domain_cmp);
659 domain_cookies = (node) ? node->cookies : NULL;
660 }
661 }
662 if (dList_length(all_cookies) >= MAX_TOTAL_COOKIES) {
663 if (Cookies_rm_expired_cookies(NULL) == 0) {
664 Cookies_too_many(NULL);
665 } else if (domain_cookies) {
666 /* Our own node might have just been deleted. */
667 node = dList_find_sorted(domains, cookie->domain,
668 Domain_node_by_domain_cmp);
669 domain_cookies = (node) ? node->cookies : NULL;
670 }
671 }
672
673 cookie->last_used = cookies_use_counter++;
674
675 /* Actually add the cookie! */
676 dList_append(all_cookies, cookie);
677
678 if (!domain_cookies) {
679 domain_cookies = dList_new(5);
680 dList_append(domain_cookies, cookie);
681 node = dNew(DomainNode, 1);
682 node->domain = dStrdup(cookie->domain);
683 node->cookies = domain_cookies;
684 dList_insert_sorted(domains, node, Domain_node_cmp);
685 } else {
686 dList_append(domain_cookies, cookie);
687 }
688 }
689 if (domain_cookies && (dList_length(domain_cookies) == 0))
690 Cookies_delete_node(node);
691 }
692
693 /*
694 * Return the attribute that is present at *cookie_str.
695 */
696 static char *Cookies_parse_attr(char **cookie_str)
697 {
698 char *str;
699 uint_t len;
700
701 while (dIsspace(**cookie_str))
702 (*cookie_str)++;
703
704 str = *cookie_str;
705 /* find '=' at end of attr, ';' after attr/val pair, '\0' end of string */
706 len = strcspn(str, "=;");
707 *cookie_str += len;
708
709 while (len && (str[len - 1] == ' ' || str[len - 1] == '\t'))
710 len--;
711 return dStrndup(str, len);
712 }
713
714 /*
715 * Get the value in *cookie_str.
716 */
717 static char *Cookies_parse_value(char **cookie_str)
718 {
719 uint_t len;
720 char *str;
721
722 if (**cookie_str == '=') {
723 (*cookie_str)++;
724 while (dIsspace(**cookie_str))
725 (*cookie_str)++;
726
727 str = *cookie_str;
728 /* finds ';' after attr/val pair or '\0' at end of string */
729 len = strcspn(str, ";");
730 *cookie_str += len;
731
732 while (len && (str[len - 1] == ' ' || str[len - 1] == '\t'))
733 len--;
734 } else {
735 str = *cookie_str;
736 len = 0;
737 }
738 return dStrndup(str, len);
739 }
740
741 /*
742 * Advance past any value
743 */
744 static void Cookies_eat_value(char **cookie_str)
745 {
746 if (**cookie_str == '=')
747 *cookie_str += strcspn(*cookie_str, ";");
748 }
749
750 /*
751 * Return the number of seconds by which our clock is ahead of the server's
752 * clock.
753 */
754 static double Cookies_server_timediff(const char *server_date)
755 {
756 double ret = 0;
757
758 if (server_date) {
759 struct tm *server_tm = Cookies_parse_date(server_date);
760
761 if (server_tm) {
762 time_t server_time = mktime(server_tm);
763
764 if (server_time != (time_t) -1)
765 ret = difftime(time(NULL), server_time);
766 dFree(server_tm);
767 }
768 }
579769 return ret;
580770 }
581771
582 /*
583 * Parse a string containing a list of port numbers.
584 */
585 static void Cookies_parse_ports(gint url_port, CookieData_t *cookie,
586 const char *port_str)
587 {
588 if ((!port_str || !port_str[0]) && url_port != 0) {
589 /* There was no list, so only the calling urls port should be allowed. */
590 cookie->ports = g_list_append(cookie->ports,
591 GINT_TO_POINTER(url_port));
592 } else if (port_str[0] == '"' && port_str[1] != '"') {
593 char **tokens, **i;
594 int port;
595
596 tokens = g_strsplit(port_str + 1, ",", -1);
597 for (i = tokens; *i; ++i) {
598 port = strtol(*i, NULL, 10);
599 if (port > 0) {
600 cookie->ports = g_list_append(cookie->ports,
601 GINT_TO_POINTER(port));
602 }
603 }
604 g_strfreev(tokens);
605 }
606 }
607
608 /*
609 * Build a string of the ports in 'cookie'.
610 */
611 static char *Cookies_build_ports_str(CookieData_t *cookie)
612 {
613 GString *gstr;
614 GList *list;
615 char *ret;
616
617 gstr = g_string_new("\"");
618 for (list = cookie->ports; list; list = g_list_next(list))
619 g_string_sprintfa(gstr, "%d,", GPOINTER_TO_INT(list->data));
620
621 /* Remove any trailing comma */
622 if (gstr->len > 1)
623 g_string_erase(gstr, gstr->len - 1, 1);
624
625 g_string_append(gstr, "\"");
626
627 ret = gstr->str;
628 g_string_free(gstr, FALSE);
629
630 return ret;
631 }
632
633 /*
634 * Used by g_list_insert_sorted() to sort the cookies by most specific path
635 */
636 static gint Cookies_compare(gconstpointer a, gconstpointer b)
637 {
638 const CookieData_t *ca = a, *cb = b;
639
640 return strcmp(ca->path, cb->path);
641 }
642
643 static void Cookies_add_cookie(CookieData_t *cookie)
644 {
645 GList *domain_cookies, *tmp;
646 char *domain_str;
647
648 /* Don't add an expired cookie */
649 if (!cookie->session_only && cookie->expires_at < time(NULL)) {
650 Cookies_free_cookie(cookie);
651 return;
652 }
653
654 domain_cookies = g_hash_table_lookup(cookies, cookie->domain);
655
656 if (domain_cookies) {
657 /* Respect the limit of 20 cookies per domain */
658 if (g_list_length(domain_cookies) > 20) {
659 MSG("There are too many cookies for this domain (%s)\n",
660 cookie->domain);
661 Cookies_free_cookie(cookie);
662 return;
663 }
664
665 /* Remove any cookies with the same name and path */
666 while ((tmp = g_list_find_custom(domain_cookies, cookie,
667 Cookies_equals))) {
668 Cookies_remove_cookie(tmp->data);
669 domain_cookies = g_hash_table_lookup(cookies, cookie->domain);
670 }
671 }
672
673 /* Allocate string key when no domain_cookies are left
674 * (because remove_cookie has then killed the key, when it was there) */
675 domain_str = domain_cookies ? cookie->domain : g_strdup(cookie->domain);
676
677 domain_cookies = g_list_insert_sorted(domain_cookies, cookie,
678 Cookies_compare);
679 g_hash_table_insert(cookies, domain_str, domain_cookies);
680 }
681
682 /*
683 * Remove the cookie from the domain list.
684 * If the domain list is empty, free the hash table entry.
685 * Free the cookie.
686 */
687 static void Cookies_remove_cookie(CookieData_t *cookie)
688 {
689 GList *list;
690 gpointer orig_key;
691 gpointer orig_val;
692
693 if (g_hash_table_lookup_extended(cookies, cookie->domain,
694 &orig_key, &orig_val)) {
695 list = g_list_remove(orig_val, cookie);
696
697 if (list) {
698 /* Make sure that we have the correct start of the list stored */
699 g_hash_table_insert(cookies, cookie->domain, list);
700 } else {
701 g_hash_table_remove(cookies, cookie->domain);
702 g_free(orig_key);
703 }
704 } else {
705 MSG("Attempting to remove a cookie that doesn't exist!\n");
706 }
707
708 Cookies_free_cookie(cookie);
709 }
710
711 /*
712 * Return the attribute that is present at *cookie_str. This function
713 * will also attempt to advance cookie_str past any equal-sign.
714 */
715 static char *Cookies_parse_attr(char **cookie_str)
716 {
717 char *str = *cookie_str;
718 guint i, end = 0;
719 gboolean got_attr = FALSE;
720
721 for (i = 0; ; i++) {
722 switch (str[i]) {
723 case ' ':
724 case '\t':
725 case '=':
726 case ';':
727 got_attr = TRUE;
728 if (end == 0)
729 end = i;
730 break;
731 case ',':
732 *cookie_str = str + i;
733 return g_strndup(str, i);
734 break;
735 case '\0':
736 if (!got_attr) {
737 end = i;
738 got_attr = TRUE;
739 }
740 /* fall through! */
741 default:
742 if (got_attr) {
743 *cookie_str = str + i;
744 return g_strndup(str, end);
745 }
746 break;
747 }
748 }
749
750 return NULL;
751 }
752
753 /*
754 * Get the value starting at *cookie_str.
755 * broken_syntax: watch out for stupid syntax (comma in unquoted string...)
756 */
757 static char *Cookies_parse_value(char **cookie_str,
758 gboolean broken_syntax,
759 gboolean keep_quotes)
760 {
761 guint i, end;
762 char *str = *cookie_str;
763
764 for (i = end = 0; !end; ++i) {
765 switch (str[i]) {
766 case ' ':
767 case '\t':
768 if (!broken_syntax && str[0] != '\'' && str[0] != '"') {
769 *cookie_str = str + i + 1;
770 end = 1;
771 }
772 break;
773 case '\'':
774 case '"':
775 if (i != 0 && str[i] == str[0]) {
776 char *tmp = str + i;
777
778 while (*tmp != '\0' && *tmp != ';' && *tmp != ',')
779 tmp++;
780
781 *cookie_str = (*tmp == ';') ? tmp + 1 : tmp;
782
783 if (keep_quotes)
784 i++;
785 end = 1;
786 }
787 break;
788 case '\0':
789 *cookie_str = str + i;
790 end = 1;
791 break;
792 case ',':
793 if (str[0] != '\'' && str[0] != '"' && !broken_syntax) {
794 /* A new cookie starts here! */
795 *cookie_str = str + i;
796 end = 1;
797 }
798 break;
799 case ';':
800 if (str[0] != '\'' && str[0] != '"') {
801 *cookie_str = str + i + 1;
802 end = 1;
803 }
804 break;
805 default:
806 break;
807 }
808 }
809 /* keep i as an index to the last char */
810 --i;
811
812 if ((str[0] == '\'' || str[0] == '"') && !keep_quotes) {
813 return i > 1 ? g_strndup(str + 1, i - 1) : NULL;
814 } else {
815 return g_strndup(str, i);
816 }
817 }
818
819 /*
820 * Parse one cookie...
821 */
822 static CookieData_t *Cookies_parse_one(gint url_port, char **cookie_str)
823 {
824 CookieData_t *cookie;
825 char *str = *cookie_str;
826 char *attr;
827 char *value;
828 int num_attr = 0;
829 gboolean max_age = FALSE;
830 gboolean discard = FALSE;
831
832 cookie = g_new0(CookieData_t, 1);
833 cookie->session_only = TRUE;
834
835 /* Iterate until there is nothing left of the string OR we come
836 * across a comma representing the start of another cookie */
837 while (*str != '\0' && *str != ',') {
838 /* Skip whitespace */
839 while (isspace(*str))
840 str++;
772 static void Cookies_unquote_string(char *str)
773 {
774 if (str && str[0] == '\"') {
775 uint_t len = strlen(str);
776
777 if (len > 1 && str[len - 1] == '\"') {
778 str[len - 1] = '\0';
779 while ((*str = str[1]))
780 str++;
781 }
782 }
783 }
784
785 /*
786 * Parse cookie. A cookie might look something like:
787 * "Name=Val; Domain=example.com; Max-Age=3600; HttpOnly"
788 */
789 static CookieData_t *Cookies_parse(char *cookie_str, const char *server_date)
790 {
791 CookieData_t *cookie = NULL;
792 char *str = cookie_str;
793 bool_t first_attr = TRUE;
794 bool_t max_age = FALSE;
795 bool_t expires = FALSE;
796
797 /* Iterate until there is nothing left of the string */
798 while (*str) {
799 char *attr;
800 char *value;
841801
842802 /* Get attribute */
843803 attr = Cookies_parse_attr(&str);
844 if (!attr) {
845 MSG("Failed to parse cookie attribute!\n");
846 Cookies_free_cookie(cookie);
847 return NULL;
848 }
849804
850805 /* Get the value for the attribute and store it */
851 if (num_attr == 0) {
852 /* The first attr, which always is the user supplied attr, may
853 * have the same name as an ordinary attr. Hence this workaround. */
854 cookie->name = g_strdup(attr);
855 cookie->value = Cookies_parse_value(&str, FALSE, TRUE);
856 } else if (g_strcasecmp(attr, "Path") == 0) {
857 value = Cookies_parse_value(&str, FALSE, FALSE);
806 if (first_attr) {
807 if (*str != '=' || *attr == '\0') {
808 /* disregard nameless cookie */
809 dFree(attr);
810 return NULL;
811 }
812 cookie = dNew0(CookieData_t, 1);
813 cookie->name = attr;
814 cookie->value = Cookies_parse_value(&str);
815
816 /* let's arbitrarily initialise with a year for now */
817 time_t now = time(NULL);
818 struct tm *tm = gmtime(&now);
819 ++tm->tm_year;
820 cookie->expires_at = mktime(tm);
821 if (cookie->expires_at == (time_t) -1)
822 cookie->expires_at = cookies_future_time;
823 } else if (dStrcasecmp(attr, "Path") == 0) {
824 value = Cookies_parse_value(&str);
825 dFree(cookie->path);
858826 cookie->path = value;
859 } else if (g_strcasecmp(attr, "Domain") == 0) {
860 value = Cookies_parse_value(&str, FALSE, FALSE);
827 } else if (dStrcasecmp(attr, "Domain") == 0) {
828 value = Cookies_parse_value(&str);
829 dFree(cookie->domain);
861830 cookie->domain = value;
862 } else if (g_strcasecmp(attr, "Discard") == 0) {
863 cookie->session_only = TRUE;
864 discard = TRUE;
865 } else if (g_strcasecmp(attr, "Max-Age") == 0) {
866 if (!discard) {
867 value = Cookies_parse_value(&str, FALSE, FALSE);
868
869 if (value) {
870 cookie->expires_at = time(NULL) + strtol(value, NULL, 10);
871 cookie->session_only = FALSE;
872 max_age = TRUE;
873 g_free(value);
831 } else if (dStrcasecmp(attr, "Max-Age") == 0) {
832 value = Cookies_parse_value(&str);
833 if (isdigit(*value) || *value == '-') {
834 time_t now = time(NULL);
835 long age = strtol(value, NULL, 10);
836 struct tm *tm = gmtime(&now);
837
838 tm->tm_sec += age;
839 cookie->expires_at = mktime(tm);
840 if (age > 0 && cookie->expires_at == (time_t) -1) {
841 cookie->expires_at = cookies_future_time;
842 }
843 _MSG("Cookie to expire at %s", ctime(&cookie->expires_at));
844 expires = max_age = TRUE;
845 }
846 dFree(value);
847 } else if (dStrcasecmp(attr, "Expires") == 0) {
848 if (!max_age) {
849 value = Cookies_parse_value(&str);
850 Cookies_unquote_string(value);
851 _MSG("Expires attribute gives %s\n", value);
852 struct tm *tm = Cookies_parse_date(value);
853 if (tm) {
854 tm->tm_sec += Cookies_server_timediff(server_date);
855 cookie->expires_at = mktime(tm);
856 if (cookie->expires_at == (time_t) -1 && tm->tm_year >= 138) {
857 /* Just checking tm_year does not ensure that the problem was
858 * inability to represent a distant date...
859 */
860 cookie->expires_at = cookies_future_time;
861 }
862 _MSG("Cookie to expire at %s", ctime(&cookie->expires_at));
863 dFree(tm);
874864 } else {
875 MSG("Failed to parse cookie value!\n");
876 Cookies_free_cookie(cookie);
877 return NULL;
865 cookie->expires_at = (time_t) -1;
878866 }
879 }
880 } else if (g_strcasecmp(attr, "Expires") == 0) {
881 if (!max_age && !discard) {
882 MSG("Old netscape-style cookie...\n");
883 value = Cookies_parse_value(&str, TRUE, FALSE);
884 if (value) {
885 cookie->expires_at = Cookies_create_timestamp(value);
886 cookie->session_only = FALSE;
887 g_free(value);
888 } else {
889 MSG("Failed to parse cookie value!\n");
890 Cookies_free_cookie(cookie);
891 return NULL;
867 expires = TRUE;
868 dFree(value);
869 } else {
870 Cookies_eat_value(&str);
871 }
872 } else if (dStrcasecmp(attr, "Secure") == 0) {
873 cookie->secure = TRUE;
874 Cookies_eat_value(&str);
875 } else if (dStrcasecmp(attr, "HttpOnly") == 0) {
876 Cookies_eat_value(&str);
877 } else {
878 MSG("Cookie contains unknown attribute: '%s'\n", attr);
879 Cookies_eat_value(&str);
880 }
881
882 if (first_attr)
883 first_attr = FALSE;
884 else
885 dFree(attr);
886
887 if (*str == ';')
888 str++;
889 }
890 cookie->session_only = expires == FALSE;
891 return cookie;
892 }
893
894 /*
895 * Compare cookies by host_only, name, and path. Return 0 if equal.
896 */
897 static int Cookies_cmp(const void *a, const void *b)
898 {
899 const CookieData_t *ca = a, *cb = b;
900
901 return (ca->host_only != cb->host_only) ||
902 (strcmp(ca->name, cb->name) != 0) ||
903 (strcmp(ca->path, cb->path) != 0);
904 }
905
906 /*
907 * Is the domain an IP address?
908 */
909 static bool_t Cookies_domain_is_ip(const char *domain)
910 {
911 uint_t len;
912
913 if (!domain)
914 return FALSE;
915
916 len = strlen(domain);
917
918 if (len == strspn(domain, "0123456789.")) {
919 _MSG("an IPv4 address\n");
920 return TRUE;
921 }
922 if (*domain == '[' &&
923 (len == strspn(domain, "0123456789abcdefABCDEF:.[]"))) {
924 /* The precise format is shown in section 3.2.2 of rfc 3986 */
925 _MSG("an IPv6 address\n");
926 return TRUE;
927 }
928 return FALSE;
929 }
930
931 /*
932 * Check whether url_path path-matches cookie_path
933 *
934 * Note different user agents apparently vary in path-matching behaviour,
935 * but this is the recommended method at the moment.
936 */
937 static bool_t Cookies_path_matches(const char *url_path,
938 const char *cookie_path)
939 {
940 bool_t ret = TRUE;
941
942 if (!url_path || !cookie_path) {
943 ret = FALSE;
944 } else {
945 uint_t c_len = strlen(cookie_path);
946 uint_t u_len = strlen(url_path);
947
948 ret = (!strncmp(cookie_path, url_path, c_len) &&
949 ((c_len == u_len) ||
950 (c_len > 0 && cookie_path[c_len - 1] == '/') ||
951 (url_path[c_len] == '/')));
952 }
953 return ret;
954 }
955
956 /*
957 * If cookie path is not properly set, remedy that.
958 */
959 static void Cookies_validate_path(CookieData_t *cookie, const char *url_path)
960 {
961 if (!cookie->path || cookie->path[0] != '/') {
962 dFree(cookie->path);
963
964 if (url_path) {
965 uint_t len = strlen(url_path);
966
967 while (len && url_path[len] != '/')
968 len--;
969 cookie->path = dStrndup(url_path, len ? len : 1);
970 } else {
971 cookie->path = dStrdup("/");
972 }
973 }
974 }
975
976 /*
977 * Check whether host name A domain-matches host name B.
978 */
979 static bool_t Cookies_domain_matches(char *A, char *B)
980 {
981 int diff;
982
983 if (!A || !*A || !B || !*B)
984 return FALSE;
985
986 if (*B == '.')
987 B++;
988
989 /* Should we concern ourselves with trailing dots in matching (here or
990 * elsewhere)? The HTTP State people have found that most user agents
991 * don't, so: No.
992 */
993
994 if (!dStrcasecmp(A, B))
995 return TRUE;
996
997 if (Cookies_domain_is_ip(B))
998 return FALSE;
999
1000 diff = strlen(A) - strlen(B);
1001
1002 if (diff > 0) {
1003 /* B is the tail of A, and the match is preceded by a '.' */
1004 return (dStrcasecmp(A + diff, B) == 0 && A[diff - 1] == '.');
1005 } else {
1006 return FALSE;
1007 }
1008 }
1009
1010 /*
1011 * Based on the host, how many internal dots do we need in a cookie domain
1012 * to make it valid? e.g., "org" is not on the list, so dillo.org is a safe
1013 * cookie domain, but "uk" is on the list, so ac.uk is not safe.
1014 *
1015 * This is imperfect, but it's something. Specifically, checking for these
1016 * TLDs is the solution that Konqueror used once upon a time, according to
1017 * reports.
1018 */
1019 static uint_t Cookies_internal_dots_required(const char *host)
1020 {
1021 uint_t ret = 1;
1022
1023 if (host) {
1024 int start, after, tld_len;
1025
1026 /* We may be able to trust the format of the host string more than
1027 * I am here. Trailing dots and no dots are real possibilities, though.
1028 */
1029 after = strlen(host);
1030 if (after > 0 && host[after - 1] == '.')
1031 after--;
1032 start = after;
1033 while (start > 0 && host[start - 1] != '.')
1034 start--;
1035 tld_len = after - start;
1036
1037 if (tld_len > 0) {
1038 /* These TLDs were chosen by examining the current publicsuffix list
1039 * in January 2010 and picking out those where it was simplest for
1040 * them to describe the situation by beginning with a "*.[tld]" rule.
1041 */
1042 const char *const tlds[] = {"ar","au","bd","bn","bt","ck","cy","do",
1043 "eg","er","et","fj","fk","gt","gu","id",
1044 "il","jm","ke","kh","kw","ml","mm","mt",
1045 "mz","ni","np","nz","om","pg","py","qa",
1046 "sv","tr","uk","uy","ve","ye","yu","za",
1047 "zm","zw"};
1048 uint_t i, tld_num = sizeof(tlds) / sizeof(tlds[0]);
1049
1050 for (i = 0; i < tld_num; i++) {
1051 if (strlen(tlds[i]) == (uint_t) tld_len &&
1052 !dStrncasecmp(tlds[i], host + start, tld_len)) {
1053 _MSG("TLD code matched %s\n", tlds[i]);
1054 ret++;
1055 break;
8921056 }
8931057 }
894 } else if (g_strcasecmp(attr, "Port") == 0) {
895 value = Cookies_parse_value(&str, FALSE, TRUE);
896 Cookies_parse_ports(url_port, cookie, value);
897 g_free(value);
898 } else if (g_strcasecmp(attr, "Comment") == 0) {
899 value = Cookies_parse_value(&str, FALSE, FALSE);
900 cookie->comment = value;
901 } else if (g_strcasecmp(attr, "CommentURL") == 0) {
902 value = Cookies_parse_value(&str, FALSE, FALSE);
903 cookie->comment_url = value;
904 } else if (g_strcasecmp(attr, "Version") == 0) {
905 value = Cookies_parse_value(&str, FALSE, FALSE);
906
907 if (value) {
908 cookie->version = strtol(value, NULL, 10);
909 g_free(value);
910 } else {
911 MSG("Failed to parse cookie value!\n");
912 Cookies_free_cookie(cookie);
913 return NULL;
914 }
915 } else if (g_strcasecmp(attr, "Secure") == 0) {
916 cookie->secure = TRUE;
917 } else {
918 /* Oops! this can't be good... */
919 g_free(attr);
920 Cookies_free_cookie(cookie);
921 MSG("Cookie contains illegal attribute!\n");
922 return NULL;
923 }
924
925 g_free(attr);
926 num_attr++;
927 }
928
929 *cookie_str = (*str == ',') ? str + 1 : str;
930
931 if (cookie->name && cookie->value) {
932 return cookie;
933 } else {
934 MSG("Cookie missing name and/or value!\n");
935 Cookies_free_cookie(cookie);
936 return NULL;
937 }
938 }
939
940 /*
941 * Iterate the cookie string until we catch all cookies.
942 * Return Value: a list with all the cookies! (or NULL upon error)
943 */
944 static GSList *Cookies_parse_string(gint url_port, char *cookie_string)
945 {
1058 }
1059 }
1060 return ret;
1061 }
1062
1063 /*
1064 * Validate cookies domain against some security checks.
1065 */
1066 static bool_t Cookies_validate_domain(CookieData_t *cookie, char *host)
1067 {
1068 uint_t i, internal_dots;
1069
1070 if (!cookie->domain) {
1071 cookie->domain = dStrdup(host);
1072 cookie->host_only = TRUE;
1073 return TRUE;
1074 }
1075
1076 if (!Cookies_domain_matches(host, cookie->domain))
1077 return FALSE;
1078
1079 internal_dots = 0;
1080 for (i = 1; i < strlen(cookie->domain) - 1; i++) {
1081 if (cookie->domain[i] == '.')
1082 internal_dots++;
1083 }
1084
1085 /* All of this dots business is a weak hack.
1086 * TODO: accept the publicsuffix.org list as an optional external file.
1087 */
1088 if (internal_dots < Cookies_internal_dots_required(host)) {
1089 MSG("not enough dots in %s\n", cookie->domain);
1090 return FALSE;
1091 }
1092
1093 _MSG("host %s and domain %s is all right\n", host, cookie->domain);
1094 return TRUE;
1095 }
1096
1097 /*
1098 * Set the value corresponding to the cookie string
1099 * Return value: 0 set OK, -1 disabled, -2 denied, -3 rejected.
1100 */
1101 static int Cookies_set(char *cookie_string, char *url_host,
1102 char *url_path, char *server_date)
1103 {
1104 CookieControlAction action;
9461105 CookieData_t *cookie;
947 GSList *ret = NULL;
948 char *str = cookie_string;
949
950 /* The string may contain several cookies separated by comma.
951 * We'll iterate until we've catched them all */
952 while (*str) {
953 cookie = Cookies_parse_one(url_port, &str);
954
955 if (cookie) {
956 ret = g_slist_append(ret, cookie);
957 } else {
958 MSG("Malformed cookie field, ignoring cookie: %s\n", cookie_string);
959 return NULL;
960 }
961 }
962
963 return ret;
964 }
965
966 /*
967 * Compare cookies by name and path (return 0 if equal)
968 */
969 static gint Cookies_equals(gconstpointer a, gconstpointer b)
970 {
971 const CookieData_t *ca = a, *cb = b;
972
973 return (strcmp(ca->name, cb->name) || strcmp(ca->path, cb->path));
974 }
975
976 /*
977 * Validate cookies domain against some security checks.
978 */
979 static gboolean Cookies_validate_domain(CookieData_t *cookie, gchar *host,
980 gchar *url_path)
981 {
982 int dots, diff, i;
983 gboolean is_ip;
984
985 /* Make sure that the path is set to something */
986 if (!cookie->path || cookie->path[0] != '/') {
987 g_free(cookie->path);
988 cookie->path = Cookies_strip_path(url_path);
989 }
990
991 /* If the server never set a domain, or set one without a leading
992 * dot (which isn't allowed), we use the calling URL's hostname. */
993 if (cookie->domain == NULL || cookie->domain[0] != '.') {
994 g_free(cookie->domain);
995 cookie->domain = g_strdup(host);
996 return TRUE;
997 }
998
999 /* Count the number of dots and also find out if it is an IP-address */
1000 is_ip = TRUE;
1001 for (i = 0, dots = 0; cookie->domain[i] != '\0'; i++) {
1002 if (cookie->domain[i] == '.')
1003 dots++;
1004 else if (!isdigit(cookie->domain[i]))
1005 is_ip = FALSE;
1006 }
1007
1008 /* A valid domain must have at least two dots in it */
1009 /* NOTE: this breaks cookies on localhost... */
1010 if (dots < 2) {
1011 return FALSE;
1012 }
1013
1014 /* Now see if the url matches the domain */
1015 diff = strlen(host) - i;
1016 if (diff > 0) {
1017 if (g_strcasecmp(host + diff, cookie->domain))
1018 return FALSE;
1019
1020 if (!is_ip) {
1021 /* "x.y.test.com" is not allowed to set cookies for ".test.com";
1022 * only an url of the form "y.test.com" would be. */
1023 while ( diff-- )
1024 if (host[diff] == '.')
1025 return FALSE;
1026 }
1027 }
1028
1029 return TRUE;
1030 }
1031
1032 /*
1033 * Strip of the filename from a full path
1034 */
1035 static char *Cookies_strip_path(const char *path)
1036 {
1037 char *ret;
1038 guint len;
1039
1040 if (path) {
1041 len = strlen(path);
1042
1043 while (len && path[len] != '/')
1044 len--;
1045 ret = g_strndup(path, len + 1);
1046 } else {
1047 ret = g_strdup("/");
1048 }
1049
1050 return ret;
1051 }
1052
1053 /*
1054 * Set the value corresponding to the cookie string
1055 */
1056 void Cookies_set(gchar *cookie_string, gchar *url_host,
1057 gchar *url_path, gint url_port)
1058 {
1059 CookieControlAction action;
1060 GSList *list;
1106 int ret = -1;
10611107
10621108 if (disabled)
1063 return;
1109 return ret;
10641110
10651111 action = Cookies_control_check_domain(url_host);
10661112 if (action == COOKIE_DENY) {
10671113 MSG("denied SET for %s\n", url_host);
1068 return;
1069 }
1070
1071 list = Cookies_parse_string(url_port, cookie_string);
1072
1073 while (list) {
1074 CookieData_t *cookie = list->data;
1075
1076 if (Cookies_validate_domain(cookie, url_host, url_path)) {
1077 if (action == COOKIE_ACCEPT_SESSION)
1078 cookie->session_only = TRUE;
1079
1080 Cookies_add_cookie(cookie);
1081 } else {
1082 MSG("Rejecting cookie for %s from host %s path %s\n",
1083 cookie->domain, url_host, url_path);
1084 Cookies_free_cookie(cookie);
1085 }
1086
1087 list = g_slist_remove(list, list->data);
1088 }
1089 }
1090
1091 /*
1092 * Compare the cookie with the supplied data to see if it matches
1093 */
1094 static gboolean Cookies_match(CookieData_t *cookie, gint port,
1095 const char *path, gboolean is_ssl)
1096 {
1097 /* Insecure cookies matches both secure and insecure urls, secure
1098 cookies matches only secure urls */
1114 ret = -2;
1115
1116 } else {
1117 MSG("%s SETTING: %s\n", url_host, cookie_string);
1118 ret = -3;
1119 if ((cookie = Cookies_parse(cookie_string, server_date))) {
1120 if (Cookies_validate_domain(cookie, url_host)) {
1121 Cookies_validate_path(cookie, url_path);
1122 if (action == COOKIE_ACCEPT_SESSION)
1123 cookie->session_only = TRUE;
1124 Cookies_add_cookie(cookie);
1125 ret = 0;
1126 } else {
1127 MSG("Rejecting cookie for domain %s from host %s path %s\n",
1128 cookie->domain, url_host, url_path);
1129 Cookies_free_cookie(cookie);
1130 }
1131 }
1132 }
1133
1134 return ret;
1135 }
1136
1137 /*
1138 * Compare the cookie with the supplied data to see whether it matches
1139 */
1140 static bool_t Cookies_match(CookieData_t *cookie, const char *url_path,
1141 bool_t host_only_val, bool_t is_ssl)
1142 {
1143 if (cookie->host_only != host_only_val)
1144 return FALSE;
1145
1146 /* Insecure cookies match both secure and insecure urls, secure
1147 cookies match only secure urls */
10991148 if (cookie->secure && !is_ssl)
11001149 return FALSE;
11011150
1102 /* Check that the cookie path is a subpath of the current path */
1103 if (strncmp(cookie->path, path, strlen(cookie->path)) != 0)
1151 if (!Cookies_path_matches(url_path, cookie->path))
11041152 return FALSE;
1105
1106 /* Check if the port of the request URL matches any
1107 * of those set in the cookie */
1108 if (cookie->ports) {
1109 GList *list;
1110
1111 for (list = cookie->ports; list; list = g_list_next(list)) {
1112 if (GPOINTER_TO_INT(list->data) == port)
1113 return TRUE;
1114 }
1115
1116 return FALSE;
1117 }
11181153
11191154 /* It's a match */
11201155 return TRUE;
11211156 }
11221157
1158 static void Cookies_add_matching_cookies(const char *domain,
1159 const char *url_path,
1160 bool_t host_only_val,
1161 Dlist *matching_cookies,
1162 bool_t is_ssl)
1163 {
1164 DomainNode *node = dList_find_sorted(domains, domain,
1165 Domain_node_by_domain_cmp);
1166 if (node) {
1167 int i;
1168 CookieData_t *cookie;
1169 Dlist *domain_cookies = node->cookies;
1170
1171 for (i = 0; (cookie = dList_nth_data(domain_cookies, i)); ++i) {
1172 /* Remove expired cookie. */
1173 if (difftime(cookie->expires_at, time(NULL)) < 0) {
1174 _MSG("Goodbye, expired cookie %s=%s d:%s p:%s\n", cookie->name,
1175 cookie->value, cookie->domain, cookie->path);
1176 dList_remove(domain_cookies, cookie);
1177 dList_remove_fast(all_cookies, cookie);
1178 Cookies_free_cookie(cookie);
1179 --i; continue;
1180 }
1181 /* Check if the cookie matches the requesting URL */
1182 if (Cookies_match(cookie, url_path, host_only_val, is_ssl)) {
1183 int j;
1184 CookieData_t *curr;
1185 uint_t path_length = strlen(cookie->path);
1186
1187 cookie->last_used = cookies_use_counter;
1188
1189 /* Longest cookies go first */
1190 for (j = 0;
1191 (curr = dList_nth_data(matching_cookies, j)) &&
1192 strlen(curr->path) >= path_length;
1193 j++) ;
1194 dList_insert_pos(matching_cookies, cookie, j);
1195 }
1196 }
1197
1198 if (dList_length(domain_cookies) == 0)
1199 Cookies_delete_node(node);
1200 }
1201 }
1202
11231203 /*
11241204 * Return a string that contains all relevant cookies as headers.
11251205 */
1126 char *Cookies_get(gchar *url_host, gchar *url_path,
1127 gchar *url_scheme, gint url_port)
1128 {
1129 char *domain_string, *q, *str, *path;
1206 static char *Cookies_get(char *url_host, char *url_path,
1207 char *url_scheme)
1208 {
1209 char *domain_str, *str;
11301210 CookieData_t *cookie;
1131 GList *matching_cookies = NULL;
1132 GList *domain_cookie;
1133 gboolean is_ssl;
1134 GString *cookie_gstring;
1211 Dlist *matching_cookies;
1212 bool_t is_ssl, is_ip_addr, host_only_val;
1213
1214 Dstr *cookie_dstring;
1215 int i;
11351216
11361217 if (disabled)
1137 return g_strdup("");
1138
1139 path = Cookies_strip_path(url_path);
1218 return dStrdup("");
1219
1220 matching_cookies = dList_new(8);
11401221
11411222 /* Check if the protocol is secure or not */
1142 is_ssl = (!g_strcasecmp(url_scheme, "https"));
1143
1144 for (domain_string = (char *) url_host;
1145 domain_string != NULL && *domain_string;
1146 domain_string = strchr(domain_string+1, '.')) {
1147 domain_cookie = g_hash_table_lookup(cookies, domain_string);
1148
1149 while (domain_cookie) {
1150 cookie = domain_cookie->data;
1151 domain_cookie = g_list_next(domain_cookie);
1152
1153 /* Remove expired cookie. */
1154 if (!cookie->session_only && cookie->expires_at < time(NULL)) {
1155 Cookies_remove_cookie(cookie);
1156 continue;
1157 }
1158
1159 /* Check if the cookie matches the requesting URL */
1160 if (Cookies_match(cookie, url_port, path, is_ssl))
1161 matching_cookies = g_list_append(matching_cookies, cookie);
1223 is_ssl = (!dStrcasecmp(url_scheme, "https"));
1224
1225 is_ip_addr = Cookies_domain_is_ip(url_host);
1226
1227 /* If a cookie is set that lacks a Domain attribute, its domain is set to
1228 * the server's host and the host_only flag is set for that cookie. Such a
1229 * cookie can only be sent back to that host. Cookies with Domain attrs do
1230 * not have the host_only flag set, and may be sent to subdomains. Domain
1231 * attrs can have leading dots, which should be ignored for matching
1232 * purposes.
1233 */
1234 host_only_val = FALSE;
1235 if (!is_ip_addr) {
1236 /* e.g., sub.example.com set a cookie with domain ".sub.example.com". */
1237 domain_str = dStrconcat(".", url_host, NULL);
1238 Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
1239 matching_cookies, is_ssl);
1240 dFree(domain_str);
1241 }
1242 host_only_val = TRUE;
1243 /* e.g., sub.example.com set a cookie with no domain attribute. */
1244 Cookies_add_matching_cookies(url_host, url_path, host_only_val,
1245 matching_cookies, is_ssl);
1246 host_only_val = FALSE;
1247 /* e.g., sub.example.com set a cookie with domain "sub.example.com". */
1248 Cookies_add_matching_cookies(url_host, url_path, host_only_val,
1249 matching_cookies, is_ssl);
1250
1251 if (!is_ip_addr) {
1252 for (domain_str = strchr(url_host+1, '.');
1253 domain_str != NULL && *domain_str;
1254 domain_str = strchr(domain_str+1, '.')) {
1255 /* e.g., sub.example.com set a cookie with domain ".example.com". */
1256 Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
1257 matching_cookies, is_ssl);
1258 if (domain_str[1]) {
1259 domain_str++;
1260 /* e.g., sub.example.com set a cookie with domain "example.com".*/
1261 Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
1262 matching_cookies, is_ssl);
1263 }
11621264 }
11631265 }
11641266
11651267 /* Found the cookies, now make the string */
1166 cookie_gstring = g_string_new("");
1167 if (matching_cookies != NULL) {
1168 CookieData_t *first_cookie = matching_cookies->data;
1169
1170 g_string_sprintfa(cookie_gstring, "Cookie: ");
1171
1172 if (first_cookie->version != 0)
1173 g_string_sprintfa(cookie_gstring, "$Version=\"%d\"; ",
1174 first_cookie->version);
1175
1176 while (matching_cookies) {
1177 cookie = matching_cookies->data;
1178 q = (cookie->version == 0 ? "" : "\"");
1179
1180 g_string_sprintfa(cookie_gstring,
1181 "%s=%s; $Path=%s%s%s; $Domain=%s%s%s",
1182 cookie->name, cookie->value,
1183 q, cookie->path, q, q, cookie->domain, q);
1184
1185 if (cookie->ports) {
1186 char *ports_str = Cookies_build_ports_str(cookie);
1187 g_string_sprintfa(cookie_gstring, "; $Port=%s", ports_str);
1188 g_free(ports_str);
1189 }
1190
1191 matching_cookies = g_list_next(matching_cookies);
1192 g_string_append(cookie_gstring, matching_cookies ? "; " : "\r\n");
1193 }
1194 }
1195
1196 g_free(path);
1197 str = cookie_gstring->str;
1198 g_string_free(cookie_gstring, FALSE);
1268 cookie_dstring = dStr_new("");
1269 if (dList_length(matching_cookies) > 0) {
1270
1271 dStr_sprintfa(cookie_dstring, "Cookie: ");
1272
1273 for (i = 0; (cookie = dList_nth_data(matching_cookies, i)); ++i) {
1274 dStr_sprintfa(cookie_dstring, "%s=%s", cookie->name, cookie->value);
1275 dStr_append(cookie_dstring,
1276 dList_length(matching_cookies) > i + 1 ? "; " : "\r\n");
1277 }
1278 }
1279
1280 dList_free(matching_cookies);
1281 str = cookie_dstring->str;
1282 dStr_free(cookie_dstring, FALSE);
1283
1284 if (*str)
1285 cookies_use_counter++;
1286
1287 MSG("%s GETTING: %s\n", url_host, str);
11991288 return str;
12001289 }
12011290
12151304 {
12161305 CookieControl cc;
12171306 FILE *stream;
1218 char *filename;
1307 char *filename, *rc;
12191308 char line[LINE_MAXLEN];
12201309 char domain[LINE_MAXLEN];
12211310 char rule[LINE_MAXLEN];
1222 int i, j;
1223 gboolean enabled = FALSE;
1311 bool_t enabled = FALSE;
12241312
12251313 /* Get a file pointer */
1226 filename = g_strconcat(g_get_home_dir(), "/", ".dillo/cookiesrc", NULL);
1227 stream = Cookies_fopen(filename, "DEFAULT DENY\n");
1228 g_free(filename);
1314 filename = dStrconcat(dGethomedir(), "/.dillo/cookiesrc", NULL);
1315 stream = Cookies_fopen(filename, "r", "DEFAULT DENY\n");
1316 dFree(filename);
12291317
12301318 if (!stream)
12311319 return 2;
12331321 /* Get all lines in the file */
12341322 while (!feof(stream)) {
12351323 line[0] = '\0';
1236 fgets(line, LINE_MAXLEN, stream);
1324 rc = fgets(line, LINE_MAXLEN, stream);
1325 if (!rc && ferror(stream)) {
1326 MSG("Error while reading rule from cookiesrc: %s\n",
1327 dStrerror(errno));
1328 break; /* bail out */
1329 }
12371330
12381331 /* Remove leading and trailing whitespaces */
1239 g_strstrip(line);
1332 dStrstrip(line);
12401333
12411334 if (line[0] != '\0' && line[0] != '#') {
1242 i = 0;
1243 j = 0;
1335 int i = 0, j = 0;
12441336
12451337 /* Get the domain */
1246 while (!isspace(line[i]))
1338 while (line[i] != '\0' && !dIsspace(line[i]))
12471339 domain[j++] = line[i++];
12481340 domain[j] = '\0';
12491341
12501342 /* Skip past whitespaces */
1251 i++;
1252 while (isspace(line[i]))
1343 while (dIsspace(line[i]))
12531344 i++;
12541345
12551346 /* Get the rule */
12561347 j = 0;
1257 while (line[i] != '\0' && !isspace(line[i]))
1348 while (line[i] != '\0' && !dIsspace(line[i]))
12581349 rule[j++] = line[i++];
12591350 rule[j] = '\0';
12601351
1261 if (g_strcasecmp(rule, "ACCEPT") == 0)
1352 if (dStrcasecmp(rule, "ACCEPT") == 0)
12621353 cc.action = COOKIE_ACCEPT;
1263 else if (g_strcasecmp(rule, "ACCEPT_SESSION") == 0)
1354 else if (dStrcasecmp(rule, "ACCEPT_SESSION") == 0)
12641355 cc.action = COOKIE_ACCEPT_SESSION;
1265 else if (g_strcasecmp(rule, "DENY") == 0)
1356 else if (dStrcasecmp(rule, "DENY") == 0)
12661357 cc.action = COOKIE_DENY;
12671358 else {
12681359 MSG("Cookies: rule '%s' for domain '%s' is not recognised.\n",
12701361 continue;
12711362 }
12721363
1273 cc.domain = g_strdup(domain);
1274 if (g_strcasecmp(cc.domain, "DEFAULT") == 0) {
1364 cc.domain = dStrdup(domain);
1365 if (dStrcasecmp(cc.domain, "DEFAULT") == 0) {
12751366 /* Set the default action */
12761367 default_action = cc.action;
1277 g_free(cc.domain);
1368 dFree(cc.domain);
12781369 } else {
1370 int i;
1371 uint_t len = strlen(cc.domain);
1372
1373 /* Insert into list such that longest rules come first. */
12791374 a_List_add(ccontrol, num_ccontrol, num_ccontrol_max);
1280 ccontrol[num_ccontrol++] = cc;
1375 for (i = num_ccontrol++;
1376 i > 0 && (len > strlen(ccontrol[i-1].domain));
1377 i--) {
1378 ccontrol[i] = ccontrol[i-1];
1379 }
1380 ccontrol[i] = cc;
12811381 }
12821382
12831383 if (cc.action != COOKIE_DENY)
12911391 }
12921392
12931393 /*
1294 * Check the rules for an appropriate action for this domain
1394 * Check the rules for an appropriate action for this domain.
1395 * The rules are ordered by domain length, with longest first, so the
1396 * first match is the most specific.
12951397 */
12961398 static CookieControlAction Cookies_control_check_domain(const char *domain)
12971399 {
13011403 if (ccontrol[i].domain[0] == '.') {
13021404 diff = strlen(domain) - strlen(ccontrol[i].domain);
13031405 if (diff >= 0) {
1304 if (g_strcasecmp(domain + diff, ccontrol[i].domain) != 0)
1406 if (dStrcasecmp(domain + diff, ccontrol[i].domain) != 0)
13051407 continue;
13061408 } else {
13071409 continue;
13081410 }
13091411 } else {
1310 if (g_strcasecmp(domain, ccontrol[i].domain) != 0)
1412 if (dStrcasecmp(domain, ccontrol[i].domain) != 0)
13111413 continue;
13121414 }
13131415
13251427 * Note: Buf is a zero terminated string
13261428 * Return code: { 0:OK, 1:Abort, 2:Close }
13271429 */
1328 static int srv_parse_buf(SockHandler *sh, char *Buf, size_t BufSize)
1329 {
1330 char *p, *cmd, *cookie, *host, *path, *scheme;
1331 gint port;
1332
1333 if (!(p = strchr(Buf, '>'))) {
1334 /* Haven't got a full tag */
1335 MSG("Haven't got a full tag!\n");
1336 return 1;
1337 }
1338
1339 cmd = a_Dpip_get_attr(Buf, BufSize, "cmd");
1340
1341 if (cmd && strcmp(cmd, "DpiBye") == 0) {
1342 g_free(cmd);
1343 MSG("Cookies dpi (pid %d): Got DpiBye.\n", (gint)getpid());
1430 static int srv_parse_tok(Dsh *sh, ClientInfo *client, char *Buf)
1431 {
1432 char *cmd, *cookie, *host, *path;
1433 int ret = 1;
1434 size_t BufSize = strlen(Buf);
1435
1436 cmd = a_Dpip_get_attr_l(Buf, BufSize, "cmd");
1437
1438 if (!cmd) {
1439 /* abort */
1440 } else if (client->status == 0) {
1441 /* authenticate */
1442 if (a_Dpip_check_auth(Buf) == 1) {
1443 client->status = 1;
1444 ret = 0;
1445 }
1446 } else if (strcmp(cmd, "DpiBye") == 0) {
1447 dFree(cmd);
1448 MSG("(pid %d): Got DpiBye.\n", (int)getpid());
13441449 exit(0);
13451450
1346 } else if (cmd && strcmp(cmd, "set_cookie") == 0) {
1347 g_free(cmd);
1348 cookie = a_Dpip_get_attr(Buf, BufSize, "cookie");
1349 host = a_Dpip_get_attr(Buf, BufSize, "host");
1350 path = a_Dpip_get_attr(Buf, BufSize, "path");
1351 p = a_Dpip_get_attr(Buf, BufSize, "port");
1352 port = strtol(p, NULL, 10);
1353 g_free(p);
1354
1355 Cookies_set(cookie, host, path, port);
1356
1357 g_free(path);
1358 g_free(host);
1359 g_free(cookie);
1360 return 2;
1361
1362 } else if (cmd && strcmp(cmd, "get_cookie") == 0) {
1363 g_free(cmd);
1364 scheme = a_Dpip_get_attr(Buf, BufSize, "scheme");
1365 host = a_Dpip_get_attr(Buf, BufSize, "host");
1366 path = a_Dpip_get_attr(Buf, BufSize, "path");
1367 p = a_Dpip_get_attr(Buf, BufSize, "port");
1368 port = strtol(p, NULL, 10);
1369 g_free(p);
1370
1371 cookie = Cookies_get(host, path, scheme, port);
1372 g_free(scheme);
1373 g_free(path);
1374 g_free(host);
1375
1451 } else if (strcmp(cmd, "set_cookie") == 0) {
1452 int st;
1453 char *date;
1454
1455 cookie = a_Dpip_get_attr_l(Buf, BufSize, "cookie");
1456 host = a_Dpip_get_attr_l(Buf, BufSize, "host");
1457 path = a_Dpip_get_attr_l(Buf, BufSize, "path");
1458 date = a_Dpip_get_attr_l(Buf, BufSize, "date");
1459
1460 st = Cookies_set(cookie, host, path, date);
1461
1462 dFree(cmd);
1463 cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "set_cookie_answer",
1464 st == 0 ? "ok" : "not set");
1465 a_Dpip_dsh_write_str(sh, 1, cmd);
1466
1467 dFree(date);
1468 dFree(path);
1469 dFree(host);
1470 dFree(cookie);
1471 ret = 2;
1472
1473 } else if (strcmp(cmd, "get_cookie") == 0) {
1474 char *scheme = a_Dpip_get_attr_l(Buf, BufSize, "scheme");
1475
1476 host = a_Dpip_get_attr_l(Buf, BufSize, "host");
1477 path = a_Dpip_get_attr_l(Buf, BufSize, "path");
1478
1479 cookie = Cookies_get(host, path, scheme);
1480 dFree(scheme);
1481 dFree(path);
1482 dFree(host);
1483
1484 dFree(cmd);
13761485 cmd = a_Dpip_build_cmd("cmd=%s cookie=%s", "get_cookie_answer", cookie);
13771486
1378 if (sock_handler_write_str(sh, cmd, 1)) {
1379 g_free(cookie);
1380 g_free(cmd);
1381 return 1;
1382 }
1383 g_free(cookie);
1384 g_free(cmd);
1385
1386 return 2;
1387 }
1388
1389 return 0;
1487 if (a_Dpip_dsh_write_str(sh, 1, cmd)) {
1488 ret = 1;
1489 } else {
1490 _MSG("a_Dpip_dsh_write_str: SUCCESS cmd={%s}\n", cmd);
1491 ret = 2;
1492 }
1493 dFree(cookie);
1494 }
1495 dFree(cmd);
1496
1497 return ret;
13901498 }
13911499
13921500 /* -- Termination handlers ----------------------------------------------- */
13961504 */
13971505 static void cleanup(void)
13981506 {
1399 Cookies_freeall();
1507 Cookies_save_and_free();
14001508 MSG("cleanup\n");
14011509 /* no more cleanup required */
14021510 }
14131521 /*
14141522 * -- MAIN -------------------------------------------------------------------
14151523 */
1416 int main (void) {
1417 struct sockaddr_un spun;
1418 int temp_sock_descriptor;
1419 int address_size;
1524 int main(void) {
1525 struct sockaddr_in sin;
1526 socklen_t address_size;
1527 ClientInfo *client;
1528 int sock_fd, code;
14201529 char *buf;
1421 int code;
1422 SockHandler *sh;
1530 Dsh *sh;
14231531
14241532 /* Arrange the cleanup function for terminations via exit() */
14251533 atexit(cleanup);
14391547 exit(1);
14401548
14411549 /* some OSes may need this... */
1442 address_size = sizeof(struct sockaddr_un);
1550 address_size = sizeof(struct sockaddr_in);
14431551
14441552 while (1) {
1445 temp_sock_descriptor =
1446 accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
1447 if (temp_sock_descriptor == -1) {
1553 sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&sin, &address_size);
1554 if (sock_fd == -1) {
14481555 perror("[accept]");
14491556 exit(1);
14501557 }
14511558
1452 /* create the SockHandler structure */
1453 sh = sock_handler_new(temp_sock_descriptor,temp_sock_descriptor,8*1024);
1559 /* create the Dsh structure */
1560 sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
1561 client = dNew(ClientInfo,1);
1562 client->sh = sh;
1563 client->status = 0;
14541564
14551565 while (1) {
14561566 code = 1;
1457 if ((buf = sock_handler_read(sh)) != NULL) {
1567 if ((buf = a_Dpip_dsh_read_token(sh, 1)) != NULL) {
14581568 /* Let's see what we fished... */
1459 code = srv_parse_buf(sh, buf, strlen(buf));
1460 }
1461 if (code == 1)
1569 _MSG(" buf = {%s}\n", buf);
1570 code = srv_parse_tok(sh, client, buf);
1571 dFree(buf);
1572 }
1573
1574 _MSG(" code = %d %s\n", code, code == 1 ? "EXIT" : "BREAK");
1575 if (code == 1) {
14621576 exit(1);
1463 else if (code == 2)
1577 } else if (code == 2) {
14641578 break;
1465 }
1466
1467 _MSG("Closing SockHandler\n");
1468 sock_handler_close(sh);
1469 sock_handler_free(sh);
1579 }
1580 }
1581
1582 _MSG("Closing Dsh\n");
1583 a_Dpip_dsh_close(sh);
1584 a_Dpip_dsh_free(sh);
1585 dFree(client);
14701586
14711587 }/*while*/
1588
1589 return 0;
14721590 }
14731591
14741592 #endif /* !DISABLE_COOKIES */
00 /*
11 * File: datauri.c
22 *
3 * Copyright (C) 2006 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * Filter dpi for the "data:" URI scheme (RFC 2397).
66 *
77 * This program is free software; you can redistribute it and/or modify
88 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
9 * the Free Software Foundation; either version 3 of the License, or
1010 * (at your option) any later version.
1111 */
1212
1414 #include <stdio.h>
1515 #include <stdlib.h>
1616 #include <string.h>
17 #include <glib.h>
17 #include <ctype.h>
18 #include <errno.h>
1819
1920 #include "../dpip/dpip.h"
2021 #include "dpiutil.h"
2223 /*
2324 * Debugging macros
2425 */
25 #define _MSG(fmt...)
26 #define MSG(fmt...) g_printerr("[datauri dpi]: " fmt)
26 #define SILENT 1
27 #define _MSG(...)
28 #if SILENT
29 #define MSG(...)
30 #else
31 #define MSG(...) fprintf(stderr, "[datauri dpi]: " __VA_ARGS__)
32 #endif
2733
2834 /*
2935 * Global variables
3036 */
31 static SockHandler *sh = NULL;
32
33
34
35 int b64decode(unsigned char* str)
36 {
37 unsigned char *cur, *start;
38 int d, dlast, phase;
39 unsigned char c;
40 static int table[256] = {
41 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
42 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
43 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
44 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
45 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
46 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
47 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
48 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
49 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
50 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
51 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
52 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
53 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
54 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
55 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
56 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
57 };
58
59 d = dlast = phase = 0;
60 start = str;
61 for (cur = str; *cur != '\0'; ++cur )
62 {
63 // jer: treat line endings as physical breaks.
64 //if (*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;}
65 d = table[(int)*cur];
66 if(d != -1)
67 {
68 switch(phase)
69 {
70 case 0:
71 ++phase;
72 break;
73 case 1:
74 c = ((dlast << 2) | ((d & 0x30) >> 4));
75 *str++ = c;
76 ++phase;
77 break;
78 case 2:
79 c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
80 *str++ = c;
81 ++phase;
82 break;
83 case 3:
84 c = (((dlast & 0x03 ) << 6) | d);
85 *str++ = c;
86 phase = 0;
87 break;
88 }
89 dlast = d;
90 }
91 }
92 *str = '\0';
93 return str - start;
37 static Dsh *sh = NULL;
38
39 static void b64strip_illegal_chars(unsigned char* str)
40 {
41 unsigned char *p, *s = str;
42
43 MSG("len=%d{%s}\n", strlen((char*)str), str);
44
45 for (p = s; (*p = *s); ++s) {
46 if (isalnum(*p) || strchr("+/=", *p))
47 ++p;
48 }
49
50 MSG("len=%d{%s}\n", strlen((char *)str), str);
51 }
52
53 static int b64decode(unsigned char* str)
54 {
55 unsigned char *cur, *start;
56 int d, dlast, phase;
57 unsigned char c;
58 static int table[256] = {
59 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
60 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
61 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
62 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
63 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
64 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
65 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
66 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
67 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
68 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
69 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
70 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
71 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
72 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
73 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
74 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
75 };
76
77 d = dlast = phase = 0;
78 start = str;
79 for (cur = str; *cur != '\0'; ++cur ) {
80 // jer: treat line endings as physical breaks.
81 //if (*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;}
82 d = table[(int)*cur];
83 if (d != -1) {
84 switch(phase) {
85 case 0:
86 ++phase;
87 break;
88 case 1:
89 c = ((dlast << 2) | ((d & 0x30) >> 4));
90 *str++ = c;
91 ++phase;
92 break;
93 case 2:
94 c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
95 *str++ = c;
96 ++phase;
97 break;
98 case 3:
99 c = (((dlast & 0x03 ) << 6) | d);
100 *str++ = c;
101 phase = 0;
102 break;
103 }
104 dlast = d;
105 }
106 }
107 *str = '\0';
108 return str - start;
94109 }
95110
96111 /* Modified from src/url.c --------------------------------------------------*/
99114 * Given an hex octet (e.g., e3, 2F, 20), return the corresponding
100115 * character if the octet is valid, and -1 otherwise
101116 */
102 static int Url_decode_hex_octet(const gchar *s)
103 {
104 gint hex_value;
105 gchar *tail, hex[3];
117 static int Url_decode_hex_octet(const char *s)
118 {
119 int hex_value;
120 char *tail, hex[3];
106121
107122 if (s && (hex[0] = s[0]) && (hex[1] = s[1])) {
108123 hex[2] = 0;
109124 hex_value = strtol(hex, &tail, 16);
110125 if (tail - hex == 2)
111 return hex_value;
126 return hex_value;
112127 }
113128 return -1;
114129 }
117132 * Parse possible hexadecimal octets in the URI path.
118133 * Returns a new allocated string.
119134 */
120 gchar *a_Url_decode_hex_str(const gchar *str, size_t *p_sz)
121 {
122 gchar *new_str, *dest;
135 char *a_Url_decode_hex_str(const char *str, size_t *p_sz)
136 {
137 char *new_str, *dest;
123138 int i, val;
124139
125140 if (!str) {
127142 return NULL;
128143 }
129144
130 dest = new_str = g_new(gchar, strlen(str) + 1);
145 dest = new_str = dNew(char, strlen(str) + 1);
131146 for (i = 0; str[i]; i++) {
132147 *dest++ = (str[i] == '%' && (val = Url_decode_hex_octet(str+i+1)) >= 0) ?
133148 i+=2, val : str[i];
134149 }
135 *dest = 0;
136
137 new_str = g_realloc(new_str, sizeof(gchar) * (dest - new_str + 1));
150 *dest = 0;
151
152 new_str = dRealloc(new_str, sizeof(char) * (dest - new_str + 1));
138153 *p_sz = (size_t)(dest - new_str);
139154 return new_str;
140155 }
144159 /*
145160 * Send decoded data to dillo in an HTTP envelope.
146161 */
147 void send_decoded_data(const char *url, const char *mime_type,
148 unsigned char *data, size_t data_sz)
162 static void send_decoded_data(const char *url, const char *mime_type,
163 unsigned char *data, size_t data_sz)
149164 {
150165 char *d_cmd;
151166
152167 /* Send dpip tag */
153168 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
154 sock_handler_write_str(sh, d_cmd, 1);
155 g_free(d_cmd);
169 a_Dpip_dsh_write_str(sh, 1, d_cmd);
170 dFree(d_cmd);
156171
157172 /* Send HTTP header. */
158 sock_handler_write_str(sh, "Content-type: ", 0);
159 sock_handler_write_str(sh, mime_type, 0);
160 sock_handler_write_str(sh, "\n\n", 1);
173 a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
174 a_Dpip_dsh_write_str(sh, 0, mime_type);
175 a_Dpip_dsh_write_str(sh, 1, "\n\n");
161176
162177 /* Send message */
163 sock_handler_write(sh, (char *)data, data_sz, 0);
164 }
165
166 void send_failure_message(const char *url, const char *mime_type,
167 unsigned char *data, size_t data_sz)
178 a_Dpip_dsh_write(sh, 0, (char *)data, data_sz);
179 }
180
181 static void send_failure_message(const char *url, const char *mime_type,
182 unsigned char *data, size_t data_sz)
168183 {
169184 char *d_cmd;
170185 char buf[1024];
171186
172 const char *msg =
187 const char *msg =
173188 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
174189 "<html><body>\n"
175190 "<hr><h1>Datauri dpi</h1><hr>\n"
178193
179194 /* Send dpip tag */
180195 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
181 sock_handler_write_str(sh, d_cmd, 1);
182 g_free(d_cmd);
196 a_Dpip_dsh_write_str(sh, 1, d_cmd);
197 dFree(d_cmd);
183198
184199 /* Send HTTP header. */
185 sock_handler_write_str(sh, "Content-type: ", 0);
186 sock_handler_write_str(sh, msg_mime_type, 0);
187 sock_handler_write_str(sh, "\n\n", 1);
200 a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
201 a_Dpip_dsh_write_str(sh, 0, msg_mime_type);
202 a_Dpip_dsh_write_str(sh, 1, "\n\n");
188203
189204 /* Send message */
190 sock_handler_write_str(sh, msg, 0);
205 a_Dpip_dsh_write_str(sh, 0, msg);
191206
192207 /* send some debug info */
193 g_snprintf(buf, 1024, "mime_type: %s<br>data size: %d<br>data: %s<br>",
194 mime_type, (int)data_sz, data);
195 sock_handler_write_str(sh, buf, 0);
208 snprintf(buf, 1024, "mime_type: %s<br>data size: %d<br>data: %s<br>",
209 mime_type, (int)data_sz, data);
210 a_Dpip_dsh_write_str(sh, 0, buf);
196211
197212 /* close page */
198 sock_handler_write_str(sh, "</body></html>", 0);
213 a_Dpip_dsh_write_str(sh, 0, "</body></html>");
199214 }
200215
201216 /*
202217 * Get mime type from the data URI.
203 * todo: there's no point in handling "charset" because current dillo
218 * TODO: there's no point in handling "charset" because current dillo
204219 * only handles ISO-LATIN-1. The FLTK2 version (utf-8) could use it in the
205220 * future.
206221 */
207 char *datauri_get_mime(char *url)
222 static char *datauri_get_mime(char *url)
208223 {
209224 char buf[256];
210225 char *mime_type = NULL, *p;
211226 size_t len = 0;
212227
213 if (g_strncasecmp(url, "data:", 5) == 0) {
228 if (dStrncasecmp(url, "data:", 5) == 0) {
214229 if ((p = strchr(url, ',')) && p - url < 256) {
215230 url += 5;
216231 len = p - url;
217232 strncpy(buf, url, len);
218233 buf[len] = 0;
219234 /* strip ";base64" */
220 if (len >= 7 && g_strcasecmp(buf + len - 7, ";base64") == 0) {
235 if (len >= 7 && dStrcasecmp(buf + len - 7, ";base64") == 0) {
221236 len -= 7;
222237 buf[len] = 0;
223238 }
225240
226241 /* that's it, now handle omitted types */
227242 if (len == 0) {
228 mime_type = g_strdup("text/plain;charset=US-ASCII");
229 } else if (!g_strncasecmp(buf, "charset", 7)) {
230 mime_type = g_strconcat("text/plain", buf, NULL);
243 mime_type = dStrdup("text/plain;charset=US-ASCII");
244 } else if (!dStrncasecmp(buf, "charset", 7)) {
245 mime_type = dStrconcat("text/plain", buf, NULL);
231246 } else {
232 mime_type = g_strdup(buf);
247 mime_type = dStrdup(buf);
233248 }
234249 }
235250
239254 /*
240255 * Return a decoded data string.
241256 */
242 unsigned char *datauri_get_data(char *url, size_t *p_sz)
257 static unsigned char *datauri_get_data(char *url, size_t *p_sz)
243258 {
244259 char *p;
245260 int is_base64 = 0;
246261 unsigned char *data = NULL;
247262
248263 if ((p = strchr(url, ',')) && p - url >= 12 && /* "data:;base64" */
249 g_strncasecmp(p - 7, ";base64", 7) == 0) {
264 dStrncasecmp(p - 7, ";base64", 7) == 0) {
250265 is_base64 = 1;
251266 }
252267
253268 if (p) {
254269 ++p;
255270 if (is_base64) {
256 data = (unsigned char *)g_strdup(p);
271 data = (unsigned char *)Unescape_uri_str(p);
272 b64strip_illegal_chars(data);
257273 *p_sz = (size_t) b64decode(data);
258274 } else {
259275 data = (unsigned char *)a_Url_decode_hex_str(p, p_sz);
260276 }
261277 } else {
262 data = g_strdup("");
278 data = (unsigned char *)dStrdup("");
263279 *p_sz = 0;
264280 }
265281
271287 */
272288 int main(void)
273289 {
274 gchar *dpip_tag = NULL, *cmd = NULL, *url = NULL, *mime_type;
290 char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *mime_type;
275291 unsigned char *data;
292 int rc;
276293 size_t data_size = 0;
277294
278295 /* Initialize the SockHandler */
279 sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
280
281 /* wget may need to write a temporary file... */
282 chdir("/tmp");
296 sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
297
298 rc = chdir("/tmp");
299 if (rc == -1) {
300 MSG("paths: error changing directory to /tmp: %s\n",
301 dStrerror(errno));
302 }
303
304 /* Authenticate our client... */
305 if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
306 a_Dpip_check_auth(dpip_tag) < 0) {
307 MSG("can't authenticate request: %s\n", dStrerror(errno));
308 a_Dpip_dsh_close(sh);
309 return 1;
310 }
311 dFree(dpip_tag);
283312
284313 /* Read the dpi command from STDIN */
285 dpip_tag = sock_handler_read(sh);
314 dpip_tag = a_Dpip_dsh_read_token(sh, 1);
286315 MSG("[%s]\n", dpip_tag);
287316
288 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
289 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
317 cmd = a_Dpip_get_attr(dpip_tag, "cmd");
318 url = a_Dpip_get_attr(dpip_tag, "url");
290319 if (!cmd || !url) {
291320 MSG("Error, cmd=%s, url=%s\n", cmd, url);
292321 exit (EXIT_FAILURE);
297326 data = datauri_get_data(url, &data_size);
298327
299328 MSG("mime_type: %s\n", mime_type);
300 MSG("data_size: %d\n", data_size);
329 MSG("data_size: %d\n", (int)data_size);
301330 MSG("data: {%s}\n", data);
302331
303332 if (mime_type && data) {
308337 send_failure_message(url, mime_type, data, data_size);
309338 }
310339
311 g_free(data);
312 g_free(mime_type);
313 g_free(url);
314 g_free(cmd);
315 g_free(dpip_tag);
340 dFree(data);
341 dFree(mime_type);
342 dFree(url);
343 dFree(cmd);
344 dFree(dpip_tag);
316345
317346 /* Finish the SockHandler */
318 sock_handler_close(sh);
319 sock_handler_free(sh);
347 a_Dpip_dsh_close(sh);
348 a_Dpip_dsh_free(sh);
320349
321350 return 0;
322351 }
+0
-310
dpi/downloads-old.c less more
0 /*
1 * Downloads server (chat version).
2 *
3 * NOTE: A simple downloads dpi that illustrates how to make a dpi-server.
4 *
5 * It uses wget to download a link. This has been tested with wget 1.8.1
6 * The server accepts multiple connections once it has been started.
7 * If there are no requests within 5 minutes it waits for all child processes
8 * to finish and then it exits.
9 *
10 * Copyright 2002-2004 Jorge Arellano Cid <jcid@dillo.org>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 */
18
19
20 #include <config.h>
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 #include <sys/un.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <signal.h>
31 #include <sys/wait.h>
32 #include <errno.h>
33 #include <sys/time.h>
34 #include <glib.h>
35 #include "../dpip/dpip.h"
36 #include "dpiutil.h"
37
38 /*
39 * Debugging macros
40 */
41 #define _MSG(fmt...)
42 #define _CMSG(fmt...)
43 #define MSG(fmt...) g_print("[downloads dpi]: " fmt)
44 #define CMSG(fmt...) g_print("[downloads (child)]: " fmt)
45
46 pid_t origpid, fpid;
47
48 /*---------------------------------------------------------------------------*/
49
50 /*
51 * Make a new name and place it in 'dl_dest'.
52 */
53 static void make_new_name(gchar **dl_dest, const gchar *url)
54 {
55 GString *gstr = g_string_new(*dl_dest);
56 gint idx = gstr->len;
57
58 if (gstr->str[idx - 1] != '/'){
59 g_string_append_c(gstr, '/');
60 ++idx;
61 }
62
63 /* Use a mangled url as name */
64 g_string_append(gstr, url);
65 for ( ; idx < gstr->len; ++idx)
66 if (!isalnum(gstr->str[idx]))
67 gstr->str[idx] = '_';
68
69 /* free memory */
70 g_free(*dl_dest);
71 *dl_dest = gstr->str;
72 g_string_free(gstr, FALSE);
73 }
74
75
76 /*---------------------------------------------------------------------------*/
77
78 /*
79 * SIGCHLD handler
80 */
81 static void sigchld(int sig)
82 {
83 MSG("received sigchld, pid=%d\n", origpid);
84 fflush(stderr);
85 while (waitpid(0, NULL, WNOHANG) > 0) {
86 }
87 }
88
89 /*
90 * Establish SIGCHLD handler
91 */
92 static void est_sigchld(void)
93 {
94 struct sigaction act;
95 sigset_t block;
96
97 sigemptyset(&block);
98 sigaddset(&block, SIGCHLD);
99 act.sa_handler = sigchld;
100 act.sa_mask = block;
101 act.sa_flags = SA_NOCLDSTOP;
102 sigaction(SIGCHLD, &act, NULL);
103 }
104
105 /*
106 * Read a single line from a socket and store it in a GString.
107 */
108 static ssize_t readline(int socket, GString ** msg)
109 {
110 ssize_t st;
111 gchar buf[16384], *aux;
112
113 /* can't use fread() */
114 do
115 st = read(socket, buf, 16384);
116 while (st < 0 && errno == EINTR);
117
118 if (st == -1)
119 MSG("readline, %s\n", strerror(errno));
120
121 if (st > 0) {
122 aux = g_strndup(buf, (guint)st);
123 g_string_assign(*msg, aux);
124 g_free(aux);
125 } else {
126 g_string_assign(*msg, "");
127 }
128
129 return st;
130 }
131
132
133 /*!
134 * Main download process.
135 */
136 int main(void)
137 {
138 int new_socket, ns;
139 socklen_t csz;
140 ssize_t rdlen;
141 struct sockaddr_un clnt_addr;
142
143 //char *wget_cmd = "wget --no-parent -t 1 -nc -k -nH --cut-dirs=30 -P";
144 //char *wget_cmd = "wget -t 1 -nH -P"; --doesn't rename, +other problems
145 char *wget_cmd = "wget -O - --load-cookies $HOME/.dillo/cookies.txt ";
146
147 char *url = NULL, *esc_url = NULL, *dl_dest = NULL, *cmd = NULL;
148 GString *gs_dl_cmd, *tag;
149 fd_set active_set, selected_set;
150 struct timeval tout;
151 sigset_t blockSC;
152
153 origpid = getpid();
154 fpid = origpid;
155 MSG("v1.1 started (pid=%u)\n", origpid);
156 fflush(stdout);
157 sigemptyset(&blockSC);
158 sigaddset(&blockSC, SIGCHLD);
159 est_sigchld();
160
161 csz = (socklen_t) sizeof(clnt_addr);
162 FD_ZERO(&active_set);
163 FD_SET(STDIN_FILENO, &active_set);
164 while (1) {
165 MSG("before select\n");
166 do {
167 /* exit if there are no download requests after this time */
168 tout.tv_sec = TOUT;
169 tout.tv_usec = 0;
170 selected_set = active_set;
171 ns = select(STDIN_FILENO + 1, &selected_set, NULL, NULL, &tout);
172 } while (ns == -1 && errno == EINTR);
173 MSG("after select\n");
174
175 if (ns == -1) {
176 MSG("select, %s\n", strerror(errno));
177 exit(1);
178
179 } else if (ns == 0) { /* exit if no download requests */
180 close(STDIN_FILENO);
181 printf("downloads server %d:Terminating.\n"
182 "Waiting for children to finish\n", origpid);
183 fflush(stdout);
184 /* BUG? Any further calls to downloads server will be queued by dpid
185 * until all the children have finished. This could be a long time */
186 while (waitpid(-1, NULL, 0) >= 0) {
187 }
188 printf("\n\nDL_SRV %d: EXITING\n", origpid);
189 fflush(stdout);
190 exit(0);
191
192 } else {
193 /* accept the request */
194 do {
195 new_socket = accept(STDIN_FILENO, (struct sockaddr *) &clnt_addr,
196 &csz);
197 } while (new_socket == -1 && errno == EINTR);
198
199 if (new_socket == -1) {
200 MSG("accept, %s\n", strerror(errno));
201 exit(1);
202 }
203 }
204
205 sigprocmask(SIG_BLOCK, &blockSC, NULL);
206 tag = g_string_new(NULL);
207 MSG("before readline\n");
208 rdlen = readline(new_socket, &tag);
209 MSG("after readline\n");
210 MSG("[%s]\n", tag->str);
211
212 if ((cmd = a_Dpip_get_attr(tag->str, (size_t)tag->len, "cmd")) == NULL) {
213 MSG("Failed to parse 'cmd' in %s\n", tag->str);
214 exit(1);
215 }
216 if (strcmp(cmd, "DpiBye") == 0) {
217 MSG("got DpiBye, terminating.\n");
218 exit(0);
219 }
220 if (strcmp(cmd, "download") != 0) {
221 MSG("unknown command: '%s'. Aborting.\n", cmd);
222 exit(1);
223 }
224 g_free(cmd);
225
226 fpid = fork();
227 if (fpid == 0) {
228 pid_t ppid, cpid;
229 FILE *in_stream, *out_stream;
230 gchar buf[4096];
231 struct stat sb;
232 size_t n;
233
234 origpid = cpid = getpid();
235 ppid = getppid();
236 CMSG("pid=%u, from parent=%u\n", (unsigned)cpid, (unsigned)ppid);
237 if (!(url = a_Dpip_get_attr(tag->str,(size_t)tag->len, "url"))){
238 CMSG("Failed to parse 'url' in %s\n", tag->str);
239 exit(1);
240 }
241
242 dl_dest = a_Dpip_get_attr(tag->str, (size_t)tag->len, "destination");
243 if (dl_dest == NULL) {
244 CMSG("Failed to parse 'destination' in %s\n", tag->str);
245 exit(1);
246 }
247
248 CMSG("url=%s, dl_dest=%s\n", url, dl_dest);
249
250 /* 'dl_dest' may be a directory */
251 if (stat(dl_dest, &sb) == 0 && S_ISDIR(sb.st_mode))
252 make_new_name(&dl_dest, url);
253
254 /* open the target stream */
255 if ((out_stream = fopen(dl_dest, "w")) == NULL) {
256 CMSG("%s\n", strerror(errno));
257 exit(1);
258 }
259
260 /* make the download command string */
261 gs_dl_cmd = g_string_new(NULL);
262 /* escape "'" character for the shell */
263 esc_url = Escape_uri_str(url, "'");
264 /* avoid malicious SMTP relaying with FTP urls */
265 if (g_strncasecmp(esc_url, "ftp:/", 5) == 0)
266 Filter_smtp_hack(esc_url);
267 g_string_sprintf(gs_dl_cmd, "%s '%s'", wget_cmd, esc_url);
268 CMSG(" cmd: %s\n", gs_dl_cmd->str);
269 CMSG(" to: %s\n", dl_dest);
270
271 g_free(dl_dest);
272 g_free(esc_url);
273 g_free(url);
274
275 CMSG("pid=%u, Running: %s\n", cpid, gs_dl_cmd->str);
276
277 /* fork through popen */
278 if ((in_stream = popen(gs_dl_cmd->str, "r")) == NULL) {
279 CMSG("popen, %s\n", strerror(errno));
280 exit(1);
281 }
282
283 /* do the file transfer */
284 while ((n = fread (buf, 1, 4096, in_stream)) > 0)
285 fwrite(buf, 1, n, out_stream);
286
287 /* close transfer */
288 if (pclose(in_stream) != 0)
289 CMSG("pclose, %s\n", strerror(errno));
290 if (fclose(out_stream) != 0)
291 CMSG("fclose, %s\n", strerror(errno));
292
293 g_string_free(gs_dl_cmd, TRUE);
294
295
296 if (close(new_socket) == -1) {
297 CMSG("close, %s\n", strerror(errno));
298 exit(EXIT_FAILURE);
299 }
300
301 CMSG("pid=%u, done!\n", cpid);
302 exit(0);
303 }
304 g_string_free(tag, TRUE);
305 sigprocmask(SIG_UNBLOCK, &blockSC, NULL);
306 close(new_socket);
307 }
308 }
309
00 /*
11 * File: downloads.cc
22 *
3 * Copyright (C) 2005 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1111 /*
12 * A FLTK2-based GUI for the downloads dpi (dillo plugin).
12 * A FLTK-based GUI for the downloads dpi (dillo plugin).
1313 */
1414
1515 #include <stdio.h>
2828 #include <sys/un.h>
2929 #include <sys/wait.h>
3030
31 #include <glib.h>
32
33 #include <fltk/run.h>
34 #include <fltk/Window.h>
35 #include <fltk/Widget.h>
36 #include <fltk/damage.h>
37 #include <fltk/Box.h>
38 #include <fltk/draw.h>
39 #include <fltk/HighlightButton.h>
40 #include <fltk/PackedGroup.h>
41 #include <fltk/ScrollGroup.h>
42 #include <fltk/ask.h>
43 #include <fltk/file_chooser.h>
31 #include <FL/Fl.H>
32 #include <FL/fl_draw.H>
33 #include <FL/Fl_File_Chooser.H>
34 #include <FL/Fl_Window.H>
35 #include <FL/Fl_Widget.H>
36 #include <FL/Fl_Group.H>
37 #include <FL/Fl_Scroll.H>
38 #include <FL/Fl_Pack.H>
39 #include <FL/Fl_Box.H>
40 #include <FL/Fl_Button.H>
4441
4542 #include "dpiutil.h"
4643 #include "../dpip/dpip.h"
4744
48 using namespace fltk;
49
5045 /*
5146 * Debugging macros
5247 */
53 #define _MSG(fmt...)
54 #define MSG(fmt...) g_print("[downloads dpi]: " fmt)
48 #define _MSG(...)
49 #define MSG(...) printf("[downloads dpi]: " __VA_ARGS__)
5550
5651 /*
5752 * Internal types
7065
7166 // ProgressBar widget --------------------------------------------------------
7267
73 // class FL_API ProgressBar : public Widget {
74 class ProgressBar : public Widget {
68 class ProgressBar : public Fl_Box {
7569 protected:
76 double mMin;
77 double mMax;
78 double mPresent;
79 double mStep;
80 bool mShowPct, mShowMsg;
81 char mMsg[64];
82 Color mTextColor;
83 void draw();
70 double mMin;
71 double mMax;
72 double mPresent;
73 double mStep;
74 bool mShowPct, mShowMsg;
75 char mMsg[64];
76 Fl_Color mTextColor;
77 void draw();
8478 public:
85 ProgressBar(int x, int y, int w, int h, const char *lbl = 0);
86 void range(double min, double max, double step = 1) {
87 mMin = min; mMax = max; mStep = step;
88 };
89 void step(double step) { mPresent += step; redraw(); };
90 void move(double step);
91 double minimum() { return mMin; }
92 double maximum() { return mMax; }
93 void minimum(double nm) { mMin = nm; };
94 void maximum(double nm) { mMax = nm; };
95 double position () { return mPresent; }
96 double step() { return mStep; }
97 void position(double pos) { mPresent = pos; redraw(); }
98 void showtext(bool st) { mShowPct = st; }
99 void message(char *msg) { mShowMsg = true; strncpy(mMsg,msg,63); redraw(); }
100 bool showtext() { return mShowPct; }
101 void text_color(Color col) { mTextColor = col; }
102 Color text_color() { return mTextColor; }
79 ProgressBar(int x, int y, int w, int h, const char *lbl = 0);
80 void range(double min, double max, double step = 1) {
81 mMin = min; mMax = max; mStep = step;
82 };
83 void step(double step) { mPresent += step; redraw(); };
84 void move(double step);
85 double minimum() { return mMin; }
86 double maximum() { return mMax; }
87 void minimum(double nm) { mMin = nm; };
88 void maximum(double nm) { mMax = nm; };
89 double position () { return mPresent; }
90 double step() { return mStep; }
91 void position(double pos) { mPresent = pos; redraw(); }
92 void showtext(bool st) { mShowPct = st; }
93 void message(char *msg) { mShowMsg = true; strncpy(mMsg,msg,63); redraw(); }
94 bool showtext() { return mShowPct; }
95 void text_color(Fl_Color col) { mTextColor = col; }
96 Fl_Color text_color() { return mTextColor; }
10397 };
10498
10599 // Download-item class -------------------------------------------------------
108102 enum {
109103 ST_newline, ST_number, ST_discard, ST_copy
110104 };
111
105
112106 pid_t mPid;
113107 int LogPipe[2];
114108 char *shortname, *fullname;
124118 int WgetStatus;
125119
126120 int gw, gh;
127 Group *group;
121 Fl_Group *group;
128122 ProgressBar *prBar;
129 HighlightButton *prButton;
130 Widget *prTitle, *prGot, *prSize, *prRate, *pr_Rate, *prETA, *prETAt;
123 Fl_Button *prButton;
124 Fl_Widget *prTitle, *prGot, *prSize, *prRate, *pr_Rate, *prETA, *prETAt;
131125
132126 public:
133127 DLItem(const char *full_filename, const char *url, DLAction action);
135129 void child_init();
136130 void father_init();
137131 void update_size(int new_sz);
138 void log_text_add(char *buf, ssize_t st);
132 void log_text_add(const char *buf, ssize_t st);
139133 void log_text_show();
140134 void abort_dl();
141135 void prButton_cb();
142136 pid_t pid() { return mPid; }
143137 void pid(pid_t p) { mPid = p; }
144138 void child_finished(int status);
145 void status_msg(char *msg) { prBar->message(msg); }
146 Widget *get_widget() { return group; }
139 void status_msg(const char *msg) { prBar->message((char*)msg); }
140 Fl_Widget *get_widget() { return group; }
147141 int widget_done() { return WidgetDone; }
148142 void widget_done(int val) { WidgetDone = val; }
149143 int updates_done() { return UpdatesDone; }
178172
179173 class DLWin {
180174 DLItemList *mDList;
181 Window *mWin;
182 ScrollGroup *mScroll;
183 PackedGroup *mPG;
175 Fl_Window *mWin;
176 Fl_Scroll *mScroll;
177 Fl_Pack *mPG;
184178
185179 public:
186180 DLWin(int ww, int wh);
222216 }
223217
224218 ProgressBar::ProgressBar(int x, int y, int w, int h, const char *lbl)
225 : Widget(x, y, w, h, lbl)
219 : Fl_Box(x, y, w, h, lbl)
226220 {
227221 mMin = mPresent = 0;
228222 mMax = 100;
229223 mShowPct = true;
230224 mShowMsg = false;
231 box(DOWN_BOX);
232 selection_color(BLUE);
233 color(WHITE);
234 textcolor(RED);
225 box(FL_THIN_UP_BOX);
226 color(FL_WHITE);
235227 }
236228
237229 void ProgressBar::draw()
238230 {
239 drawstyle(style(), flags());
240 if (damage() & DAMAGE_ALL)
241 draw_box();
242 Rectangle r(w(), h());
243 box()->inset(r);
231 struct Rectangle {
232 int x, y, w, h;
233 };
234
235 //drawstyle(style(), flags());
236 draw_box();
237 Rectangle r = {x(), y(), w(), h()};
244238 if (mPresent > mMax)
245239 mPresent = mMax;
246240 if (mPresent < mMin)
247241 mPresent = mMin;
248242 double pct = (mPresent - mMin) / mMax;
249243
250 if (vertical()) {
251 int barHeight = int (r.h() * pct + .5);
252 r.y(r.y() + r.h() - barHeight);
253 r.h(barHeight);
254 } else {
255 r.w(int (r.w() * pct + .5));
256 }
257
258 setcolor(selection_color());
259
260 if (mShowPct) {
261 fillrect(r);
262 } else {
263 Rectangle r2(int (r.w() * pct), 0, int (w() * .1), h());
264 push_clip(r2);
265 fillrect(r);
266 pop_clip();
267 }
244 r.w = r.w * pct + .5;
245 fl_rectf(r.x, r.y, r.w, r.h, FL_BLUE);
268246
269247 if (mShowMsg) {
270 setcolor(textcolor());
271 setfont(this->labelfont(), this->labelsize());
272 drawtext(mMsg, Rectangle(w(), h()), ALIGN_CENTER);
248 fl_color(FL_RED);
249 fl_font(this->labelfont(), this->labelsize());
250 fl_draw(mMsg, x(), y(), w(), h(), FL_ALIGN_CENTER);
273251 } else if (mShowPct) {
274252 char buffer[30];
275253 sprintf(buffer, "%d%%", int (pct * 100 + .5));
276 setcolor(textcolor());
277 setfont(this->labelfont(), this->labelsize());
278 drawtext(buffer, Rectangle(w(), h()), ALIGN_CENTER);
254 fl_color(FL_RED);
255 fl_font(this->labelfont(), this->labelsize());
256 fl_draw(buffer, x(), y(), w(), h(), FL_ALIGN_CENTER);
279257 }
280258 }
281259
282260
283261 // Download-item class -------------------------------------------------------
284262
285 static void prButton_scb(Widget *, void *cb_data)
263 static void prButton_scb(Fl_Widget *, void *cb_data)
286264 {
287265 DLItem *i = (DLItem *)cb_data;
288266
292270 DLItem::DLItem(const char *full_filename, const char *url, DLAction action)
293271 {
294272 struct stat ss;
295 char *p, *esc_url;
273 const char *p;
274 char *esc_url;
296275
297276 if (pipe(LogPipe) < 0) {
298 MSG("pipe, %s\n", strerror(errno));
277 MSG("pipe, %s\n", dStrerror(errno));
299278 return;
300279 }
301280 /* Set FD to background */
302281 fcntl(LogPipe[0], F_SETFL,
303282 O_NONBLOCK | fcntl(LogPipe[0], F_GETFL));
304283
305 fullname = strdup(full_filename);
284 fullname = dStrdup(full_filename);
306285 p = strrchr(fullname, '/');
307 shortname = (p) ? strdup(p + 1) : strdup("??");
286 shortname = (p) ? dStrdup(p + 1) : dStrdup("??");
308287 p = strrchr(full_filename, '/');
309 target_dir= p ? g_strndup(full_filename,p-full_filename+1) : g_strdup("??");
288 target_dir= p ? dStrndup(full_filename,p-full_filename+1) : dStrdup("??");
310289
311290 log_len = 0;
312291 log_max = 0;
318297 // Init value. Reset later, upon the first data bytes arrival
319298 init_time = time(NULL);
320299
300 twosec_time = onesec_time = init_time;
301
321302 // BUG:? test a URL with ' inside.
322303 /* escape "'" character for the shell. Is it necessary? */
323304 esc_url = Escape_uri_str(url, "'");
324305 /* avoid malicious SMTP relaying with FTP urls */
325 if (g_strncasecmp(esc_url, "ftp:/", 5) == 0)
306 if (dStrncasecmp(esc_url, "ftp:/", 5) == 0)
326307 Filter_smtp_hack(esc_url);
327308 dl_argv = new char*[8];
328309 int i = 0;
329 dl_argv[i++] = "wget";
310 dl_argv[i++] = (char*)"wget";
330311 if (action == DL_CONTINUE) {
331312 if (stat(fullname, &ss) == 0)
332313 init_bytesize = (int)ss.st_size;
333 dl_argv[i++] = "-c";
334 }
335 dl_argv[i++] = "--load-cookies";
336 dl_argv[i++] = g_strconcat(g_get_home_dir(), "/.dillo/cookies.txt", NULL);
337 dl_argv[i++] = "-O";
314 dl_argv[i++] = (char*)"-c";
315 }
316 dl_argv[i++] = (char*)"--load-cookies";
317 dl_argv[i++] = dStrconcat(dGethomedir(), "/.dillo/cookies.txt", NULL);
318 dl_argv[i++] = (char*)"-O";
338319 dl_argv[i++] = fullname;
339 dl_argv[i++] = esc_url; //g_strdup_printf("'%s'", esc_url);
320 dl_argv[i++] = esc_url;
340321 dl_argv[i++] = NULL;
341 //g_free(esc_url);
342322
343323 DataDone = 0;
344324 LogDone = 0;
347327 WidgetDone = 0;
348328 WgetStatus = -1;
349329
350 gw = 470, gh = 70;
351 group = new Group(0,0,gw,gh);
330 gw = 400, gh = 70;
331 group = new Fl_Group(0,0,gw,gh);
352332 group->begin();
353 prTitle = new Widget(24, 7, 290, 23, shortname);
354 prTitle->box(fltk::RSHADOW_BOX);
355 prTitle->align(ALIGN_LEFT|ALIGN_INSIDE|ALIGN_CLIP);
333 prTitle = new Fl_Box(24, 7, 290, 23);
334 prTitle->box(FL_RSHADOW_BOX);
335 prTitle->color(FL_WHITE);
336 prTitle->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CLIP);
337 prTitle->copy_label(shortname);
356338 // Attach this 'log_text' to the tooltip
357339 log_text_add("Target File: ", 13);
358340 log_text_add(fullname, strlen(fullname));
359341 log_text_add("\n\n", 2);
360342
361343 prBar = new ProgressBar(24, 40, 92, 20);
362 prBar->box(BORDER_BOX); // ENGRAVED_BOX
344 prBar->box(FL_THIN_UP_BOX);
363345 prBar->tooltip("Progress Status");
364346
365 int ix = 122, iy = 36, iw = 50, ih = 14;
366 Widget *o = new Widget(ix,iy,iw,ih, "Got");
367 o->box(RFLAT_BOX);
368 o->color((Color)0xc0c0c000);
347 int ix = 122, iy = 37, iw = 50, ih = 14;
348 Fl_Widget *o = new Fl_Box(ix,iy,iw,ih, "Got");
349 o->box(FL_RFLAT_BOX);
350 o->color(FL_DARK2);
351 o->labelsize(12);
369352 o->tooltip("Downloaded Size");
370 prGot = new Widget(ix,iy+14,iw,ih, "0KB");
371 prGot->labelcolor((Color)0x6c6cbd00);
372 prGot->box(fltk::NO_BOX);
353 prGot = new Fl_Box(ix,iy+14,iw,ih, "0KB");
354 prGot->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
355 prGot->labelcolor(FL_BLUE);
356 prGot->labelsize(12);
357 prGot->box(FL_NO_BOX);
373358
374359 ix += iw;
375 o = new Widget(ix,iy,iw,ih, "Size");
376 o->box(RFLAT_BOX);
377 o->color((Color)0xc0c0c000);
360 o = new Fl_Box(ix,iy,iw,ih, "Size");
361 o->box(FL_RFLAT_BOX);
362 o->color(FL_DARK2);
363 o->labelsize(12);
378364 o->tooltip("Total Size");
379 prSize = new Widget(ix,iy+14,iw,ih, "??");
380 prSize->box(fltk::NO_BOX);
365 prSize = new Fl_Box(ix,iy+14,iw,ih, "??");
366 prSize->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
367 prSize->labelsize(12);
368 prSize->box(FL_NO_BOX);
381369
382370 ix += iw;
383 o = new Widget(ix,iy,iw,ih, "Rate");
384 o->box(RFLAT_BOX);
385 o->color((Color)0xc0c0c000);
371 o = new Fl_Box(ix,iy,iw,ih, "Rate");
372 o->box(FL_RFLAT_BOX);
373 o->color(FL_DARK2);
374 o->labelsize(12);
386375 o->tooltip("Current transfer Rate (KBytes/sec)");
387 prRate = new Widget(ix,iy+14,iw,ih, "??");
388 prRate->box(fltk::NO_BOX);
376 prRate = new Fl_Box(ix,iy+14,iw,ih, "??");
377 prRate->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
378 prRate->labelsize(12);
379 prRate->box(FL_NO_BOX);
389380
390381 ix += iw;
391 o = new Widget(ix,iy,iw,ih, "~Rate");
392 o->box(RFLAT_BOX);
393 o->color((Color)0xc0c0c000);
382 o = new Fl_Box(ix,iy,iw,ih, "~Rate");
383 o->box(FL_RFLAT_BOX);
384 o->color(FL_DARK2);
385 o->labelsize(12);
394386 o->tooltip("Average transfer Rate (KBytes/sec)");
395 pr_Rate = new Widget(ix,iy+14,iw,ih, "??");
396 pr_Rate->box(fltk::NO_BOX);
387 pr_Rate = new Fl_Box(ix,iy+14,iw,ih, "??");
388 pr_Rate->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
389 pr_Rate->labelsize(12);
390 pr_Rate->box(FL_NO_BOX);
397391
398392 ix += iw;
399 prETAt = o = new Widget(ix,iy,iw,ih, "ETA");
400 o->box(RFLAT_BOX);
401 o->color((Color)0xc0c0c000);
393 prETAt = o = new Fl_Box(ix,iy,iw,ih, "ETA");
394 o->box(FL_RFLAT_BOX);
395 o->color(FL_DARK2);
396 o->labelsize(12);
402397 o->tooltip("Estimated Time of Arrival");
403 prETA = new Widget(ix,iy+14,iw,ih, "??");
404 prETA->box(fltk::NO_BOX);
405
406 //ix += 50;
407 //prButton = new HighlightButton(ix, 41, 38, 19, "Stop");
408 prButton = new HighlightButton(328, 9, 38, 19, "Stop");
398 prETA = new Fl_Box(ix,iy+14,iw,ih, "??");
399 prETA->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
400 prETA->labelsize(12);
401 prETA->box(FL_NO_BOX);
402
403 prButton = new Fl_Button(326, 9, 44, 19, "Stop");
409404 prButton->tooltip("Stop this transfer");
410 prButton->box(UP_BOX);
411 prButton->clear_tab_to_focus();
405 prButton->box(FL_UP_BOX);
406 prButton->clear_visible_focus();
412407 prButton->callback(prButton_scb, this);
413408
414 //group->resizable(group);
415 group->box(ROUND_UP_BOX);
409 group->box(FL_ROUNDED_BOX);
416410 group->end();
417411 }
418412
419413 DLItem::~DLItem()
420414 {
421415 free(shortname);
422 g_free(fullname);
423 g_free(target_dir);
416 dFree(fullname);
417 dFree(target_dir);
424418 free(log_text);
425419 int idx = (strcmp(dl_argv[1], "-c")) ? 2 : 3;
426 g_free(dl_argv[idx]);
427 g_free(dl_argv[idx+3]);
428 delete(dl_argv);
420 dFree(dl_argv[idx]);
421 dFree(dl_argv[idx+3]);
422 delete [] dl_argv;
429423
430424 delete(group);
431425 }
437431 {
438432 if (!log_done()) {
439433 close(LogPipe[0]);
440 remove_fd(LogPipe[0]);
434 Fl::remove_fd(LogPipe[0]);
441435 log_done(1);
442436 // Stop wget
443437 if (!fork_done())
476470 else
477471 snprintf(num, 64, "%.0fKB", (float)newsize / 1024);
478472 prSize->copy_label(num);
479 prSize->redraw();
480 }
481
482 void DLItem::log_text_add(char *buf, ssize_t st)
483 {
484 char *p, *q, *d, num[64];
473 }
474
475 void DLItem::log_text_add(const char *buf, ssize_t st)
476 {
477 const char *p;
478 char *q, *d, num[64];
485479
486480 // Make room...
487481 if (log_len + st >= log_max) {
510504 if (isdigit(*q++ = *p)) {
511505 // keep here
512506 } else if (*p == 'K') {
513 for(--q; isdigit(q[-1]); --q); log_state = ST_discard;
507 for (--q; isdigit(q[-1]); --q) ; log_state = ST_discard;
514508 } else {
515509 log_state = ST_copy;
516510 }
539533 total_bytesize = strtol (num, NULL, 10);
540534 // Update displayed size
541535 update_prSize(total_bytesize);
536
537 // WORKAROUND: For unknown reasons a redraw is needed here for some
538 // machines --jcid
539 group->redraw();
542540 }
543541 }
544542
545543 // Show we're connecting...
546544 if (curr_bytesize == 0) {
547 prTitle->label("Connecting...");
548 prTitle->redraw();
545 prTitle->copy_label("Connecting...");
549546 }
550547 }
551548
552549 ///
553550 void DLItem::log_text_show()
554551 {
555 fprintf(stderr, "\nStored Log:\n%s", log_text);
552 MSG("\nStored Log:\n%s", log_text);
556553 }
557554
558555 void DLItem::update_size(int new_sz)
563560 // Start the timer with the first bytes got
564561 init_time = time(NULL);
565562 // Update the title
566 prTitle->label(shortname);
567 prTitle->redraw();
563 prTitle->copy_label(shortname);
564 // WORKAROUND: For unknown reasons a redraw is needed here for some
565 // machines --jcid
566 group->redraw();
568567 }
569568
570569 curr_bytesize = new_sz;
573572 else
574573 snprintf(buf, 64, "%.0fKB", (float)curr_bytesize / 1024);
575574 prGot->copy_label(buf);
576 prGot->redraw();
577575 if (total_bytesize == -1) {
578576 prBar->showtext(false);
579577 prBar->move(1);
580578 } else {
581579 prBar->showtext(true);
582 double pos = 100.0 * (double)curr_bytesize / total_bytesize;
580 double pos = 100.0;
581 if (total_bytesize > 0)
582 pos *= (double)curr_bytesize / total_bytesize;
583583 prBar->position(pos);
584584 }
585585 }
587587 static void read_log_cb(int fd_in, void *data)
588588 {
589589 DLItem *dl_item = (DLItem *)data;
590 int BufLen = 4096;
590 const int BufLen = 4096;
591591 char Buf[BufLen];
592592 ssize_t st;
593 int ret = -1;
594593
595594 do {
596595 st = read(fd_in, Buf, BufLen);
597596 if (st < 0) {
598597 if (errno == EAGAIN) {
599 ret = 1;
600598 break;
601599 }
602600 perror("read, ");
603601 break;
604602 } else if (st == 0) {
605603 close(fd_in);
606 remove_fd(fd_in, 1);
604 Fl::remove_fd(fd_in, 1);
607605 dl_item->log_done(1);
608 ret = 0;
609606 break;
610607 } else {
611608 dl_item->log_text_add(Buf, st);
616613 void DLItem::father_init()
617614 {
618615 close(LogPipe[1]);
619 add_fd(LogPipe[0], 1, read_log_cb, this); // Read
616 Fl::add_fd(LogPipe[0], 1, read_log_cb, this); // Read
620617
621618 // Start the timer after the child is running.
622619 // (this makes a big difference with wget)
639636 status_msg("ABORTED");
640637 if (curr_bytesize == 0) {
641638 // Update the title
642 prTitle->label(shortname);
643 prTitle->redraw();
639 prTitle->copy_label(shortname);
644640 }
645641 }
646642 prButton->activate();
651647 /*
652648 * Convert seconds into human readable [hour]:[min]:[sec] string.
653649 */
654 void secs2timestr(int et, char *str)
650 static void secs2timestr(int et, char *str)
655651 {
656652 int eh, em, es;
657653
682678
683679 /* Update curr_size */
684680 if (stat(fullname, &ss) == -1) {
685 MSG("stat, %s\n", strerror(errno));
681 MSG("stat, %s\n", dStrerror(errno));
686682 return;
687683 }
688684 update_size((int)ss.st_size);
697693 rate = ((float)(curr_bytesize-twosec_bytesize) / 1024) / tsec;
698694 snprintf(str, 64, (rate < 100) ? "%.1fK/s" : "%.0fK/s", rate);
699695 prRate->copy_label(str);
700 prRate->redraw();
701696 }
702697 /* ~Rate */
703698 if (csec >= 1) {
704699 _rate = ((float)(curr_bytesize-init_bytesize) / 1024) / csec;
705700 snprintf(str, 64, (_rate < 100) ? "%.1fK/s" : "%.0fK/s", _rate);
706701 pr_Rate->copy_label(str);
707 pr_Rate->redraw();
708702 }
709703
710704 /* ETA */
727721 prETA->copy_label(str);
728722 }
729723 }
730 prETA->redraw();
731724
732725 /* Update one and two secs ago times and bytesizes */
733726 twosec_time = onesec_time;
740733
741734 /*! SIGCHLD handler
742735 */
743 void raw_sigchld(int)
744 {
736 static void raw_sigchld(int)
737 {
745738 caught_sigchld = 1;
746739 }
747740
748741 /*! Establish SIGCHLD handler */
749 void est_sigchld(void)
742 static void est_sigchld(void)
750743 {
751744 struct sigaction sigact;
752745 sigset_t set;
764757 /*
765758 * Timeout function to check wget's exit status.
766759 */
767 void cleanup_cb(void *data)
760 static void cleanup_cb(void *data)
768761 {
769762 DLItemList *list = (DLItemList *)data;
770763
783776 }
784777 sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
785778
786 repeat_timeout(1.0,cleanup_cb,data);
779 Fl::repeat_timeout(1.0,cleanup_cb,data);
787780 }
788781
789782 /*
790783 * Timeout function to update the widget indicators,
791784 * also remove widgets marked "done".
792785 */
793 void update_cb(void *data)
786 static void update_cb(void *data)
794787 {
795788 static int cb_used = 0;
796789
810803 if (cb_used && list->num() == 0)
811804 exit(0);
812805
813 repeat_timeout(1.0,update_cb,data);
806 Fl::repeat_timeout(1.0,update_cb,data);
814807 }
815808
816809
817810 // DLWin ---------------------------------------------------------------------
818811
819812 /*
820 * Read a single line from a socket and store it in a GString.
821 */
822 static ssize_t readline(int socket, GString ** msg)
823 {
824 ssize_t st;
825 gchar buf[16384], *aux;
826
827 /* can't use fread() */
828 do
829 st = read(socket, buf, 16384);
830 while (st < 0 && errno == EINTR);
831
832 if (st == -1)
833 MSG("readline, %s\n", strerror(errno));
834
835 if (st > 0) {
836 aux = g_strndup(buf, (guint)st);
837 g_string_assign(*msg, aux);
838 g_free(aux);
839 } else {
840 g_string_assign(*msg, "");
841 }
842
843 return st;
844 }
845
846 /*
847813 * Make a new name and place it in 'dl_dest'.
848814 */
849 static void make_new_name(gchar **dl_dest, const gchar *url)
850 {
851 GString *gstr = g_string_new(*dl_dest);
852 gint idx = gstr->len;
815 static void make_new_name(char **dl_dest, const char *url)
816 {
817 Dstr *gstr = dStr_new(*dl_dest);
818 int idx = gstr->len;
853819
854820 if (gstr->str[idx - 1] != '/'){
855 g_string_append_c(gstr, '/');
821 dStr_append_c(gstr, '/');
856822 ++idx;
857823 }
858824
859825 /* Use a mangled url as name */
860 g_string_append(gstr, url);
826 dStr_append(gstr, url);
861827 for ( ; idx < gstr->len; ++idx)
862828 if (!isalnum(gstr->str[idx]))
863829 gstr->str[idx] = '_';
864830
865831 /* free memory */
866 g_free(*dl_dest);
832 dFree(*dl_dest);
867833 *dl_dest = gstr->str;
868 g_string_free(gstr, FALSE);
834 dStr_free(gstr, FALSE);
869835 }
870836
871837 /*
874840 */
875841 static void read_req_cb(int req_fd, void *)
876842 {
877 GString *tag;
878843 struct sockaddr_un clnt_addr;
879 int new_socket;
844 int sock_fd;
880845 socklen_t csz;
881846 struct stat sb;
882 char *cmd = NULL, *url = NULL, *dl_dest = NULL;
847 Dsh *sh = NULL;
848 char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *dl_dest = NULL;
883849 DLAction action = DL_ABORT; /* compiler happiness */
884850
885851 /* Initialize the value-result parameter */
886852 csz = sizeof(struct sockaddr_un);
887853 /* accept the request */
888854 do {
889 new_socket = accept(req_fd, (struct sockaddr *) &clnt_addr, &csz);
890 } while (new_socket == -1 && errno == EINTR);
891 if (new_socket == -1) {
892 MSG("accept, %s fd=%d\n", strerror(errno), req_fd);
855 sock_fd = accept(req_fd, (struct sockaddr *) &clnt_addr, &csz);
856 } while (sock_fd == -1 && errno == EINTR);
857 if (sock_fd == -1) {
858 MSG("accept, %s fd=%d\n", dStrerror(errno), req_fd);
893859 return;
894860 }
895861
896 //sigprocmask(SIG_BLOCK, &blockSC, NULL);
897 tag = g_string_new(NULL);
898 readline(new_socket, &tag);
899 close(new_socket);
900 _MSG("Received tag={%s}\n", tag->str);
901
902 if ((cmd = a_Dpip_get_attr(tag->str, (size_t)tag->len, "cmd")) == NULL) {
903 MSG("Failed to parse 'cmd' in %s\n", tag->str);
862 /* create a sock handler */
863 sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
864
865 /* Authenticate our client... */
866 if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
867 a_Dpip_check_auth(dpip_tag) < 0) {
868 MSG("can't authenticate request: %s fd=%d\n", dStrerror(errno), sock_fd);
869 a_Dpip_dsh_close(sh);
870 goto end;
871 }
872 dFree(dpip_tag);
873
874 /* Read request */
875 if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1))) {
876 MSG("can't read request: %s fd=%d\n", dStrerror(errno), sock_fd);
877 a_Dpip_dsh_close(sh);
878 goto end;
879 }
880 a_Dpip_dsh_close(sh);
881 _MSG("Received tag={%s}\n", dpip_tag);
882
883 if ((cmd = a_Dpip_get_attr(dpip_tag, "cmd")) == NULL) {
884 MSG("Failed to parse 'cmd' in {%s}\n", dpip_tag);
904885 goto end;
905886 }
906887 if (strcmp(cmd, "DpiBye") == 0) {
911892 MSG("unknown command: '%s'. Aborting.\n", cmd);
912893 goto end;
913894 }
914 if (!(url = a_Dpip_get_attr(tag->str,(size_t)tag->len, "url"))){
915 MSG("Failed to parse 'url' in %s\n", tag->str);
895 if (!(url = a_Dpip_get_attr(dpip_tag, "url"))){
896 MSG("Failed to parse 'url' in {%s}\n", dpip_tag);
916897 goto end;
917898 }
918 if (!(dl_dest = a_Dpip_get_attr(tag->str,(size_t)tag->len,"destination"))){
919 MSG("Failed to parse 'destination' in %s\n", tag->str);
899 if (!(dl_dest = a_Dpip_get_attr(dpip_tag, "destination"))){
900 MSG("Failed to parse 'destination' in {%s}\n", dpip_tag);
920901 goto end;
921902 }
922903 /* 'dl_dest' may be a directory */
923 if (stat(dl_dest, &sb) == 0 && S_ISDIR(sb.st_mode))
904 if (stat(dl_dest, &sb) == 0 && S_ISDIR(sb.st_mode)) {
924905 make_new_name(&dl_dest, url);
925
906 }
926907 action = dl_win->check_filename(&dl_dest);
927908 if (action != DL_ABORT) {
928 // Start the whole thing whithin FLTK.
909 // Start the whole thing within FLTK.
929910 dl_win->add(dl_dest, url, action);
930911 } else if (dl_win->num() == 0) {
931912 exit(0);
932913 }
933914
934915 end:
935 g_free(cmd);
936 g_free(url);
937 g_free(dl_dest);
938 g_string_free(tag, TRUE);
916 dFree(cmd);
917 dFree(url);
918 dFree(dl_dest);
919 dFree(dpip_tag);
920 a_Dpip_dsh_free(sh);
939921 }
940922
941923 /*
942924 * Callback for close window request (WM or EscapeKey press)
943925 */
944 static void dlwin_esc_cb(Widget *, void *)
945 {
946 char *msg = "There are running downloads.\n"
947 "ABORT them and EXIT anyway?";
926 static void dlwin_esc_cb(Fl_Widget *, void *)
927 {
928 const char *msg = "There are running downloads.\n"
929 "ABORT them and EXIT anyway?";
948930
949931 if (dl_win && dl_win->num_running() > 0) {
950 int ch = fltk::choice(msg, "Yes", "*No", "Cancel");
951 if (ch != 0)
932 int ch = fl_choice("%s", "Cancel", "*No", "Yes", msg);
933 if (ch == 0 || ch == 1)
952934 return;
953935 }
954936
964946 {
965947 DLItem *dl_item = new DLItem(full_filename, url, action);
966948 mDList->add(dl_item);
967 //mPG->add(*dl_item->get_widget());
968949 mPG->insert(*dl_item->get_widget(), 0);
969950
970951 _MSG("Child index = %d\n", mPG->find(dl_item->get_widget()));
978959 } else if (f_pid < 0) {
979960 perror("fork, ");
980961 exit(1);
981 } else {
962 } else {
982963 /* father */
964 dl_item->get_widget()->show();
983965 dl_win->show();
984966 dl_item->pid(f_pid);
985967 dl_item->father_init();
987969 }
988970
989971 /*
990 * Decide what to do whe the filename already exists.
972 * Decide what to do when the filename already exists.
991973 * (renaming takes place here when necessary)
992974 */
993975 DLAction DLWin::check_filename(char **p_fullname)
994976 {
995977 struct stat ss;
996 char *msg;
978 Dstr *ds;
997979 int ch;
998980 DLAction ret = DL_ABORT;
999981
1000982 if (stat(*p_fullname, &ss) == -1)
1001983 return DL_NEWFILE;
1002984
1003 msg = g_strdup_printf(
1004 "The file:\n %s (%d Bytes)\nalready exists. What do we do?",
1005 *p_fullname, (int)ss.st_size);
1006 ch = fltk::choice(msg, "Rename", "Continue", "Abort");
1007 g_free(msg);
985 ds = dStr_sized_new(128);
986 dStr_sprintf(ds,
987 "The file:\n %s (%d Bytes)\nalready exists. What do we do?",
988 *p_fullname, (int)ss.st_size);
989 ch = fl_choice("%s", "Abort", "Continue", "Rename", ds->str);
990 dStr_free(ds, 1);
1008991 MSG("Choice %d\n", ch);
1009 if (ch == 0) {
992 if (ch == 2) {
1010993 const char *p;
1011 p = fltk::file_chooser("Enter a new name:", NULL, *p_fullname);
994 p = fl_file_chooser("Enter a new name:", NULL, *p_fullname);
1012995 if (p) {
1013 g_free(*p_fullname);
1014 *p_fullname = g_strdup(p);
996 dFree(*p_fullname);
997 *p_fullname = dStrdup(p);
1015998 ret = check_filename(p_fullname);
1016999 }
10171000 } else if (ch == 1) {
10211004 }
10221005
10231006 /*
1024 * Add a new download request to the main window and
1025 * fork a child to do the job.
1007 * Delete a download request from the main window.
10261008 */
10271009 void DLWin::del(int n_item)
10281010 {
10291011 DLItem *dl_item = mDList->get(n_item);
10301012
1031 // Remove the widget from the scroll group
1013 // Remove the widget from the packed group
10321014 mPG->remove(dl_item->get_widget());
1033 // Resize the scroll group
1034 mPG->resize(mWin->w(), 1);
1035
1015 mScroll->redraw();
10361016 mDList->del(n_item);
10371017 delete(dl_item);
10381018 }
10631043 */
10641044 void DLWin::listen(int req_fd)
10651045 {
1066 add_fd(req_fd, 1, read_req_cb, NULL); // Read
1046 Fl::add_fd(req_fd, 1, read_req_cb, NULL); // Read
10671047 }
10681048
10691049 /*
10841064 mDList = new DLItemList();
10851065
10861066 // Create the empty main window
1087 mWin = new Window(ww, wh, "Downloads:");
1067 mWin = new Fl_Window(ww, wh, "Downloads:");
10881068 mWin->begin();
1089 mScroll = new ScrollGroup(0,0,ww,wh);
1069 mScroll = new Fl_Scroll(0,0,ww,wh);
10901070 mScroll->begin();
1091 mPG = new PackedGroup(0,0,ww,wh);
1071 mPG = new Fl_Pack(0,0,ww-18,wh);
10921072 mPG->end();
1093 //mPG->spacing(10);
10941073 mScroll->end();
1095 mWin->resizable(mWin);
1074 mScroll->type(Fl_Scroll::VERTICAL);
10961075 mWin->end();
1076 mWin->resizable(mScroll);
10971077 mWin->callback(dlwin_esc_cb, NULL);
10981078 mWin->show();
10991079
11031083 est_sigchld();
11041084
11051085 // Set the cleanup timeout
1106 add_timeout(1.0, cleanup_cb, mDList);
1086 Fl::add_timeout(1.0, cleanup_cb, mDList);
11071087 // Set the update timeout
1108 add_timeout(1.0, update_cb, mDList);
1088 Fl::add_timeout(1.0, update_cb, mDList);
11091089 }
11101090
11111091
11181098 {
11191099 int ww = 420, wh = 85;
11201100
1121 lock();
1101 Fl::lock();
11221102
11231103 // Create the download window
11241104 dl_win = new DLWin(ww, wh);
11281108
11291109 MSG("started...\n");
11301110
1131 return run();
1132 }
1133
1111 return Fl::run();
1112 }
1113
00 /*
11 * File: dpiutil.c
22 *
3 * Copyright 2004 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright 2004-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 *
1010 */
1111
12 #include "dpiutil.h"
1312 #include <unistd.h>
1413 #include <stdio.h>
1514 #include <stdarg.h>
1716 #include <ctype.h>
1817 #include <errno.h>
1918 #include <sys/socket.h>
20 #include <glib.h>
19
20 #include "dpiutil.h"
21
22 /*
23 * Debugging macros
24 */
25 #define _MSG(...)
26 #define MSG(...) printf("[dpiutil.c]: " __VA_ARGS__)
27
2128
2229 /* Escaping/De-escaping ---------------------------------------------------*/
2330
2532 * Escape URI characters in 'esc_set' as %XX sequences.
2633 * Return value: New escaped string.
2734 */
28 gchar *Escape_uri_str(const gchar *str, gchar *p_esc_set)
35 char *Escape_uri_str(const char *str, const char *p_esc_set)
2936 {
30 static const char *hex = "0123456789ABCDEF";
31 gchar *p, *esc_set;
32 GString *gstr;
33 gint i;
37 static const char *esc_set, *hex = "0123456789ABCDEF";
38 char *p;
39 Dstr *dstr;
40 int i;
3441
3542 esc_set = (p_esc_set) ? p_esc_set : "%#:' ";
36 gstr = g_string_sized_new(64);
43 dstr = dStr_sized_new(64);
3744 for (i = 0; str[i]; ++i) {
3845 if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) {
39 g_string_append_c(gstr, '%');
40 g_string_append_c(gstr, hex[(str[i] >> 4) & 15]);
41 g_string_append_c(gstr, hex[str[i] & 15]);
46 dStr_append_c(dstr, '%');
47 dStr_append_c(dstr, hex[(str[i] >> 4) & 15]);
48 dStr_append_c(dstr, hex[str[i] & 15]);
4249 } else {
43 g_string_append_c(gstr, str[i]);
50 dStr_append_c(dstr, str[i]);
4451 }
4552 }
46 p = gstr->str;
47 g_string_free(gstr, FALSE);
53 p = dstr->str;
54 dStr_free(dstr, FALSE);
4855
4956 return p;
5057 }
58
59 /*
60 * Unescape %XX sequences in a string.
61 * Return value: a new unescaped string
62 */
63 char *Unescape_uri_str(const char *s)
64 {
65 char *p, *buf = dStrdup(s);
66
67 if (strchr(s, '%')) {
68 for (p = buf; (*p = *s); ++s, ++p) {
69 if (*p == '%' && isxdigit(s[1]) && isxdigit(s[2])) {
70 *p = (isdigit(s[1]) ? (s[1] - '0') : toupper(s[1]) - 'A' + 10)*16;
71 *p += isdigit(s[2]) ? (s[2] - '0') : toupper(s[2]) - 'A' + 10;
72 s += 2;
73 }
74 }
75 }
76
77 return buf;
78 }
79
5180
5281 static const char *unsafe_chars = "&<>\"'";
5382 static const char *unsafe_rep[] =
5887 * Escape unsafe characters as html entities.
5988 * Return value: New escaped string.
6089 */
61 gchar *Escape_html_str(const gchar *str)
90 char *Escape_html_str(const char *str)
6291 {
63 gint i;
64 gchar *p;
65 GString *gstr = g_string_sized_new(64);
92 int i;
93 char *p;
94 Dstr *dstr = dStr_sized_new(64);
6695
6796 for (i = 0; str[i]; ++i) {
6897 if ((p = strchr(unsafe_chars, str[i])))
69 g_string_append(gstr, unsafe_rep[p - unsafe_chars]);
98 dStr_append(dstr, unsafe_rep[p - unsafe_chars]);
7099 else
71 g_string_append_c(gstr, str[i]);
100 dStr_append_c(dstr, str[i]);
72101 }
73 p = gstr->str;
74 g_string_free(gstr, FALSE);
102 p = dstr->str;
103 dStr_free(dstr, FALSE);
75104
76105 return p;
77106 }
80109 * Unescape a few HTML entities (inverse of Escape_html_str)
81110 * Return value: New unescaped string.
82111 */
83 gchar *Unescape_html_str(const gchar *str)
112 char *Unescape_html_str(const char *str)
84113 {
85 gint i, j, k;
86 gchar *u_str = g_strdup(str);
114 int i, j, k;
115 char *u_str = dStrdup(str);
87116
88117 if (!strchr(str, '&'))
89118 return u_str;
91120 for (i = 0, j = 0; str[i]; ++i) {
92121 if (str[i] == '&') {
93122 for (k = 0; k < 5; ++k) {
94 if (!g_strncasecmp(str + i, unsafe_rep[k], unsafe_rep_len[k])) {
123 if (!dStrncasecmp(str + i, unsafe_rep[k], unsafe_rep_len[k])) {
95124 i += unsafe_rep_len[k] - 1;
96125 break;
97126 }
133162 return url;
134163 }
135164
136
137 /* Streamed Sockets API (not mandatory) ----------------------------------*/
138
139 /*
140 * Create and initialize the SockHandler structure
141 */
142 SockHandler *sock_handler_new(int fd_in, int fd_out, int flush_sz)
143 {
144 SockHandler *sh = g_new(SockHandler, 1);
145
146 /* init descriptors and streams */
147 sh->fd_in = fd_in;
148 sh->fd_out = fd_out;
149 sh->out = fdopen(fd_out, "w");
150
151 /* init buffer */
152 sh->buf_max = 8 * 1024 + 128;
153 sh->buf = g_new(char, sh->buf_max);
154 sh->buf_sz = 0;
155 sh->flush_sz = flush_sz;
156
157 return sh;
158 }
159
160 /*
161 * Streamed write to socket
162 * Return: 0 on success, 1 on error.
163 */
164 int sock_handler_write(SockHandler *sh, const char *Data, size_t DataSize,
165 int flush)
166 {
167 gint retval = 1;
168
169 /* append to buf */
170 while (sh->buf_max < sh->buf_sz + DataSize) {
171 sh->buf_max <<= 1;
172 sh->buf = g_realloc(sh->buf, sh->buf_max);
173 }
174 memcpy(sh->buf + sh->buf_sz, Data, DataSize);
175 sh->buf_sz += DataSize;
176 /*
177 g_printerr(
178 "sh->buf=%p, sh->buf_sz=%d, sh->buf_max=%d, sh->flush_sz=%d\n",
179 sh->buf, sh->buf_sz, sh->buf_max, sh->flush_sz);
180 */
181 /**/
182 #if 0
183 {
184 guint i;
185 /* Test dpip's stream handling by chopping data into characters */
186 for (i = 0; i < sh->buf_sz; ++i) {
187 fputc(sh->buf[i], sh->out);
188 fflush(sh->out);
189 usleep(50);
190 }
191 if (i == sh->buf_sz) {
192 sh->buf_sz = 0;
193 retval = 0;
194 }
195 }
196 #else
197 /* flush data if necessary */
198 if (flush || sh->buf_sz >= sh->flush_sz) {
199 if (sh->buf_sz && fwrite (sh->buf, sh->buf_sz, 1, sh->out) != 1) {
200 perror("[sock_handler_write]");
201 } else {
202 fflush(sh->out);
203 sh->buf_sz = 0;
204 retval = 0;
205 }
206
207 } else {
208 retval = 0;
209 }
210 #endif
211 return retval;
212 }
213
214 /*
215 * Convenience function.
216 */
217 int sock_handler_write_str(SockHandler *sh, const char *str, int flush)
218 {
219 return sock_handler_write(sh, str, strlen(str), flush);
220 }
221
222 /*
223 * Return a newlly allocated string with the contents read from the socket.
224 */
225 gchar *sock_handler_read(SockHandler *sh)
226 {
227 ssize_t st;
228 gchar buf[16384];
229
230 /* can't use fread() */
231 do
232 st = read(sh->fd_in, buf, 16384);
233 while (st < 0 && errno == EINTR);
234
235 if (st == -1)
236 perror("[sock_handler_read]");
237
238 return (st > 0) ? g_strndup(buf, (guint)st) : NULL;
239 }
240
241 /*
242 * Close this socket for reading and writing.
243 */
244 void sock_handler_close(SockHandler *sh)
245 {
246 /* flush before closing */
247 sock_handler_write(sh, "", 0, 1);
248
249 fclose(sh->out);
250 close(sh->fd_out);
251 }
252
253 /*
254 * Free the SockHandler structure
255 */
256 void sock_handler_free(SockHandler *sh)
257 {
258 g_free(sh->buf);
259 g_free(sh);
260 }
261
262 /* ------------------------------------------------------------------------ */
263
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 *
1010 */
1818 #define __DPIUTIL_H__
1919
2020 #include <stdio.h>
21 #include <glib.h>
21 #include "d_size.h"
22 #include "../dlib/dlib.h"
23
2224
2325 #ifdef __cplusplus
2426 extern "C" {
2527 #endif /* __cplusplus */
2628
2729
28 #define BUFLEN 256
29 #define TOUT 300
30
31
32 /* Streamed Sockets API (not mandatory) ----------------------------------*/
33
34 typedef struct _SockHandler SockHandler;
35 struct _SockHandler {
36 gint fd_in;
37 gint fd_out;
38 /* FILE *in; --Unused. The stream functions block when reading. */
39 FILE *out;
40
41 gchar *buf; /* internal buffer */
42 guint buf_sz; /* data size */
43 guint buf_max; /* allocated size */
44 guint flush_sz; /* max size before flush */
45 };
46
47 SockHandler *sock_handler_new(int fd_in, int fd_out, int flush_sz);
48 int sock_handler_write(SockHandler *sh, const char *Data,size_t DataSize,
49 int flush);
50 int sock_handler_write_str(SockHandler *sh, const char *str, int flush);
51 gchar *sock_handler_read(SockHandler *sh);
52 void sock_handler_close(SockHandler *sh);
53 void sock_handler_free(SockHandler *sh);
54
55 #define sock_handler_printf(sh, flush, fmt...) \
56 G_STMT_START { \
57 gchar *str = g_strdup_printf(fmt); \
58 sock_handler_write(sh, str, strlen(str), flush); \
59 g_free(str); \
60 } G_STMT_END
61
62 /* ----------------------------------------------------------------------- */
63
6430 /*
6531 * Escape URI characters in 'esc_set' as %XX sequences.
6632 * Return value: New escaped string.
6733 */
68 gchar *Escape_uri_str(const gchar *str, gchar *p_esc_set);
34 char *Escape_uri_str(const char *str, const char *p_esc_set);
35
36 /*
37 * Unescape %XX sequences in a string.
38 * Return value: a new unescaped string
39 */
40 char *Unescape_uri_str(const char *str);
6941
7042 /*
7143 * Escape unsafe characters as html entities.
7244 * Return value: New escaped string.
7345 */
74 gchar *Escape_html_str(const gchar *str);
46 char *Escape_html_str(const char *str);
7547
7648 /*
7749 * Unescape a few HTML entities (inverse of Escape_html_str)
7850 * Return value: New unescaped string.
7951 */
80 gchar *Unescape_html_str(const gchar *str);
52 char *Unescape_html_str(const char *str);
8153
8254 /*
8355 * Filter an SMTP hack with a FTP URI
00 /*
11 * File: file.c :)
22 *
3 * Copyright (C) 2000 - 2004 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1313 * Directory entries on top, files next.
1414 * With new HTML layout.
1515 */
16
17 #include <pthread.h>
1816
1917 #include <ctype.h> /* for tolower */
2018 #include <errno.h> /* for errno */
2220 #include <stdlib.h>
2321 #include <string.h>
2422 #include <unistd.h>
23 #include <sys/select.h>
2524 #include <sys/socket.h>
2625 #include <sys/stat.h>
2726 #include <sys/types.h>
3130 #include <fcntl.h>
3231 #include <time.h>
3332 #include <signal.h>
34 #include <glib.h>
33 #include <netinet/in.h>
3534
3635 #include "../dpip/dpip.h"
3736 #include "dpiutil.h"
37 #include "d_size.h"
38
39 /*
40 * Debugging macros
41 */
42 #define _MSG(...)
43 #define MSG(...) printf("[file dpi]: " __VA_ARGS__)
44 #define _MSG_RAW(...)
45 #define MSG_RAW(...) printf(__VA_ARGS__)
46
3847
3948 #define MAXNAMESIZE 30
4049 #define HIDE_DOTFILES TRUE
4150
42 #define _MSG(fmt...)
43 #define MSG(fmt...) g_print("[file dpi]: " fmt)
44
45 enum {
46 FILE_OK,
47 FILE_NOT_FOUND,
48 FILE_NO_ACCESS
49 };
51 /*
52 * Communication flags
53 */
54 #define FILE_AUTH_OK 1 /* Authentication done */
55 #define FILE_READ 2 /* Waiting data */
56 #define FILE_WRITE 4 /* Sending data */
57 #define FILE_DONE 8 /* Operation done */
58 #define FILE_ERR 16 /* Operation error */
59
60
61 typedef enum {
62 st_start = 10,
63 st_dpip,
64 st_http,
65 st_content,
66 st_done,
67 st_err
68 } FileState;
5069
5170 typedef struct {
5271 char *full_path;
5877
5978 typedef struct {
6079 char *dirname;
61 GList *flist; /* List of files and subdirectories (for sorting) */
80 Dlist *flist; /* List of files and subdirectories (for sorting) */
6281 } DilloDir;
6382
6483 typedef struct {
65 SockHandler *sh;
66 gint status;
67 gint old_style;
68 pthread_t thrID;
69 gint done;
84 Dsh *sh;
85 char *orig_url;
86 char *filename;
87 int file_fd;
88 off_t file_sz;
89 DilloDir *d_dir;
90 FileState state;
91 int err_code;
92 int flags;
93 int old_style;
7094 } ClientInfo;
7195
7296 /*
7397 * Forward references
7498 */
7599 static const char *File_content_type(const char *filename);
76 static gint File_get_file(ClientInfo *Client,
77 const gchar *filename,
78 struct stat *sb,
79 const char *orig_url);
80 static gint File_get_dir(ClientInfo *Client,
81 const gchar *DirName,
82 const char *orig_url);
83100
84101 /*
85102 * Global variables
86103 */
87 static volatile gint DPIBYE = 0;
88 static volatile gint ThreadRunning = 0;
89 static gint OLD_STYLE = 0;
104 static int DPIBYE = 0;
105 static int OLD_STYLE = 0;
90106 /* A list for the clients we are serving */
91 static GList *Clients = NULL;
92 /* a mutex for operations on clients */
93 static pthread_mutex_t ClMut;
107 static Dlist *Clients;
108 /* Set of filedescriptors we're working on */
109 fd_set read_set, write_set;
110
94111
95112 /*
96113 * Close a file descriptor, but handling EINTR
97114 */
98115 static void File_close(int fd)
99116 {
100 while (close(fd) < 0 && errno == EINTR)
117 while (fd >= 0 && close(fd) < 0 && errno == EINTR)
101118 ;
102119 }
103120
109126 * 'Data' is a pointer to the first bytes of the raw data.
110127 * (this is based on a_Misc_get_content_type_from_data())
111128 */
112 static const gchar *File_get_content_type_from_data(void *Data, size_t Size)
113 {
114 static const gchar *Types[] = {
129 static const char *File_get_content_type_from_data(void *Data, size_t Size)
130 {
131 static const char *Types[] = {
115132 "application/octet-stream",
116133 "text/html", "text/plain",
117134 "image/gif", "image/png", "image/jpeg",
118135 };
119 gint Type = 0;
120 gchar *p = Data;
136 int Type = 0;
137 char *p = Data;
121138 size_t i, non_ascci;
122139
123140 _MSG("File_get_content_type_from_data:: Size = %d\n", Size);
124141
125142 /* HTML try */
126 for (i = 0; i < Size && isspace(p[i]); ++i);
127 if ((Size - i >= 5 && !g_strncasecmp(p+i, "<html", 5)) ||
128 (Size - i >= 5 && !g_strncasecmp(p+i, "<head", 5)) ||
129 (Size - i >= 6 && !g_strncasecmp(p+i, "<title", 6)) ||
130 (Size - i >= 14 && !g_strncasecmp(p+i, "<!doctype html", 14)) ||
143 for (i = 0; i < Size && dIsspace(p[i]); ++i);
144 if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) ||
145 (Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) ||
146 (Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) ||
147 (Size - i >= 14 && !dStrncasecmp(p+i, "<!doctype html", 14)) ||
131148 /* this line is workaround for FTP through the Squid proxy */
132 (Size - i >= 17 && !g_strncasecmp(p+i, "<!-- HTML listing", 17))) {
149 (Size - i >= 17 && !dStrncasecmp(p+i, "<!-- HTML listing", 17))) {
133150
134151 Type = 1;
135152
136153 /* Images */
137 } else if (Size >= 4 && !g_strncasecmp(p, "GIF8", 4)) {
154 } else if (Size >= 4 && !dStrncasecmp(p, "GIF8", 4)) {
138155 Type = 3;
139 } else if (Size >= 4 && !g_strncasecmp(p, "\x89PNG", 4)) {
156 } else if (Size >= 4 && !dStrncasecmp(p, "\x89PNG", 4)) {
140157 Type = 4;
141 } else if (Size >= 2 && !g_strncasecmp(p, "\xff\xd8", 2)) {
158 } else if (Size >= 2 && !dStrncasecmp(p, "\xff\xd8", 2)) {
142159 /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
143160 * at the character representation should be machine independent. */
144161 Type = 5;
148165 /* We'll assume "text/plain" if the set of chars above 127 is <= 10
149166 * in a 256-bytes sample. Better heuristics are welcomed! :-) */
150167 non_ascci = 0;
151 Size = MIN (Size, 256);
152 for (i = 0; i < Size; i++)
153 if ((unsigned char) p[i] > 127)
168 Size = MIN (Size, 256);
169 for (i = 0; i < Size; i++)
170 if ((uchar_t) p[i] > 127)
154171 ++non_ascci;
155172 if (Size == 256) {
156173 Type = (non_ascci > 10) ? 0 : 2;
166183 * Compare two FileInfo pointers
167184 * This function is used for sorting directories
168185 */
169 static gint File_comp(gconstpointer a, gconstpointer b)
170 {
171 FileInfo *f1 = (FileInfo *) a;
172 FileInfo *f2 = (FileInfo *) b;
173
186 static int File_comp(const FileInfo *f1, const FileInfo *f2)
187 {
174188 if (S_ISDIR(f1->mode)) {
175189 if (S_ISDIR(f2->mode)) {
176190 return strcmp(f1->filename, f2->filename);
199213 char *fname;
200214 int dirname_len;
201215
202 if ( !(dir = opendir(dirname)))
216 if (!(dir = opendir(dirname)))
203217 return NULL;
204218
205 Ddir = g_new(DilloDir, 1);
206 Ddir->dirname = g_strdup(dirname);
207 Ddir->flist = NULL;
219 Ddir = dNew(DilloDir, 1);
220 Ddir->dirname = dStrdup(dirname);
221 Ddir->flist = dList_new(512);
208222
209223 dirname_len = strlen(Ddir->dirname);
210224
222236 continue;
223237 }
224238
225 fname = g_strdup_printf("%s/%s", Ddir->dirname, de->d_name);
226
239 fname = dStrconcat(Ddir->dirname, de->d_name, NULL);
227240 if (stat(fname, &sb) == -1) {
228 g_free(fname);
241 dFree(fname);
229242 continue; /* ignore files we can't stat */
230243 }
231244
232 finfo = g_new(FileInfo, 1);
245 finfo = dNew(FileInfo, 1);
233246 finfo->full_path = fname;
234 finfo->filename = fname + dirname_len + 1;
247 finfo->filename = fname + dirname_len;
235248 finfo->size = sb.st_size;
236249 finfo->mode = sb.st_mode;
237250 finfo->mtime = sb.st_mtime;
238251
239 Ddir->flist = g_list_prepend(Ddir->flist, finfo);
252 dList_append(Ddir->flist, finfo);
240253 }
241254
242255 closedir(dir);
243256
244257 /* sort the entries */
245 Ddir->flist = g_list_sort(Ddir->flist, File_comp);
258 dList_sort(Ddir->flist, (dCompareFunc)File_comp);
246259
247260 return Ddir;
248261 }
252265 */
253266 static void File_dillodir_free(DilloDir *Ddir)
254267 {
255 GList *list;
256
257 for (list = Ddir->flist; list; list = g_list_next(list)) {
258 FileInfo *finfo = list->data;
259 g_free(finfo->full_path);
260 g_free(finfo);
261 }
262
263 g_list_free(Ddir->flist);
264 g_free(Ddir->dirname);
265 g_free(Ddir);
268 int i;
269 FileInfo *finfo;
270
271 dReturn_if (Ddir == NULL);
272
273 for (i = 0; i < dList_length(Ddir->flist); ++i) {
274 finfo = dList_nth_data(Ddir->flist, i);
275 dFree(finfo->full_path);
276 dFree(finfo);
277 }
278
279 dList_free(Ddir->flist);
280 dFree(Ddir->dirname);
281 dFree(Ddir);
266282 }
267283
268284 /*
269285 * Output the string for parent directory
270286 */
271 static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
287 static void File_print_parent_dir(ClientInfo *client, const char *dirname)
272288 {
273289 if (strcmp(dirname, "/") != 0) { /* Not the root dir */
274290 char *p, *parent, *HUparent, *Uparent;
275291
276 parent = g_strdup(dirname);
292 parent = dStrdup(dirname);
277293 /* cut trailing slash */
278294 parent[strlen(parent) - 1] = '\0';
279295 /* make 'parent' have the parent dir path */
280 if ( (p = strrchr(parent, '/')) )
296 if ((p = strrchr(parent, '/')))
281297 *(p + 1) = '\0';
282298
283299 Uparent = Escape_uri_str(parent, NULL);
284300 HUparent = Escape_html_str(Uparent);
285 sock_handler_printf(Client->sh, 0,
301 a_Dpip_dsh_printf(client->sh, 0,
286302 "<a href='file:%s'>Parent directory</a>", HUparent);
287 g_free(HUparent);
288 g_free(Uparent);
289 g_free(parent);
303 dFree(HUparent);
304 dFree(Uparent);
305 dFree(parent);
290306 }
291307 }
292308
293309 /*
294310 * Given a timestamp, output an HTML-formatted date string.
295311 */
296 static void File_print_mtime(ClientInfo *Client, time_t mtime)
312 static void File_print_mtime(ClientInfo *client, time_t mtime)
297313 {
298314 char *ds = ctime(&mtime);
299315
300316 /* Month, day and {hour or year} */
301 if (Client->old_style) {
302 sock_handler_printf(Client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
317 if (client->old_style) {
318 a_Dpip_dsh_printf(client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
303319 if (time(NULL) - mtime > 15811200) {
304 sock_handler_printf(Client->sh, 0, " %.4s", ds + 20);
320 a_Dpip_dsh_printf(client->sh, 0, " %.4s", ds + 20);
305321 } else {
306 sock_handler_printf(Client->sh, 0, " %.5s", ds + 11);
322 a_Dpip_dsh_printf(client->sh, 0, " %.5s", ds + 11);
307323 }
308324 } else {
309 sock_handler_printf(Client->sh, 0,
325 a_Dpip_dsh_printf(client->sh, 0,
310326 "<td>%.3s&nbsp;%.2s&nbsp;%.5s", ds + 4, ds + 8,
311327 /* (more than 6 months old) ? year : hour; */
312328 (time(NULL) - mtime > 15811200) ? ds + 20 : ds + 11);
316332 /*
317333 * Return a HTML-line from file info.
318334 */
319 static void File_info2html(ClientInfo *Client,
320 FileInfo *finfo, const char *dirname, gint n)
321 {
322 gint size;
335 static void File_info2html(ClientInfo *client, FileInfo *finfo, int n)
336 {
337 int size;
323338 char *sizeunits;
324339 char namebuf[MAXNAMESIZE + 1];
325340 char *Uref, *HUref, *Hname;
360375 HUref = Escape_html_str(Uref);
361376 Hname = Escape_html_str(name);
362377
363 if (Client->old_style) {
378 if (client->old_style) {
364379 char *dots = ".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..";
365 gint ndots = MAXNAMESIZE - strlen(name);
366 sock_handler_printf(Client->sh, 0,
380 int ndots = MAXNAMESIZE - strlen(name);
381 a_Dpip_dsh_printf(client->sh, 0,
367382 "%s<a href='%s'>%s</a>"
368383 " %s"
369384 " %-11s%4d %-5s",
372387 filecont, size, sizeunits);
373388
374389 } else {
375 sock_handler_printf(Client->sh, 0,
390 a_Dpip_dsh_printf(client->sh, 0,
376391 "<tr align=center %s><td>%s<td align=left><a href='%s'>%s</a>"
377392 "<td>%s<td>%d&nbsp;%s",
378393 (n & 1) ? "bgcolor=#dcdcdc" : "",
379394 S_ISDIR (finfo->mode) ? ">" : " ", HUref, Hname,
380395 filecont, size, sizeunits);
381396 }
382 File_print_mtime(Client, finfo->mtime);
383 sock_handler_printf(Client->sh, 0, "\n");
384
385 g_free(Hname);
386 g_free(HUref);
387 g_free(Uref);
388 }
389
390 /*
391 * Read a local directory and translate it to html.
392 */
393 static void File_transfer_dir(ClientInfo *Client,
394 DilloDir *Ddir, const char *orig_url)
395 {
396 gint n;
397 GList *list;
397 File_print_mtime(client, finfo->mtime);
398 a_Dpip_dsh_write_str(client->sh, 0, "\n");
399
400 dFree(Hname);
401 dFree(HUref);
402 dFree(Uref);
403 }
404
405 /*
406 * Send the HTML directory page in HTTP.
407 */
408 static void File_send_dir(ClientInfo *client)
409 {
410 int n;
398411 char *d_cmd, *Hdirname, *Udirname, *HUdirname;
399
400 /* Send DPI header */
401 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
402 sock_handler_write_str(Client->sh, d_cmd, 1);
403 g_free(d_cmd);
404
405 /* Send page title */
406 Udirname = Escape_uri_str(Ddir->dirname, NULL);
407 HUdirname = Escape_html_str(Udirname);
408 Hdirname = Escape_html_str(Ddir->dirname);
409
410 sock_handler_printf(Client->sh, 0,
411 "Content-Type: text/html\n\n"
412 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
413 "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
414 " <TITLE>file:%s</TITLE>\n</HEAD>\n"
415 "<BODY><H1>Directory listing of %s</H1>\n",
416 HUdirname, Hdirname, Hdirname);
417 g_free(Hdirname);
418 g_free(HUdirname);
419 g_free(Udirname);
420
421 if (Client->old_style) {
422 sock_handler_printf(Client->sh, 0, "<pre>\n");
423 }
424
425 /* Output the parent directory */
426 File_print_parent_dir(Client, Ddir->dirname);
427
428 /* HTML style toggle */
429 sock_handler_printf(Client->sh, 0,
430 "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%%</a>\n");
431
432 if (Ddir->flist) {
433 if (Client->old_style) {
434 sock_handler_printf(Client->sh, 0, "\n\n");
412 DilloDir *Ddir = client->d_dir;
413
414 if (client->state == st_start) {
415 /* Send DPI command */
416 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
417 client->orig_url);
418 a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
419 dFree(d_cmd);
420 client->state = st_dpip;
421
422 } else if (client->state == st_dpip) {
423 /* send HTTP header and HTML top part */
424
425 /* Send page title */
426 Udirname = Escape_uri_str(Ddir->dirname, NULL);
427 HUdirname = Escape_html_str(Udirname);
428 Hdirname = Escape_html_str(Ddir->dirname);
429
430 a_Dpip_dsh_printf(client->sh, 0,
431 "HTTP/1.1 200 OK\r\n"
432 "Content-Type: text/html\r\n"
433 "\r\n"
434 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
435 "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
436 " <TITLE>file:%s</TITLE>\n</HEAD>\n"
437 "<BODY><H1>Directory listing of %s</H1>\n",
438 HUdirname, Hdirname, Hdirname);
439 dFree(Hdirname);
440 dFree(HUdirname);
441 dFree(Udirname);
442
443 if (client->old_style) {
444 a_Dpip_dsh_write_str(client->sh, 0, "<pre>\n");
445 }
446
447 /* Output the parent directory */
448 File_print_parent_dir(client, Ddir->dirname);
449
450 /* HTML style toggle */
451 a_Dpip_dsh_write_str(client->sh, 0,
452 "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%</a>\n");
453
454 if (dList_length(Ddir->flist)) {
455 if (client->old_style) {
456 a_Dpip_dsh_write_str(client->sh, 0, "\n\n");
457 } else {
458 a_Dpip_dsh_write_str(client->sh, 0,
459 "<br><br>\n"
460 "<table border=0 cellpadding=1 cellspacing=0"
461 " bgcolor=#E0E0E0 width=100%>\n"
462 "<tr align=center>\n"
463 "<td>\n"
464 "<td width=60%><b>Filename</b>"
465 "<td><b>Type</b>"
466 "<td><b>Size</b>"
467 "<td><b>Modified&nbsp;at</b>\n");
468 }
435469 } else {
436 sock_handler_printf(Client->sh, 0,
437 "<br><br>\n"
438 "<table border=0 cellpadding=1 cellspacing=0"
439 " bgcolor=#E0E0E0 width=100%%>\n"
440 "<tr align=center>\n"
441 "<td>\n"
442 "<td width=60%%><b>Filename</b>"
443 "<td><b>Type</b>"
444 "<td><b>Size</b>"
445 "<td><b>Modified&nbsp;at</b>\n");
446 }
447 } else {
448 sock_handler_printf(Client->sh, 0, "<br><br>Directory is empty...");
449 }
450
451 /* Output entries */
452 for (n = 0, list = Ddir->flist; list; list = g_list_next(list)) {
453 File_info2html(Client, list->data, Ddir->dirname, ++n);
454 }
455
456 if (Ddir->flist) {
457 if (Client->old_style) {
458 sock_handler_printf(Client->sh, 0, "</pre>\n");
459 } else {
460 sock_handler_printf(Client->sh, 0, "</table>\n");
461 }
462 }
463
464 sock_handler_printf(Client->sh, 0, "</BODY></HTML>\n");
470 a_Dpip_dsh_write_str(client->sh, 0, "<br><br>Directory is empty...");
471 }
472 client->state = st_http;
473
474 } else if (client->state == st_http) {
475 /* send directories as HTML contents */
476 for (n = 0; n < dList_length(Ddir->flist); ++n) {
477 File_info2html(client, dList_nth_data(Ddir->flist,n), n+1);
478 }
479
480 if (client->old_style) {
481 a_Dpip_dsh_write_str(client->sh, 0, "</pre>\n");
482 } else if (dList_length(Ddir->flist)) {
483 a_Dpip_dsh_write_str(client->sh, 0, "</table>\n");
484 }
485
486 a_Dpip_dsh_write_str(client->sh, 1, "</BODY></HTML>\n");
487 client->state = st_content;
488 client->flags |= FILE_DONE;
489 }
465490 }
466491
467492 /*
471496 {
472497 char *e;
473498
474 if ( !(e = strrchr(filename, '.')) )
499 if (!(e = strrchr(filename, '.')))
475500 return NULL;
476501
477502 e++;
478503
479 if (!strcasecmp(e, "gif")) {
504 if (!dStrcasecmp(e, "gif")) {
480505 return "image/gif";
481 } else if (!strcasecmp(e, "jpg") ||
482 !strcasecmp(e, "jpeg")) {
506 } else if (!dStrcasecmp(e, "jpg") ||
507 !dStrcasecmp(e, "jpeg")) {
483508 return "image/jpeg";
484 } else if (!strcasecmp(e, "png")) {
509 } else if (!dStrcasecmp(e, "png")) {
485510 return "image/png";
486 } else if (!strcasecmp(e, "html") ||
487 !strcasecmp(e, "htm") ||
488 !strcasecmp(e, "shtml")) {
511 } else if (!dStrcasecmp(e, "html") ||
512 !dStrcasecmp(e, "htm") ||
513 !dStrcasecmp(e, "shtml")) {
489514 return "text/html";
515 } else if (!dStrcasecmp(e, "txt")) {
516 return "text/plain";
490517 } else {
491518 return NULL;
492519 }
498525 */
499526 static const char *File_content_type(const char *filename)
500527 {
501 gint fd;
528 int fd;
502529 struct stat sb;
503 const gchar *ct;
504 gchar buf[256];
530 const char *ct;
531 char buf[256];
505532 ssize_t buf_size;
506533
507534 if (!(ct = File_ext(filename))) {
517544 File_close(fd);
518545 }
519546 }
520
547 _MSG("File_content_type: name=%s ct=%s\n", filename, ct);
521548 return ct;
522549 }
523550
524551 /*
552 * Send an error page
553 */
554 static void File_prepare_send_error_page(ClientInfo *client, int res,
555 const char *orig_url)
556 {
557 client->state = st_err;
558 client->err_code = res;
559 client->orig_url = dStrdup(orig_url);
560 client->flags &= ~FILE_READ;
561 client->flags |= FILE_WRITE;
562 }
563
564 /*
565 * Send an error page
566 */
567 static void File_send_error_page(ClientInfo *client)
568 {
569 const char *status;
570 char *d_cmd;
571 Dstr *body = dStr_sized_new(128);
572
573 if (client->err_code == EACCES) {
574 status = "403 Forbidden";
575 } else if (client->err_code == ENOENT) {
576 status = "404 Not Found";
577 } else {
578 /* good enough */
579 status = "500 Internal Server Error";
580 }
581 dStr_append(body, status);
582 dStr_append(body, "\n");
583 dStr_append(body, dStrerror(client->err_code));
584
585 /* Send DPI command */
586 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
587 client->orig_url);
588 a_Dpip_dsh_write_str(client->sh, 0, d_cmd);
589 dFree(d_cmd);
590
591 a_Dpip_dsh_printf(client->sh, 0,
592 "HTTP/1.1 %s\r\n"
593 "Content-Type: text/plain\r\n"
594 "Content-Length: %d\r\n"
595 "\r\n"
596 "%s",
597 status, body->len, body->str);
598 dStr_free(body, TRUE);
599
600 client->flags |= FILE_DONE;
601 }
602
603 /*
604 * Scan the directory, sort and prepare to send it enclosed in HTTP.
605 */
606 static int File_prepare_send_dir(ClientInfo *client,
607 const char *DirName, const char *orig_url)
608 {
609 Dstr *ds_dirname;
610 DilloDir *Ddir;
611
612 /* Let's make sure this directory url has a trailing slash */
613 ds_dirname = dStr_new(DirName);
614 if (ds_dirname->str[ds_dirname->len - 1] != '/')
615 dStr_append(ds_dirname, "/");
616
617 /* Let's get a structure ready for transfer */
618 Ddir = File_dillodir_new(ds_dirname->str);
619 dStr_free(ds_dirname, TRUE);
620 if (Ddir) {
621 /* looks ok, set things accordingly */
622 client->orig_url = dStrdup(orig_url);
623 client->d_dir = Ddir;
624 client->state = st_start;
625 client->flags &= ~FILE_READ;
626 client->flags |= FILE_WRITE;
627 return 0;
628 } else
629 return EACCES;
630 }
631
632 /*
633 * Prepare to send HTTP headers and then the file itself.
634 */
635 static int File_prepare_send_file(ClientInfo *client,
636 const char *filename,
637 const char *orig_url)
638 {
639 int fd, res = -1;
640 struct stat sb;
641
642 if (stat(filename, &sb) != 0) {
643 /* prepare a file-not-found error */
644 res = ENOENT;
645 } else if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) {
646 /* prepare an error message */
647 res = errno;
648 } else {
649 /* looks ok, set things accordingly */
650 client->file_fd = fd;
651 client->file_sz = sb.st_size;
652 client->d_dir = NULL;
653 client->state = st_start;
654 client->filename = dStrdup(filename);
655 client->orig_url = dStrdup(orig_url);
656 client->flags &= ~FILE_READ;
657 client->flags |= FILE_WRITE;
658 res = 0;
659 }
660 return res;
661 }
662
663 /*
525664 * Try to stat the file and determine if it's readable.
526665 */
527 static void File_get(ClientInfo *Client, const char *filename,
666 static void File_get(ClientInfo *client, const char *filename,
528667 const char *orig_url)
529668 {
530669 int res;
531670 struct stat sb;
532 char *msg = NULL, *d_cmd;
533671
534672 if (stat(filename, &sb) != 0) {
535673 /* stat failed, prepare a file-not-found error. */
536 res = FILE_NOT_FOUND;
674 res = ENOENT;
537675 } else if (S_ISDIR(sb.st_mode)) {
538676 /* set up for reading directory */
539 res = File_get_dir(Client, filename, orig_url);
677 res = File_prepare_send_dir(client, filename, orig_url);
540678 } else {
541679 /* set up for reading a file */
542 res = File_get_file(Client, filename, &sb, orig_url);
543 }
544
545 if (res == FILE_NOT_FOUND) {
546 msg = g_strdup_printf("%s Not Found: %s",
547 S_ISDIR(sb.st_mode) ? "Directory" : "File", filename);
548 } else if (res == FILE_NO_ACCESS) {
549 msg = g_strdup_printf("Access denied to %s: %s",
550 S_ISDIR(sb.st_mode) ? "Directory" : "File", filename);
551 }
552 if (msg) {
553 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_status_message", msg);
554 sock_handler_write_str(Client->sh, d_cmd, 1);
555 g_free(d_cmd);
556 g_free(msg);
557 }
558 }
559
560 /*
561 *
562 */
563 static gint File_get_dir(ClientInfo *Client,
564 const gchar *DirName, const char *orig_url)
565 {
566 GString *g_dirname;
567 DilloDir *Ddir;
568
569 /* Let's make sure this directory url has a trailing slash */
570 g_dirname = g_string_new(DirName);
571 if ( g_dirname->str[g_dirname->len - 1] != '/' )
572 g_string_append(g_dirname, "/");
573
574 /* Let's get a structure ready for transfer */
575 Ddir = File_dillodir_new(g_dirname->str);
576 g_string_free(g_dirname, TRUE);
577 if ( Ddir ) {
578 File_transfer_dir(Client, Ddir, orig_url);
579 File_dillodir_free(Ddir);
580 return FILE_OK;
581 } else
582 return FILE_NO_ACCESS;
583 }
584
585 /*
586 * Send the MIME content/type and then send the file itself.
587 */
588 static gint File_get_file(ClientInfo *Client,
589 const gchar *filename,
590 struct stat *sb,
591 const char *orig_url)
592 {
680 res = File_prepare_send_file(client, filename, orig_url);
681 }
682 if (res != 0) {
683 File_prepare_send_error_page(client, res, orig_url);
684 }
685 }
686
687 /*
688 * Send HTTP headers and then the file itself.
689 */
690 static int File_send_file(ClientInfo *client)
691 {
692 //#define LBUF 1
593693 #define LBUF 16*1024
594694
595 const gchar *ct;
596 char buf[LBUF], *d_cmd;
597 gint fd, st;
598
599 if ( (fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
600 return FILE_NO_ACCESS;
601
602 /* Content-Type info is based on filename extension. If there's no
603 * known extension, then we do data sniffing. If this doesn't lead
604 * to a conclusion, "application/octet-stream" is sent.
605 */
606 if (!(ct = File_content_type(filename)))
607 ct = "application/octet-stream";
608
609 /* Send DPI command */
610 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
611 sock_handler_write_str(Client->sh, d_cmd, 1);
612 g_free(d_cmd);
613
614 /* Send HTTP stream */
615 sock_handler_printf(Client->sh, 0,
616 "Content-Type: %s\n"
617 "Content-length: %ld\n\n",
618 ct, sb->st_size);
619
620 /* Send raw file contents */
621 do {
622 if ((st = read(fd, buf, LBUF)) > 0) {
623 if (sock_handler_write(Client->sh, buf, (size_t)st, 0) != 0)
624 break;
625 } else if (st < 0) {
626 perror("[read]");
627 if (errno == EINTR || errno == EAGAIN)
628 continue;
629 }
630 } while (st > 0);
631
632 /* todo: It may be better to send an error report to dillo instead of
633 * calling abort from g_error() */
634 if (st == -1)
635 g_error("ERROR while reading from file \"%s\", error was \"%s\"\n",
636 filename, strerror(errno));
637
638 File_close(fd);
639 return FILE_OK;
640 }
641
642 /*
643 * Given an hex octet (e3, 2F, 20), return the corresponding
695 const char *ct;
696 const char *unknown_type = "application/octet-stream";
697 char buf[LBUF], *d_cmd, *name;
698 int st, st2, namelen;
699 bool_t gzipped = FALSE;
700
701 if (client->state == st_start) {
702 /* Send DPI command */
703 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
704 client->orig_url);
705 a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
706 dFree(d_cmd);
707 client->state = st_dpip;
708
709 } else if (client->state == st_dpip) {
710 /* send HTTP header */
711
712 /* Check for gzipped file */
713 namelen = strlen(client->filename);
714 if (namelen > 3 && !dStrcasecmp(client->filename + namelen - 3, ".gz")) {
715 gzipped = TRUE;
716 namelen -= 3;
717 }
718 /* Content-Type info is based on filename extension (with ".gz" removed).
719 * If there's no known extension, perform data sniffing.
720 * If this doesn't lead to a conclusion, use "application/octet-stream".
721 */
722 name = dStrndup(client->filename, namelen);
723 if (!(ct = File_content_type(name)))
724 ct = unknown_type;
725 dFree(name);
726
727 /* Send HTTP headers */
728 a_Dpip_dsh_write_str(client->sh, 0, "HTTP/1.1 200 OK\r\n");
729 if (gzipped) {
730 a_Dpip_dsh_write_str(client->sh, 0, "Content-Encoding: gzip\r\n");
731 }
732 if (!gzipped || strcmp(ct, unknown_type)) {
733 a_Dpip_dsh_printf(client->sh, 0, "Content-Type: %s\r\n", ct);
734 } else {
735 /* If we don't know type for gzipped data, let dillo figure it out. */
736 }
737 a_Dpip_dsh_printf(client->sh, 1,
738 "Content-Length: %ld\r\n"
739 "\r\n",
740 client->file_sz);
741 client->state = st_http;
742
743 } else if (client->state == st_http) {
744 /* Send body -- raw file contents */
745 if ((st = a_Dpip_dsh_tryflush(client->sh)) < 0) {
746 client->flags |= (st == -3) ? FILE_ERR : 0;
747 } else {
748 /* no pending data, let's send new data */
749 do {
750 st2 = read(client->file_fd, buf, LBUF);
751 } while (st2 < 0 && errno == EINTR);
752 if (st2 < 0) {
753 MSG("\nERROR while reading from file '%s': %s\n\n",
754 client->filename, dStrerror(errno));
755 client->flags |= FILE_ERR;
756 } else if (st2 == 0) {
757 client->state = st_content;
758 client->flags |= FILE_DONE;
759 } else {
760 /* ok to write */
761 st = a_Dpip_dsh_trywrite(client->sh, buf, st2);
762 client->flags |= (st == -3) ? FILE_ERR : 0;
763 }
764 }
765 }
766
767 return 0;
768 }
769
770 /*
771 * Given a hex octet (e3, 2F, 20), return the corresponding
644772 * character if the octet is valid, and -1 otherwise
645773 */
646 static int File_parse_hex_octet(const gchar *s)
647 {
648 gint hex_value;
649 gchar *tail, hex[3];
650
651 if ( (hex[0] = s[0]) && (hex[1] = s[1]) ) {
774 static int File_parse_hex_octet(const char *s)
775 {
776 int hex_value;
777 char *tail, hex[3];
778
779 if ((hex[0] = s[0]) && (hex[1] = s[1])) {
652780 hex[2] = 0;
653781 hex_value = strtol(hex, &tail, 16);
654782 if (tail - hex == 2)
658786 return -1;
659787 }
660788
661 static void File_parse_hex_octets(char **str)
662 {
663 gchar *dest, *tmp, *orig = *str;
664 int i, val;
665
666 if (strchr(orig, '%')) {
667 dest = tmp = g_new(gchar, strlen(orig) + 1);
668
669 for (i = 0; orig[i]; i++) {
670 if (orig[i] == '%' &&
671 (val = File_parse_hex_octet(orig + i + 1)) >= 0) {
672 *dest++ = val;
673 i += 2;
674 } else {
675 *dest++ = orig[i];
676 }
677 }
678 *dest = '\0';
679
680 g_free(orig);
681 *str = tmp;
682 }
683 }
684
685789 /*
686790 * Make a file URL into a human (and machine) readable path.
687791 * The idea is to always have a path that starts with only one slash.
689793 */
690794 static char *File_normalize_path(const char *orig)
691795 {
692 char *str = (char *) orig, *basename = NULL, *ret, *p;
693
694 /* Make sure the string starts with file: (it should, but...) */
695 if (strncmp(str, "file:", 5) != 0)
696 return NULL;
697
796 char *str = (char *) orig, *basename = NULL, *ret = NULL, *p;
797
798 dReturn_val_if (orig == NULL, ret);
799
800 /* Make sure the string starts with "file:/" */
801 if (strncmp(str, "file:/", 5) != 0)
802 return ret;
698803 str += 5;
699804
700 /* Skip slashes */
701 while (str[0] == '/' && str[1] != '\0' && str[1] == '/')
805 /* Skip "localhost" */
806 if (dStrncasecmp(str, "//localhost/", 12) == 0)
807 str += 11;
808
809 /* Skip packed slashes, and leave just one */
810 while (str[0] == '/' && str[1] == '/')
702811 str++;
703812
704 if (*str == '\0') {
705 /* Give CWD if the string is only "file:" */
706 basename = g_get_current_dir();
707 } else if (*str == '~') {
708 if (str[1] == '\0' || str[1] == '/') {
709 /* Expand 'tilde' to user's home */
710 basename = g_strdup(g_get_home_dir());
711 str++;
712 }
713 } else if (*str == '.') {
714 if (str[1] == '\0' || str[1] == '/') {
715 /* User wants the CWD */
716 basename = g_get_current_dir();
717 str++;
718 } else if (str[1] == '.') {
719 /* One level down from the CWD */
720 char *tmp1 = g_get_current_dir();
721 char *tmp2 = strrchr(tmp1, '/');
722
723 if (tmp2) {
724 basename = g_strndup(tmp1, (guint)(tmp2 - tmp1));
813 {
814 int i, val;
815 Dstr *ds = dStr_sized_new(32);
816 dStr_sprintf(ds, "%s%s%s",
817 basename ? basename : "",
818 basename ? "/" : "",
819 str);
820 dFree(basename);
821
822 /* Parse possible hexadecimal octets in the URI path */
823 for (i = 0; ds->str[i]; ++i) {
824 if (ds->str[i] == '%' &&
825 (val = File_parse_hex_octet(ds->str + i+1)) != -1) {
826 ds->str[i] = val;
827 dStr_erase(ds, i+1, 2);
725828 }
726 str += 2;
727 g_free(tmp1);
728 }
729 } else if (*str != '/') {
730 return NULL;
731 }
732
733 ret = g_strdup_printf("%s%s%s",
734 basename ? basename : "",
735 basename ? "/" : "",
736 str);
737 g_free(basename);
738
739 /* remove the fragment if present */
740 if ((p = strrchr(ret, '#')) != NULL)
741 *p = 0;
742 /* Parse possible hexadecimal octets in the URI path */
743 File_parse_hex_octets(&ret);
829 }
830 /* Remove the fragment if not a part of the filename */
831 if ((p = strrchr(ds->str, '#')) != NULL && access(ds->str, F_OK) != 0)
832 dStr_truncate(ds, p - ds->str);
833 ret = ds->str;
834 dStr_free(ds, 0);
835 }
744836
745837 return ret;
746838 }
747839
748840 /*
749 * Set the style flag and ask for a reload, so it shows inmediatly.
750 */
751 static void File_toggle_html_style(ClientInfo *Client)
841 * Set the style flag and ask for a reload, so it shows immediately.
842 */
843 static void File_toggle_html_style(ClientInfo *client)
752844 {
753845 char *d_cmd;
754846
755847 OLD_STYLE = !OLD_STYLE;
756848 d_cmd = a_Dpip_build_cmd("cmd=%s", "reload_request");
757 sock_handler_write_str(Client->sh, d_cmd, 1);
758 g_free(d_cmd);
849 a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
850 dFree(d_cmd);
759851 }
760852
761853 /*
763855 */
764856 static void termination_handler(int signum)
765857 {
858 MSG("\nexit(signum), signum=%d\n\n", signum);
766859 exit(signum);
767860 }
768861
772865 /*
773866 * Add a new client to the list.
774867 */
775 static ClientInfo *File_add_client(gint sock_fd)
776 {
777 ClientInfo *NewClient;
778
779 NewClient = g_new(ClientInfo, 1);
780 NewClient->sh = sock_handler_new(sock_fd, sock_fd, 8*1024);
781 NewClient->status = 0;
782 NewClient->done = 0;
783 NewClient->old_style = OLD_STYLE;
784 pthread_mutex_lock(&ClMut);
785 Clients = g_list_append(Clients, NewClient);
786 pthread_mutex_unlock(&ClMut);
787 return NewClient;
788 }
789
790 /*
791 * Get client record by number
792 */
793 static void *File_get_client_n(guint n)
794 {
795 void *client;
796
797 pthread_mutex_lock(&ClMut);
798 client = g_list_nth_data(Clients, n);
799 pthread_mutex_unlock(&ClMut);
800
801 return client;
868 static ClientInfo *File_add_client(int sock_fd)
869 {
870 ClientInfo *new_client;
871
872 new_client = dNew(ClientInfo, 1);
873 new_client->sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
874 new_client->orig_url = NULL;
875 new_client->filename = NULL;
876 new_client->file_fd = -1;
877 new_client->file_sz = 0;
878 new_client->d_dir = NULL;
879 new_client->state = 0;
880 new_client->err_code = 0;
881 new_client->flags = FILE_READ;
882 new_client->old_style = OLD_STYLE;
883
884 dList_append(Clients, new_client);
885 return new_client;
802886 }
803887
804888 /*
805889 * Remove a client from the list.
806890 */
807 static void File_remove_client_n(guint n)
808 {
809 ClientInfo *Client;
810
811 pthread_mutex_lock(&ClMut);
812 Client = g_list_nth_data(Clients, n);
813 Clients = g_list_remove(Clients, (void *)Client);
814 pthread_mutex_unlock(&ClMut);
891 static void File_remove_client(ClientInfo *client)
892 {
893 dList_remove(Clients, (void *)client);
815894
816895 _MSG("Closing Socket Handler\n");
817 sock_handler_close(Client->sh);
818 sock_handler_free(Client->sh);
819 g_free(Client);
820 }
821
822 /*
823 * Return the number of clients.
824 */
825 static gint File_num_clients(void)
826 {
827 guint n;
828
829 pthread_mutex_lock(&ClMut);
830 n = g_list_length(Clients);
831 pthread_mutex_unlock(&ClMut);
832
833 return n;
896 a_Dpip_dsh_close(client->sh);
897 a_Dpip_dsh_free(client->sh);
898 File_close(client->file_fd);
899 dFree(client->orig_url);
900 dFree(client->filename);
901 File_dillodir_free(client->d_dir);
902
903 dFree(client);
834904 }
835905
836906 /*
837907 * Serve this client.
838 * (this function runs on its own thread)
839 */
840 static void *File_serve_client(void *data)
841 {
842 char *dpip_tag, *cmd = NULL, *url = NULL, *path;
843 ClientInfo *Client = data;
844
845 /* Read the dpi command */
846 dpip_tag = sock_handler_read(Client->sh);
847 MSG("dpip_tag={%s}\n", dpip_tag);
848
849 if (dpip_tag) {
850 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
851 if (cmd) {
852 if (strcmp(cmd, "DpiBye") == 0) {
853 DPIBYE = 1;
908 */
909 static void File_serve_client(void *data, int f_write)
910 {
911 char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *path;
912 ClientInfo *client = data;
913 int st;
914
915 while (1) {
916 _MSG("File_serve_client %p, flags=%d state=%d\n",
917 client, client->flags, client->state);
918 if (client->flags & (FILE_DONE | FILE_ERR))
919 break;
920 if (client->flags & FILE_READ) {
921 dpip_tag = a_Dpip_dsh_read_token(client->sh, 0);
922 _MSG("dpip_tag={%s}\n", dpip_tag);
923 if (!dpip_tag)
924 break;
925 }
926
927 if (client->flags & FILE_READ) {
928 if (!(client->flags & FILE_AUTH_OK)) {
929 /* Authenticate our client... */
930 st = a_Dpip_check_auth(dpip_tag);
931 _MSG("a_Dpip_check_auth returned %d\n", st);
932 client->flags |= (st == 1) ? FILE_AUTH_OK : FILE_ERR;
854933 } else {
855 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
856 if (!url)
857 MSG("file.dpi:: Failed to parse 'url'\n");
934 /* Get file request */
935 cmd = a_Dpip_get_attr(dpip_tag, "cmd");
936 url = a_Dpip_get_attr(dpip_tag, "url");
937 path = File_normalize_path(url);
938 if (cmd) {
939 if (strcmp(cmd, "DpiBye") == 0) {
940 DPIBYE = 1;
941 MSG("(pid %d): Got DpiBye.\n", (int)getpid());
942 client->flags |= FILE_DONE;
943 } else if (url && strcmp(url, "dpi:/file/toggle") == 0) {
944 File_toggle_html_style(client);
945 } else if (path) {
946 File_get(client, path, url);
947 } else {
948 client->flags |= FILE_ERR;
949 MSG("ERROR: URL was %s\n", url);
950 }
951 }
952 dFree(path);
953 dFree(url);
954 dFree(cmd);
955 dFree(dpip_tag);
956 break;
858957 }
859 }
860 }
861 g_free(cmd);
862 g_free(dpip_tag);
863
864 if (!DPIBYE && url) {
865 _MSG("url = '%s'\n", url);
866
867 path = File_normalize_path(url);
868 if (path) {
869 _MSG("path = '%s'\n", path);
870 File_get(Client, path, url);
871 } else if (strcmp(url, "dpi:/file/toggle") == 0) {
872 File_toggle_html_style(Client);
873 } else {
874 MSG("ERROR: URL path was %s\n", url);
875 }
876 g_free(path);
877 }
878 g_free(url);
879
880 /* flag the the transfer finished */
881 Client->done = 1;
882
883 return NULL;
958 dFree(dpip_tag);
959
960 } else if (f_write) {
961 /* send our answer */
962 if (client->state == st_err)
963 File_send_error_page(client);
964 else if (client->d_dir)
965 File_send_dir(client);
966 else
967 File_send_file(client);
968 break;
969 }
970 } /*while*/
971
972 client->flags |= (client->sh->status & DPIP_ERROR) ? FILE_ERR : 0;
973 client->flags |= (client->sh->status & DPIP_EOF) ? FILE_DONE : 0;
884974 }
885975
886976 /*
887977 * Serve the client queue.
888 * (this function runs on its own thread)
889 */
890 static void *File_serve_clients(void *client)
891 {
892 /* switch to detached state */
893 pthread_detach(pthread_self());
894
895 while (File_num_clients()) {
896 client = File_get_client_n((guint)0);
897 File_serve_client(client);
898 File_remove_client_n((guint)0);
899 }
900 ThreadRunning = 0;
901
902 return NULL;
978 */
979 static void File_serve_clients()
980 {
981 int i, f_read, f_write;
982 ClientInfo *client;
983
984 for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
985 f_read = FD_ISSET(client->sh->fd_in, &read_set);
986 f_write = FD_ISSET(client->sh->fd_out, &write_set);
987 if (!f_read && !f_write)
988 continue;
989 File_serve_client(client, f_write);
990 if (client->flags & (FILE_DONE | FILE_ERR)) {
991 File_remove_client(client);
992 --i;
993 }
994 }
903995 }
904996
905997 /* --------------------------------------------------------------------------*/
906998
907999 /*
908 * Check a fd for activity, with a max timeout.
1000 * Check the fd sets for activity, with a max timeout.
9091001 * return value: 0 if timeout, 1 if input available, -1 if error.
9101002 */
911 int File_check_fd(int filedes, unsigned int seconds)
912 {
913 int st;
914 fd_set set;
915 struct timeval timeout;
916
917 /* Initialize the file descriptor set. */
918 FD_ZERO (&set);
919 FD_SET (filedes, &set);
920
921 /* Initialize the timeout data structure. */
922 timeout.tv_sec = seconds;
923 timeout.tv_usec = 0;
924
925 do {
926 st = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
927 } while (st == -1 && errno == EINTR);
928
929 return st;
1003 static int File_check_fds(uint_t seconds)
1004 {
1005 int i, st;
1006 ClientInfo *client;
1007 struct timeval timeout;
1008
1009 /* initialize observed file descriptors */
1010 FD_ZERO (&read_set);
1011 FD_ZERO (&write_set);
1012 FD_SET (STDIN_FILENO, &read_set);
1013 for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
1014 if (client->flags & FILE_READ)
1015 FD_SET (client->sh->fd_in, &read_set);
1016 if (client->flags & FILE_WRITE)
1017 FD_SET (client->sh->fd_out, &write_set);
1018 }
1019 _MSG("Watching %d fds\n", dList_length(Clients) + 1);
1020
1021 /* Initialize the timeout data structure. */
1022 timeout.tv_sec = seconds;
1023 timeout.tv_usec = 0;
1024
1025 do {
1026 st = select(FD_SETSIZE, &read_set, &write_set, NULL, &timeout);
1027 } while (st == -1 && errno == EINTR);
1028 /*
1029 MSG_RAW(" (%d%s%s)", STDIN_FILENO,
1030 FD_ISSET(STDIN_FILENO, &read_set) ? "R" : "",
1031 FD_ISSET(STDIN_FILENO, &write_set) ? "W" : "");
1032 for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
1033 MSG_RAW(" (%d%s%s)", client->sh->fd_in,
1034 FD_ISSET(client->sh->fd_in, &read_set) ? "R" : "",
1035 FD_ISSET(client->sh->fd_out, &write_set) ? "W" : "");
1036 }
1037 MSG_RAW("\n");
1038 */
1039 return st;
9301040 }
9311041
9321042
9331043 int main(void)
9341044 {
935 ClientInfo *NewClient;
936 struct sockaddr_un spun;
937 gint temp_sock_descriptor;
938 gint address_size, c_st, st = 1;
939 guint i;
1045 struct sockaddr_in sin;
1046 socklen_t sin_sz;
1047 int sock_fd, c_st, st = 1;
9401048
9411049 /* Arrange the cleanup function for abnormal terminations */
9421050 if (signal (SIGINT, termination_handler) == SIG_IGN)
9461054 if (signal (SIGTERM, termination_handler) == SIG_IGN)
9471055 signal (SIGTERM, SIG_IGN);
9481056
949 MSG("(v.1) accepting connections...\n");
950
951 /* initialize mutex */
952 pthread_mutex_init(&ClMut, NULL);
1057 MSG("(v.2) accepting connections...\n");
1058 //sleep(20);
1059
1060 /* initialize observed file descriptors */
1061 FD_ZERO (&read_set);
1062 FD_ZERO (&write_set);
1063 FD_SET (STDIN_FILENO, &read_set);
1064
1065 /* Set STDIN socket nonblocking (to ensure accept() never blocks) */
1066 fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK | fcntl(STDIN_FILENO, F_GETFL));
1067
1068 /* initialize Clients list */
1069 Clients = dList_new(512);
9531070
9541071 /* some OSes may need this... */
955 address_size = sizeof(struct sockaddr_un);
1072 sin_sz = sizeof(sin);
9561073
9571074 /* start the service loop */
9581075 while (!DPIBYE) {
959 /* wait for a connection */
1076 /* wait for activity */
9601077 do {
961 c_st = File_check_fd(STDIN_FILENO, 1);
1078 c_st = File_check_fds(10);
9621079 } while (c_st == 0 && !DPIBYE);
9631080 if (c_st < 0) {
964 perror("[select]");
1081 MSG(" select() %s\n", dStrerror(errno));
9651082 break;
9661083 }
9671084 if (DPIBYE)
9681085 break;
9691086
970 temp_sock_descriptor =
971 accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
972
973 if (temp_sock_descriptor == -1) {
974 perror("[accept]");
975 break;
976 }
977
978 /* Create and initialize a new client */
979 NewClient = File_add_client(temp_sock_descriptor);
980
981 if (!ThreadRunning) {
982 ThreadRunning = 1;
983 /* Serve the client from a thread (avoids deadlocks) */
984 if (pthread_create(&NewClient->thrID, NULL,
985 File_serve_clients, NewClient) != 0) {
986 perror("[pthread_create]");
987 ThreadRunning = 0;
1087 if (FD_ISSET(STDIN_FILENO, &read_set)) {
1088 /* accept the incoming connection */
1089 do {
1090 sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&sin, &sin_sz);
1091 } while (sock_fd < 0 && errno == EINTR);
1092 if (sock_fd == -1) {
1093 if (errno == EAGAIN)
1094 continue;
1095 MSG(" accept() %s\n", dStrerror(errno));
9881096 break;
1097 } else {
1098 _MSG(" accept() fd=%d\n", sock_fd);
1099 /* Set nonblocking */
1100 fcntl(sock_fd, F_SETFL, O_NONBLOCK | fcntl(sock_fd, F_GETFL));
1101 /* Create and initialize a new client */
1102 File_add_client(sock_fd);
9891103 }
990 }
991 }
992
993 /* todo: handle a running thread better. */
994 for (i = 0; i < 5 && ThreadRunning; ++i) {
995 MSG("sleep i=%u", i);
996 sleep(i);
1104 continue;
1105 }
1106
1107 File_serve_clients();
9971108 }
9981109
9991110 if (DPIBYE)
22 *
33 * This server checks the ftp-URL to be a directory (requires wget).
44 * If true, it sends back an html representation of it, and if not
5 * a dpip message (which is catched by dillo who redirects the ftp URL
5 * a dpip message (which is caught by dillo who redirects the ftp URL
66 * to the downloads server).
77 *
88 * Feel free to polish!
99 *
10 * Copyright 2003-2005 Jorge Arellano Cid <jcid@dillo.org>
10 * Copyright 2003-2007 Jorge Arellano Cid <jcid@dillo.org>
1111 *
1212 * This program is free software; you can redistribute it and/or modify
1313 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
14 * the Free Software Foundation; either version 3 of the License, or
1515 * (at your option) any later version.
1616 *
1717 */
4040 #include <sys/time.h>
4141 #include <ctype.h>
4242
43 #include <glib.h>
44
4543 #include "../dpip/dpip.h"
4644 #include "dpiutil.h"
45 #include "d_size.h"
4746
4847 /*
4948 * Debugging macros
50 */
51 #define _MSG(fmt...)
52 #define MSG(fmt...) g_printerr("[ftp dpi]: " fmt)
49 * (Set debugging messages to stderr, to see them)
50 */
51 #define _MSG(...)
52 //#define MSG(...) fprintf(stderr, "[ftp dpi]: " __VA_ARGS__)
53 #define MSG(...) printf("[ftp dpi]: " __VA_ARGS__)
5354
5455 /*
5556 * Global variables
5657 */
57 static SockHandler *sh = NULL;
58 char **dl_argv = NULL;
58 static Dsh *sh = NULL;
59 static char **dl_argv = NULL;
5960
6061 /*---------------------------------------------------------------------------*/
6162
8586 *
8687 * Return value: (0 on success, 1 on doubt, 2 on lack of data).
8788 */
88 int a_Misc_get_content_type_from_data(void *Data, size_t Size,
89 const char **PT)
89 static int a_Misc_get_content_type_from_data2(void *Data, size_t Size,
90 const char **PT)
9091 {
9192 int st = 1; /* default to "doubt' */
9293 int Type = 0; /* default to "application/octet-stream" */
9394 char *p = Data;
95 uchar_t ch;
9496 size_t i, non_ascci;
9597
9698 /* HTML try */
97 for (i = 0; i < Size && isspace(p[i]); ++i);
98 if ((Size - i >= 5 && !g_strncasecmp(p+i, "<html", 5)) ||
99 (Size - i >= 5 && !g_strncasecmp(p+i, "<head", 5)) ||
100 (Size - i >= 6 && !g_strncasecmp(p+i, "<title", 6)) ||
101 (Size - i >= 14 && !g_strncasecmp(p+i, "<!doctype html", 14)) ||
99 for (i = 0; i < Size && dIsspace(p[i]); ++i);
100 if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) ||
101 (Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) ||
102 (Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) ||
103 (Size - i >= 14 && !dStrncasecmp(p+i, "<!doctype html", 14)) ||
102104 /* this line is workaround for FTP through the Squid proxy */
103 (Size - i >= 17 && !g_strncasecmp(p+i, "<!-- HTML listing", 17))) {
105 (Size - i >= 17 && !dStrncasecmp(p+i, "<!-- HTML listing", 17))) {
104106
105107 Type = 1;
106108 st = 0;
107109 /* Images */
108 } else if (Size >= 4 && !g_strncasecmp(p, "GIF8", 4)) {
110 } else if (Size >= 4 && !dStrncasecmp(p, "GIF8", 4)) {
109111 Type = 3;
110112 st = 0;
111 } else if (Size >= 4 && !g_strncasecmp(p, "\x89PNG", 4)) {
113 } else if (Size >= 4 && !dStrncasecmp(p, "\x89PNG", 4)) {
112114 Type = 4;
113115 st = 0;
114 } else if (Size >= 2 && !g_strncasecmp(p, "\xff\xd8", 2)) {
116 } else if (Size >= 2 && !dStrncasecmp(p, "\xff\xd8", 2)) {
115117 /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
116118 * at the character representation should be machine independent. */
117119 Type = 5;
119121
120122 /* Text */
121123 } else {
122 /* We'll assume "text/plain" if the set of chars above 127 is <= 10
123 * in a 256-bytes sample. Better heuristics are welcomed! :-) */
124 /* We'll assume "text/plain" if the set of chars above 127 is <= 10%
125 * of the sample. This helps to catch ASCII, LATIN1 and UTF-8 as text.
126 * Better heuristics are welcomed! :-) */
124127 non_ascci = 0;
125128 Size = MIN (Size, 256);
126 for (i = 0; i < Size; i++)
127 if ((unsigned char) p[i] > 127)
129 for (i = 0; i < Size; i++) {
130 ch = (uchar_t) p[i];
131 if ((ch < 32 || ch > 126) && !dIsspace(ch))
128132 ++non_ascci;
133 }
129134 if (Size == 256) {
130 Type = (non_ascci > 10) ? 0 : 2;
135 Type = (non_ascci > Size/10) ? 0 : 2;
131136 st = 0;
132137 } else {
133 Type = (non_ascci > 0) ? 0 : 2;
138 Type = (non_ascci > Size/10) ? 0 : 2;
134139 }
135140 }
136141
143148 /*
144149 * Build a shell command using wget for this URL.
145150 */
146 static void make_wget_argv(gchar *url)
151 static void make_wget_argv(char *url)
147152 {
148 gchar *esc_url;
153 char *esc_url;
149154
150155 if (dl_argv) {
151 g_free(dl_argv[2]);
152 g_free(dl_argv);
153 }
154 dl_argv = g_new(gchar*, 10);
156 dFree(dl_argv[2]);
157 dFree(dl_argv);
158 }
159 dl_argv = dNew(char*, 10);
155160
156161 esc_url = Escape_uri_str(url, "'");
157162 /* avoid malicious SMTP relaying with FTP urls */
159164
160165 dl_argv[0] = "wget";
161166 dl_argv[1] = "-O-";
162 dl_argv[2] = g_strdup(esc_url);
167 dl_argv[2] = esc_url;
163168 dl_argv[3] = NULL;
164 /*
165 dl_argv[0] = "wget";
166 dl_argv[1] = "-t2";
167 dl_argv[2] = "-O-";
168 dl_argv[3] = g_strdup_printf("'%s'", esc_url);
169 dl_argv[4] = "2>/dev/null";
170 dl_argv[5] = NULL;
171 */
172 g_free(esc_url);
173169 }
174170
175171 /*
176172 * Fork, exec command, get its output and send via stdout.
177 * Return: Number of bytes transfered.
178 */
179 static gint try_ftp_transfer(gchar *url)
173 * Return: Number of bytes transfered, -1 if file-not_found, -2 if aborted.
174 */
175 static int try_ftp_transfer(char *url)
180176 {
181 #define MinSZ 256
177 #define MIN_SZ 256
178 #define READ_SZ 16*1024
182179
183180 ssize_t n;
184 gint nb, minibuf_sz;
185 const gchar *mime_type;
186 char buf[4096], minibuf[MinSZ], *d_cmd;
181 int nb, has_mime_type, has_html_header, no_such_file, offer_download;
182 const char *mime_type = "application/octet-stream";
183 char buf[READ_SZ], *d_cmd;
184 Dstr *dbuf = dStr_sized_new(READ_SZ);
187185 pid_t ch_pid;
188 gint aborted = 0;
186 int aborted = 0;
189187 int DataPipe[2];
190188
191189 if (pipe(DataPipe) < 0) {
192 MSG("pipe, %s\n", strerror(errno));
190 MSG("pipe, %s\n", dStrerror(errno));
193191 return 0;
194192 }
195193
207205 } else if (ch_pid < 0) {
208206 perror("fork, ");
209207 exit(1);
210 } else {
208 } else {
211209 /* father continues below */
212210 close(DataPipe[1]);
213211 }
214212
215213 /* Read/Write the real data */
216 minibuf_sz = 0;
217 for (nb = 0; 1; nb += n) {
218 while ((n = read(DataPipe[0], buf, 4096)) < 0 && errno == EINTR);
219 if (n <= 0)
214 nb = 0;
215 has_mime_type = 0;
216 has_html_header = 0;
217 no_such_file = 0;
218 offer_download = 0;
219 do {
220 while ((n = read(DataPipe[0], buf, READ_SZ)) < 0 && errno == EINTR);
221 if (n > 0) {
222 dStr_append_l(dbuf, buf, n);
223 if (!has_mime_type && dbuf->len < MIN_SZ)
224 continue;
225 } else if (n < 0)
220226 break;
221227
222 if (minibuf_sz < MinSZ) {
223 memcpy(minibuf + minibuf_sz, buf, MIN(n, MinSZ - minibuf_sz));
224 minibuf_sz += MIN(n, MinSZ - minibuf_sz);
225 if (minibuf_sz < MinSZ)
226 continue;
227 a_Misc_get_content_type_from_data(minibuf, minibuf_sz, &mime_type);
228 if (!has_mime_type) {
229 if (dbuf->len == 0) {
230 /* When the file doesn't exist, the transfer size is zero */
231 no_such_file = 1;
232 break;
233 }
234 a_Misc_get_content_type_from_data2(dbuf->str, dbuf->len, &mime_type);
235 has_mime_type = 1;
236
228237 if (strcmp(mime_type, "application/octet-stream") == 0) {
229238 /* abort transfer */
230239 kill(ch_pid, SIGTERM);
231240 /* The "application/octet-stream" MIME type will be sent and
232241 * Dillo will offer a download dialog */
242 offer_download = 1;
233243 aborted = 1;
234244 }
235245 }
236246
237 if (nb == 0) {
247 if (offer_download || (!aborted && !has_html_header && dbuf->len)) {
238248 /* Send dpip tag */
239249 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
240 sock_handler_write_str(sh, d_cmd, 1);
241 g_free(d_cmd);
250 a_Dpip_dsh_write_str(sh, 1, d_cmd);
251 dFree(d_cmd);
242252
243253 /* Send HTTP header. */
244 sock_handler_write_str(sh, "Content-type: ", 0);
245 sock_handler_write_str(sh, mime_type, 0);
246 sock_handler_write_str(sh, "\n\n", 1);
247 }
248
249 if (!aborted)
250 sock_handler_write(sh, buf, n, 0);
251 }
252
253 return nb;
254 a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
255 a_Dpip_dsh_write_str(sh, 0, mime_type);
256 a_Dpip_dsh_write_str(sh, 1, "\r\n\r\n");
257 has_html_header = 1;
258 }
259
260 if (!aborted && dbuf->len) {
261 a_Dpip_dsh_write(sh, 1, dbuf->str, dbuf->len);
262 nb += dbuf->len;
263 dStr_truncate(dbuf, 0);
264 }
265 } while (n > 0 && !aborted);
266
267 dStr_free(dbuf, 1);
268 return (no_such_file ? -1 : (aborted ? -2 : nb));
254269 }
255270
256271 /*
257272 *
258273 */
259 int main(void)
274 int main(int argc, char **argv)
260275 {
261 gchar *dpip_tag = NULL, *cmd = NULL, *url = NULL, *url2 = NULL;
262 gint nb;
263 gchar *p, *d_cmd;
276 const char *err_msg = "404 Not Found\nNo such file or directory";
277 char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *url2 = NULL;
278 int st, rc;
279 char *p, *d_cmd;
280
281 /* wget may need to write a temporary file... */
282 rc = chdir("/tmp");
283 if (rc == -1) {
284 MSG("paths: error changing directory to /tmp: %s\n",
285 dStrerror(errno));
286 }
264287
265288 /* Initialize the SockHandler */
266 sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
267
268 /* wget may need to write a temporary file... */
269 chdir("/tmp");
270
271 /* Read the dpi command from STDIN */
272 dpip_tag = sock_handler_read(sh);
273 g_printerr("ftp.dpi::[%s]\n", dpip_tag);
274
275 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
276 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
289 sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
290
291 if (argc == 2) {
292 /* Debugging with a command line argument */
293 dpip_tag = dStrdup(argv[1]);
294 } else {
295 /* Authenticate our client... */
296 if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
297 a_Dpip_check_auth(dpip_tag) < 0) {
298 MSG("can't authenticate request: %s\n", dStrerror(errno));
299 a_Dpip_dsh_close(sh);
300 return 1;
301 }
302 dFree(dpip_tag);
303 /* Read the dpi command from STDIN */
304 dpip_tag = a_Dpip_dsh_read_token(sh, 1);
305 }
306 MSG("tag=[%s]\n", dpip_tag);
307
308 cmd = a_Dpip_get_attr(dpip_tag, "cmd");
309 url = a_Dpip_get_attr(dpip_tag, "url");
277310 if (!cmd || !url) {
278 g_printerr("ftp.dpi:: Error, cmd=%s, url=%s\n", cmd, url);
311 MSG("ERROR, cmd=%s, url=%s\n", cmd, url);
279312 exit (EXIT_FAILURE);
280313 }
281314
282 if ((nb = try_ftp_transfer(url)) == 0) {
315 if ((st = try_ftp_transfer(url)) == -1) {
283316 /* Transfer failed, the requested file may not exist or be a symlink
284317 * to a directory. Try again... */
285318 if ((p = strrchr(url, '/')) && p[1]) {
286 url2 = g_strconcat(url, "/", NULL);
287 nb = try_ftp_transfer(url2);
288 }
289 }
290
291 if (nb == 0) {
319 url2 = dStrconcat(url, "/", NULL);
320 st = try_ftp_transfer(url2);
321 }
322 }
323
324 if (st == -1) {
292325 /* The transfer failed, let dillo know... */
293 d_cmd = a_Dpip_build_cmd("cmd=%s to_cmd=%s msg=%s",
294 "answer", "open_url", "not a directory");
295 sock_handler_write_str(sh, d_cmd, 1);
296 g_free(d_cmd);
297 }
298
299 g_free(cmd);
300 g_free(url);
301 g_free(url2);
302 g_free(dpip_tag);
326 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
327 a_Dpip_dsh_write_str(sh, 0, d_cmd);
328 dFree(d_cmd);
329 a_Dpip_dsh_printf(sh, 1,
330 "HTTP/1.1 404 Not Found\r\n"
331 "Content-Type: text/plain\r\n"
332 "Content-Length: %d\r\n"
333 "\r\n"
334 "%s",
335 strlen(err_msg), err_msg);
336 }
337
338 dFree(cmd);
339 dFree(url);
340 dFree(url2);
341 dFree(dpip_tag);
303342
304343 /* Finish the SockHandler */
305 sock_handler_close(sh);
306 sock_handler_free(sh);
344 a_Dpip_dsh_close(sh);
345 a_Dpip_dsh_free(sh);
307346
308347 return 0;
309348 }
22 *
33 * This server is an example. Play with it and modify to your taste.
44 *
5 * Copyright 2003 Jorge Arellano Cid <jcid@dillo.org>
5 * Copyright 2003-2007 Jorge Arellano Cid <jcid@dillo.org>
66 *
77 * This program is free software; you can redistribute it and/or modify
88 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
9 * the Free Software Foundation; either version 3 of the License, or
1010 * (at your option) any later version.
1111 *
1212 */
1313
14 #include <glib.h>
1514 #include <unistd.h>
1615 #include <sys/types.h>
1716 #include <stdio.h>
2019 #include <errno.h>
2120 #include "../dpip/dpip.h"
2221 #include "dpiutil.h"
22
23 /*
24 * Debugging macros
25 */
26 #define _MSG(...)
27 #define MSG(...) printf("[hello dpi]: " __VA_ARGS__)
2328
2429 /*---------------------------------------------------------------------------*/
2530
3035 int main(void)
3136 {
3237 FILE *in_stream;
33 SockHandler *sh;
34 gchar *dpip_tag, *cmd = NULL, *url = NULL, *child_cmd = NULL;
35 gchar *esc_tag, *d_cmd;
38 Dsh *sh;
39 char *dpip_tag, *cmd = NULL, *url = NULL, *child_cmd = NULL;
40 char *esc_tag, *d_cmd;
3641 size_t n;
37 gint ret;
38 gchar buf[4096];
39 gchar *choice[] = {"Window was closed", "Yes", "No",
42 int ret;
43 char buf[4096];
44 char *choice[] = {"Window was closed", "Yes", "No",
4045 "Could be", "It's OK", "Cancel"};
4146 /* "Could>be", ">It's OK", "Can'>cel"}; --for testing */
42 gint choice_num;
47 int choice_num = -1;
4348
44 g_printerr("hello.dpi:: starting...\n");
49 MSG("starting...\n");
50 /* sleep(20) */
4551
46 /* Initialize the SockHandler */
47 sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
52 /* Initialize the SockHandler.
53 * This means we'll use stdin for input and stdout for output.
54 * In case of a server dpi, we'd use a socket and pass its file descriptor
55 * twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
56 * (Note: by now the last parameter is not used) */
57 sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
4858
49 /* Read the dpi command from STDIN */
50 dpip_tag = sock_handler_read(sh);
51 g_printerr("[%s]\n", dpip_tag);
59 /* Authenticate our client...
60 * As we're using Internet domain sockets, DPIP checks whether the client
61 * runs with the user's ID, by means of a shared secret. The DPIP API does
62 * the work for us. */
63 if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
64 a_Dpip_check_auth(dpip_tag) < 0) {
65 MSG("can't authenticate request: %s\n", dStrerror(errno));
66 a_Dpip_dsh_close(sh);
67 return 1;
68 }
69 dFree(dpip_tag);
5270
53 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
54 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
71 /* Read the dpi command from STDIN
72 * Now we're past the authentication phase, let's see what's dillo
73 * asking from us. a_Dpip_dsh_read_token() will block and return
74 * a full dpip token or null on error (it's commented in dpip.c) */
75 dpip_tag = a_Dpip_dsh_read_token(sh, 1);
76 MSG("tag = [%s]\n", dpip_tag);
77
78 /* Now that we have the dpip_tag, let's isolate the command and url */
79 cmd = a_Dpip_get_attr(dpip_tag, "cmd");
80 url = a_Dpip_get_attr(dpip_tag, "url");
5581
5682 /*-- Dialog part */
83 /* This is the dialog window. This is an example of interaction with
84 * the user. If you're starting to understand dpis, comment this out
85 * by switching to "#if 0" and the dialog will be disabled. */
86 #if 1
5787 {
58 gchar *dpip_tag2, *dialog_msg;
88 char *dpip_tag2, *dialog_msg;
5989
6090 /* Let's confirm the request */
6191 /* NOTE: you can send less alternatives (e.g. only alt1 and alt2) */
6393 "cmd=%s msg=%s alt1=%s alt2=%s alt3=%s alt4=%s alt5=%s",
6494 "dialog", "Do you want to see the hello page?",
6595 choice[1], choice[2], choice[3], choice[4], choice[5]);
66 sock_handler_write_str(sh, d_cmd, 1);
67 g_free(d_cmd);
96 a_Dpip_dsh_write_str(sh, 1, d_cmd);
97 dFree(d_cmd);
6898
6999 /* Get the answer */
70 dpip_tag2 = sock_handler_read(sh);
71 g_printerr("[%s]\n", dpip_tag2);
100 dpip_tag2 = a_Dpip_dsh_read_token(sh, 1);
101 MSG("tag = [%s]\n", dpip_tag2);
72102
73103 /* Get "msg" value */
74 dialog_msg = a_Dpip_get_attr(dpip_tag2, strlen(dpip_tag2), "msg");
104 dialog_msg = a_Dpip_get_attr(dpip_tag2, "msg");
75105 choice_num = 0;
76106 if (dialog_msg)
77107 choice_num = *dialog_msg - '0';
78108
79 g_free(dialog_msg);
80 g_free(dpip_tag2);
109 dFree(dialog_msg);
110 dFree(dpip_tag2);
81111 }
112 #endif
82113 /*-- EOD part */
83114
84 /* Start sending our answer */
115 /* Start sending our answer.
116 * (You can read the comments for DPIP API functions in dpip/dpip.c) */
85117 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
86 sock_handler_write_str(sh, d_cmd, 0);
87 g_free(d_cmd);
118 a_Dpip_dsh_write_str(sh, 0, d_cmd);
119 dFree(d_cmd);
88120
89 sock_handler_printf(sh, 0,
121 a_Dpip_dsh_printf(sh, 0,
90122 "Content-type: text/html\n\n"
91123 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
92124 "<html>\n"
94126 "<body><hr><h1>Hello world!</h1><hr>\n<br><br>\n");
95127
96128 /* Show the choice received with the dialog */
97 sock_handler_printf(sh, 0,
129 a_Dpip_dsh_printf(sh, 0,
98130 "<hr>\n"
99131 "<table width='100%%' border='1' bgcolor='burlywood'><tr><td>\n"
100132 "<big><em>Dialog question:</em> Do you want to see the hello page?<br>\n"
101133 "<em>Answer received:</em> <b>%s</b></big> </table>\n"
102134 "<hr>\n",
103 choice[choice_num]);
135 choice_num < 0 ? "There was NO dialog!" : choice[choice_num]);
104136
105137 /* Show the dpip tag we received */
106138 esc_tag = Escape_html_str(dpip_tag);
107 sock_handler_printf(sh, 0,
139 a_Dpip_dsh_printf(sh, 0,
108140 "<h3>dpip tag received:</h3>\n"
109141 "<pre>\n%s</pre>\n"
110142 "<br><small>(<b>dpip:</b> dpi protocol)</small><br><br><br>\n",
111143 esc_tag);
112 g_free(esc_tag);
144 dFree(esc_tag);
113145
114146
115147 /* Now something more interesting,
116 * fork a command and show its feedback */
148 * fork a command and show its feedback.
149 * (An example of generating dynamic content with an external
150 * program). */
117151 if (cmd && url) {
118 child_cmd = g_strdup("date -R");
119 g_printerr("[%s]\n", child_cmd);
152 child_cmd = dStrdup("date -R");
153 MSG("[%s]\n", child_cmd);
120154
121155 /* Fork, exec command, get its output and answer */
122156 if ((in_stream = popen(child_cmd, "r")) == NULL) {
124158 return EXIT_FAILURE;
125159 }
126160
127 sock_handler_printf(sh, 0, "<h3>date:</h3>\n");
128 sock_handler_printf(sh, 0, "<pre>\n");
161 a_Dpip_dsh_write_str(sh, 0, "<h3>date:</h3>\n");
162 a_Dpip_dsh_write_str(sh, 0, "<pre>\n");
129163
130164 /* Read/Write */
131165 while ((n = fread (buf, 1, 4096, in_stream)) > 0) {
132 sock_handler_write(sh, buf, n, 0);
166 a_Dpip_dsh_write(sh, 0, buf, n);
133167 }
134168
135 sock_handler_printf(sh, 0, "</pre>\n");
169 a_Dpip_dsh_write_str(sh, 0, "</pre>\n");
136170
137171 if ((ret = pclose(in_stream)) != 0)
138 g_printerr("popen: [%d]\n", ret);
172 MSG("popen: [%d]\n", ret);
139173
140 g_free(child_cmd);
174 dFree(child_cmd);
141175 }
142176
143 sock_handler_printf(sh, 1, "</body></html>\n");
177 a_Dpip_dsh_write_str(sh, 1, "</body></html>\n");
144178
145 g_free(cmd);
146 g_free(url);
147 g_free(dpip_tag);
179 dFree(cmd);
180 dFree(url);
181 dFree(dpip_tag);
148182
149183 /* Finish the SockHandler */
150 sock_handler_close(sh);
151 sock_handler_free(sh);
184 a_Dpip_dsh_close(sh);
185 a_Dpip_dsh_free(sh);
152186
153187 return 0;
154188 }
1717 *
1818 * This program is free software; you can redistribute it and/or modify
1919 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
20 * the Free Software Foundation; either version 3 of the License, or
2121 * (at your option) any later version.
2222 *
2323 * As a special exception permission is granted to link the code of
2424 * the https dillo plugin with the OpenSSL project's "OpenSSL"
2525 * library, and distribute the linked executables, without including
2626 * the source code for OpenSSL in the source distribution. You must
27 * obey the GNU General Public License, version 2, in all respects
27 * obey the GNU General Public License, version 3, in all respects
2828 * for all of the code used other than "OpenSSL".
2929 *
3030 */
5656 #include <errno.h>
5757 #include <sys/time.h>
5858 #include <sys/stat.h>
59 #include <glib.h>
59
6060 #include "../dpip/dpip.h"
6161 #include "dpiutil.h"
6262
63 #define ENABLE_SSL
64 #undef ENABLE_SSL
63 /*
64 * Debugging macros
65 */
66 #define SILENT 1
67 #define _MSG(...)
68 #if SILENT
69 #define MSG(...)
70 #else
71 #define MSG(...) fprintf(stderr, "[https dpi]: " __VA_ARGS__)
72 #endif
73
74
6575 #ifdef ENABLE_SSL
6676
6777 #include <openssl/ssl.h>
6878 #include <openssl/rand.h>
6979
70 static int get_network_connection(gchar * url);
80 static int get_network_connection(char * url);
7181 static int handle_certificate_problem(SSL * ssl_connection);
7282 static int save_certificate_home(X509 * cert);
7383
7484 #endif
7585
76 int main(void);
86
7787
7888 /*---------------------------------------------------------------------------*/
7989 /*
8090 * Global variables
8191 */
82 static gchar *root_url = NULL; /*Holds the URL we are connecting to*/
83 static SockHandler *sh;
92 static char *root_url = NULL; /*Holds the URL we are connecting to*/
93 static Dsh *sh;
8494
8595
8696 #ifdef ENABLE_SSL
90100 * the user-selected alternative.
91101 * Return: (-1: parse error, 0: window closed, 1-5 alt. number)
92102 */
93 static gint dialog_get_answer_number(void)
103 static int dialog_get_answer_number(void)
94104 {
95 gint response_number = -1;
96 gchar *dpip_tag, *response;
105 int response_number = -1;
106 char *dpip_tag, *response;
97107
98108 /* Read the dpi command from STDIN */
99 dpip_tag = sock_handler_read(sh);
100 response = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "msg");
109 dpip_tag = a_Dpip_dsh_read_token(sh, 1);
110 response = a_Dpip_get_attr(dpip_tag, "msg");
101111 response_number = (response) ? strtol (response, NULL, 10) : -1;
102 g_free(dpip_tag);
103 g_free(response);
112 dFree(dpip_tag);
113 dFree(response);
104114
105115 return response_number;
106116 }
114124 /* The following variable will be set to 1 in the event of
115125 * an error and skip any further processing
116126 */
117 gint exit_error = 0;
127 int exit_error = 0;
118128 SSL_CTX * ssl_context = NULL;
119129 SSL * ssl_connection = NULL;
120130
121 gchar *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL;
122 gchar buf[4096];
123 int retval = 0;
131 char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL,
132 *proxy_url = NULL, *proxy_connect = NULL;
133 char buf[4096];
134 int ret = 0;
124135 int network_socket = -1;
125136
126137
127 g_printerr("{In https.filter.dpi}\n");
138 MSG("{In https.filter.dpi}\n");
128139
129140 /*Initialize library*/
130141 SSL_load_error_strings();
131142 SSL_library_init();
132143 if (RAND_status() != 1){
133144 /*Insufficient entropy. Deal with it?*/
134 g_printerr("Insufficient random entropy\n");
145 MSG("Insufficient random entropy\n");
135146 }
136147
137148 /*Create context and SSL object*/
138149 if (exit_error == 0){
139150 ssl_context = SSL_CTX_new(SSLv23_client_method());
140151 if (ssl_context == NULL){
141 g_printerr("Error creating SSL context\n");
152 MSG("Error creating SSL context\n");
142153 exit_error = 1;
143154 }
144155 }
148159 if (exit_error == 0){
149160 if (SSL_CTX_load_verify_locations(
150161 ssl_context, NULL, "/etc/ssl/certs/" ) == 0){
151 g_printerr("Error opening system x509 certificate location\n");
162 MSG("Error opening system x509 certificate location\n");
152163 exit_error = 1;
153164 }
154165 }
155166
156167 if (exit_error == 0){
157 g_snprintf(buf, 4095, "%s/.dillo/certs/", g_get_home_dir() );
168 snprintf(buf, 4095, "%s/.dillo/certs/", dGethomedir());
158169 if (SSL_CTX_load_verify_locations(ssl_context, NULL, buf )==0){
159 g_printerr("Error opening user x509 certificate location\n");
170 MSG("Error opening user x509 certificate location\n");
160171 exit_error = 1;
161172 }
162173 }
164175 if (exit_error == 0){
165176 ssl_connection = SSL_new(ssl_context);
166177 if (ssl_connection == NULL){
167 g_printerr("Error creating SSL connection\n");
178 MSG("Error creating SSL connection\n");
168179 exit_error = 1;
169180 }
170181 }
181192 SSL_set_verify(ssl_connection, SSL_VERIFY_NONE, 0);
182193
183194 /*Get the network address and command to be used*/
184 dpip_tag = sock_handler_read(sh);
185 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
186 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
187 http_query = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "query");
195 dpip_tag = a_Dpip_dsh_read_token(sh, 1);
196 cmd = a_Dpip_get_attr(dpip_tag, "cmd");
197 proxy_url = a_Dpip_get_attr(dpip_tag, "proxy_url");
198 proxy_connect =
199 a_Dpip_get_attr(dpip_tag, "proxy_connect");
200 url = a_Dpip_get_attr(dpip_tag, "url");
201 http_query = a_Dpip_get_attr(dpip_tag, "query");
188202
189203 if (cmd == NULL || url == NULL || http_query == NULL){
190 g_printerr("***Value of cmd, url or http_query is NULL"
204 MSG("***Value of cmd, url or http_query is NULL"
191205 " - cannot continue\n");
192206 exit_error = 1;
193207 }
194208 }
195209
196210 if (exit_error == 0){
197 network_socket = get_network_connection(url);
211 char *connect_url = proxy_url ? proxy_url : url;
212
213 network_socket = get_network_connection(connect_url);
198214 if (network_socket<0){
199 g_printerr("Network socket create error\n");
215 MSG("Network socket create error\n");
200216 exit_error = 1;
201217 }
202218 }
203219
220 if (exit_error == 0 && proxy_connect != NULL) {
221 ssize_t St;
222 const char *p = proxy_connect;
223 int writelen = strlen(proxy_connect);
224
225 while (writelen > 0) {
226 St = write(network_socket, p, writelen);
227 if (St < 0) {
228 /* Error */
229 if (errno != EINTR) {
230 MSG("Error writing to proxy.\n");
231 exit_error = 1;
232 break;
233 }
234 } else {
235 p += St;
236 writelen -= St;
237 }
238 }
239 if (exit_error == 0) {
240 const size_t buflen = 200;
241 char buf[buflen];
242 Dstr *reply = dStr_new("");
243
244 while (1) {
245 St = read(network_socket, buf, buflen);
246 if (St > 0) {
247 dStr_append_l(reply, buf, St);
248 if (strstr(reply->str, "\r\n\r\n")) {
249 /* have whole reply header */
250 if (reply->len >= 12 && reply->str[9] == '2') {
251 /* e.g. "HTTP/1.1 200 Connection established[...]" */
252 MSG("CONNECT through proxy succeeded.\n");
253 } else {
254 /* TODO: send reply body to dillo */
255 exit_error = 1;
256 MSG("CONNECT through proxy failed.\n");
257 }
258 break;
259 }
260 } else if (St < 0) {
261 if (errno != EINTR) {
262 exit_error = 1;
263 MSG("Error reading from proxy.\n");
264 break;
265 }
266 }
267 }
268 dStr_free(reply, 1);
269 }
270 }
204271
205272 if (exit_error == 0){
206273 /* Configure SSL to use network file descriptor */
207274 if (SSL_set_fd(ssl_connection, network_socket) == 0){
208 g_printerr("Error connecting network socket to SSL\n");
275 MSG("Error connecting network socket to SSL\n");
209276 exit_error = 1;
210277 }
211278 }
213280 if (exit_error == 0){
214281 /*Actually do SSL connection handshake*/
215282 if (SSL_connect(ssl_connection) != 1){
216 g_printerr("SSL_connect failed\n");
283 MSG("SSL_connect failed\n");
217284 exit_error = 1;
218285 }
219286 }
221288 /*Use handle error function to decide what to do*/
222289 if (exit_error == 0){
223290 if (handle_certificate_problem(ssl_connection) < 0){
224 g_printerr("Certificate verification error\n");
291 MSG("Certificate verification error\n");
225292 exit_error = 1;
226293 }
227294 }
236303
237304 /*Send dpi command*/
238305 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
239 sock_handler_write_str(sh, d_cmd, 1);
240 g_free(d_cmd);
306 a_Dpip_dsh_write_str(sh, 1, d_cmd);
307 dFree(d_cmd);
241308
242309 /*Send remaining data*/
243310
244 while ((retval = SSL_read(ssl_connection, buf, 4096)) > 0 ){
245 sock_handler_write(sh, buf, (size_t)retval, 0);
311 while ((ret = SSL_read(ssl_connection, buf, 4096)) > 0 ){
312 /* flush is good for dialup speed */
313 a_Dpip_dsh_write(sh, 1, buf, (size_t)ret);
246314 }
247315 }
248316
249317 /*Begin cleanup of all resources used*/
250 g_free(dpip_tag);
251 g_free(cmd);
252 g_free(url);
253 g_free(http_query);
318 dFree(dpip_tag);
319 dFree(cmd);
320 dFree(url);
321 dFree(http_query);
322 dFree(proxy_url);
323 dFree(proxy_connect);
254324
255325 if (network_socket != -1){
256326 close(network_socket);
271341 * remote server and return the file descriptor number of the
272342 * socket. Returns -1 in the event of an error
273343 */
274 static int get_network_connection(gchar * url)
344 static int get_network_connection(char * url)
275345 {
276346 struct sockaddr_in address;
277347 struct hostent *hp;
279349 int s;
280350 int url_offset = 0;
281351 int portnum = 443;
282 unsigned int url_look_up_length = 0;
283 gchar * url_look_up = NULL;
352 uint_t url_look_up_length = 0;
353 char * url_look_up = NULL;
284354
285355 /*Determine how much of url we chop off as unneeded*/
286 if (g_strncasecmp(url, "https://", 8) == 0){
356 if (dStrncasecmp(url, "https://", 8) == 0){
287357 url_offset = 8;
358 } else if (dStrncasecmp(url, "http://", 7) == 0) {
359 url_offset = 7;
360 portnum = 80;
288361 }
289362
290363 /*Find end of URL*/
291364
292365 if (strpbrk(url+url_offset, ":/") != NULL){
293366 url_look_up_length = strpbrk(url+url_offset, ":/") - (url+url_offset);
294 url_look_up = g_strndup(url+url_offset, url_look_up_length);
367 url_look_up = dStrndup(url+url_offset, url_look_up_length);
295368
296369 /*Check for port number*/
297370 if (strchr(url+url_offset, ':') ==
298371 (url + url_offset + url_look_up_length)){
299 portnum = atoi(url + url_offset + url_look_up_length + 1);
372 portnum = strtol(url + url_offset + url_look_up_length + 1, NULL, 10);
300373 }
301374 } else {
302375 url_look_up = url + url_offset;
303376 }
304377
305 root_url = g_strdup(url_look_up);
378 root_url = dStrdup(url_look_up);
306379 hp=gethostbyname(url_look_up);
307380
308 /*url_look_uip no longer needed, so free if neccessary*/
381 /*url_look_uip no longer needed, so free if necessary*/
309382 if (url_look_up_length != 0){
310 g_free(url_look_up);
383 dFree(url_look_up);
311384 }
312385
313386 if (hp == NULL){
314 g_printerr("gethostbyname() failed\n");
387 MSG("gethostbyname() failed\n");
315388 return -1;
316389 }
317390
324397 if (connect(s, (struct sockaddr *)&address, sizeof(address)) != 0){
325398 close(s);
326399 s = -1;
327 g_printerr("errno: %i\n", errno);
400 MSG("errno: %i\n", errno);
328401 }
329402 return s;
330403 }
335408 * allow the user to decide what to do. It may save the
336409 * certificate to the user's .dillo directory if it is
337410 * trusted.
411 *
412 * TODO: Rearrange this to get rid of redundancy.
413 *
338414 * Return value: -1 on abort, 0 or higher on continue
339415 */
340416 static int handle_certificate_problem(SSL * ssl_connection)
341417 {
342 gint response_number;
343 int retval = -1;
418 int response_number;
419 int ret = -1;
344420 long st;
345 char *cn, *cn_end;
421 char *cn;
346422 char buf[4096], *d_cmd, *msg;
347423
348424 X509 * remote_cert;
357433 "This site CAN NOT be trusted. Sending data is NOT SAFE.\n"
358434 "What do I do?",
359435 "Continue", "Cancel");
360 sock_handler_write_str(sh, d_cmd, 1);
361 g_free(d_cmd);
436 a_Dpip_dsh_write_str(sh, 1, d_cmd);
437 dFree(d_cmd);
362438
363439 /*Read the user's response*/
364440 response_number = dialog_get_answer_number();
365441
366442 /*Abort on anything but "Continue"*/
367443 if (response_number == 1){
368 retval = 0;
444 ret = 0;
369445 }
370446
371447 } else {
373449 st = SSL_get_verify_result(ssl_connection);
374450 switch (st) {
375451 case X509_V_OK: /*Everything is Kosher*/
376 retval = 0;
452 ret = 0;
377453 break;
378454 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
379455 /*Either self signed and untrusted*/
380456 /*Extract CN from certificate name information*/
381 cn = strstr(remote_cert->name, "/CN=") + 4;
382 if (cn == NULL)
383 break;
384
385 if ((cn_end = strstr(cn, "/")) == NULL )
386 cn_end = cn + strlen(cn);
387
388 strncpy(buf, cn, (size_t) (cn_end - cn));
389
390 /*Add terminating NULL*/
391 buf[cn_end - cn] = 0;
392
393 msg = g_strdup_printf("The remote certificate is self-signed and "
394 "untrusted.\nFor address: %s", buf);
457 if ((cn = strstr(remote_cert->name, "/CN=")) == NULL) {
458 strcpy(buf, "(no CN given)");
459 } else {
460 char *cn_end;
461
462 cn += 4;
463
464 if ((cn_end = strstr(cn, "/")) == NULL )
465 cn_end = cn + strlen(cn);
466
467 strncpy(buf, cn, (size_t) (cn_end - cn));
468 buf[cn_end - cn] = '\0';
469 }
470 msg = dStrconcat("The remote certificate is self-signed and "
471 "untrusted.\nFor address: ", buf, NULL);
395472 d_cmd = a_Dpip_build_cmd(
396473 "cmd=%s msg=%s alt1=%s alt2=%s alt3=%s",
397 "dialog", msg, "Continue", "Cancel", "Trust Certificate");
398 sock_handler_write_str(sh, d_cmd, 1);
399 g_free(d_cmd);
400 g_free(msg);
474 "dialog", msg, "Continue", "Cancel", "Save Certificate");
475 a_Dpip_dsh_write_str(sh, 1, d_cmd);
476 dFree(d_cmd);
477 dFree(msg);
401478
402479 response_number = dialog_get_answer_number();
403480 switch (response_number){
404481 case 1:
405 retval = 0;
482 ret = 0;
406483 break;
407484 case 2:
408485 break;
411488 /*Potential security problems because we are writing
412489 *to the filesystem*/
413490 save_certificate_home(remote_cert);
414 retval = 1;
491 ret = 1;
415492 break;
416493 default:
417494 break;
425502 "The issuer for the remote certificate cannot be found\n"
426503 "The authenticity of the remote certificate cannot be trusted",
427504 "Continue", "Cancel");
428 sock_handler_write_str(sh, d_cmd, 1);
429 g_free(d_cmd);
505 a_Dpip_dsh_write_str(sh, 1, d_cmd);
506 dFree(d_cmd);
430507
431508 response_number = dialog_get_answer_number();
432509 if (response_number == 1) {
433 retval = 0;
510 ret = 0;
434511 }
435512 break;
436513
444521 "The remote certificate signature could not be read\n"
445522 "or is invalid and should not be trusted",
446523 "Continue", "Cancel");
447 sock_handler_write_str(sh, d_cmd, 1);
448 g_free(d_cmd);
524 a_Dpip_dsh_write_str(sh, 1, d_cmd);
525 dFree(d_cmd);
449526
450527 response_number = dialog_get_answer_number();
451528 if (response_number == 1) {
452 retval = 0;
529 ret = 0;
453530 }
454531 break;
455532 case X509_V_ERR_CERT_NOT_YET_VALID:
463540 "presented has a starting validity after today's date\n"
464541 "You should be cautious about using this site",
465542 "Continue", "Cancel");
466 sock_handler_write_str(sh, d_cmd, 1);
467 g_free(d_cmd);
543 a_Dpip_dsh_write_str(sh, 1, d_cmd);
544 dFree(d_cmd);
468545
469546 response_number = dialog_get_answer_number();
470547 if (response_number == 1) {
471 retval = 0;
548 ret = 0;
472549 }
473550 break;
474551 case X509_V_ERR_CERT_HAS_EXPIRED:
480557 "wasn't designed to last this long. You should avoid \n"
481558 "this site.",
482559 "Continue", "Cancel");
483 sock_handler_write_str(sh, d_cmd, 1);
484 g_free(d_cmd);
560 a_Dpip_dsh_write_str(sh, 1, d_cmd);
561 dFree(d_cmd);
485562 response_number = dialog_get_answer_number();
486563 if (response_number == 1) {
487 retval = 0;
564 ret = 0;
488565 }
489566 break;
490567 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
499576 "making it impossible to determine if the certificate\n"
500577 "is valid. You should not trust this certificate.",
501578 "Continue", "Cancel");
502 sock_handler_write_str(sh, d_cmd, 1);
503 g_free(d_cmd);
579 a_Dpip_dsh_write_str(sh, 1, d_cmd);
580 dFree(d_cmd);
504581 response_number = dialog_get_answer_number();
505582 if (response_number == 1) {
506 retval = 0;
583 ret = 0;
507584 }
508585 break;
509586 case X509_V_ERR_INVALID_CA:
519596 "with the remote system. The connection should not\n"
520597 "be trusted",
521598 "Continue", "Cancel");
522 sock_handler_write_str(sh, d_cmd, 1);
523 g_free(d_cmd);
599 a_Dpip_dsh_write_str(sh, 1, d_cmd);
600 dFree(d_cmd);
524601 response_number = dialog_get_answer_number();
525602 if (response_number == 1) {
526 retval = 0;
603 ret = 0;
527604 }
528605 break;
529606 case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
534611 "dialog",
535612 "Some of the information presented by the remote system\n"
536613 "does not match other information presented\n"
537 "This may be an attempt to evesdrop on communications",
614 "This may be an attempt to eavesdrop on communications",
538615 "Continue", "Cancel");
539 sock_handler_write_str(sh, d_cmd, 1);
540 g_free(d_cmd);
616 a_Dpip_dsh_write_str(sh, 1, d_cmd);
617 dFree(d_cmd);
618 response_number = dialog_get_answer_number();
619 if (response_number == 1) {
620 ret = 0;
621 }
622 break;
623 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
624 d_cmd = a_Dpip_build_cmd(
625 "cmd=%s msg=%s alt1=%s alt2=%s",
626 "dialog",
627 "Self signed certificate in certificate chain. The certificate "
628 "chain could be built up using the untrusted certificates but the "
629 "root could not be found locally.",
630 "Continue", "Cancel");
631 a_Dpip_dsh_write_str(sh, 1, d_cmd);
632 dFree(d_cmd);
633 response_number = dialog_get_answer_number();
634 if (response_number == 1) {
635 ret = 0;
636 }
637 break;
638 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
639 d_cmd = a_Dpip_build_cmd(
640 "cmd=%s msg=%s alt1=%s alt2=%s",
641 "dialog",
642 "Unable to get local issuer certificate. The issuer certificate "
643 "of an untrusted certificate cannot be found.",
644 "Continue", "Cancel");
645 a_Dpip_dsh_write_str(sh, 1, d_cmd);
646 dFree(d_cmd);
647 response_number = dialog_get_answer_number();
648 if (response_number == 1) {
649 ret = 0;
650 }
651 break;
541652 default: /*Need to add more options later*/
542 msg = g_strdup_printf(
543 "The remote certificate cannot be verified (code %ld)", st);
544 d_cmd = a_Dpip_build_cmd(
545 "cmd=%s msg=%s alt1=%s alt2=%s",
546 "dialog", msg, "Continue", "Cancel");
547 sock_handler_write_str(sh, d_cmd, 1);
548 g_free(d_cmd);
549 g_free(msg);
653 snprintf(buf, 80,
654 "The remote certificate cannot be verified (code %ld)", st);
655 d_cmd = a_Dpip_build_cmd(
656 "cmd=%s msg=%s alt1=%s alt2=%s",
657 "dialog", buf, "Continue", "Cancel");
658 a_Dpip_dsh_write_str(sh, 1, d_cmd);
659 dFree(d_cmd);
550660 response_number = dialog_get_answer_number();
551661 /*abort on anything but "Continue"*/
552662 if (response_number == 1){
553 retval = 0;
663 ret = 0;
554664 }
555665 }
556666 X509_free(remote_cert);
557667 remote_cert = 0;
558668 }
559669
560 return retval;
670 return ret;
561671 }
562672
563673 /*
569679 char buf[4096];
570680
571681 FILE * fp = NULL;
572 unsigned int i = 0;
573 int retval = 1;
682 uint_t i = 0;
683 int ret = 1;
574684
575685 /*Attempt to create .dillo/certs blindly - check later*/
576 g_snprintf(buf,4096,"%s/.dillo/", g_get_home_dir());
686 snprintf(buf,4096,"%s/.dillo/", dGethomedir());
577687 mkdir(buf, 01777);
578 g_snprintf(buf,4096,"%s/.dillo/certs/", g_get_home_dir());
688 snprintf(buf,4096,"%s/.dillo/certs/", dGethomedir());
579689 mkdir(buf, 01777);
580690
581 do{
582 g_snprintf(buf,4096,"%s/.dillo/certs/%lx.%u",
583 g_get_home_dir(), X509_subject_name_hash(cert), i);
691 do {
692 snprintf(buf, 4096, "%s/.dillo/certs/%lx.%u",
693 dGethomedir(), X509_subject_name_hash(cert), i);
584694
585695 fp=fopen(buf, "r");
586696 if (fp == NULL){
587697 /*File name doesn't exist so we can use it safely*/
588698 fp=fopen(buf, "w");
589699 if (fp == NULL){
590 g_printerr("Unable to open cert save file in home dir\n");
700 MSG("Unable to open cert save file in home dir\n");
591701 break;
592702 } else {
593703 PEM_write_X509(fp, cert);
594704 fclose(fp);
595 g_printerr("Wrote certificate\n");
596 retval = 0;
705 MSG("Wrote certificate\n");
706 ret = 0;
597707 break;
598708 }
599709 } else {
601711 }
602712 i++;
603713 /*Don't loop too many times - just give up*/
604 } while( i < 1024 );
605
606 return retval;
714 } while (i < 1024);
715
716 return ret;
607717 }
608718
609719
617727 */
618728 static void no_ssl_support(void)
619729 {
620 gchar *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL;
730 char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL;
621731 char *d_cmd;
622732
623733 /* Read the dpi command from STDIN */
624 dpip_tag = sock_handler_read(sh);
625
626 g_printerr("{In https.filter.dpi}\n");
627 g_printerr("no_ssl_support version\n");
628
629 cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
630 url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
631 http_query = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "query");
632
633 g_printerr("{ cmd: %s}\n", cmd);
634 g_printerr("{ url: %s}\n", url);
635 g_printerr("{ http_query:\n%s}\n", http_query);
636
637 g_printerr("{ sending dpip cmd...}\n");
734 dpip_tag = a_Dpip_dsh_read_token(sh, 1);
735
736 MSG("{In https.filter.dpi}\n");
737 MSG("no_ssl_support version\n");
738
739 cmd = a_Dpip_get_attr(dpip_tag, "cmd");
740 url = a_Dpip_get_attr(dpip_tag, "url");
741 http_query = a_Dpip_get_attr(dpip_tag, "query");
742
743 MSG("{ cmd: %s}\n", cmd);
744 MSG("{ url: %s}\n", url);
745 MSG("{ http_query:\n%s}\n", http_query);
746
747 MSG("{ sending dpip cmd...}\n");
638748
639749 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
640 sock_handler_write_str(sh, d_cmd, 1);
641 g_free(d_cmd);
642
643 g_printerr("{ dpip cmd sent.}\n");
644
645 g_printerr("{ sending HTML...}\n");
646
647 sock_handler_printf(sh, 1,
750 a_Dpip_dsh_write_str(sh, 1, d_cmd);
751 dFree(d_cmd);
752
753 MSG("{ dpip cmd sent.}\n");
754
755 MSG("{ sending HTML...}\n");
756
757 a_Dpip_dsh_printf(sh, 1,
648758 "Content-type: text/html\n\n"
649759 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
650 "<html><body><pre>\n"
651 "<b>Hi!\n\n"
652 " This is the https dpi that just got a request to send\n"
653 " the following HTTP query:\n{</b>\n"
654 "<code>%s</code>\n"
655 "<b>}</b>\n\n"
656 " <b>*** Dillo's prototype plugin for https support"
657 " is disabled now ***</b>\n\n"
658 " If you want to test this <b>alpha</b> support code, just remove\n"
659 " line 65 from https.c, recompile and reinstall.\n\n"
660 " (beware that this https support is very limited now)\n\n"
760 "<html><head><title>SSL support is disabled</title></head>\n"
761 "<body>\n"
762 "<p>\n"
763 " The https dpi was unable to send\n"
764 " the following HTTP query:\n"
765 " <blockquote><pre>%s</pre></blockquote>\n"
766 " because Dillo's prototype plugin for https support"
767 " is disabled.\n\n"
768 "<p>\n"
769 " If you want to test this <b>alpha</b> support code,\n"
770 " just reconfigure with <code>--enable-ssl</code>,\n"
771 " recompile and reinstall.\n\n"
772 " (Beware that this https support is very limited now)\n\n"
661773 " To use https and SSL, you must have \n"
662774 " the OpenSSL development libraries installed. Check your\n"
663775 " O/S distribution provider, or check out\n"
664 " <a href=\"http://www.openssl.org\">www.openssl.org</a>\n\n"
665 " --\n"
666 "</pre></body></html>\n",
776 " <a href=\"http://www.openssl.org\">www.openssl.org</a>.\n\n"
777 "</p>\n\n"
778 "</body></html>\n",
667779 http_query
668780 );
669 g_printerr("{ HTML content sent.}\n");
670
671 g_free(cmd);
672 g_free(url);
673 g_free(http_query);
674 g_free(dpip_tag);
675
676 g_printerr("{ exiting https.dpi}\n");
781 MSG("{ HTML content sent.}\n");
782
783 dFree(cmd);
784 dFree(url);
785 dFree(http_query);
786 dFree(dpip_tag);
787
788 MSG("{ exiting https.dpi}\n");
677789
678790 }
679791
683795 /*---------------------------------------------------------------------------*/
684796 int main(void)
685797 {
798 char *dpip_tag;
799
686800 /* Initialize the SockHandler for this filter dpi */
687 sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
801 sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
802
803 /* Authenticate our client... */
804 if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
805 a_Dpip_check_auth(dpip_tag) < 0) {
806 MSG("can't authenticate request: %s\n", dStrerror(errno));
807 a_Dpip_dsh_close(sh);
808 return 1;
809 }
810 dFree(dpip_tag);
688811
689812 #ifdef ENABLE_SSL
690813 yes_ssl_support();
693816 #endif
694817
695818 /* Finish the SockHandler */
696 sock_handler_close(sh);
697 sock_handler_free(sh);
698
699 g_free(root_url);
700
701 g_printerr("{ exiting https.dpi}\n");
819 a_Dpip_dsh_close(sh);
820 a_Dpip_dsh_free(sh);
821
822 dFree(root_url);
823
824 MSG("{ exiting https.dpi}\n");
702825
703826 return 0;
704827 }
0 /*
1 * Dpi for "View source".
2 *
3 * This server is an example. Play with it and modify to your taste.
4 *
5 * Copyright 2010 Jorge Arellano Cid <jcid@dillo.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 */
13
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <errno.h>
20 #include "../dpip/dpip.h"
21 #include "dpiutil.h"
22
23 /*
24 * Debugging macros
25 */
26 #define _MSG(...)
27 #define MSG(...) printf("[vsource dpi]: " __VA_ARGS__)
28
29 /*---------------------------------------------------------------------------*/
30
31 const char *DOCTYPE=
32 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>";
33
34
35 void send_dpip_tag(Dsh *sh, char *dpip_tag)
36 {
37 a_Dpip_dsh_write_str(sh, 0, "\nDpip tag received: ");
38 a_Dpip_dsh_write_str(sh, 0, dpip_tag ? dpip_tag : "None");
39 a_Dpip_dsh_write_str(sh, 1, "\n\n");
40 }
41
42 /*
43 * Send source as plain text
44 */
45 void send_plain_text(Dsh *sh, int data_size)
46 {
47 int bytes_read = 0;
48 char *src_str;
49
50 /* Send HTTP header for plain text MIME type */
51 a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
52
53 while (bytes_read < data_size &&
54 (src_str = a_Dpip_dsh_read_token(sh, 1))) {
55 bytes_read += strlen(src_str);
56 a_Dpip_dsh_write_str(sh, 1, src_str);
57 dFree(src_str);
58 }
59 }
60
61 /*
62 * Send source as plain text with line numbers
63 */
64 void send_numbered_text(Dsh *sh, int data_size)
65 {
66 int bytes_read = 0, line = 1;
67 char *p, *q, *src_str, line_str[32];
68
69 /* Send HTTP header for plain text MIME type */
70 a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
71
72 while (bytes_read < data_size &&
73 (src_str = a_Dpip_dsh_read_token(sh, 1))) {
74 bytes_read += strlen(src_str);
75 p = q = src_str;
76
77 while (*p) {
78 snprintf(line_str, 32, "%2d: ", line);
79 a_Dpip_dsh_write_str(sh, 0, line_str);
80 if ((p = strchr(q, '\n'))) {
81 a_Dpip_dsh_write(sh, 0, q, p - q + 1);
82 if (p[1] == '\r')
83 ++p;
84 ++line;
85 } else {
86 a_Dpip_dsh_write_str(sh, 1, q);
87 break;
88 }
89 q = ++p;
90 }
91 dFree(src_str);
92 }
93 }
94
95 /*
96 * Send source as html text with line numbers
97 */
98 void send_html_text(Dsh *sh, const char *url, int data_size)
99 {
100 int bytes_read = 0, old_line = 0, line = 1;
101 char *p, *q, *src_str, line_str[128];
102
103 if (strncmp(url, "dpi:/vsource/:", 14) == 0)
104 url += 14;
105
106 /* Send HTTP header for plain text MIME type */
107 a_Dpip_dsh_write_str(sh, 0, "Content-type: text/html\n\n");
108
109 a_Dpip_dsh_write_str(sh, 0, DOCTYPE);
110 a_Dpip_dsh_printf(sh, 0,
111 "\n"
112 "<html><head>\n"
113 "<title>Source for %s</title>\n"
114 "<style type=\"text/css\">PRE {white-space: pre-wrap}\n"
115 "</style>\n"
116 "</head>\n"
117 "<body id=\"dillo_vs\">\n<table cellpadding='0'>\n", url);
118
119 while (bytes_read < data_size &&
120 (src_str = a_Dpip_dsh_read_token(sh, 1))) {
121 bytes_read += strlen(src_str);
122 p = q = src_str;
123
124 while (*p) {
125 if (line > old_line) {
126 snprintf(line_str, 128,
127 "%s<tr><td bgcolor='%s'>%d%s<td><pre>",
128 (line > 1) ? "</pre>" : "",
129 (line & 1) ? "#B87333" : "#DD7F32", line,
130 (line == 1 || (line % 10) == 0) ? "&nbsp;&nbsp;" : "");
131 a_Dpip_dsh_write_str(sh, 0, line_str);
132 old_line = line;
133 }
134 if ((p = strpbrk(q, "\r\n<&"))) {
135 if (*p == '\r' || *p == '\n') {
136 a_Dpip_dsh_write(sh, 0, q, p - q + 1);
137 if (*p == '\r' && p[1] == '\n')
138 p++;
139 ++line;
140 } else {
141 a_Dpip_dsh_write(sh, 0, q, p - q);
142 a_Dpip_dsh_write_str(sh, 0, (*p == '<') ? "&lt;" : "&amp;");
143 }
144 } else {
145 a_Dpip_dsh_write_str(sh, 1, q);
146 break;
147 }
148 q = ++p;
149 }
150 dFree(src_str);
151 }
152
153 if (data_size > 0)
154 a_Dpip_dsh_write_str(sh, 0, "</pre>");
155 a_Dpip_dsh_write_str(sh, 1, "</table></body></html>");
156 }
157
158 /*
159 *
160 */
161 int main(void)
162 {
163 Dsh *sh;
164 int data_size;
165 char *dpip_tag, *cmd = NULL, *cmd2 = NULL, *url = NULL, *size_str = NULL;
166 char *d_cmd;
167
168 _MSG("starting...\n");
169 //sleep(20);
170
171 /* Initialize the SockHandler.
172 * This means we'll use stdin for input and stdout for output.
173 * In case of a server dpi, we'd use a socket and pass its file descriptor
174 * twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
175 * (Note: by now the last parameter is not used) */
176 sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
177
178 /* Authenticate our client...
179 * As we're using Internet domain sockets, DPIP checks whether the client
180 * runs with the user's ID, by means of a shared secret. The DPIP API does
181 * the work for us. */
182 if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
183 a_Dpip_check_auth(dpip_tag) < 0) {
184 MSG("can't authenticate request: %s\n", dStrerror(errno));
185 a_Dpip_dsh_close(sh);
186 return 1;
187 }
188 dFree(dpip_tag);
189
190 /* Read the dpi command from STDIN
191 * Now we're past the authentication phase, let's see what's dillo
192 * asking from us. a_Dpip_dsh_read_token() will block and return
193 * a full dpip token or null on error (it's commented in dpip.c) */
194 dpip_tag = a_Dpip_dsh_read_token(sh, 1);
195 MSG("tag = [%s]\n", dpip_tag);
196
197 /* Now that we have the dpip_tag, let's isolate the command and url */
198 cmd = a_Dpip_get_attr(dpip_tag, "cmd");
199 url = a_Dpip_get_attr(dpip_tag, "url");
200
201 /* Start sending our answer.
202 * (You can read the comments for DPIP API functions in dpip/dpip.c) */
203 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
204 a_Dpip_dsh_write_str(sh, 0, d_cmd);
205 dFree(d_cmd);
206 dFree(dpip_tag);
207
208 dpip_tag = a_Dpip_dsh_read_token(sh, 1);
209 cmd2 = a_Dpip_get_attr(dpip_tag, "cmd");
210 if (cmd2) {
211 if (strcmp(cmd2, "start_send_page") == 0 &&
212 (size_str = a_Dpip_get_attr(dpip_tag, "data_size"))) {
213 data_size = strtol(size_str, NULL, 10);
214 /* Choose your flavour */
215 //send_plain_text(sh, data_size);
216 //send_numbered_text(sh, data_size);
217 send_html_text(sh, url, data_size);
218 } else if (strcmp(cmd2, "DpiError") == 0) {
219 /* Dillo detected an error (other failures just close the socket) */
220 a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
221 a_Dpip_dsh_write_str(sh, 1, "[vsource dpi]: "
222 "ERROR: Page not cached.\n");
223 }
224 dFree(cmd2);
225 }
226
227 dFree(cmd);
228 dFree(url);
229 dFree(size_str);
230 dFree(dpip_tag);
231
232 /* Finish the SockHandler */
233 a_Dpip_dsh_close(sh);
234 a_Dpip_dsh_free(sh);
235
236 return 0;
237 }
238
0 AM_CPPFLAGS=-DDPIDRC_SYS='"$(sysconfdir)/dpidrc"'
1 AM_CFLAGS = @GLIB_CFLAGS@
0 AM_CPPFLAGS = \
1 -I$(top_srcdir) \
2 -DDPIDRC_SYS='"$(sysconfdir)/dpidrc"'
23
3 bin_PROGRAMS = dpid
4 dpid_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
4 bin_PROGRAMS = dpid dpidc
5 dpid_LDADD = \
6 $(top_builddir)/dpip/libDpip.a \
7 $(top_builddir)/dlib/libDlib.a
8 dpidc_LDADD = \
9 $(top_builddir)/dpip/libDpip.a \
10 $(top_builddir)/dlib/libDlib.a
511
6 EXTRA_DIST = dpidc
7 bin_SCRIPTS = dpidc
12 EXTRA_DIST = dpidrc.in
813
914 dpid_SOURCES = \
10 dpi.h \
11 dpi_service.h \
12 dpi_socket_dir.h \
13 dpid.h \
14 dpid_common.h \
15 misc_new.h \
16 dpi.c \
17 dpi_service.c \
18 dpi_socket_dir.c \
19 dpid.c \
20 dpid_common.c \
21 main.c \
22 misc_new.c
15 dpi.h \
16 dpi_socket_dir.h \
17 dpid.h \
18 dpid_common.h \
19 misc_new.h \
20 dpi.c \
21 dpi_socket_dir.c \
22 dpid.c \
23 dpid_common.c \
24 main.c \
25 misc_new.c
2326
24 install-data-local :
25 $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
26 echo dpi_dir=$(libdir)/dillo/dpi > $(DESTDIR)$(sysconfdir)/dpidrc
27 dpidc_SOURCES = dpidc.c
28
29 sysconf_DATA = dpidrc
30 CLEANFILES = $(sysconf_DATA)
31
32 dpidrc: $(srcdir)/dpidrc.in Makefile
33 sed -e 's|[@]libdir[@]|$(libdir)|' $(srcdir)/dpidrc.in > dpidrc
34
+0
-488
dpid/Makefile.in less more
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16
17 SOURCES = $(dpid_SOURCES)
18
19 srcdir = @srcdir@
20 top_srcdir = @top_srcdir@
21 VPATH = @srcdir@
22 pkgdatadir = $(datadir)/@PACKAGE@
23 pkglibdir = $(libdir)/@PACKAGE@
24 pkgincludedir = $(includedir)/@PACKAGE@
25 top_builddir = ..
26 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
27 INSTALL = @INSTALL@
28 install_sh_DATA = $(install_sh) -c -m 644
29 install_sh_PROGRAM = $(install_sh) -c
30 install_sh_SCRIPT = $(install_sh) -c
31 INSTALL_HEADER = $(INSTALL_DATA)
32 transform = $(program_transform_name)
33 NORMAL_INSTALL = :
34 PRE_INSTALL = :
35 POST_INSTALL = :
36 NORMAL_UNINSTALL = :
37 PRE_UNINSTALL = :
38 POST_UNINSTALL = :
39 build_triplet = @build@
40 host_triplet = @host@
41 target_triplet = @target@
42 bin_PROGRAMS = dpid$(EXEEXT)
43 subdir = dpid
44 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in TODO
45 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
46 am__aclocal_m4_deps = $(top_srcdir)/configure.in
47 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
48 $(ACLOCAL_M4)
49 mkinstalldirs = $(install_sh) -d
50 CONFIG_HEADER = $(top_builddir)/config.h
51 CONFIG_CLEAN_FILES =
52 am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)"
53 binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
54 PROGRAMS = $(bin_PROGRAMS)
55 am_dpid_OBJECTS = dpi.$(OBJEXT) dpi_service.$(OBJEXT) \
56 dpi_socket_dir.$(OBJEXT) dpid.$(OBJEXT) dpid_common.$(OBJEXT) \
57 main.$(OBJEXT) misc_new.$(OBJEXT)
58 dpid_OBJECTS = $(am_dpid_OBJECTS)
59 dpid_DEPENDENCIES = ../dpip/libDpip.a
60 binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
61 SCRIPTS = $(bin_SCRIPTS)
62 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
63 depcomp = $(SHELL) $(top_srcdir)/depcomp
64 am__depfiles_maybe = depfiles
65 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
66 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
67 CCLD = $(CC)
68 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
69 SOURCES = $(dpid_SOURCES)
70 DIST_SOURCES = $(dpid_SOURCES)
71 ETAGS = etags
72 CTAGS = ctags
73 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
74 ACLOCAL = @ACLOCAL@
75 AMDEP_FALSE = @AMDEP_FALSE@
76 AMDEP_TRUE = @AMDEP_TRUE@
77 AMTAR = @AMTAR@
78 AUTOCONF = @AUTOCONF@
79 AUTOHEADER = @AUTOHEADER@
80 AUTOMAKE = @AUTOMAKE@
81 AWK = @AWK@
82 CC = @CC@
83 CCDEPMODE = @CCDEPMODE@
84 CFLAGS = @CFLAGS@
85 CPP = @CPP@
86 CPPFLAGS = @CPPFLAGS@
87 CXX = @CXX@
88 CXXDEPMODE = @CXXDEPMODE@
89 CXXFLAGS = @CXXFLAGS@
90 CYGPATH_W = @CYGPATH_W@
91 DEFS = @DEFS@
92 DEPDIR = @DEPDIR@
93 DLGUI_FALSE = @DLGUI_FALSE@
94 DLGUI_TRUE = @DLGUI_TRUE@
95 ECHO_C = @ECHO_C@
96 ECHO_N = @ECHO_N@
97 ECHO_T = @ECHO_T@
98 EGREP = @EGREP@
99 EXEEXT = @EXEEXT@
100 GLIB_CFLAGS = @GLIB_CFLAGS@
101 GLIB_CONFIG = @GLIB_CONFIG@
102 GLIB_LIBS = @GLIB_LIBS@
103 GTK_CFLAGS = @GTK_CFLAGS@
104 GTK_CONFIG = @GTK_CONFIG@
105 GTK_LIBS = @GTK_LIBS@
106 INSTALL_DATA = @INSTALL_DATA@
107 INSTALL_PROGRAM = @INSTALL_PROGRAM@
108 INSTALL_SCRIPT = @INSTALL_SCRIPT@
109 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
110 LDFLAGS = @LDFLAGS@
111 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
112 LIBFLTK_LIBS = @LIBFLTK_LIBS@
113 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
114 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
115 LIBJPEG_LIBS = @LIBJPEG_LIBS@
116 LIBOBJS = @LIBOBJS@
117 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
118 LIBPNG_LIBS = @LIBPNG_LIBS@
119 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
120 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
121 LIBS = @LIBS@
122 LIBSSL_LIBS = @LIBSSL_LIBS@
123 LIBZ_LIBS = @LIBZ_LIBS@
124 LTLIBOBJS = @LTLIBOBJS@
125 MAKEINFO = @MAKEINFO@
126 OBJEXT = @OBJEXT@
127 PACKAGE = @PACKAGE@
128 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
129 PACKAGE_NAME = @PACKAGE_NAME@
130 PACKAGE_STRING = @PACKAGE_STRING@
131 PACKAGE_TARNAME = @PACKAGE_TARNAME@
132 PACKAGE_VERSION = @PACKAGE_VERSION@
133 PATH_SEPARATOR = @PATH_SEPARATOR@
134 RANLIB = @RANLIB@
135 SET_MAKE = @SET_MAKE@
136 SHELL = @SHELL@
137 STRIP = @STRIP@
138 VERSION = @VERSION@
139 ac_ct_CC = @ac_ct_CC@
140 ac_ct_CXX = @ac_ct_CXX@
141 ac_ct_RANLIB = @ac_ct_RANLIB@
142 ac_ct_STRIP = @ac_ct_STRIP@
143 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
144 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
145 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
146 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
147 am__include = @am__include@
148 am__leading_dot = @am__leading_dot@
149 am__quote = @am__quote@
150 am__tar = @am__tar@
151 am__untar = @am__untar@
152 bindir = @bindir@
153 build = @build@
154 build_alias = @build_alias@
155 build_cpu = @build_cpu@
156 build_os = @build_os@
157 build_vendor = @build_vendor@
158 datadir = @datadir@
159 exec_prefix = @exec_prefix@
160 host = @host@
161 host_alias = @host_alias@
162 host_cpu = @host_cpu@
163 host_os = @host_os@
164 host_vendor = @host_vendor@
165 includedir = @includedir@
166 infodir = @infodir@
167 install_sh = @install_sh@
168 libdir = @libdir@
169 libexecdir = @libexecdir@
170 localstatedir = @localstatedir@
171 mandir = @mandir@
172 mkdir_p = @mkdir_p@
173 oldincludedir = @oldincludedir@
174 prefix = @prefix@
175 program_transform_name = @program_transform_name@
176 sbindir = @sbindir@
177 sharedstatedir = @sharedstatedir@
178 sysconfdir = @sysconfdir@
179 target = @target@
180 target_alias = @target_alias@
181 target_cpu = @target_cpu@
182 target_os = @target_os@
183 target_vendor = @target_vendor@
184 AM_CPPFLAGS = -DDPIDRC_SYS='"$(sysconfdir)/dpidrc"'
185 AM_CFLAGS = @GLIB_CFLAGS@
186 dpid_LDADD = @GLIB_LIBS@ ../dpip/libDpip.a
187 EXTRA_DIST = dpidc
188 bin_SCRIPTS = dpidc
189 dpid_SOURCES = \
190 dpi.h \
191 dpi_service.h \
192 dpi_socket_dir.h \
193 dpid.h \
194 dpid_common.h \
195 misc_new.h \
196 dpi.c \
197 dpi_service.c \
198 dpi_socket_dir.c \
199 dpid.c \
200 dpid_common.c \
201 main.c \
202 misc_new.c
203
204 all: all-am
205
206 .SUFFIXES:
207 .SUFFIXES: .c .o .obj
208 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
209 @for dep in $?; do \
210 case '$(am__configure_deps)' in \
211 *$$dep*) \
212 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
213 && exit 0; \
214 exit 1;; \
215 esac; \
216 done; \
217 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu dpid/Makefile'; \
218 cd $(top_srcdir) && \
219 $(AUTOMAKE) --gnu dpid/Makefile
220 .PRECIOUS: Makefile
221 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
222 @case '$?' in \
223 *config.status*) \
224 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
225 *) \
226 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
227 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
228 esac;
229
230 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
231 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
232
233 $(top_srcdir)/configure: $(am__configure_deps)
234 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
235 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
236 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
237 install-binPROGRAMS: $(bin_PROGRAMS)
238 @$(NORMAL_INSTALL)
239 test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
240 @list='$(bin_PROGRAMS)'; for p in $$list; do \
241 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
242 if test -f $$p \
243 ; then \
244 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
245 echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
246 $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
247 else :; fi; \
248 done
249
250 uninstall-binPROGRAMS:
251 @$(NORMAL_UNINSTALL)
252 @list='$(bin_PROGRAMS)'; for p in $$list; do \
253 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
254 echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
255 rm -f "$(DESTDIR)$(bindir)/$$f"; \
256 done
257
258 clean-binPROGRAMS:
259 -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
260 dpid$(EXEEXT): $(dpid_OBJECTS) $(dpid_DEPENDENCIES)
261 @rm -f dpid$(EXEEXT)
262 $(LINK) $(dpid_LDFLAGS) $(dpid_OBJECTS) $(dpid_LDADD) $(LIBS)
263 install-binSCRIPTS: $(bin_SCRIPTS)
264 @$(NORMAL_INSTALL)
265 test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
266 @list='$(bin_SCRIPTS)'; for p in $$list; do \
267 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
268 if test -f $$d$$p; then \
269 f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
270 echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
271 $(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
272 else :; fi; \
273 done
274
275 uninstall-binSCRIPTS:
276 @$(NORMAL_UNINSTALL)
277 @list='$(bin_SCRIPTS)'; for p in $$list; do \
278 f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
279 echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
280 rm -f "$(DESTDIR)$(bindir)/$$f"; \
281 done
282
283 mostlyclean-compile:
284 -rm -f *.$(OBJEXT)
285
286 distclean-compile:
287 -rm -f *.tab.c
288
289 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpi.Po@am__quote@
290 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpi_service.Po@am__quote@
291 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpi_socket_dir.Po@am__quote@
292 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpid.Po@am__quote@
293 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpid_common.Po@am__quote@
294 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
295 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc_new.Po@am__quote@
296
297 .c.o:
298 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
299 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
300 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
301 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
302 @am__fastdepCC_FALSE@ $(COMPILE) -c $<
303
304 .c.obj:
305 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
306 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
307 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
308 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
309 @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
310 uninstall-info-am:
311
312 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
313 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
314 unique=`for i in $$list; do \
315 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
316 done | \
317 $(AWK) ' { files[$$0] = 1; } \
318 END { for (i in files) print i; }'`; \
319 mkid -fID $$unique
320 tags: TAGS
321
322 TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
323 $(TAGS_FILES) $(LISP)
324 tags=; \
325 here=`pwd`; \
326 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
327 unique=`for i in $$list; do \
328 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
329 done | \
330 $(AWK) ' { files[$$0] = 1; } \
331 END { for (i in files) print i; }'`; \
332 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
333 test -n "$$unique" || unique=$$empty_fix; \
334 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
335 $$tags $$unique; \
336 fi
337 ctags: CTAGS
338 CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
339 $(TAGS_FILES) $(LISP)
340 tags=; \
341 here=`pwd`; \
342 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
343 unique=`for i in $$list; do \
344 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
345 done | \
346 $(AWK) ' { files[$$0] = 1; } \
347 END { for (i in files) print i; }'`; \
348 test -z "$(CTAGS_ARGS)$$tags$$unique" \
349 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
350 $$tags $$unique
351
352 GTAGS:
353 here=`$(am__cd) $(top_builddir) && pwd` \
354 && cd $(top_srcdir) \
355 && gtags -i $(GTAGS_ARGS) $$here
356
357 distclean-tags:
358 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
359
360 distdir: $(DISTFILES)
361 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
362 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
363 list='$(DISTFILES)'; for file in $$list; do \
364 case $$file in \
365 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
366 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
367 esac; \
368 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
369 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
370 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
371 dir="/$$dir"; \
372 $(mkdir_p) "$(distdir)$$dir"; \
373 else \
374 dir=''; \
375 fi; \
376 if test -d $$d/$$file; then \
377 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
378 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
379 fi; \
380 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
381 else \
382 test -f $(distdir)/$$file \
383 || cp -p $$d/$$file $(distdir)/$$file \
384 || exit 1; \
385 fi; \
386 done
387 check-am: all-am
388 check: check-am
389 all-am: Makefile $(PROGRAMS) $(SCRIPTS)
390 installdirs:
391 for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)"; do \
392 test -z "$$dir" || $(mkdir_p) "$$dir"; \
393 done
394 install: install-am
395 install-exec: install-exec-am
396 install-data: install-data-am
397 uninstall: uninstall-am
398
399 install-am: all-am
400 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
401
402 installcheck: installcheck-am
403 install-strip:
404 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
405 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
406 `test -z '$(STRIP)' || \
407 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
408 mostlyclean-generic:
409
410 clean-generic:
411
412 distclean-generic:
413 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
414
415 maintainer-clean-generic:
416 @echo "This command is intended for maintainers to use"
417 @echo "it deletes files that may require special tools to rebuild."
418 clean: clean-am
419
420 clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
421
422 distclean: distclean-am
423 -rm -rf ./$(DEPDIR)
424 -rm -f Makefile
425 distclean-am: clean-am distclean-compile distclean-generic \
426 distclean-tags
427
428 dvi: dvi-am
429
430 dvi-am:
431
432 html: html-am
433
434 info: info-am
435
436 info-am:
437
438 install-data-am: install-data-local
439
440 install-exec-am: install-binPROGRAMS install-binSCRIPTS
441
442 install-info: install-info-am
443
444 install-man:
445
446 installcheck-am:
447
448 maintainer-clean: maintainer-clean-am
449 -rm -rf ./$(DEPDIR)
450 -rm -f Makefile
451 maintainer-clean-am: distclean-am maintainer-clean-generic
452
453 mostlyclean: mostlyclean-am
454
455 mostlyclean-am: mostlyclean-compile mostlyclean-generic
456
457 pdf: pdf-am
458
459 pdf-am:
460
461 ps: ps-am
462
463 ps-am:
464
465 uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \
466 uninstall-info-am
467
468 .PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
469 clean-generic ctags distclean distclean-compile \
470 distclean-generic distclean-tags distdir dvi dvi-am html \
471 html-am info info-am install install-am install-binPROGRAMS \
472 install-binSCRIPTS install-data install-data-am \
473 install-data-local install-exec install-exec-am install-info \
474 install-info-am install-man install-strip installcheck \
475 installcheck-am installdirs maintainer-clean \
476 maintainer-clean-generic mostlyclean mostlyclean-compile \
477 mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
478 uninstall-am uninstall-binPROGRAMS uninstall-binSCRIPTS \
479 uninstall-info-am
480
481
482 install-data-local :
483 $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
484 echo dpi_dir=$(libdir)/dillo/dpi > $(DESTDIR)$(sysconfdir)/dpidrc
485 # Tell versions [3.59,3.63) of GNU make to not export all variables.
486 # Otherwise a system limit (for SysV at least) may be exceeded.
487 .NOEXPORT:
22
33 This program is free software; you can redistribute it and/or modify
44 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
5 the Free Software Foundation; either version 3 of the License, or
66 (at your option) any later version.
77
88 This program is distributed in the hope that it will be useful,
1111 GNU General Public License for more details.
1212
1313 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
1615 */
1716
1817 /*! \file
2221 */
2322
2423 #include <errno.h>
25 #include <glib.h>
24 #include <stdlib.h> /* for exit */
2625 #include "dpid_common.h"
2726 #include "dpi.h"
2827 #include "misc_new.h"
2928
3029 /*! \Return
3130 * Returns path to the dpi_socket_dir file
32 * Use g_free to free memory
31 * Use dFree to free memory
3332 */
3433 char *a_Dpi_sockdir_file(void)
3534 {
36 char *dpi_socket_dir = NULL, *dirfile_path = "/.dillo/dpi_socket_dir";
35 char *dpi_socket_dir, *dirfile_path = "/.dillo/dpi_socket_dir";
3736
38 dpi_socket_dir = g_strconcat(a_Misc_get_home(), dirfile_path, NULL);
37 dpi_socket_dir = dStrconcat(dGethomedir(), dirfile_path, NULL);
3938 return dpi_socket_dir;
4039 }
4140
5352 FILE *dir;
5453 char *sockdir = NULL, *rcpath;
5554
56 rcpath = g_strconcat(a_Misc_get_home(), "/.dillo", NULL);
55 rcpath = dStrconcat(dGethomedir(), "/.dillo", NULL);
5756
5857 /* If .dillo does not exist it is an unrecoverable error */
5958 if (access(rcpath, F_OK) == -1) {
6059 ERRMSG("a_Dpi_rd_dpi_socket_dir", "access", errno);
61 fprintf(stderr, " - %s\n", rcpath);
60 MSG_ERR(" - %s\n", rcpath);
6261 exit(1);
6362 }
64 g_free(rcpath);
63 dFree(rcpath);
6564
6665 if ((dir = fopen(dirname, "r")) != NULL) {
67 sockdir = a_Misc_get_line(dir);
66 sockdir = dGetline(dir);
6867 fclose(dir);
6968 } else if (errno == ENOENT) {
7069 ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno);
71 fprintf(stderr, " - %s\n", dirname);
70 MSG_ERR(" - %s\n", dirname);
7271 } else if (errno != ENOENT) {
7372 ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno);
74 fprintf(stderr, " - %s\n", dirname);
73 MSG_ERR(" - %s\n", dirname);
7574 exit(1);
7675 }
7776
8988 char *dirfile_path, *sockdir, *srs_name;
9089
9190 dirfile_path = a_Dpi_sockdir_file();
92 sockdir = g_strstrip(a_Dpi_rd_dpi_socket_dir(dirfile_path));
93 srs_name = g_strconcat(sockdir, "/", "dpid.srs", NULL);
94 g_free(sockdir);
95 g_free(dirfile_path);
91 sockdir = dStrstrip(a_Dpi_rd_dpi_socket_dir(dirfile_path));
92 srs_name = dStrconcat(sockdir, "/", "dpid.srs", NULL);
93 dFree(sockdir);
94 dFree(dirfile_path);
9695 return (srs_name);
9796 }
66 #ifndef DPI_H
77 #define DPI_H
88
9 #include <config.h>
10 #include <stdio.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <glib.h>
17
18 /* Check the Unix98 goodie */
19 #ifndef socklen_t
20 #define socklen_t guint32
21 #endif
9 #include <unistd.h> /* for socklen_t */
10 #include <sys/socket.h> /* for socklen_t and AF_LOCAL */
2211
2312 /* Some systems may not have this one... */
2413 #ifndef AF_LOCAL
3827 */
3928 enum {
4029 UNKNOWN_CMD,
41 BYE_CMD, /* "DpiBye" */
30 AUTH_CMD, /* authentication */
31 BYE_CMD, /* "DpiBye" */
4232 CHECK_SERVER_CMD, /* "check_server" */
4333 REGISTER_ALL_CMD, /* "register_all" */
4434 REGISTER_SERVICE_CMD /* "register_service" */
+0
-149
dpid/dpi_service.c less more
0 /*
1 Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 /*! \file
19 * \todo
20 * This module should be removed because its original functions
21 * have been removed or modified.
22 * Put these functions in dpid.c
23 */
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <glib.h>
30 #include "dpid_common.h"
31 #include "dpid.h"
32 #include "../dpip/dpip.h"
33
34 #ifdef TEST
35 #include "testdat.h"
36 #endif
37
38 /* exported functions */
39 char *get_dpi_dir(char *dpidrc);
40
41
42 /*
43 * Read a line of text up to the newline character, store it into a newly
44 * allocated string and return it.
45 * \Note
46 * Copied from dpi/bookmarks.c
47 */
48 static char *Get_line(FILE *stream)
49 {
50 guint i, size = 64;
51 int ch;
52 char *buf;
53
54 buf = g_new(char, size);
55
56 for (i = 0; (ch = fgetc(stream)) != EOF; ++i) {
57 if (i + 1 == size) {
58 size *= 2;
59 buf = g_realloc(buf, size);
60 }
61 if ((buf[i] = ch) == '\n' && ++i)
62 break;
63 }
64 buf[i] = 0;
65
66 if (i > 0) {
67 buf = g_realloc(buf, i + 1);
68 } else {
69 g_free(buf);
70 buf = NULL;
71 }
72 return buf;
73 }
74
75 /*! Get dpi directory path from dpidrc
76 * \Return
77 * dpi directory on success, NULL on failure
78 * \Important
79 * The dpi_dir definition in dpidrc must have no leading white space.
80 */
81 char *get_dpi_dir(char *dpidrc)
82 {
83 FILE *In;
84 gint len;
85 char *rcline = NULL, *value = NULL, *p;
86
87 if ((In = fopen(dpidrc, "r")) == NULL) {
88 ERRMSG("dpi_dir", "fopen", errno);
89 fprintf(stderr, " - %s\n", dpidrc);
90 return (NULL);
91 }
92
93 while ((rcline = Get_line(In)) != NULL) {
94 if (strncmp(rcline, "dpi_dir", 7) == 0)
95 break;
96 g_free(rcline);
97 }
98 fclose(In);
99
100 if (!rcline) {
101 ERRMSG("dpi_dir", "Failed to find a dpi_dir entry in dpidrc", 0);
102 fprintf(stderr, "Put your dillo plugins path in %s\n", dpidrc);
103 fprintf(stderr, "eg. dpi_dir=/usr/local/lib/dillo/dpi ");
104 fprintf(stderr, "with no leading spaces.\n");
105 value = NULL;
106 } else {
107 len = (int) strlen(rcline);
108 if (len && rcline[len - 1] == '\n')
109 rcline[len - 1] = 0;
110
111 if ((p = strchr(rcline, '='))) {
112 while (*++p == ' ');
113 value = g_strdup(p);
114 } else {
115 ERRMSG("dpi_dir", "strchr", 0);
116 fprintf(stderr, " - '=' not found in %s\n", rcline);
117 value = NULL;
118 }
119 }
120
121 if (rcline)
122 g_free(rcline);
123 return (value);
124 }
125
126 /*! Send the list of available dpi IDs to a client
127 * \Return
128 * 1 on success, -1 on failure.
129 *
130 static int send_service_list(int sock, struct dp *dpi_attr_list, int srv_num)
131 {
132 int i;
133 gchar *buf;
134 ssize_t wlen = 0;
135
136 for (i = 0; i < srv_num && wlen != -1; i++) {
137 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
138 "send_data", dpi_attr_list[i].id);
139 wlen = write(sock, d_cmd, strlen(d_cmd));
140 g_free(d_cmd);
141 }
142 if (wlen == -1) {
143 ERRMSG("send_service_list", "write", errno);
144 return (-1);
145 }
146 return (1);
147 }
148 */
+0
-22
dpid/dpi_service.h less more
0 /*! \file
1 * \todo
2 * This module should be removed because its original functions
3 * have been removed or modified.
4 * Put these functions in dpid.c
5 */
6
7 #ifndef DPI_SERVICE_H
8 #define DPI_SERVICE_H
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include "dpid.h"
14
15 char *Get_line(FILE *stream);
16
17 char *get_dpi_dir(char *dpidrc);
18
19 int send_service_list(int sock, struct dp *dpi_attr_list, int srv_num);
20
21 #endif
22
33 This program is free software; you can redistribute it and/or modify
44 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
5 the Free Software Foundation; either version 3 of the License, or
66 (at your option) any later version.
77
88 This program is distributed in the hope that it will be useful,
1111 GNU General Public License for more details.
1212
1313 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
1615 */
1716
1817 /*! \file
2019 */
2120
2221 #include <errno.h>
22 #include <stdlib.h>
2323 #include "dpid_common.h"
2424 #include "dpi.h"
2525 #include "misc_new.h"
5353 int tst_dir(char *dir)
5454 {
5555 char *dirtest;
56 gint ret = 0;
56 int ret = 0;
5757
5858 /* test for a directory */
59 dirtest = g_strconcat(dir, "/", NULL);
59 dirtest = dStrconcat(dir, "/", NULL);
6060 if (access(dirtest, F_OK) == -1) {
6161 ERRMSG("tst_dir", "access", errno);
62 fprintf(stderr, " - %s\n", dirtest);
62 MSG_ERR(" - %s\n", dirtest);
6363 } else {
6464 ret = 1;
6565 }
66 g_free(dirtest);
66 dFree(dirtest);
6767
6868 return ret;
6969 }
7575 */
7676 char *mk_sockdir(void)
7777 {
78 char *username, *template;
78 char *template, *logname;
7979
80 username = a_Misc_get_user();
81 template = g_strconcat("/tmp/", username, "-", "XXXXXX", NULL);
80 logname = getenv("LOGNAME") ? getenv("LOGNAME") : "dillo";
81 template = dStrconcat("/tmp/", logname, "-", "XXXXXX", NULL);
8282 if (a_Misc_mkdtemp(template) == NULL) {
8383 ERRMSG("mk_sockdir", "a_Misc_mkdtemp", 0);
84 fprintf(stderr, " - %s\n", template);
85 g_free(template);
84 MSG_ERR(" - %s\n", template);
85 dFree(template);
8686 return (NULL);
8787 }
8888 return template;
100100 int dir_ok = 0;
101101
102102 if ((sockdir = a_Dpi_rd_dpi_socket_dir(dpi_socket_dir)) == NULL) {
103 fprintf(stderr, "debug_msg - init_sockdir: ");
104 fprintf(stderr, "The dpi_socket_dir file does not exist\n");
103 MSG_ERR("init_sockdir: The dpi_socket_dir file %s does not exist\n",
104 dpi_socket_dir);
105105 } else {
106106 if ((dir_ok = tst_dir(sockdir)) == 1) {
107 fprintf(stderr,
108 "debug_msg - init_sockdir: The socket directory ");
109 fprintf(stderr, "%s exists and is OK\n", sockdir);
107 MSG_ERR("init_sockdir: The socket directory %s exists and is OK\n",
108 sockdir);
110109 } else {
111 fprintf(stderr,
112 "debug_msg - init_sockdir: The socket directory ");
113 fprintf(stderr, "%s does not exist or is not a directory\n",
114 sockdir);
115 g_free(sockdir);
110 MSG_ERR("init_sockdir: The socket directory %s does not exist "
111 "or is not a directory\n", sockdir);
112 dFree(sockdir);
116113 }
117114 }
118115 if (!dir_ok) {
119116 sockdir = mk_sockdir();
120117 if (sockdir == NULL) {
121118 ERRMSG("init_sockdir", "mk_sockdir", 0);
122 fprintf(stderr, " - Failed to create dpi socket directory\n");
119 MSG_ERR(" - Failed to create dpi socket directory\n");
123120 } else if ((w_dpi_socket_dir(dpi_socket_dir, sockdir)) == -1) {
124121 ERRMSG("init_sockdir", "w_dpi_socket_dir", 0);
125 fprintf(stderr, " - failed to save %s\n", sockdir);
126 g_free(sockdir);
122 MSG_ERR(" - failed to save %s\n", sockdir);
123 dFree(sockdir);
127124 sockdir = NULL;
128125 }
129126 }
22
33 This program is free software; you can redistribute it and/or modify
44 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
5 the Free Software Foundation; either version 3 of the License, or
66 (at your option) any later version.
77
88 This program is distributed in the hope that it will be useful,
1111 GNU General Public License for more details.
1212
1313 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
1615 */
1716
1817 /*! \file
1918 * Main functions to set-up dpi information and to initialise sockets
2019 */
2120 #include <errno.h>
22 #include <glib.h>
21 #include <stdlib.h> /* for exit */
22 #include <fcntl.h> /* for F_SETFD, F_GETFD, FD_CLOEXEC */
23
2324 #include <sys/stat.h>
2425 #include <sys/wait.h>
26 #include <sys/socket.h>
27 #include <netinet/tcp.h>
28
2529 #include <unistd.h>
2630 #include "dpid_common.h"
2731 #include "dpid.h"
2832 #include "dpi.h"
2933 #include "dpi_socket_dir.h"
30 #include "dpi_service.h"
3134 #include "misc_new.h"
3235
3336 #include "../dpip/dpip.h"
3538 #define QUEUE 5
3639
3740 volatile sig_atomic_t caught_sigchld = 0;
38
39 /*! Close and remove the sockets in the
40 * given dpi attribute list
41 */
42 void rm_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
43 {
44 gint i;
45
46 for (i = 0; i < numdpis; i++) {
47 a_Misc_close_fd(dpi_attr_list[i].socket);
48 (void) unlink(dpi_attr_list[i].sockpath);
49 }
50 }
51
52 /*! Close and remove inactive dpi sockets
53 * \Return
54 * Number of active dpis.
55 */
56 int rm_inactive_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
57 {
58 int i, active = 0;
59
60 for (i = 0; i < numdpis; i++) {
61 if (dpi_attr_list[i].pid == 1) {
62 a_Misc_close_fd(dpi_attr_list[i].socket);
63 (void) unlink(dpi_attr_list[i].sockpath);
64 } else
65 active++;
66 }
67 return (active);
68 }
69
70 /*! Remove sockets
71 */
72 void cleanup(char *socket_dir)
73 {
74 DIR *dir;
75 struct dirent *dir_entry = NULL;
76 char *sockpath;
77
78 dir = opendir(socket_dir);
79 if (dir == NULL) {
80 ERRMSG("cleanup", "opendir", errno);
81 return;
82 }
83 while ( (dir_entry = readdir(dir)) != NULL ) {
84 if ( dir_entry->d_name[0] == '.' )
85 continue;
86 sockpath = g_strconcat(socket_dir, "/", dir_entry->d_name, NULL);
87 unlink(sockpath);
88 g_free(sockpath);
89 }
90 closedir(dir);
41 char *SharedKey = NULL;
42
43 /*! Remove dpid_comm_keys file.
44 * This avoids that dillo instances connect to a stale port after dpid
45 * has exited (e.g. after a reboot).
46 */
47 void cleanup()
48 {
49 char *fname;
50 fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
51 unlink(fname);
52 dFree(fname);
9153 }
9254
9355 /*! Free memory used to describe
9658 void free_dpi_attr(struct dp *dpi_attr)
9759 {
9860 if (dpi_attr->id != NULL) {
99 g_free(dpi_attr->id);
61 dFree(dpi_attr->id);
10062 dpi_attr->id = NULL;
10163 }
10264 if (dpi_attr->path != NULL) {
103 g_free(dpi_attr->path);
65 dFree(dpi_attr->path);
10466 dpi_attr->path = NULL;
105 }
106 if (dpi_attr->sockpath != NULL) {
107 g_free(dpi_attr->sockpath);
108 dpi_attr->sockpath = NULL;
10967 }
11068 }
11169
12280 for (i = 0; i < numdpis; i++)
12381 free_dpi_attr(dpi_attr_list + i);
12482
125 g_free(dpi_attr_list);
83 dFree(dpi_attr_list);
12684 *dpi_attr_list_ptr = NULL;
12785 }
12886
129 /*! \todo
130 * Remove terminator and est_terminator unless we really want to clean up
131 * on abnormal exit.
132 */
133 #if 0
87 /*! Free memory used by the services list
88 */
89 void free_services_list(Dlist *s_list)
90 {
91 int i = 0;
92 struct service *s;
93
94 for (i=0; i < dList_length(s_list) ; i++) {
95 s = dList_nth_data(s_list, i);
96 dFree(s->name);
97 }
98 dList_free(s_list);
99 }
100
134101 /*! Signal handler for SIGINT, SIGQUIT, and SIGTERM. Calls cleanup
135102 */
136 void terminator(int sig)
137 {
138 (void) signal(SIGCHLD, SIG_DFL);
103 static void terminator(int sig)
104 {
105 (void) sig; /* suppress unused parameter warning */
139106 cleanup();
140 (void) signal(sig, SIG_DFL);
141 (void) raise(sig);
142107 _exit(0);
143108 }
144109
145110 /*! Establish handler for termination signals
146111 * and register cleanup with atexit */
147 void est_terminator(void)
112 void est_dpi_terminator()
148113 {
149114 struct sigaction act;
150115 sigset_t block;
151116
152 (void) sigemptyset(&block);
153 (void) sigaddset(&block, SIGINT);
154 (void) sigaddset(&block, SIGQUIT);
155 (void) sigaddset(&block, SIGTERM);
156 (void) sigaddset(&block, SIGSEGV);
117 sigemptyset(&block);
118 sigaddset(&block, SIGHUP);
119 sigaddset(&block, SIGINT);
120 sigaddset(&block, SIGQUIT);
121 sigaddset(&block, SIGTERM);
157122
158123 act.sa_handler = terminator;
159124 act.sa_mask = block;
160125 act.sa_flags = 0;
161126
162 if (sigaction(SIGINT, &act, NULL) ||
127 if (sigaction(SIGHUP, &act, NULL) ||
128 sigaction(SIGINT, &act, NULL) ||
163129 sigaction(SIGQUIT, &act, NULL) ||
164 sigaction(SIGTERM, &act, NULL) || sigaction(SIGSEGV, &act, NULL)) {
165 ERRMSG("est_terminator", "sigaction", errno);
130 sigaction(SIGTERM, &act, NULL)) {
131 ERRMSG("est_dpi_terminator", "sigaction", errno);
166132 exit(1);
167133 }
168134
169135 if (atexit(cleanup) != 0) {
170 ERRMSG("est_terminator", "atexit", 0);
171 fprintf(stderr, "Hey! atexit failed, how did that happen?\n");
136 ERRMSG("est_dpi_terminator", "atexit", 0);
137 MSG_ERR("Hey! atexit failed, how did that happen?\n");
172138 exit(1);
173139 }
174140 }
175 #endif
176141
177142 /*! Identify a given file
178143 * Currently there is only one file type associated with dpis.
179144 * More file types will be added as needed
180145 */
181 enum file_type get_file_type(gchar *file_name)
182 {
183 gchar *dot = strrchr(file_name, '.');
146 enum file_type get_file_type(char *file_name)
147 {
148 char *dot = strrchr(file_name, '.');
184149
185150 if (dot && !strcmp(dot, ".dpi"))
186151 return DPI_FILE; /* Any filename ending in ".dpi" */
187152 else {
188 fprintf(stderr, "get_file_type: Unknown file type for %s\n", file_name);
153 MSG_ERR("get_file_type: Unknown file type for %s\n", file_name);
189154 return UNKNOWN_FILE;
190155 }
156 }
157
158 /*! Get dpi directory path from dpidrc
159 * \Return
160 * dpi directory on success, NULL on failure
161 * \Important
162 * The dpi_dir definition in dpidrc must have no leading white space.
163 */
164 char *get_dpi_dir(char *dpidrc)
165 {
166 FILE *In;
167 int len;
168 char *rcline = NULL, *value = NULL, *p;
169
170 if ((In = fopen(dpidrc, "r")) == NULL) {
171 ERRMSG("dpi_dir", "fopen", errno);
172 MSG_ERR(" - %s\n", dpidrc);
173 return (NULL);
174 }
175
176 while ((rcline = dGetline(In)) != NULL) {
177 if (strncmp(rcline, "dpi_dir", 7) == 0)
178 break;
179 dFree(rcline);
180 }
181 fclose(In);
182
183 if (!rcline) {
184 ERRMSG("dpi_dir", "Failed to find a dpi_dir entry in dpidrc", 0);
185 MSG_ERR("Put your dillo plugins path in %s\n", dpidrc);
186 MSG_ERR("e.g. dpi_dir=/usr/local/lib/dillo/dpi\n");
187 MSG_ERR("with no leading spaces.\n");
188 value = NULL;
189 } else {
190 len = (int) strlen(rcline);
191 if (len && rcline[len - 1] == '\n')
192 rcline[len - 1] = 0;
193
194 if ((p = strchr(rcline, '='))) {
195 while (*++p == ' ');
196 value = dStrdup(p);
197 } else {
198 ERRMSG("dpi_dir", "strchr", 0);
199 MSG_ERR(" - '=' not found in %s\n", rcline);
200 value = NULL;
201 }
202 }
203
204 dFree(rcline);
205 return (value);
191206 }
192207
193208 /*! Scans a service directory in dpi_dir and fills dpi_attr
205220 char *service_dir = NULL;
206221 struct stat statinfo;
207222 enum file_type ftype;
208 int retval = -1;
223 int ret = -1;
209224 DIR *dir_stream;
210225 struct dirent *dir_entry = NULL;
211226
212 service_dir = g_strconcat(dpi_dir, "/", service, NULL);
227 service_dir = dStrconcat(dpi_dir, "/", service, NULL);
213228 if (stat(service_dir, &statinfo) == -1) {
214229 ERRMSG("get_dpi_attr", "stat", errno);
215 fprintf(stderr, "file=%s\n", service_dir);
216 } else if ( (dir_stream = opendir(service_dir)) == NULL) {
230 MSG_ERR("file=%s\n", service_dir);
231 } else if ((dir_stream = opendir(service_dir)) == NULL) {
217232 ERRMSG("get_dpi_attr", "opendir", errno);
218233 } else {
219 /* Scan the directory loking for dpi files.
234 /* Scan the directory looking for dpi files.
220235 * (currently there's only the dpi program, but in the future
221236 * there may also be helper scripts.) */
222237 while ( (dir_entry = readdir(dir_stream)) != NULL) {
227242 switch (ftype) {
228243 case DPI_FILE:
229244 dpi_attr->path =
230 g_strconcat(service_dir, "/", dir_entry->d_name, NULL);
231 dpi_attr->id = g_strdup(service);
232 dpi_attr->sockpath = NULL;
245 dStrconcat(service_dir, "/", dir_entry->d_name, NULL);
246 dpi_attr->id = dStrdup(service);
247 dpi_attr->port = 0;
233248 dpi_attr->pid = 1;
234249 if (strstr(dpi_attr->path, ".filter") != NULL)
235250 dpi_attr->filter = 1;
236251 else
237252 dpi_attr->filter = 0;
238 retval = 0;
253 ret = 0;
239254 break;
240255 default:
241256 break;
243258 }
244259 closedir(dir_stream);
245260
246 if (retval != 0)
247 fprintf(stderr,"get_dpi_attr: No dpi plug-in in %s/%s\n",
261 if (ret != 0)
262 MSG_ERR("get_dpi_attr: No dpi plug-in in %s/%s\n",
248263 dpi_dir, service);
249264 }
250 g_free(service_dir);
251 return retval;
265 dFree(service_dir);
266 return ret;
252267 }
253268
254269 /*! Register a service
263278 int register_service(struct dp *dpi_attr, char *service)
264279 {
265280 char *user_dpi_dir, *dpidrc, *user_service_dir, *dir = NULL;
266 int retval = -1;
267
268 /* g_get_home_dir makes it hard to test for mem leaks */
269 user_dpi_dir = g_strconcat(a_Misc_get_home(), "/", dotDILLO_DPI, NULL);
281 int ret = -1;
282
283 user_dpi_dir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
270284 user_service_dir =
271 g_strconcat(a_Misc_get_home(), "/", dotDILLO_DPI, "/", service, NULL);
272
273 dpidrc = g_strconcat(a_Misc_get_home(), "/", dotDILLO_DPIDRC, NULL);
274 if ( access(dpidrc, F_OK) == -1 ) {
275 if ( access(DPIDRC_SYS, F_OK) == -1 ) {
285 dStrconcat(dGethomedir(), "/", dotDILLO_DPI, "/", service, NULL);
286
287 dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
288 if (access(dpidrc, F_OK) == -1) {
289 if (access(DPIDRC_SYS, F_OK) == -1) {
276290 ERRMSG("register_service", "Error ", 0);
277 fprintf(stderr, "\n - There is no %s or %s file\n", dpidrc,
291 MSG_ERR("\n - There is no %s or %s file\n", dpidrc,
278292 DPIDRC_SYS);
279 g_free(user_dpi_dir);
280 g_free(user_service_dir);
281 g_free(dpidrc);
293 dFree(user_dpi_dir);
294 dFree(user_service_dir);
295 dFree(dpidrc);
282296 return(-1);
283297 }
284 g_free(dpidrc);
285 dpidrc = g_strdup(DPIDRC_SYS);
298 dFree(dpidrc);
299 dpidrc = dStrdup(DPIDRC_SYS);
286300 }
287301
288302 /* Check home dir for dpis */
289303 if (access(user_service_dir, F_OK) == 0) {
290304 get_dpi_attr(user_dpi_dir, service, dpi_attr);
291 retval = 0;
305 ret = 0;
292306 } else { /* Check system wide dpis */
293307 if ((dir = get_dpi_dir(dpidrc)) != NULL) {
294308 if (access(dir, F_OK) == 0) {
295309 get_dpi_attr(dir, service, dpi_attr);
296 retval = 0;
310 ret = 0;
297311 } else {
298 ERRMSG("register_service", "get_dpi_attr failed\n", 0);
312 ERRMSG("register_service", "get_dpi_attr failed", 0);
299313 }
300314 } else {
301 ERRMSG("register_service", "dpi_dir: Error getting dpi dir.\n", 0);
302 }
303 }
304 g_free(user_dpi_dir);
305 g_free(user_service_dir);
306 g_free(dpidrc);
307 g_free(dir);
308 return (retval);
315 ERRMSG("register_service", "dpi_dir: Error getting dpi dir.", 0);
316 }
317 }
318 dFree(user_dpi_dir);
319 dFree(user_service_dir);
320 dFree(dpidrc);
321 dFree(dir);
322 return ret;
309323 }
310324
311325 /*!
319333 {
320334 DIR *user_dir_stream, *sys_dir_stream;
321335 char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
322 char *basename=NULL;
323336 struct dirent *user_dirent, *sys_dirent;
324 int j, st, not_in_user_list;
337 int st;
325338 int snum, usr_srv_num;
326339 size_t dp_sz = sizeof(struct dp);
327340
328341 if (*attlist != NULL) {
329 ERRMSG("register_all", "attlist parameter should be NULL\n", 0);
342 ERRMSG("register_all", "attlist parameter should be NULL", 0);
330343 return -1;
331344 }
332345
333 user_dpidir = g_strconcat(a_Misc_get_home(), "/", dotDILLO_DPI, NULL);
346 user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
334347 if (access(user_dpidir, F_OK) == -1) {
335348 /* no dpis in user's space */
336 g_free(user_dpidir);
349 dFree(user_dpidir);
337350 user_dpidir = NULL;
338351 }
339 dpidrc = g_strconcat(a_Misc_get_home(), "/", dotDILLO_DPIDRC, NULL);
352 dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
340353 if (access(dpidrc, F_OK) == -1) {
341 g_free(dpidrc);
342 dpidrc = g_strdup(DPIDRC_SYS);
354 dFree(dpidrc);
355 dpidrc = dStrdup(DPIDRC_SYS);
343356 if (access(dpidrc, F_OK) == -1) {
344 g_free(dpidrc);
357 dFree(dpidrc);
345358 dpidrc = NULL;
346359 }
347360 }
348361 if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
349362 sys_dpidir = NULL;
350 g_free(dpidrc);
363 dFree(dpidrc);
351364
352365 if (!user_dpidir && !sys_dpidir) {
353366 ERRMSG("register_all", "Fatal error ", 0);
354 fprintf(stderr, "\n - Can't find the directory for dpis.\n");
367 MSG_ERR("\n - Can't find the directory for dpis.\n");
355368 exit(1);
356369 }
357370
361374 while ((user_dirent = readdir(user_dir_stream)) != NULL) {
362375 if (user_dirent->d_name[0] == '.')
363376 continue;
364 *attlist = (struct dp *) g_realloc(*attlist, (snum + 1) * dp_sz);
377 *attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz);
365378 st=get_dpi_attr(user_dpidir, user_dirent->d_name, &(*attlist)[snum]);
366379 if (st == 0)
367380 snum++;
374387 while ((sys_dirent = readdir(sys_dir_stream)) != NULL) {
375388 if (sys_dirent->d_name[0] == '.')
376389 continue;
377 not_in_user_list = 1;
378 for (j = 0; j < usr_srv_num; j++) {
379 basename = g_basename((*attlist)[j].path);
380 if (strcmp(sys_dirent->d_name, basename) == 0) {
381 not_in_user_list = 0;
382 break;
383 }
384 }
385 if (not_in_user_list) {
386 *attlist = (struct dp *) g_realloc(*attlist, (snum + 1) * dp_sz);
387 st=get_dpi_attr(sys_dpidir, sys_dirent->d_name, &(*attlist)[snum]);
388 if (st == 0)
389 snum++;
390 }
390 *attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz);
391 st=get_dpi_attr(sys_dpidir, sys_dirent->d_name, &(*attlist)[snum]);
392 if (st == 0)
393 snum++;
391394 }
392395 closedir(sys_dir_stream);
393396 }
394397
395 g_free(sys_dpidir);
396 g_free(user_dpidir);
397
398 /* todo: do we consider snum == 0 an error?
398 dFree(sys_dpidir);
399 dFree(user_dpidir);
400
401 /* TODO: do we consider snum == 0 an error?
399402 * (if so, we should return -1 ) */
400403 return (snum);
401404 }
402405
403 /*! Initialise the service request socket
406 /*
407 * Compare two struct service pointers
408 * This function is used for sorting services
409 */
410 static int services_alpha_comp(const struct service *s1,
411 const struct service *s2)
412 {
413 return -strcmp(s1->name, s2->name);
414 }
415
416 /*! Add services reading a dpidrc file
417 * each non empty or commented line has the form
418 * service = path_relative_to_dpidir
419 * \Return:
420 * \li Returns number of available services on success
421 * \li -1 on failure
422 */
423 int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list)
424 {
425 FILE *dpidrc_stream;
426 char *p, *line = NULL, *service, *path;
427 int i, st;
428 struct service *s;
429 char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
430
431 user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
432 if (access(user_dpidir, F_OK) == -1) {
433 /* no dpis in user's space */
434 dFree(user_dpidir);
435 user_dpidir = NULL;
436 }
437 dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
438 if (access(dpidrc, F_OK) == -1) {
439 dFree(dpidrc);
440 dpidrc = dStrdup(DPIDRC_SYS);
441 if (access(dpidrc, F_OK) == -1) {
442 dFree(dpidrc);
443 dpidrc = NULL;
444 }
445 }
446 if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
447 sys_dpidir = NULL;
448
449 if (!user_dpidir && !sys_dpidir) {
450 ERRMSG("fill_services_list", "Fatal error ", 0);
451 MSG_ERR("\n - Can't find the directory for dpis.\n");
452 exit(1);
453 }
454
455 if ((dpidrc_stream = fopen(dpidrc, "r")) == NULL) {
456 ERRMSG("fill_services_list", "popen failed", errno);
457 dFree(dpidrc);
458 dFree(sys_dpidir);
459 dFree(user_dpidir);
460 return (-1);
461 }
462
463 if (*services_list != NULL) {
464 ERRMSG("fill_services_list", "services_list parameter is not NULL", 0);
465 return -1;
466 }
467 *services_list = dList_new(8);
468
469 /* dpidrc parser loop */
470 for (;(line = dGetline(dpidrc_stream)) != NULL; dFree(line)) {
471 st = dParser_parse_rc_line(&line, &service, &path);
472 if (st < 0) {
473 MSG_ERR("dpid: Syntax error in %s: service=\"%s\" path=\"%s\"\n",
474 dpidrc, service, path);
475 continue;
476 } else if (st != 0) {
477 continue;
478 }
479
480 _MSG("dpid: service=%s, path=%s\n", service, path);
481
482 /* ignore dpi_dir silently */
483 if (strcmp(service, "dpi_dir") == 0)
484 continue;
485
486 s = dNew(struct service, 1);
487 /* init services list entry */
488 s->name = dStrdup(service);
489 s->dp_index = -1;
490
491 dList_append(*services_list, s);
492 /* search the dpi for a service by its path */
493 for (i = 0; i < numdpis; i++)
494 if ((p = strstr(attlist[i].path, path)) && *(p - 1) == '/' &&
495 strlen(p) == strlen(path))
496 break;
497 /* if the dpi exist bind service and dpi */
498 if (i < numdpis)
499 s->dp_index = i;
500 }
501 fclose(dpidrc_stream);
502
503 dList_sort(*services_list, (dCompareFunc)services_alpha_comp);
504
505 dFree(dpidrc);
506 dFree(sys_dpidir);
507 dFree(user_dpidir);
508
509 return (dList_length(*services_list));
510 }
511
512 /*
513 * Return a socket file descriptor
514 * (useful to set socket options in a uniform way)
515 */
516 static int make_socket_fd()
517 {
518 int ret, one = 1;
519
520 if ((ret = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
521 ERRMSG("make_socket_fd", "socket", errno);
522 } else {
523 /* avoid delays when sending small pieces of data */
524 setsockopt(ret, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
525 }
526
527 /* set some buffering to increase the transfer's speed */
528 //setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF,
529 // &sock_buflen, (socklen_t)sizeof(sock_buflen));
530
531 return ret;
532 }
533
534 /*! Bind a socket port on localhost. Try to be close to base_port.
535 * \Return
536 * \li listening socket file descriptor on success
537 * \li -1 on failure
538 */
539 int bind_socket_fd(int base_port, int *p_port)
540 {
541 int sock_fd, port;
542 struct sockaddr_in sin;
543 int ok = 0, last_port = base_port + 50;
544
545 if ((sock_fd = make_socket_fd()) == -1) {
546 return (-1); /* avoids nested ifs */
547 }
548 /* Set the socket FD to close on exec */
549 fcntl(sock_fd, F_SETFD, FD_CLOEXEC | fcntl(sock_fd, F_GETFD));
550
551
552 memset(&sin, 0, sizeof(sin));
553 sin.sin_family = AF_INET;
554 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
555
556 /* Try to bind a port on localhost */
557 for (port = base_port; port <= last_port; ++port) {
558 sin.sin_port = htons(port);
559 if ((bind(sock_fd, (struct sockaddr *)&sin, sizeof(sin))) == -1) {
560 if (errno == EADDRINUSE || errno == EADDRNOTAVAIL)
561 continue;
562 ERRMSG("bind_socket_fd", "bind", errno);
563 } else if (listen(sock_fd, QUEUE) == -1) {
564 ERRMSG("bind_socket_fd", "listen", errno);
565 } else {
566 *p_port = port;
567 ok = 1;
568 break;
569 }
570 }
571 if (port > last_port) {
572 MSG_ERR("Hey! Can't find an available port from %d to %d\n",
573 base_port, last_port);
574 }
575
576 return ok ? sock_fd : -1;
577 }
578
579 /*! Save the current port and a shared secret in a file so dillo can find it.
580 * \Return:
581 * \li -1 on failure
582 */
583 int save_comm_keys(int srs_port)
584 {
585 int fd, ret = -1;
586 char *fname, port_str[32];
587
588 fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
589 fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
590 dFree(fname);
591 if (fd == -1) {
592 MSG("save_comm_keys: open %s\n", dStrerror(errno));
593 } else {
594 snprintf(port_str, 16, "%d %s\n", srs_port, SharedKey);
595 if (CKD_WRITE(fd, port_str) != -1 && CKD_CLOSE(fd) != -1) {
596 ret = 1;
597 }
598 }
599
600 return ret;
601 }
602
603 /*! Initialise the service request socket (IDS)
404604 * \Return:
405605 * \li Number of sockets (1 == success)
406606 * \li -1 on failure
407607 */
408 int init_srs_socket(char *sockdir)
409 {
410 int retval = -1;
411 struct sockaddr_un srs_sa;
412 size_t sun_path_len;
413 socklen_t addr_sz;
414
415 srs_name = g_strconcat(sockdir, "/", SRS_NAME, NULL);
608 int init_ids_srs_socket()
609 {
610 int srs_port, ret = -1;
611
416612 FD_ZERO(&sock_set);
417613
418 /* Initialise srs, service request socket on startup */
419 if ((srs = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
420 ERRMSG("init_srs_socket", "socket", errno);
421 return (retval); /* avoids nesting ifs too deeply */
422 }
423 /* Set srs to close on exec */
424 fcntl(srs, F_SETFD, FD_CLOEXEC | fcntl(srs, F_GETFD));
425
426 srs_sa.sun_family = AF_LOCAL;
427
428 sun_path_len = sizeof(srs_sa.sun_path);
429 if ( strlen(srs_name) > sun_path_len) {
430 ERRMSG("init_srs_socket", "srs_name is too long", 0);
431 fprintf(stderr, "\n - it should be <= %lu chars", (gulong)sun_path_len);
432 fprintf(stderr, "\n - srs_name = %s\n", srs_name);
433 return(retval);
434 }
435 strncpy(srs_sa.sun_path, srs_name, sun_path_len);
436 addr_sz = (socklen_t) D_SUN_LEN(&srs_sa);
437
438 if ((bind(srs, (struct sockaddr *) &srs_sa, addr_sz)) == -1) {
439 if (errno == EADDRINUSE) {
440 ERRMSG("init_srs_socket", "bind", errno);
441 fprintf(stderr, "srs_sa.sun_path = %s\n", srs_sa.sun_path);
442 dpi_errno = dpid_srs_addrinuse;
443 } else {
444 ERRMSG("init_srs_socket", "bind", errno);
445 fprintf(stderr, "srs_sa.sun_path = %s\n", srs_sa.sun_path);
446 }
447 } else if (chmod(srs_sa.sun_path, S_IRUSR | S_IWUSR) == -1) {
448 ERRMSG("init_srs_socket", "chmod", errno);
449 fprintf(stderr, "srs_sa.sun_path = %s\n", srs_sa.sun_path);
450 } else if (listen(srs, QUEUE) == -1) {
451 ERRMSG("init_srs_socket", "listen", errno);
452 } else {
453 retval = 1;
454 }
455
456 FD_SET(srs, &sock_set);
457 return (retval);
458 }
459
460 /*! Initialise a single dpi socket
614 if ((srs_fd = bind_socket_fd(DPID_BASE_PORT, &srs_port)) != -1) {
615 /* create the shared secret */
616 SharedKey = a_Misc_mksecret(8);
617 /* save port number and SharedKey */
618 if (save_comm_keys(srs_port) != -1) {
619 FD_SET(srs_fd, &sock_set);
620 ret = 1;
621 }
622 }
623
624 return ret;
625 }
626
627 /*! Initialize a single dpi socket
461628 * \Return
462629 * \li 1 on success
463630 * \li -1 on failure
464631 */
465 int init_dpi_socket(struct dp *dpi_attr, char *sockdir)
466 {
467 int caught_error = 0, s;
468 char *dpi_nm; /* pointer to basename in dpi_attr->path */
469 struct sockaddr_un sa;
470 size_t sp_len;
471 socklen_t addr_sz;
472 size_t sock_buflen = 8192;
473
474 sp_len = sizeof(sa.sun_path);
475 if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
476 ERRMSG("init_all_dpi_sockets", "socket", errno);
477 return (-1); /* avoids nested ifs */
478 }
479 /* Set the socket FD to close on exec */
480 fcntl(s, F_SETFD, FD_CLOEXEC | fcntl(s, F_GETFD));
481
482 /* set some buffering to increase the transfer's speed */
483 setsockopt(s, SOL_SOCKET, SO_SNDBUF,
484 &sock_buflen, (socklen_t)sizeof(sock_buflen));
485
486 dpi_attr->socket = s;
487 dpi_attr->sa.sun_family = AF_LOCAL;
488 dpi_nm = g_basename(dpi_attr->path);
489
490 dpi_attr->sockpath = g_strconcat(sockdir, "/", dpi_nm, NULL);
491 if ( strlen(dpi_attr->sockpath) > sp_len) {
492 ERRMSG("init_all_dpi_sockets", "socket path is too long", 0);
493 fprintf(stderr, "\n - it should be <= %lu chars", (gulong)sp_len);
494 fprintf(stderr, "\n - socket path = %s\n", dpi_attr->sockpath);
495 return(-1);
496 }
497 strncpy(dpi_attr->sa.sun_path, dpi_attr->sockpath, sp_len);
498 addr_sz = (socklen_t) D_SUN_LEN(&dpi_attr->sa);
499
500 if ((bind(s, (struct sockaddr *) &dpi_attr->sa, addr_sz)) == -1) {
501 ERRMSG("init_all_dpi_sockets", "bind", errno);
502 fprintf(stderr, "%s\n", dpi_attr->sa.sun_path);
503 caught_error = 1;
504 } else if (chmod(dpi_attr->sa.sun_path, S_IRUSR | S_IWUSR) == -1) {
505 ERRMSG("init_all_dpi_sockets", "chmod", errno);
506 fprintf(stderr, "%s\n", dpi_attr->sa.sun_path);
507 caught_error = 1;
508 } else if (listen(s, QUEUE) == -1) {
509 ERRMSG("init_all_dpi_sockets", "listen", errno);
510 caught_error = 1;
511 }
512
513 if (caught_error) {
514 return (-1);
515 } else {
516 FD_SET(s, &sock_set);
517 return (1);
518 }
632 int init_dpi_socket(struct dp *dpi_attr)
633 {
634 int s_fd, port, ret = -1;
635
636 if ((s_fd = bind_socket_fd(DPID_BASE_PORT, &port)) != -1) {
637 dpi_attr->sock_fd = s_fd;
638 dpi_attr->port = port;
639 FD_SET(s_fd, &sock_set);
640 ret = 1;
641 }
642
643 return ret;
519644 }
520645
521646 /*! Setup sockets for the plugins and add them to
522 * to the set of sockets (sock_set) watched by select.
647 * the set of sockets (sock_set) watched by select.
523648 * \Return
524649 * \li Number of sockets on success
525650 * \li -1 on failure
528653 * \Uses
529654 * numdpis, srs, srs_name
530655 */
531 int init_all_dpi_sockets(struct dp *dpi_attr_list, char *sockdir)
656 int init_all_dpi_sockets(struct dp *dpi_attr_list)
532657 {
533658 int i;
534 struct sockaddr_un sa;
535 size_t sp_len;
536
537 sp_len = sizeof(sa.sun_path);
538659
539660 /* Initialise sockets for each dpi */
540661 for (i = 0; i < numdpis; i++) {
541 if (init_dpi_socket(dpi_attr_list + i, sockdir) == -1)
662 if (init_dpi_socket(dpi_attr_list + i) == -1)
542663 return (-1);
543664 numsocks++;
544665 }
550671 */
551672 void dpi_sigchld(int sig)
552673 {
553 caught_sigchld = 1;
674 if (sig == SIGCHLD)
675 caught_sigchld = 1;
554676 }
555677
556678 /*! Called by main loop when caught_sigchld == 1 */
557679 void handle_sigchld(void)
558680 {
559 int i, status;
681 // pid_t pid;
682 int i, status; //, num_active;
560683
561684 /* For all of the dpis in the current list
562685 * add the ones that have exited to the set of sockets being
565688 for (i = 0; i < numdpis; i++) {
566689 if (waitpid(dpi_attr_list[i].pid, &status, WNOHANG) > 0) {
567690 dpi_attr_list[i].pid = 1;
568 FD_SET(dpi_attr_list[i].socket, &sock_set);
691 FD_SET(dpi_attr_list[i].sock_fd, &sock_set);
569692 numsocks++;
570693 }
571694 }
591714 }
592715 }
593716
717 /*! EINTR aware connect() call */
718 int ckd_connect (int sock_fd, struct sockaddr *addr, socklen_t len)
719 {
720 ssize_t ret;
721
722 do {
723 ret = connect(sock_fd, addr, len);
724 } while (ret == -1 && errno == EINTR);
725 if (ret == -1) {
726 ERRMSG("dpid.c", "connect", errno);
727 }
728 return ret;
729 }
730
594731 /*! Send DpiBye command to all active non-filter dpis
595732 */
596733 void stop_active_dpis(struct dp *dpi_attr_list, int numdpis)
597734 {
598 static char *DpiBye_cmd = NULL;
599 int i, dpi_socket;
600 struct sockaddr_un dpi_addr;
601 struct sockaddr_un sa;
602 size_t sun_path_len, addr_len;
603
604 if (!DpiBye_cmd)
605 DpiBye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
606
607 sun_path_len = sizeof(sa.sun_path);
608 addr_len = sizeof(dpi_addr);
609
610 dpi_addr.sun_family = AF_LOCAL;
735 char *bye_cmd, *auth_cmd;
736 int i, sock_fd;
737 struct sockaddr_in sin;
738
739 bye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
740 auth_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey);
741
742 memset(&sin, 0, sizeof(sin));
743 sin.sin_family = AF_INET;
744 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
611745
612746 for (i = 0; i < numdpis; i++) {
613747 /* Skip inactive dpis and filters */
614748 if (dpi_attr_list[i].pid == 1 || dpi_attr_list[i].filter)
615749 continue;
616750
617 if ((dpi_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
751 if ((sock_fd = make_socket_fd()) == -1) {
618752 ERRMSG("stop_active_dpis", "socket", errno);
619 }
620 if ( strlen(dpi_attr_list[i].sockpath) > sun_path_len) {
621 ERRMSG("stop_active_dpis", "socket path is too long", 0);
622 fprintf(stderr,"\n - it should be <= %lu chars",(gulong)sun_path_len);
623 fprintf(stderr,"\n - socket path = %s\n", dpi_attr_list[i].sockpath);
624 }
625 strncpy(dpi_addr.sun_path, dpi_attr_list[i].sockpath, sun_path_len);
626 if (connect(dpi_socket, (struct sockaddr *) &dpi_addr, addr_len) == -1) {
753 continue;
754 }
755
756 sin.sin_port = htons(dpi_attr_list[i].port);
757 if (ckd_connect(sock_fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
627758 ERRMSG("stop_active_dpis", "connect", errno);
628 fprintf(stderr, "%s\n", dpi_addr.sun_path);
629 }
630 (void) write(dpi_socket, DpiBye_cmd, strlen(DpiBye_cmd));
631 a_Misc_close_fd(dpi_socket);
632 }
759 MSG_ERR("%s\n", dpi_attr_list[i].path);
760 } else if (CKD_WRITE(sock_fd, auth_cmd) == -1) {
761 ERRMSG("stop_active_dpis", "write", errno);
762 } else if (CKD_WRITE(sock_fd, bye_cmd) == -1) {
763 ERRMSG("stop_active_dpis", "write", errno);
764 }
765 a_Misc_close_fd(sock_fd);
766 }
767
768 dFree(auth_cmd);
769 dFree(bye_cmd);
770
771 /* Allow child dpis some time to read dpid_comm_keys before erasing it */
772 sleep (1);
633773 }
634774
635775 /*! Removes dpis in dpi_attr_list from the
641781 int i;
642782
643783 for (i = 0; i < numdpis; i++) {
644 FD_CLR(dpi_attr_list[i].socket, &sock_set);
645 a_Misc_close_fd(dpi_attr_list[i].socket);
784 FD_CLR(dpi_attr_list[i].sock_fd, &sock_set);
785 a_Misc_close_fd(dpi_attr_list[i].sock_fd);
646786 }
647787 }
648788
653793 * \Return
654794 * Number of available dpis
655795 */
656 int register_all_cmd(char *sockdir)
796 int register_all_cmd()
657797 {
658798 stop_active_dpis(dpi_attr_list, numdpis);
659 rm_dpi_sockets(dpi_attr_list, numdpis);
660799 free_plugin_list(&dpi_attr_list, numdpis);
800 free_services_list(services_list);
801 services_list = NULL;
661802 numdpis = 0;
662803 numsocks = 1; /* the srs socket */
663804 FD_ZERO(&sock_set);
664 FD_SET(srs, &sock_set);
805 FD_SET(srs_fd, &sock_set);
665806 numdpis = register_all(&dpi_attr_list);
666 numsocks = init_all_dpi_sockets(dpi_attr_list, sockdir);
807 fill_services_list(dpi_attr_list, numdpis, &services_list);
808 numsocks = init_all_dpi_sockets(dpi_attr_list);
667809 return (numdpis);
668810 }
669811
672814 * \Return
673815 * message on success, NULL on failure
674816 */
675 char *get_message(int sock, char *dpi_tag)
817 char *get_message(int sock_fd, char *dpi_tag)
676818 {
677819 char *msg, *d_cmd;
678820
679 msg = a_Dpip_get_attr(dpi_tag, strlen(dpi_tag), "msg");
821 msg = a_Dpip_get_attr(dpi_tag, "msg");
680822 if (msg == NULL) {
681 ERRMSG("get_message", "failed to parse msg\n", 0);
823 ERRMSG("get_message", "failed to parse msg", 0);
682824 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
683825 "DpiError", "Failed to parse request");
684 (void) CKD_WRITE(sock, d_cmd);
685 g_free(d_cmd);
826 (void) CKD_WRITE(sock_fd, d_cmd);
827 dFree(d_cmd);
686828 }
687829 return (msg);
688830 }
689831
832 /*
833 * Compare a struct service pointer and a service name
834 * This function is used for searching services by name
835 */
836 int service_match(const struct service *A, const char *B)
837 {
838 int A_len, B_len, len;
839
840 A_len = strlen(A->name);
841 B_len = strlen(B);
842 len = MAX (A_len, B_len);
843
844 if (A->name[A_len - 1] == '*')
845 len = A_len - 1;
846
847 return(dStrncasecmp(A->name, B, len));
848 }
849
690850 /*!
691 * Send socket path that matches dpi_id to client
692 */
693 void send_sockpath(gint sock, gchar *dpi_tag, struct dp *dpi_attr_list)
694 {
695 gint i;
696 gchar *dpi_id;
697 char *d_cmd;
698
699 g_return_if_fail((dpi_id = get_message(sock, dpi_tag)) != NULL);
700
701 for (i = 0; i < numdpis; i++)
702 if (strstr(dpi_attr_list[i].id, dpi_id))
703 break;
851 * Send socket port that matches dpi_id to client
852 */
853 void send_sockport(int sock_fd, char *dpi_tag, struct dp *dpi_attr_list)
854 {
855 int i;
856 char *dpi_id, *d_cmd, port_str[16];
857 struct service *serv;
858
859 dReturn_if_fail((dpi_id = get_message(sock_fd, dpi_tag)) != NULL);
860
861 serv = dList_find_custom(services_list,dpi_id,(dCompareFunc)service_match);
862
863 if (serv == NULL || (i = serv->dp_index) == -1)
864 for (i = 0; i < numdpis; i++)
865 if (!strncmp(dpi_attr_list[i].id, dpi_id,
866 dpi_attr_list[i].id - strchr(dpi_attr_list[i].id, '.')))
867 break;
868
704869 if (i < numdpis) {
705870 /* found */
706 if (access(dpi_attr_list[i].path, F_OK) == -1) {
707 ERRMSG("send_sockpath", "access", errno);
708 fprintf(stderr, " - %s\n", dpi_attr_list[i].sockpath);
709 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
710 "DpiError", "Plugin currently unavailable");
711 (void) CKD_WRITE(sock, d_cmd);
712 g_free(d_cmd);
713 } else {
714 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
715 "send_data", dpi_attr_list[i].sockpath);
716 (void) CKD_WRITE(sock, d_cmd);
717 g_free(d_cmd);
718 }
719 }
720
721 g_free(dpi_id);
722 }
871 snprintf(port_str, 8, "%d", dpi_attr_list[i].port);
872 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_data", port_str);
873 (void) CKD_WRITE(sock_fd, d_cmd);
874 dFree(d_cmd);
875 }
876
877 dFree(dpi_id);
878 }
44 #ifndef DPID_H
55 #define DPID_H
66
7 #include <assert.h>
8 #include <signal.h>
9 #include <fcntl.h>
10 #include <sys/stat.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16 #include <string.h>
177 #include <sys/socket.h>
18 #include <sys/time.h>
8 #include <sys/select.h> /* for fd_set */
199 #include <sys/un.h>
20 #include <errno.h>
21 #include <glib.h>
10 #include <signal.h> /* for sig_atomic_t */
11 #include <netinet/in.h> /* for ntohl, IPPORT_USERRESERVED and stuff */
12
13 #include "d_size.h"
14
15 /* FreeBSD 6.4 doesn't have it */
16 #ifndef IPPORT_USERRESERVED
17 #define IPPORT_USERRESERVED 5000
18 #endif
2219
2320 #define PATH_LEN 50
2421 #define CMDLEN 20
2522 #define MSGLEN 50
26 /*! \todo: Should read this from dillorc */
23 #define DPID_BASE_PORT (IPPORT_USERRESERVED + 20)
24
25 /*! \TODO: Should read this from dillorc */
2726 #define SRS_NAME "dpid.srs"
2827 char *srs_name;
2928
30 /*! dpid service request socket */
31 int srs;
29 /*! dpid's service request socket file descriptor */
30 int srs_fd;
3231
3332 /*! plugin state information
3433 */
3534 struct dp {
3635 char *id;
3736 char *path;
38 char *sockpath;
39 int socket;
40 struct sockaddr_un sa;
37 int sock_fd;
38 int port;
4139 pid_t pid;
4240 int filter;
41 };
42
43 /*! bind dpi with service
44 */
45 struct service {
46 char *name;
47 int dp_index;
4348 };
4449
4550 /*! Number of available plugins */
5156 /*! State information for each plugin. */
5257 struct dp *dpi_attr_list;
5358
59 /*! service served for each plugin */
60 Dlist *services_list;
61
5462 /*! Set of sockets watched for connections */
5563 fd_set sock_set;
5664
5967
6068 void rm_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
6169
62 int rm_inactive_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
63
64 void cleanup(char *socket_dir);
70 void cleanup();
6571
6672 void free_dpi_attr(struct dp *dpi_attr);
6773
6874 void free_plugin_list(struct dp **dpi_attr_list_ptr, int numdpis);
6975
70 enum file_type get_file_type(gchar *file_name);
76 void free_services_list(Dlist *s_list);
77
78 enum file_type get_file_type(char *file_name);
7179
7280 int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr);
7381
7583
7684 int register_all(struct dp **attlist);
7785
78 int init_srs_socket(char *sockdir);
86 int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list);
7987
80 int init_dpi_socket(struct dp *dpi_attr, char *sockdir);
88 int init_ids_srs_socket();
8189
82 int init_all_dpi_sockets(struct dp *dpi_attr_list, char *sockdir);
90 int init_dpi_socket(struct dp *dpi_attr);
91
92 int init_all_dpi_sockets(struct dp *dpi_attr_list);
8393
8494 void dpi_sigchld(int sig);
8595
8797
8898 void est_dpi_sigchld(void);
8999
100 void est_dpi_terminator(void);
101
90102 void stop_active_dpis(struct dp *dpi_attr_list, int numdpis);
91103
92104 void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
93105
94 int register_all_cmd(char *sockdir);
106 int register_all_cmd();
95107
96108 char *get_message(int sock, char *dpi_tag);
97109
98 void send_sockpath(gint sock, gchar * dpi_tag, struct dp *dpi_attr_list);
110 int service_match(const struct service *A, const char *B);
111
112 void send_sockport(int sock_fd, char * dpi_tag, struct dp *dpi_attr_list);
99113
100114 #endif
0 /*
1 * File: dpid_common.c
2 *
3 * Copyright 2008 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <errno.h>
012 #include <stdio.h>
1 #include <string.h>
213 #include <unistd.h>
3 #include <glib.h>
414 #include "dpid_common.h"
515
616 /*
818 */
919 void errmsg(char *caller, char *called, int errornum, char *file, int line)
1020 {
11 fprintf(stderr, "%s:%d: %s: %s : ", file, line, caller, called);
21 MSG_ERR("%s:%d: %s: %s\n", file, line, caller, called);
1222 if (errornum > 0)
13 fprintf(stderr, "%s\n", g_strerror(errornum));
23 MSG_ERR("%s\n", dStrerror(errornum));
1424 }
1525
1626 /*! Selector function for scandir
3747 ret = write(fd, msg, strlen(msg));
3848 } while (ret == -1 && errno == EINTR);
3949 if (ret == -1) {
40 fprintf(stderr, "%s:%d: write: %s\n", file, line, g_strerror(errno));
50 MSG_ERR("%s:%d: write: %s\n", file, line, dStrerror(errno));
4151 }
4252 return (ret);
4353 }
54
55 /*!
56 * Provides an error checked close() call.
57 * Call this via the CKD_CLOSE macro
58 * \return close return value
59 */
60 ssize_t ckd_close(int fd, char *file, int line)
61 {
62 ssize_t ret;
63
64 do {
65 ret = close(fd);
66 } while (ret == -1 && errno == EINTR);
67 if (ret == -1) {
68 MSG_ERR("%s:%d: close: %s\n", file, line, dStrerror(errno));
69 }
70 return (ret);
71 }
72
88 * the next patch
99 */
1010
11 #include <errno.h>
12 #include <sys/types.h>
1311 #include <dirent.h>
12
13 #include "../dlib/dlib.h"
14
15 /*
16 * Debugging macros
17 */
18 #define _MSG(...)
19 #define MSG(...) printf("[dpid]: " __VA_ARGS__)
20 #define _MSG_ERR(...)
21 #define MSG_ERR(...) fprintf(stderr, "[dpid]: " __VA_ARGS__)
1422
1523 #define dotDILLO_DPI ".dillo/dpi"
1624 #define dotDILLO_DPIDRC ".dillo/dpidrc"
25 #define dotDILLO_DPID_COMM_KEYS ".dillo/dpid_comm_keys"
26
1727 #define ERRMSG(CALLER, CALLED, ERR)\
1828 errmsg(CALLER, CALLED, ERR, __FILE__, __LINE__)
29 #define _ERRMSG(CALLER, CALLED, ERR)
30
1931
2032 /*!
21 * Macro for calling the ckd_write function
33 * Macros for calling ckd_write and ckd_close functions
2234 */
2335 #define CKD_WRITE(fd, msg) ckd_write(fd, msg, __FILE__, __LINE__)
36 #define CKD_CLOSE(fd) ckd_close(fd, __FILE__, __LINE__)
2437
2538
2639 /*! Error codes for dpid */
4356 int no_dotfiles(const struct dirent *filedat);
4457
4558 ssize_t ckd_write(int fd, char *msg, char *file, int line);
59 ssize_t ckd_close(int fd, char *file, int line);
4660
4761 #endif
+0
-31
dpid/dpidc less more
0 #!/usr/bin/perl -w
1 # Author: Ferdi Franceschini
2 #
3 # dpid control program
4 # Currently allows
5 # register: Tells dpid to register all available dpis
6 # stop: Stops dpid.
7
8 use strict;
9 use IO::Socket::UNIX;
10
11 # Get socket directory name
12 open(DSD, "<$ENV{HOME}/.dillo/dpi_socket_dir");
13 my $dir = <DSD>;
14 close(DSD);
15
16 my $socket = IO::Socket::UNIX->new(Peer => "$dir/dpid.srs", Type => SOCK_STREAM, Timeout => 1000 ) or die "new: $@";
17
18 $socket->autoflush(1);
19
20 my %dpi_command = (
21 "register" => "<dpi cmd='register_all' '>",
22 "stop" => "<dpi cmd='DpiBye' '>",
23 );
24
25 if ( exists($dpi_command{$ARGV[0]}) ) {
26 print $socket $dpi_command{$ARGV[0]};
27 } else {
28 close($socket);
29 print "Usage: dpidc register|stop\n";
30 }
0 #include <stdio.h>
1 #include <stdlib.h> /* for exit */
2 #include <string.h> /* for bzero */
3 #include <unistd.h> /* for read and write */
4 #include <ctype.h> /* for isxdigit */
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <netdb.h>
9 #include <errno.h>
10
11 #include "../dpip/dpip.h"
12
13 #define MSG_ERR(...) printf("** ERROR **: " __VA_ARGS__);
14
15 char *CMD_REGISTER = "<cmd='register_all' '>";
16 char *CMD_STOP = "<cmd='DpiBye' '>";
17
18 static char SharedKey[32];
19
20 static void print_usage(const char *prgname)
21 {
22 fprintf(stderr,"Control program for the Dillo plugin daemon\n"
23 "Usage: %s {stop|register|chat}\n\n", prgname);
24 }
25
26 static void error(char *msg)
27 {
28 perror(msg);
29 exit(1);
30 }
31
32 /*
33 * Read dpid's communication keys from its saved file.
34 * Return value: 1 on success, -1 on error.
35 */
36 static int Dpi_read_comm_keys(int *port)
37 {
38 FILE *In;
39 char *fname, *rcline = NULL, *tail;
40 int i, ret = -1;
41
42 fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
43 if ((In = fopen(fname, "r")) == NULL) {
44 MSG_ERR("[Dpi_read_comm_keys] %s\n", dStrerror(errno));
45 } else if ((rcline = dGetline(In)) == NULL) {
46 MSG_ERR("[Dpi_read_comm_keys] empty file: %s\n", fname);
47 } else {
48 *port = strtol(rcline, &tail, 10);
49 for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
50 SharedKey[i] = tail[i+1];
51 SharedKey[i] = 0;
52 ret = 1;
53 }
54 dFree(rcline);
55 dFree(fname);
56
57 return ret;
58 }
59
60 int main(int argc, char *argv[])
61 {
62 int sockfd, portno, n;
63 struct sockaddr_in serv_addr;
64 char buffer[256];
65
66 if (argc != 2) {
67 print_usage(argv[0]);
68 exit(1);
69 }
70
71 /* Read dpid's port number from saved file */
72 if (Dpi_read_comm_keys(&portno) == -1) {
73 MSG_ERR("main: Can't read dpid's port number\n");
74 exit(1);
75 }
76
77 sockfd = socket(AF_INET, SOCK_STREAM, 0);
78 if (sockfd < 0)
79 error("ERROR opening socket");
80 bzero((char *) &serv_addr, sizeof(serv_addr));
81 serv_addr.sin_family = AF_INET;
82 serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
83
84 serv_addr.sin_port = htons(portno);
85 if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
86 error("ERROR connecting");
87
88 snprintf(buffer, sizeof(buffer), "<cmd='auth' msg='%s' '>", SharedKey);
89 n = write(sockfd, buffer, strlen(buffer));
90 if (n < 0)
91 error("ERROR writing to socket");
92
93 if (strcmp(argv[1], "stop") == 0) {
94 strcpy(buffer, CMD_STOP);
95 } else if (strcmp(argv[1], "register") == 0) {
96 strcpy(buffer, CMD_REGISTER);
97 } else if (strcmp(argv[1], "chat") == 0) {
98 printf("Please enter the message: ");
99 bzero(buffer,256);
100 if (fgets(buffer,255,stdin) == NULL)
101 MSG_ERR("dpidc: Can't read the message\n");
102 } else {
103 MSG_ERR("main: Unknown operation '%s'\n", argv[1]);
104 print_usage(argv[0]);
105 exit(1);
106 }
107
108 n = write(sockfd,buffer,strlen(buffer));
109 if (n < 0)
110 error("ERROR writing to socket");
111 /*
112 bzero(buffer,256);
113 n = read(sockfd,buffer,255);
114 if (n < 0)
115 error("ERROR reading from socket");
116 printf("%s\n",buffer);
117 */
118 close(sockfd);
119 return 0;
120 }
0 dpi_dir=@libdir@/dillo/dpi
1
2 proto.file=file/file.dpi
3 proto.ftp=ftp/ftp.filter.dpi
4 proto.https=https/https.filter.dpi
5 proto.data=datauri/datauri.filter.dpi
22
33 This program is free software; you can redistribute it and/or modify
44 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
5 the Free Software Foundation; either version 3 of the License, or
66 (at your option) any later version.
77
88 This program is distributed in the hope that it will be useful,
1111 GNU General Public License for more details.
1212
1313 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 #include <errno.h>
19 #include <unistd.h>
20 #include <limits.h>
21 #include <sys/stat.h>
22 #include <sys/time.h>
23 #include <glib.h>
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <errno.h> /* for ckd_write */
18 #include <unistd.h> /* for ckd_write */
19 #include <stdlib.h> /* for exit */
20 #include <assert.h> /* for assert */
21 #include <sys/stat.h> /* for umask */
22
2423 #include "dpid_common.h"
2524 #include "dpid.h"
2625 #include "dpi.h"
4544
4645 csz = (socklen_t) sizeof(clnt_addr);
4746
48 newsock = accept(dpi_attr.socket, (struct sockaddr *) &clnt_addr, &csz);
47 newsock = accept(dpi_attr.sock_fd, (struct sockaddr *) &clnt_addr, &csz);
4948 if (newsock == -1)
5049 ERRMSG("start_plugin", "accept", errno);
5150
5251 dup2(STDIN_FILENO, old_stdin);
5352 if (dup2(newsock, STDIN_FILENO) == -1) {
5453 ERRMSG("start_plugin", "dup2", errno);
55 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
54 MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
5655 exit(1);
5756 }
5857
5958 dup2(STDOUT_FILENO, old_stdout);
6059 if (dup2(newsock, STDOUT_FILENO) == -1) {
6160 ERRMSG("start_plugin", "dup2", errno);
62 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
61 MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
6362 exit(1);
6463 }
6564 if ((pid = fork()) == -1) {
6665 ERRMSG("main", "fork", errno);
6766 return 0;
6867 }
69 if ( pid == 0) {
68 if (pid == 0) {
7069 /* Child, start plugin */
71 if (execl(dpi_attr.path, dpi_attr.path, NULL) == -1) {
70 if (execl(dpi_attr.path, dpi_attr.path, (char*)NULL) == -1) {
7271 ERRMSG("start_plugin", "execl", errno);
73 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
72 MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
7473 exit(1);
7574 }
7675 }
7877 /* Parent, Close sockets fix stdio and return pid */
7978 if (a_Misc_close_fd(newsock) == -1) {
8079 ERRMSG("start_plugin", "close", errno);
81 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
80 MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
8281 exit(1);
8382 }
8483 a_Misc_close_fd(STDIN_FILENO);
9089
9190 static void start_server_plugin(struct dp dpi_attr)
9291 {
93 if (dup2(dpi_attr.socket, STDIN_FILENO) == -1) {
92 if (dup2(dpi_attr.sock_fd, STDIN_FILENO) == -1) {
9493 ERRMSG("start_plugin", "dup2", errno);
95 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
96 exit(1);
97 }
98 if (a_Misc_close_fd(dpi_attr.socket) == -1) {
94 MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
95 exit(1);
96 }
97 if (a_Misc_close_fd(dpi_attr.sock_fd) == -1) {
9998 ERRMSG("start_plugin", "close", errno);
100 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
101 exit(1);
102 }
103 if (execl(dpi_attr.path, dpi_attr.path, NULL) == -1) {
99 MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
100 exit(1);
101 }
102 if (execl(dpi_attr.path, dpi_attr.path, (char*)NULL) == -1) {
104103 ERRMSG("start_plugin", "execl", errno);
105 fprintf(stderr, "ERROR in child proc for %s\n", dpi_attr.path);
104 MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
106105 exit(1);
107106 }
108107 }
112111 * \Return
113112 * pointer to dynamically allocated request tag
114113 */
115 static char *get_request(int sock)
116 {
117 char *req, buf[10];
118 size_t buflen;
119 size_t rqsz;
120 ssize_t rdln;
121
122 req = NULL;
123 buf[0] = '\0';
124 buflen = sizeof(buf) / sizeof(buf[0]);
114 static char *get_request(Dsh *sh)
115 {
116 char *dpip_tag;
125117
126118 (void) sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
127 for (rqsz = 0; (rdln = read(sock, buf, buflen)) != 0; rqsz += rdln) {
128 if (rdln == -1)
129 break;
130 req = (char *) realloc(req, rqsz + rdln + 1);
131 if (rqsz == 0)
132 req[0] = '\0';
133 strncat(req, buf, (size_t) rdln);
134 }
119 dpip_tag = a_Dpip_dsh_read_token(sh, 1);
135120 (void) sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
136 if (rdln == -1) {
137 ERRMSG("get_request", "read", errno);
138 }
139
140 return (req);
121
122 return dpip_tag;
141123 }
142124
143125 /*!
145127 * \Return
146128 * command code on success, -1 on failure
147129 */
148 static int get_command(int sock, char *dpi_tag, struct dp *dpi_attr_list)
130 static int get_command(Dsh *sh, char *dpi_tag)
149131 {
150132 char *cmd, *d_cmd;
151133 int COMMAND;
152134
153135 if (dpi_tag == NULL) {
154 ERRMSG("get_command", "dpid tag is NULL\n", 0);
136 _ERRMSG("get_command", "dpid tag is NULL", 0);
155137 return (-1);
156138 }
157139
158 cmd = a_Dpip_get_attr(dpi_tag, strlen(dpi_tag), "cmd");
140 cmd = a_Dpip_get_attr(dpi_tag, "cmd");
159141
160142 if (cmd == NULL) {
161143 ERRMSG("get_command", "a_Dpip_get_attr", 0);
162 fprintf(stderr, ": dpid failed to parse cmd in %s\n", dpi_tag);
144 MSG_ERR(": dpid failed to parse cmd in %s\n", dpi_tag);
163145 d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
164146 "DpiError", "Failed to parse request");
165 (void) CKD_WRITE(sock, d_cmd);
166 g_free(d_cmd);
147 a_Dpip_dsh_write_str(sh, 1, d_cmd);
148 dFree(d_cmd);
167149 COMMAND = -1;
150 } else if (strcmp("auth", cmd) == 0) {
151 COMMAND = AUTH_CMD;
168152 } else if (strcmp("DpiBye", cmd) == 0) {
169153 COMMAND = BYE_CMD;
170154 } else if (strcmp("check_server", cmd) == 0) {
177161 COMMAND = UNKNOWN_CMD;
178162 }
179163
180 g_free(cmd);
164 dFree(cmd);
181165 return (COMMAND);
182166 }
183167
184168 /*
185169 * Check whether a dpi server is running
186170 */
187 int server_is_running(char *server_id)
171 static int server_is_running(char *server_id)
188172 {
189173 int i;
190174
220204 int main(void)
221205 {
222206 int i, n = 0, open_max;
223 char *dirname = NULL, *sockdir = NULL;
224207 int dpid_idle_timeout = 60 * 60; /* default, in seconds */
225208 struct timeval select_timeout;
226209 sigset_t mask_none;
227210 fd_set selected_set;
228211
229212 dpi_attr_list = NULL;
230 /* daemon(0,0); */ /* Use 0,1 for feedback */
231 /* todo: call setsid() ?? */
213 services_list = NULL;
214 //daemon(0,0); /* Use 0,1 for feedback */
215 /* TODO: call setsid() ?? */
232216
233217 /* Allow read and write access, but only for the user.
234 * todo: can this cause trouble with umount? */
218 * TODO: can this cause trouble with umount? */
235219 umask(0077);
236 /* todo: make dpid work on any directory. */
237 /* chdir("/"); */
220 /* TODO: make dpid work on any directory. */
221 // chdir("/");
238222
239223 /* close inherited file descriptors */
240224 open_max = get_open_max();
242226 a_Misc_close_fd(i);
243227
244228 /* this sleep used to unmask a race condition */
245 /* sleep(2); */
229 // sleep(2);
246230
247231 dpi_errno = no_errors;
248232
249233 /* Get list of available dpis */
250234 numdpis = register_all(&dpi_attr_list);
251235
236 #if 0
252237 /* Get name of socket directory */
253238 dirname = a_Dpi_sockdir_file();
254239 if ((sockdir = init_sockdir(dirname)) == NULL) {
255240 ERRMSG("main", "init_sockdir", 0);
256 fprintf(stderr, "Failed to create socket directory\n");
257 exit(1);
258 }
241 MSG_ERR("Failed to create socket directory\n");
242 exit(1);
243 }
244 #endif
245
246 /* Init and get services list */
247 fill_services_list(dpi_attr_list, numdpis, &services_list);
259248
260249 /* Remove any sockets that may have been leftover from a crash */
261 cleanup(sockdir);
250 //cleanup();
251
262252 /* Initialise sockets */
263 if ((numsocks = init_srs_socket(sockdir)) == -1) {
253 if ((numsocks = init_ids_srs_socket()) == -1) {
264254 switch (dpi_errno) {
265255 case dpid_srs_addrinuse:
266 fprintf(stderr, "dpid refuses to start, possibly because:\n");
267 fprintf(stderr, "\t1) An instance of dpid is already running.\n");
268 fprintf(stderr, "\t2) A previous dpid didn't clean up on exit.\n");
256 MSG_ERR("dpid refuses to start, possibly because:\n");
257 MSG_ERR("\t1) An instance of dpid is already running.\n");
258 MSG_ERR("\t2) A previous dpid didn't clean up on exit.\n");
269259 exit(1);
270260 default:
271 ERRMSG("main", "init_srs_sockets failed\n", 0);
261 //ERRMSG("main", "init_srs_socket failed", 0);
262 ERRMSG("main", "init_ids_srs_socket failed", 0);
272263 exit(1);
273264 }
274265 }
275 numsocks = init_all_dpi_sockets(dpi_attr_list, sockdir);
276 /* est_terminator(); Do we still want to clean up on an abnormal exit? */
277
266 numsocks = init_all_dpi_sockets(dpi_attr_list);
267 est_dpi_terminator();
278268 est_dpi_sigchld();
279269
280270 (void) sigemptyset(&mask_sigchld);
296286 select_timeout.tv_usec = 0;
297287 selected_set = sock_set;
298288 n = select(FD_SETSIZE, &selected_set, NULL, NULL, &select_timeout);
299 if ( n == 0 ) { /* select timed out, try to exit */
289 if (n == 0) { /* select timed out, try to exit */
300290 /* BUG: This is a workaround for dpid not to exit when the
301291 * downloads server is active. The proper way to handle it is with
302292 * a dpip command that asks the server whether it's busy.
305295 continue;
306296
307297 stop_active_dpis(dpi_attr_list, numdpis);
308 cleanup(sockdir);
298 //cleanup();
309299 exit(0);
310300 }
311301 } while (n == -1 && errno == EINTR);
312302
313 /* g_mem_profile(); */
314303 if (n == -1) {
315304 ERRMSG("main", "select", errno);
316305 exit(1);
317306 }
318307 /* If the service req socket is selected then service the req. */
319 if (FD_ISSET(srs, &selected_set)) {
320 int sock;
321 socklen_t csz;
322 struct sockaddr_un clnt_addr;
308 if (FD_ISSET(srs_fd, &selected_set)) {
309 int sock_fd;
310 socklen_t sin_sz;
311 struct sockaddr_in sin;
323312 char *req = NULL;
324313
325314 --n;
326 g_assert(n >= 0);
327 csz = (socklen_t) sizeof(clnt_addr);
328 sock = accept(srs, (struct sockaddr *) &clnt_addr, &csz);
329 if (sock == -1) {
315 assert(n >= 0);
316 sin_sz = (socklen_t) sizeof(sin);
317 sock_fd = accept(srs_fd, (struct sockaddr *)&sin, &sin_sz);
318 if (sock_fd == -1) {
330319 ERRMSG("main", "accept", errno);
331 fprintf(stderr, "accept on srs socket failed\n");
332 fprintf(stderr, "service pending connections, and continue\n");
320 MSG_ERR("accept on srs socket failed\n");
321 MSG_ERR("service pending connections, and continue\n");
333322 } else {
334323 int command;
335
336 req = get_request(sock);
337 command = get_command(sock, req, dpi_attr_list);
324 Dsh *sh;
325
326 sh = a_Dpip_dsh_new(sock_fd, sock_fd, 1024);
327 read_next:
328 req = get_request(sh);
329 command = get_command(sh, req);
338330 switch (command) {
331 case AUTH_CMD:
332 if (a_Dpip_check_auth(req) != -1) {
333 dFree(req);
334 goto read_next;
335 }
336 break;
339337 case BYE_CMD:
340338 stop_active_dpis(dpi_attr_list, numdpis);
341 cleanup(sockdir);
339 //cleanup();
342340 exit(0);
343341 break;
344342 case CHECK_SERVER_CMD:
345 send_sockpath(sock, req, dpi_attr_list);
343 send_sockport(sock_fd, req, dpi_attr_list);
346344 break;
347345 case REGISTER_ALL_CMD:
348 register_all_cmd(sockdir);
346 register_all_cmd();
349347 break;
350348 case UNKNOWN_CMD:
351349 {
352350 char *d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
353351 "DpiError", "Unknown command");
354 (void) CKD_WRITE(sock, d_cmd);
355 g_free(d_cmd);
352 (void) CKD_WRITE(sock_fd, d_cmd);
353 dFree(d_cmd);
356354 ERRMSG("main", "Unknown command", 0);
357 fprintf(stderr, " for request: %s\n", req);
355 MSG_ERR(" for request: %s\n", req);
358356 break;
359357 }
360358 case -1:
361 ERRMSG("main", "get_command failed\n", 0);
359 _ERRMSG("main", "get_command failed", 0);
362360 break;
363361 }
364362 if (req)
365363 free(req);
366 a_Misc_close_fd(sock);
364 a_Dpip_dsh_close(sh);
365 a_Dpip_dsh_free(sh);
367366 }
368367 }
369368
370369 /* While there's a request on one of the plugin sockets
371370 * find the matching plugin and start it. */
372371 for (i = 0; n > 0 && i < numdpis; i++) {
373 if (FD_ISSET(dpi_attr_list[i].socket, &selected_set)) {
372 if (FD_ISSET(dpi_attr_list[i].sock_fd, &selected_set)) {
374373 --n;
375 g_assert(n >= 0);
374 assert(n >= 0);
376375
377376 if (dpi_attr_list[i].filter) {
378377 /* start a dpi filter plugin and continue watching its socket
383382 /* start a dpi server plugin but don't wait for new connections
384383 * on its socket */
385384 numsocks--;
386 g_assert(numsocks >= 0);
387 FD_CLR(dpi_attr_list[i].socket, &sock_set);
385 assert(numsocks >= 0);
386 FD_CLR(dpi_attr_list[i].sock_fd, &sock_set);
388387 if ((dpi_attr_list[i].pid = fork()) == -1) {
389388 ERRMSG("main", "fork", errno);
390389 /* exit(1); */
391390 } else if (dpi_attr_list[i].pid == 0) {
391 /* child */
392392 (void) sigprocmask(SIG_SETMASK, &mask_none, NULL);
393393 start_server_plugin(dpi_attr_list[i]);
394394 }
0 #include <stdio.h>
0 /*
1 * File: misc_new.c
2 *
3 * Copyright 2008 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <errno.h> /* errno, err-codes */
12 #include <unistd.h>
113 #include <time.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <glib.h>
14 #include <sys/stat.h> /* stat */
15 #include <stdlib.h> /* rand, srand */
16
17 #include "../dlib/dlib.h"
1018 #include "dpid_common.h"
11
12 #include "misc_new.h" /* for function prototypes */
13
14 /* define to 1 when checking for memory leaks
15 * \todo
16 * Eliminate need for a_Misc_get_home and a_Misc_get_user when testing for
17 * memory leaks by using ld --wrap option to replace g_get_home_dir and
18 * g_get_user_name with wrapper functions.
19 */
20 #ifndef TEST
21 #define TEST 0
22 #endif
19 #include "misc_new.h" /* for function prototypes */
2320
2421
2522 /*
2623 * Close a FD handling EINTR.
2724 */
28 gint a_Misc_close_fd(gint fd)
29 {
30 gint st;
25 int a_Misc_close_fd(int fd)
26 {
27 int st;
3128
3229 do {
3330 st = close(fd);
3532 return st;
3633 }
3734
38 /*
39 * Return the user's home directory.
40 * Don't free the returned string!
41 */
42 gchar *a_Misc_get_home(void)
43 {
44 gchar *ret;
45 ret = (TEST) ? getenv("HOME") : g_get_home_dir();
46 return ret;
47 }
48
49 /*
50 * Return the user.
51 * Don't free the returned string!
52 */
53 gchar *a_Misc_get_user(void)
54 {
55 gchar *ret;
56 ret = (TEST) ? getenv("USER") : g_get_user_name();
57 return ret;
58 }
59
60 /*
61 * Prepend the users home-dir to 'file' string i.e,
62 * pass in .dillo/bookmarks.html and it will return
63 * /home/imain/.dillo/bookmarks.html
64 *
65 * Remember to g_free() returned value!
66 * copied from misc.c
67 */
68 gchar *a_Misc_prepend_user_home(const char *file)
69 {
70 return (g_strconcat(a_Misc_get_home(), "/", file, NULL));
71 }
72
73 /*
74 * Read a line of text up to the newline character, store it into a newly
75 * allocated string and return it.
76 * (copied from dpi/bm_srv12.c)
77 */
78 char *a_Misc_get_line(FILE *stream)
79 {
80 guint i, size = 64;
81 int ch;
82 char *buf;
83
84 buf = g_new(char, size);
85
86 for (i = 0; (ch = fgetc(stream)) != EOF; ++i) {
87 if (i + 1 == size) {
88 size *= 2;
89 buf = g_realloc(buf, size);
90 }
91 if ((buf[i] = ch) == '\n' && ++i)
92 break;
93 }
94 buf[i] = 0;
95
96 if (i > 0) {
97 buf = g_realloc(buf, i + 1);
98 } else {
99 g_free(buf);
100 buf = NULL;
101 }
102 return buf;
103 }
104
10535 /*! Reads a dpi tag from a socket
10636 * \li Continues after a signal interrupt
10737 * \Return
108 * Gstring pointer to tag on success, NULL on failure
109 * \important Caller is responsible for freeing the returned GString *
110 */
111 GString *a_Misc_rdtag(int socket)
38 * Dstr pointer to tag on success, NULL on failure
39 * \important Caller is responsible for freeing the returned Dstr *
40 */
41 Dstr *a_Misc_rdtag(int socket)
11242 {
11343 char c = '\0';
11444 ssize_t rdlen;
115 GString *tag;
116
117 tag = g_string_new(NULL);
45 Dstr *tag;
46
47 tag = dStr_sized_new(64);
11848
11949 errno = 0;
12050
12252 rdlen = read(socket, &c, 1);
12353 if (rdlen == -1 && errno != EINTR)
12454 break;
125 g_string_append_c(tag, c);
55 dStr_append_c(tag, c);
12656 } while (c != '>');
12757
12858 if (rdlen == -1) {
12959 perror("a_Misc_rdtag");
130 g_string_free(tag, TRUE);
60 dStr_free(tag, TRUE);
13161 return (NULL);
13262 }
13363 return (tag);
14070 */
14171 char *a_Misc_readtag(int sock)
14272 {
143 char *tag, c, buf[10];
144 size_t buflen, i;
73 char *tag, c;
74 size_t i;
14575 size_t taglen = 0, tagmem = 10;
14676 ssize_t rdln = 1;
14777
14878 tag = NULL;
149 buf[0] = '\0';
150 buflen = sizeof(buf) / sizeof(buf[0]);
151 /* new start */
152 tag = (char *) g_malloc(tagmem + 1);
79 // new start
80 tag = (char *) dMalloc(tagmem + 1);
15381 for (i = 0; (rdln = read(sock, &c, 1)) != 0; i++) {
15482 if (i == tagmem) {
15583 tagmem += tagmem;
156 tag = (char *) g_realloc(tag, tagmem + 1);
84 tag = (char *) dRealloc(tag, tagmem + 1);
15785 }
15886 tag[i] = c;
15987 taglen += rdln;
16290 break;
16391 }
16492 }
165 /* new end */
93 // new end
16694 if (rdln == -1) {
16795 ERRMSG("a_Misc_readtag", "read", errno);
16896 }
176104 * \li 1 on success
177105 * \li 0 if input is not available within timeout microseconds.
178106 * \li -1 on failure
179 * \important Caller is responsible for freeing the returned GString *
107 * \important Caller is responsible for freeing the returned Dstr *
180108 */
181109 /* Is this useful?
182 int a_Misc_nohang_rdtag(int socket, int timeout, GString **tag)
110 int a_Misc_nohang_rdtag(int socket, int timeout, Dstr **tag)
183111 {
184112 int n_fd;
185113 fd_set sock_set, select_set;
197125 } while (n_fd == -1 && errno == EINTR);
198126
199127 if (n_fd == -1) {
200 fprintf(stderr, "%s:%d: a_Misc_nohang_rdtag: %s\n",
201 __FILE__, __LINE__, g_strerror(errno));
128 MSG_ERR("%s:%d: a_Misc_nohang_rdtag: %s\n",
129 __FILE__, __LINE__, dStrerror(errno));
202130 return(-1);
203131 }
204132 if (n_fd == 0) {
213141 /*
214142 * Alternative to mkdtemp().
215143 * Not as strong as mkdtemp, but enough for creating a directory.
216 * (adapted from dietlibc)
217144 */
218145 char *a_Misc_mkdtemp(char *template)
146 {
147 for (;;) {
148 if (a_Misc_mkfname(template) && mkdir(template, 0700) == 0)
149 break;
150 if (errno == EEXIST)
151 continue;
152 return 0;
153 }
154 return template;
155 }
156
157 /*
158 * Return a new, nonexistent file name from a template
159 * (adapted from dietlibc; alternative to mkdtemp())
160 */
161 char *a_Misc_mkfname(char *template)
219162 {
220163 char *tmp = template + strlen(template) - 6;
221164 int i;
222 unsigned int random;
165 uint_t random;
166 struct stat stat_buf;
223167
224168 if (tmp < template)
225169 goto error;
229173 errno = EINVAL;
230174 return 0;
231175 }
232 srand((guint)(time(0) ^ getpid()));
176 srand((uint_t)(time(0) ^ getpid()));
177
233178 for (;;) {
234179 random = (unsigned) rand();
235180 for (i = 0; i < 6; ++i) {
237182
238183 tmp[i] = hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
239184 }
240 if (mkdir(template, 0700) == 0)
241 break;
242 if (errno == EEXIST)
243 continue;
244 return 0;
245 }
246 return template;
247 }
185 if (stat(template, &stat_buf) == -1 && errno == ENOENT)
186 return template;
187
188 MSG_ERR("a_Misc_mkfname: another round for %s \n", template);
189 }
190 }
191
192 /*
193 * Return a new, random hexadecimal string of 'nchar' characters.
194 */
195 char *a_Misc_mksecret(int nchar)
196 {
197 int i;
198 uint_t random;
199 char *secret = dNew(char, nchar + 1);
200
201 srand((uint_t)(time(0) ^ getpid()));
202 random = (unsigned) rand();
203 for (i = 0; i < nchar; ++i) {
204 int hexdigit = (random >> (i * 5)) & 0x0f;
205
206 secret[i] = hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
207 }
208 secret[i] = 0;
209 MSG("a_Misc_mksecret: %s\n", secret);
210
211 return secret;
212 }
213
00 #ifndef MISC_NEW_H
11 #define MISC_NEW_H
22
3 #include <glib.h>
43
5 gint a_Misc_close_fd(gint fd);
6
7 gchar *a_Misc_get_home(void);
8
9 gchar *a_Misc_get_user(void);
10
11 gchar *a_Misc_prepend_user_home(const char *file);
12
13 char *a_Misc_get_line(FILE *stream);
14
15 GString *a_Misc_rdtag(int socket);
16
4 int a_Misc_close_fd(int fd);
5 Dstr *a_Misc_rdtag(int socket);
176 char *a_Misc_readtag(int sock);
18
197 char *a_Misc_mkdtemp(char *template);
8 char *a_Misc_mkfname(char *template);
9 char *a_Misc_mksecret(int nchar);
2010
2111 #endif
0 AM_CFLAGS = @GLIB_CFLAGS@
1 AM_LIBS = @GLIB_LIBS@
0 AM_CPPFLAGS = \
1 -I$(top_srcdir)
22
33 noinst_LIBRARIES = libDpip.a
44
+0
-416
dpip/Makefile.in less more
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 SOURCES = $(libDpip_a_SOURCES)
17
18 srcdir = @srcdir@
19 top_srcdir = @top_srcdir@
20 VPATH = @srcdir@
21 pkgdatadir = $(datadir)/@PACKAGE@
22 pkglibdir = $(libdir)/@PACKAGE@
23 pkgincludedir = $(includedir)/@PACKAGE@
24 top_builddir = ..
25 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
26 INSTALL = @INSTALL@
27 install_sh_DATA = $(install_sh) -c -m 644
28 install_sh_PROGRAM = $(install_sh) -c
29 install_sh_SCRIPT = $(install_sh) -c
30 INSTALL_HEADER = $(INSTALL_DATA)
31 transform = $(program_transform_name)
32 NORMAL_INSTALL = :
33 PRE_INSTALL = :
34 POST_INSTALL = :
35 NORMAL_UNINSTALL = :
36 PRE_UNINSTALL = :
37 POST_UNINSTALL = :
38 build_triplet = @build@
39 host_triplet = @host@
40 target_triplet = @target@
41 subdir = dpip
42 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
43 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
44 am__aclocal_m4_deps = $(top_srcdir)/configure.in
45 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
46 $(ACLOCAL_M4)
47 mkinstalldirs = $(install_sh) -d
48 CONFIG_HEADER = $(top_builddir)/config.h
49 CONFIG_CLEAN_FILES =
50 LIBRARIES = $(noinst_LIBRARIES)
51 AR = ar
52 ARFLAGS = cru
53 libDpip_a_AR = $(AR) $(ARFLAGS)
54 libDpip_a_LIBADD =
55 am_libDpip_a_OBJECTS = dpip.$(OBJEXT)
56 libDpip_a_OBJECTS = $(am_libDpip_a_OBJECTS)
57 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
58 depcomp = $(SHELL) $(top_srcdir)/depcomp
59 am__depfiles_maybe = depfiles
60 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
61 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
62 CCLD = $(CC)
63 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
64 SOURCES = $(libDpip_a_SOURCES)
65 DIST_SOURCES = $(libDpip_a_SOURCES)
66 ETAGS = etags
67 CTAGS = ctags
68 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
69 ACLOCAL = @ACLOCAL@
70 AMDEP_FALSE = @AMDEP_FALSE@
71 AMDEP_TRUE = @AMDEP_TRUE@
72 AMTAR = @AMTAR@
73 AUTOCONF = @AUTOCONF@
74 AUTOHEADER = @AUTOHEADER@
75 AUTOMAKE = @AUTOMAKE@
76 AWK = @AWK@
77 CC = @CC@
78 CCDEPMODE = @CCDEPMODE@
79 CFLAGS = @CFLAGS@
80 CPP = @CPP@
81 CPPFLAGS = @CPPFLAGS@
82 CXX = @CXX@
83 CXXDEPMODE = @CXXDEPMODE@
84 CXXFLAGS = @CXXFLAGS@
85 CYGPATH_W = @CYGPATH_W@
86 DEFS = @DEFS@
87 DEPDIR = @DEPDIR@
88 DLGUI_FALSE = @DLGUI_FALSE@
89 DLGUI_TRUE = @DLGUI_TRUE@
90 ECHO_C = @ECHO_C@
91 ECHO_N = @ECHO_N@
92 ECHO_T = @ECHO_T@
93 EGREP = @EGREP@
94 EXEEXT = @EXEEXT@
95 GLIB_CFLAGS = @GLIB_CFLAGS@
96 GLIB_CONFIG = @GLIB_CONFIG@
97 GLIB_LIBS = @GLIB_LIBS@
98 GTK_CFLAGS = @GTK_CFLAGS@
99 GTK_CONFIG = @GTK_CONFIG@
100 GTK_LIBS = @GTK_LIBS@
101 INSTALL_DATA = @INSTALL_DATA@
102 INSTALL_PROGRAM = @INSTALL_PROGRAM@
103 INSTALL_SCRIPT = @INSTALL_SCRIPT@
104 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
105 LDFLAGS = @LDFLAGS@
106 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
107 LIBFLTK_LIBS = @LIBFLTK_LIBS@
108 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
109 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
110 LIBJPEG_LIBS = @LIBJPEG_LIBS@
111 LIBOBJS = @LIBOBJS@
112 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
113 LIBPNG_LIBS = @LIBPNG_LIBS@
114 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
115 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
116 LIBS = @LIBS@
117 LIBSSL_LIBS = @LIBSSL_LIBS@
118 LIBZ_LIBS = @LIBZ_LIBS@
119 LTLIBOBJS = @LTLIBOBJS@
120 MAKEINFO = @MAKEINFO@
121 OBJEXT = @OBJEXT@
122 PACKAGE = @PACKAGE@
123 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
124 PACKAGE_NAME = @PACKAGE_NAME@
125 PACKAGE_STRING = @PACKAGE_STRING@
126 PACKAGE_TARNAME = @PACKAGE_TARNAME@
127 PACKAGE_VERSION = @PACKAGE_VERSION@
128 PATH_SEPARATOR = @PATH_SEPARATOR@
129 RANLIB = @RANLIB@
130 SET_MAKE = @SET_MAKE@
131 SHELL = @SHELL@
132 STRIP = @STRIP@
133 VERSION = @VERSION@
134 ac_ct_CC = @ac_ct_CC@
135 ac_ct_CXX = @ac_ct_CXX@
136 ac_ct_RANLIB = @ac_ct_RANLIB@
137 ac_ct_STRIP = @ac_ct_STRIP@
138 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
139 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
140 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
141 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
142 am__include = @am__include@
143 am__leading_dot = @am__leading_dot@
144 am__quote = @am__quote@
145 am__tar = @am__tar@
146 am__untar = @am__untar@
147 bindir = @bindir@
148 build = @build@
149 build_alias = @build_alias@
150 build_cpu = @build_cpu@
151 build_os = @build_os@
152 build_vendor = @build_vendor@
153 datadir = @datadir@
154 exec_prefix = @exec_prefix@
155 host = @host@
156 host_alias = @host_alias@
157 host_cpu = @host_cpu@
158 host_os = @host_os@
159 host_vendor = @host_vendor@
160 includedir = @includedir@
161 infodir = @infodir@
162 install_sh = @install_sh@
163 libdir = @libdir@
164 libexecdir = @libexecdir@
165 localstatedir = @localstatedir@
166 mandir = @mandir@
167 mkdir_p = @mkdir_p@
168 oldincludedir = @oldincludedir@
169 prefix = @prefix@
170 program_transform_name = @program_transform_name@
171 sbindir = @sbindir@
172 sharedstatedir = @sharedstatedir@
173 sysconfdir = @sysconfdir@
174 target = @target@
175 target_alias = @target_alias@
176 target_cpu = @target_cpu@
177 target_os = @target_os@
178 target_vendor = @target_vendor@
179 AM_CFLAGS = @GLIB_CFLAGS@
180 AM_LIBS = @GLIB_LIBS@
181 noinst_LIBRARIES = libDpip.a
182 libDpip_a_SOURCES = \
183 dpip.h \
184 dpip.c
185
186 all: all-am
187
188 .SUFFIXES:
189 .SUFFIXES: .c .o .obj
190 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
191 @for dep in $?; do \
192 case '$(am__configure_deps)' in \
193 *$$dep*) \
194 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
195 && exit 0; \
196 exit 1;; \
197 esac; \
198 done; \
199 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu dpip/Makefile'; \
200 cd $(top_srcdir) && \
201 $(AUTOMAKE) --gnu dpip/Makefile
202 .PRECIOUS: Makefile
203 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
204 @case '$?' in \
205 *config.status*) \
206 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
207 *) \
208 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
209 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
210 esac;
211
212 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
213 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
214
215 $(top_srcdir)/configure: $(am__configure_deps)
216 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
217 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
218 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
219
220 clean-noinstLIBRARIES:
221 -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
222 libDpip.a: $(libDpip_a_OBJECTS) $(libDpip_a_DEPENDENCIES)
223 -rm -f libDpip.a
224 $(libDpip_a_AR) libDpip.a $(libDpip_a_OBJECTS) $(libDpip_a_LIBADD)
225 $(RANLIB) libDpip.a
226
227 mostlyclean-compile:
228 -rm -f *.$(OBJEXT)
229
230 distclean-compile:
231 -rm -f *.tab.c
232
233 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpip.Po@am__quote@
234
235 .c.o:
236 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
237 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
238 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
239 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
240 @am__fastdepCC_FALSE@ $(COMPILE) -c $<
241
242 .c.obj:
243 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
244 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
245 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
246 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
247 @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
248 uninstall-info-am:
249
250 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
251 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
252 unique=`for i in $$list; do \
253 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
254 done | \
255 $(AWK) ' { files[$$0] = 1; } \
256 END { for (i in files) print i; }'`; \
257 mkid -fID $$unique
258 tags: TAGS
259
260 TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
261 $(TAGS_FILES) $(LISP)
262 tags=; \
263 here=`pwd`; \
264 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
265 unique=`for i in $$list; do \
266 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
267 done | \
268 $(AWK) ' { files[$$0] = 1; } \
269 END { for (i in files) print i; }'`; \
270 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
271 test -n "$$unique" || unique=$$empty_fix; \
272 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
273 $$tags $$unique; \
274 fi
275 ctags: CTAGS
276 CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
277 $(TAGS_FILES) $(LISP)
278 tags=; \
279 here=`pwd`; \
280 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
281 unique=`for i in $$list; do \
282 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
283 done | \
284 $(AWK) ' { files[$$0] = 1; } \
285 END { for (i in files) print i; }'`; \
286 test -z "$(CTAGS_ARGS)$$tags$$unique" \
287 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
288 $$tags $$unique
289
290 GTAGS:
291 here=`$(am__cd) $(top_builddir) && pwd` \
292 && cd $(top_srcdir) \
293 && gtags -i $(GTAGS_ARGS) $$here
294
295 distclean-tags:
296 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
297
298 distdir: $(DISTFILES)
299 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
300 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
301 list='$(DISTFILES)'; for file in $$list; do \
302 case $$file in \
303 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
304 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
305 esac; \
306 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
307 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
308 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
309 dir="/$$dir"; \
310 $(mkdir_p) "$(distdir)$$dir"; \
311 else \
312 dir=''; \
313 fi; \
314 if test -d $$d/$$file; then \
315 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
316 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
317 fi; \
318 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
319 else \
320 test -f $(distdir)/$$file \
321 || cp -p $$d/$$file $(distdir)/$$file \
322 || exit 1; \
323 fi; \
324 done
325 check-am: all-am
326 check: check-am
327 all-am: Makefile $(LIBRARIES)
328 installdirs:
329 install: install-am
330 install-exec: install-exec-am
331 install-data: install-data-am
332 uninstall: uninstall-am
333
334 install-am: all-am
335 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
336
337 installcheck: installcheck-am
338 install-strip:
339 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
340 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
341 `test -z '$(STRIP)' || \
342 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
343 mostlyclean-generic:
344
345 clean-generic:
346
347 distclean-generic:
348 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
349
350 maintainer-clean-generic:
351 @echo "This command is intended for maintainers to use"
352 @echo "it deletes files that may require special tools to rebuild."
353 clean: clean-am
354
355 clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am
356
357 distclean: distclean-am
358 -rm -rf ./$(DEPDIR)
359 -rm -f Makefile
360 distclean-am: clean-am distclean-compile distclean-generic \
361 distclean-tags
362
363 dvi: dvi-am
364
365 dvi-am:
366
367 html: html-am
368
369 info: info-am
370
371 info-am:
372
373 install-data-am:
374
375 install-exec-am:
376
377 install-info: install-info-am
378
379 install-man:
380
381 installcheck-am:
382
383 maintainer-clean: maintainer-clean-am
384 -rm -rf ./$(DEPDIR)
385 -rm -f Makefile
386 maintainer-clean-am: distclean-am maintainer-clean-generic
387
388 mostlyclean: mostlyclean-am
389
390 mostlyclean-am: mostlyclean-compile mostlyclean-generic
391
392 pdf: pdf-am
393
394 pdf-am:
395
396 ps: ps-am
397
398 ps-am:
399
400 uninstall-am: uninstall-info-am
401
402 .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
403 clean-noinstLIBRARIES ctags distclean distclean-compile \
404 distclean-generic distclean-tags distdir dvi dvi-am html \
405 html-am info info-am install install-am install-data \
406 install-data-am install-exec install-exec-am install-info \
407 install-info-am install-man install-strip installcheck \
408 installcheck-am installdirs maintainer-clean \
409 maintainer-clean-generic mostlyclean mostlyclean-compile \
410 mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
411 uninstall-am uninstall-info-am
412
413 # Tell versions [3.59,3.63) of GNU make to not export all variables.
414 # Otherwise a system limit (for SysV at least) may be exceeded.
415 .NOEXPORT:
00 /*
11 * File: dpip.c
22 *
3 * Copyright 2005 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 *
1010 */
1111
12 #include <errno.h>
1213 #include <stdio.h>
14 #include <stdlib.h>
1315 #include <stdarg.h>
1416 #include <string.h>
15 #include <glib.h>
17 #include <ctype.h>
18 #include <unistd.h> /* for close */
19 #include <fcntl.h> /* for fcntl */
1620
1721 #include "dpip.h"
18
19 static char Quote = '\'';
22 #include "d_size.h"
23
24 #define RBUF_SZ 16*1024
25 //#define RBUF_SZ 1
26
27 #define DPIP_TAG_END " '>"
28 #define DPIP_MODE_SWITCH_TAG "cmd='start_send_page' "
29 #define MSG_ERR(...) fprintf(stderr, "[dpip]: " __VA_ARGS__)
30
31 /*
32 * Local variables
33 */
34 static const char Quote = '\'';
2035
2136 /*
2237 * Basically the syntax of a dpip tag is:
6378 {
6479 va_list argp;
6580 char *p, *q, *s;
66 GString *cmd;
81 Dstr *cmd;
6782
6883 /* Don't allow Quote characters in attribute names */
6984 if (strchr(format, Quote))
7085 return NULL;
7186
72 cmd = g_string_sized_new(64);
73 g_string_append_c(cmd, '<');
87 cmd = dStr_sized_new(64);
88 dStr_append_c(cmd, '<');
7489 va_start(argp, format);
7590 for (p = q = (char*)format; *q; ) {
7691 p = strstr(q, "%s");
7792 if (!p) {
78 g_string_append(cmd, q);
93 dStr_append(cmd, q);
7994 break;
8095 } else {
8196 /* Copy format's part */
8297 while (q != p)
83 g_string_append_c(cmd, *q++);
98 dStr_append_c(cmd, *q++);
8499 q += 2;
85100
86 g_string_append_c(cmd, Quote);
101 dStr_append_c(cmd, Quote);
87102 /* Stuff-copy of argument */
88103 s = va_arg (argp, char *);
89104 for ( ; *s; ++s) {
90 g_string_append_c(cmd, *s);
105 dStr_append_c(cmd, *s);
91106 if (*s == Quote)
92 g_string_append_c(cmd, *s);
107 dStr_append_c(cmd, *s);
93108 }
94 g_string_append_c(cmd, Quote);
109 dStr_append_c(cmd, Quote);
95110 }
96111 }
97112 va_end(argp);
98 g_string_append_c(cmd, ' ');
99 g_string_append_c(cmd, Quote);
100 g_string_append_c(cmd, '>');
113 dStr_append_c(cmd, ' ');
114 dStr_append_c(cmd, Quote);
115 dStr_append_c(cmd, '>');
101116
102117 p = cmd->str;
103 g_string_free(cmd, FALSE);
118 dStr_free(cmd, FALSE);
104119 return p;
105120 }
106121
107122 /*
108 * Task: given a tag and an attribute name, return its value.
109 * (stuffing of ' is removed here)
123 * Task: given a tag, its size and an attribute name, return the
124 * attribute value (stuffing of ' is removed here).
125 *
110126 * Return value: the attribute value, or NULL if not present or malformed.
111127 */
112 char *a_Dpip_get_attr(char *tag, size_t tagsize, char *attrname)
113 {
114 guint i, n = 0, found = 0;
115 char *p, *q, *start, *val = NULL;
128 char *a_Dpip_get_attr_l(const char *tag, size_t tagsize, const char *attrname)
129 {
130 uint_t i, n = 0, found = 0;
131 const char *p, *q, *start;
132 char *r, *s, *val = NULL;
116133 DpipTagParsingState state = SEEK_NAME;
117134
118 if (!attrname || !*attrname)
135 if (!tag || !tagsize || !attrname || !*attrname)
119136 return NULL;
120137
121138 for (i = 1; i < tagsize && !found; ++i) {
153170 while ((q = strchr(p, Quote)) && q[1] == Quote)
154171 p = q + 2;
155172 if (q && q[1] == ' ') {
156 val = g_strndup(start, (guint)(q - start));
157 for (p = q = val; (*q = *p); ++p, ++q)
158 if (*p == Quote && p[1] == p[0])
159 ++p;
173 val = dStrndup(start, (uint_t)(q - start));
174 for (r = s = val; (*r = *s); ++r, ++s)
175 if (s[0] == Quote && s[0] == s[1])
176 ++s;
160177 }
161178 }
162179 return val;
163180 }
164181
165 /* ------------------------------------------------------------------------- */
166
182 /*
183 * Task: given a tag and an attribute name, return its value.
184 * Return value: the attribute value, or NULL if not present or malformed.
185 */
186 char *a_Dpip_get_attr(const char *tag, const char *attrname)
187 {
188 return (tag ? a_Dpip_get_attr_l(tag, strlen(tag), attrname) : NULL);
189 }
190
191 /*
192 * Check whether the given 'auth' string equals what dpid saved.
193 * Return value: 1 if equal, -1 otherwise
194 */
195 int a_Dpip_check_auth(const char *auth_tag)
196 {
197 char SharedSecret[32];
198 FILE *In;
199 char *fname, *rcline = NULL, *tail, *cmd, *msg;
200 int i, port, ret = -1;
201
202 /* sanity checks */
203 if (!auth_tag ||
204 !(cmd = a_Dpip_get_attr(auth_tag, "cmd")) || strcmp(cmd, "auth") ||
205 !(msg = a_Dpip_get_attr(auth_tag, "msg"))) {
206 return ret;
207 }
208
209 fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
210 if ((In = fopen(fname, "r")) == NULL) {
211 MSG_ERR("[a_Dpip_check_auth] %s\n", dStrerror(errno));
212 } else if ((rcline = dGetline(In)) == NULL) {
213 MSG_ERR("[a_Dpip_check_auth] empty file: %s\n", fname);
214 } else {
215 port = strtol(rcline, &tail, 10);
216 if (tail && port != 0) {
217 for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
218 SharedSecret[i] = tail[i+1];
219 SharedSecret[i] = 0;
220 if (strcmp(msg, SharedSecret) == 0)
221 ret = 1;
222 }
223 }
224 if (In)
225 fclose(In);
226 dFree(rcline);
227 dFree(fname);
228 dFree(msg);
229 dFree(cmd);
230
231 return ret;
232 }
233
234 /* --------------------------------------------------------------------------
235 * Dpip socket API ----------------------------------------------------------
236 */
237
238 /*
239 * Create and initialize a dpip socket handler
240 */
241 Dsh *a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz)
242 {
243 Dsh *dsh = dNew(Dsh, 1);
244
245 /* init descriptors and streams */
246 dsh->fd_in = fd_in;
247 dsh->fd_out = fd_out;
248
249 /* init buffer */
250 dsh->wrbuf = dStr_sized_new(8 *1024);
251 dsh->rdbuf = dStr_sized_new(8 *1024);
252 dsh->flush_sz = flush_sz;
253 dsh->mode = DPIP_TAG;
254 if (fcntl(dsh->fd_in, F_GETFL) & O_NONBLOCK)
255 dsh->mode |= DPIP_NONBLOCK;
256 dsh->status = 0;
257
258 return dsh;
259 }
260
261 /*
262 * Return value: 1..DataSize sent, -1 eagain, or -3 on big Error
263 */
264 static int Dpip_dsh_write(Dsh *dsh, int nb, const char *Data, int DataSize)
265 {
266 int req_mode, old_flags = 0, st, ret = -3, sent = 0;
267
268 req_mode = (nb) ? DPIP_NONBLOCK : 0;
269 if ((dsh->mode & DPIP_NONBLOCK) != req_mode) {
270 /* change mode temporarily... */
271 old_flags = fcntl(dsh->fd_out, F_GETFL);
272 fcntl(dsh->fd_out, F_SETFL,
273 (nb) ? O_NONBLOCK | old_flags : old_flags & ~O_NONBLOCK);
274 }
275
276 while (1) {
277 st = write(dsh->fd_out, Data + sent, DataSize - sent);
278 if (st < 0) {
279 if (errno == EINTR) {
280 continue;
281 } else if (errno == EAGAIN) {
282 dsh->status = DPIP_EAGAIN;
283 ret = -1;
284 break;
285 } else {
286 MSG_ERR("[Dpip_dsh_write] %s\n", dStrerror(errno));
287 dsh->status = DPIP_ERROR;
288 break;
289 }
290 } else {
291 sent += st;
292 if (nb || sent == DataSize) {
293 ret = sent;
294 break;
295 }
296 }
297 }
298
299 if ((dsh->mode & DPIP_NONBLOCK) != req_mode) {
300 /* restore old mode */
301 fcntl(dsh->fd_out, F_SETFL, old_flags);
302 }
303
304 return ret;
305 }
306
307 /*
308 * Streamed write to socket
309 * Return: 0 on success, 1 on error.
310 */
311 int a_Dpip_dsh_write(Dsh *dsh, int flush, const char *Data, int DataSize)
312 {
313 int ret = 1;
314
315 /* append to buf */
316 dStr_append_l(dsh->wrbuf, Data, DataSize);
317
318 if (!flush || dsh->wrbuf->len == 0)
319 return 0;
320
321 ret = Dpip_dsh_write(dsh, 0, dsh->wrbuf->str, dsh->wrbuf->len);
322 if (ret == dsh->wrbuf->len) {
323 dStr_truncate(dsh->wrbuf, 0);
324 ret = 0;
325 }
326
327 return ret;
328 }
329
330 /*
331 * Return value: 0 on success or empty buffer,
332 * 1..DataSize sent, -1 eagain, or -3 on big Error
333 */
334 int a_Dpip_dsh_tryflush(Dsh *dsh)
335 {
336 int st;
337
338 if (dsh->wrbuf->len == 0) {
339 st = 0;
340 } else {
341 st = Dpip_dsh_write(dsh, 1, dsh->wrbuf->str, dsh->wrbuf->len);
342 if (st > 0) {
343 /* update internal buffer */
344 dStr_erase(dsh->wrbuf, 0, st);
345 }
346 }
347 return (dsh->wrbuf->len == 0) ? 0 : st;
348 }
349
350 /*
351 * Return value: 1..DataSize sent, -1 eagain, or -3 on big Error
352 */
353 int a_Dpip_dsh_trywrite(Dsh *dsh, const char *Data, int DataSize)
354 {
355 int st;
356
357 if ((st = Dpip_dsh_write(dsh, 1, Data, DataSize)) > 0) {
358 /* update internal buffer */
359 if (st < DataSize)
360 dStr_append_l(dsh->wrbuf, Data + st, DataSize - st);
361 }
362 return st;
363 }
364
365 /*
366 * Convenience function.
367 */
368 int a_Dpip_dsh_write_str(Dsh *dsh, int flush, const char *str)
369 {
370 return a_Dpip_dsh_write(dsh, flush, str, (int)strlen(str));
371 }
372
373 /*
374 * Read raw data from the socket into our buffer in
375 * either BLOCKING or NONBLOCKING mode.
376 */
377 static void Dpip_dsh_read(Dsh *dsh, int blocking)
378 {
379 char buf[RBUF_SZ];
380 int req_mode, old_flags = 0, st, nb = !blocking;
381
382 dReturn_if (dsh->status == DPIP_ERROR || dsh->status == DPIP_EOF);
383
384 req_mode = (nb) ? DPIP_NONBLOCK : 0;
385 if ((dsh->mode & DPIP_NONBLOCK) != req_mode) {
386 /* change mode temporarily... */
387 old_flags = fcntl(dsh->fd_in, F_GETFL);
388 fcntl(dsh->fd_in, F_SETFL,
389 (nb) ? O_NONBLOCK | old_flags : old_flags & ~O_NONBLOCK);
390 }
391
392 while (1) {
393 st = read(dsh->fd_in, buf, RBUF_SZ);
394 if (st < 0) {
395 if (errno == EINTR) {
396 continue;
397 } else if (errno == EAGAIN) {
398 dsh->status = DPIP_EAGAIN;
399 break;
400 } else {
401 MSG_ERR("[Dpip_dsh_read] %s\n", dStrerror(errno));
402 dsh->status = DPIP_ERROR;
403 break;
404 }
405 } else if (st == 0) {
406 dsh->status = DPIP_EOF;
407 break;
408 } else {
409 /* append to buf */
410 dStr_append_l(dsh->rdbuf, buf, st);
411 if (blocking)
412 break;
413 }
414 }
415
416 if ((dsh->mode & DPIP_NONBLOCK) != req_mode) {
417 /* restore old mode */
418 fcntl(dsh->fd_out, F_SETFL, old_flags);
419 }
420
421 /* assert there's no more data in the wire...
422 * (st < buf upon interrupt || st == buf and no more data) */
423 if (blocking)
424 Dpip_dsh_read(dsh, 0);
425 }
426
427 /*
428 * Return a newlly allocated string with the next dpip token in the socket.
429 * Return value: token string on success, NULL otherwise
430 */
431 char *a_Dpip_dsh_read_token(Dsh *dsh, int blocking)
432 {
433 char *p, *ret = NULL;
434
435 /* Read all available data without blocking */
436 Dpip_dsh_read(dsh, 0);
437
438 /* switch mode upon request */
439 if (dsh->mode & DPIP_LAST_TAG)
440 dsh->mode = DPIP_RAW;
441
442 if (blocking) {
443 if (dsh->mode & DPIP_TAG) {
444 /* Only wait for data when the tag is incomplete */
445 if (!strstr(dsh->rdbuf->str, DPIP_TAG_END)) {
446 do {
447 Dpip_dsh_read(dsh, 1);
448 p = strstr(dsh->rdbuf->str, DPIP_TAG_END);
449 } while (!p && dsh->status == EAGAIN);
450 }
451
452 } else if (dsh->mode & DPIP_RAW) {
453 /* Wait for data when the buffer is empty and there's no EOF yet */
454 while (dsh->rdbuf->len == 0 && dsh->status != DPIP_EOF)
455 Dpip_dsh_read(dsh, 1);
456 }
457 }
458
459 if (dsh->mode & DPIP_TAG) {
460 /* return a full tag */
461 if ((p = strstr(dsh->rdbuf->str, DPIP_TAG_END))) {
462 ret = dStrndup(dsh->rdbuf->str, p - dsh->rdbuf->str + 3);
463 dStr_erase(dsh->rdbuf, 0, p - dsh->rdbuf->str + 3);
464 if (strstr(ret, DPIP_MODE_SWITCH_TAG))
465 dsh->mode |= DPIP_LAST_TAG;
466 }
467 } else {
468 /* raw mode, return what we have "as is" */
469 if (dsh->rdbuf->len > 0) {
470 ret = dStrndup(dsh->rdbuf->str, dsh->rdbuf->len);
471 dStr_truncate(dsh->rdbuf, 0);
472 }
473 }
474
475 return ret;
476 }
477
478 /*
479 * Close this socket for reading and writing.
480 * (flush pending data)
481 */
482 void a_Dpip_dsh_close(Dsh *dsh)
483 {
484 int st;
485
486 /* flush internal buffer */
487 a_Dpip_dsh_write(dsh, 1, "", 0);
488
489 /* close fds */
490 while((st = close(dsh->fd_in)) < 0 && errno == EINTR) ;
491 if (st < 0)
492 MSG_ERR("[a_Dpip_dsh_close] close: %s\n", dStrerror(errno));
493 if (dsh->fd_out != dsh->fd_in) {
494 while((st = close(dsh->fd_out)) < 0 && errno == EINTR) ;
495 if (st < 0)
496 MSG_ERR("[a_Dpip_dsh_close] close: %s\n", dStrerror(errno));
497 }
498 }
499
500 /*
501 * Free the SockHandler structure
502 */
503 void a_Dpip_dsh_free(Dsh *dsh)
504 {
505 dReturn_if (dsh == NULL);
506
507 dStr_free(dsh->wrbuf, 1);
508 dStr_free(dsh->rdbuf, 1);
509 dFree(dsh);
510 }
511
77 #ifdef __cplusplus
88 extern "C" {
99 #endif /* __cplusplus */
10
11 #include "../dlib/dlib.h"
12
13 /*
14 * Communication mode flags
15 */
16 #define DPIP_TAG 1 /* Dpip tags in the socket */
17 #define DPIP_LAST_TAG 2 /* Dpip mode-switching tag */
18 #define DPIP_RAW 4 /* Raw data in the socket */
19 #define DPIP_NONBLOCK 8 /* Nonblocking IO */
20
21 typedef enum {
22 DPIP_EAGAIN,
23 DPIP_ERROR,
24 DPIP_EOF
25 } DpipDshStatus;
26
27 /*
28 * Dpip socket handler type.
29 */
30 typedef struct _DpipSocketHandler Dsh;
31 struct _DpipSocketHandler {
32 int fd_in;
33 int fd_out;
34 /* FILE *in; --Unused. The stream functions block when reading. */
35 FILE *out;
36
37 Dstr *wrbuf; /* write buffer */
38 Dstr *rdbuf; /* read buffer */
39 int flush_sz; /* max size before flush */
40
41 int mode; /* mode flags: DPIP_TAG | DPIP_LAST_TAG | DPIP_RAW */
42 int status; /* status code: DPIP_EAGAIN | DPIP_ERROR | DPIP_EOF */
43 };
1044
1145
1246 /*
2256 * (dpip character escaping is removed here)
2357 * Return value: the attribute value, or NULL if not present or malformed.
2458 */
25 char *a_Dpip_get_attr(char *tag, size_t tagsize, char *attrname);
59 char *a_Dpip_get_attr(const char *tag, const char *attrname);
60 char *a_Dpip_get_attr_l(const char *tag, size_t tagsize, const char *attrname);
2661
62 int a_Dpip_check_auth(const char *auth);
63
64 /*
65 * Dpip socket API
66 */
67 Dsh *a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz);
68 int a_Dpip_dsh_write(Dsh *dsh, int flush, const char *Data, int DataSize);
69 int a_Dpip_dsh_write_str(Dsh *dsh, int flush, const char *str);
70 int a_Dpip_dsh_tryflush(Dsh *dsh);
71 int a_Dpip_dsh_trywrite(Dsh *dsh, const char *Data, int DataSize);
72 char *a_Dpip_dsh_read_token(Dsh *dsh, int blocking);
73 void a_Dpip_dsh_close(Dsh *dsh);
74 void a_Dpip_dsh_free(Dsh *dsh);
75
76 #define a_Dpip_dsh_printf(sh, flush, ...) \
77 D_STMT_START { \
78 Dstr *dstr = dStr_sized_new(128); \
79 dStr_sprintf(dstr, __VA_ARGS__); \
80 a_Dpip_dsh_write(sh, flush, dstr->str, dstr->len); \
81 dStr_free(dstr, 1); \
82 } D_STMT_END
2783
2884 #ifdef __cplusplus
2985 }
0 AM_CPPFLAGS = \
1 -I$(top_srcdir)
2
3 noinst_LIBRARIES = \
4 libDw-core.a \
5 libDw-fltk.a \
6 libDw-widgets.a
7
8 libDw_core_a_SOURCES = \
9 core.hh \
10 events.hh \
11 findtext.cc \
12 findtext.hh \
13 imgbuf.hh \
14 iterator.cc \
15 iterator.hh \
16 layout.cc \
17 layout.hh \
18 platform.hh \
19 selection.hh \
20 selection.cc \
21 style.cc \
22 style.hh \
23 types.cc \
24 types.hh \
25 ui.cc \
26 ui.hh \
27 view.hh \
28 widget.cc \
29 widget.hh
30
31 libDw_fltk_a_SOURCES = \
32 fltkcomplexbutton.cc \
33 fltkcomplexbutton.hh \
34 fltkcore.hh \
35 fltkflatview.cc \
36 fltkflatview.hh \
37 fltkimgbuf.cc \
38 fltkimgbuf.hh \
39 fltkmisc.cc \
40 fltkmisc.hh \
41 fltkplatform.cc \
42 fltkplatform.hh \
43 fltkpreview.hh \
44 fltkpreview.cc \
45 fltkui.cc \
46 fltkui.hh \
47 fltkviewbase.cc \
48 fltkviewbase.hh \
49 fltkviewport.cc \
50 fltkviewport.hh
51
52 libDw_fltk_a_CXXFLAGS = @LIBFLTK_CXXFLAGS@
53
54 libDw_widgets_a_SOURCES = \
55 alignedtextblock.cc \
56 alignedtextblock.hh \
57 bullet.cc \
58 bullet.hh \
59 image.cc \
60 image.hh \
61 listitem.cc \
62 listitem.hh \
63 ruler.cc \
64 ruler.hh \
65 table.cc \
66 table.hh \
67 tablecell.cc \
68 tablecell.hh \
69 textblock.cc \
70 textblock.hh
71
72 EXTRA_DIST = preview.xbm
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "alignedtextblock.hh"
22 #include <stdio.h>
23
24 namespace dw {
25
26 AlignedTextblock::List::List ()
27 {
28 textblocks = new lout::misc::SimpleVector <AlignedTextblock*> (4);
29 values = new lout::misc::SimpleVector <int> (4);
30 maxValue = 0;
31 refCount = 0;
32 }
33
34 AlignedTextblock::List::~List ()
35 {
36 delete textblocks;
37 delete values;
38 }
39
40 int AlignedTextblock::List::add(AlignedTextblock *textblock)
41 {
42 textblocks->increase ();
43 values->increase ();
44 textblocks->set (textblocks->size () - 1, textblock);
45 refCount++;
46 return textblocks->size () - 1;
47 }
48
49 void AlignedTextblock::List::unref(int pos)
50 {
51 assert (textblocks->get (pos) != NULL);
52 textblocks->set (pos, NULL);
53 refCount--;
54
55 if (refCount == 0)
56 delete this;
57 }
58
59 int AlignedTextblock::CLASS_ID = -1;
60
61 AlignedTextblock::AlignedTextblock (bool limitTextWidth):
62 Textblock (limitTextWidth)
63 {
64 registerName ("dw::AlignedTextblock", &CLASS_ID);
65 }
66
67 void AlignedTextblock::setRefTextblock (AlignedTextblock *ref)
68 {
69 if (ref == NULL)
70 list = new List();
71 else
72 list = ref->list;
73
74 listPos = list->add (this);
75 updateValue ();
76 }
77
78 AlignedTextblock::~AlignedTextblock()
79 {
80 list->unref (listPos);
81 }
82
83 void AlignedTextblock::updateValue ()
84 {
85 if (list) {
86 list->setValue (listPos, getValue ());
87
88 if (list->getValue (listPos) > list->getMaxValue ()) {
89 // New value greater than current maximum -> apply it to others.
90 list->setMaxValue (list->getValue (listPos));
91
92 for (int i = 0; i < list->size (); i++)
93 if (list->getTextblock (i))
94 list->getTextblock (i)->setMaxValue (list->getMaxValue (),
95 list->getValue (i));
96 } else {
97 /* No change, apply old max_value only to this page. */
98 setMaxValue (list->getMaxValue (), list->getValue (listPos));
99 }
100 }
101 }
102
103 } // namespace dw
0 #ifndef __DW_ALIGNEDTEXTBLOCK_HH__
1 #define __DW_ALIGNEDTEXTBLOCK_HH__
2
3 #include "core.hh"
4 #include "textblock.hh"
5
6 namespace dw {
7
8 /**
9 * \brief Base widget for all textblocks (sub classes of dw::Textblock), which
10 * are positioned vertically and aligned horizontally.
11 */
12 class AlignedTextblock: public Textblock
13 {
14 private:
15 class List
16 {
17 private:
18 lout::misc::SimpleVector <AlignedTextblock*> *textblocks;
19 lout::misc::SimpleVector <int> *values;
20 int maxValue, refCount;
21
22 ~List ();
23
24 public:
25 List ();
26 inline int add (AlignedTextblock *textblock);
27 void unref (int pos);
28
29 inline int getMaxValue () { return maxValue; }
30 inline void setMaxValue (int maxValue) { this->maxValue = maxValue; }
31
32 inline int size () { return textblocks->size (); }
33 inline AlignedTextblock *getTextblock (int pos) {
34 return textblocks->get (pos); }
35 inline int getValue (int pos) {return values->get (pos); }
36 inline void setValue (int pos, int value) {
37 return values->set (pos, value); }
38 };
39
40 List *list;
41 int listPos;
42
43 protected:
44 AlignedTextblock(bool limitTextWidth);
45
46 virtual int getValue () = 0;
47 virtual void setMaxValue (int maxValue, int value) = 0;
48
49 void setRefTextblock (AlignedTextblock *ref);
50 void updateValue ();
51
52 public:
53 static int CLASS_ID;
54
55 ~AlignedTextblock();
56 };
57
58 } // namespace dw
59
60 #endif // __DW_ALIGNEDTEXTBLOCK_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "bullet.hh"
22
23 #include <stdio.h>
24
25 namespace dw {
26
27 Bullet::Bullet ()
28 {
29 }
30
31 void Bullet::sizeRequestImpl (core::Requisition *requisition)
32 {
33 requisition->width = lout::misc::max (getStyle()->font->xHeight * 4 / 5, 1);
34 requisition->ascent = lout::misc::max (getStyle()->font->xHeight, 1);
35 requisition->descent = 0;
36 }
37
38 void Bullet::draw (core::View *view, core::Rectangle *area)
39 {
40 int x, y, l;
41 bool filled = true;
42
43 l = lout::misc::min (allocation.width, allocation.ascent);
44 x = allocation.x;
45 y = allocation.y + allocation.ascent - getStyle()->font->xHeight;
46
47 switch (getStyle()->listStyleType) {
48 case core::style::LIST_STYLE_TYPE_SQUARE:
49 view->drawRectangle (getStyle()->color,
50 core::style::Color::SHADING_NORMAL,
51 false, x, y, l, l);
52 break;
53 case core::style::LIST_STYLE_TYPE_CIRCLE:
54 filled = false;
55 // Fall Through
56 case core::style::LIST_STYLE_TYPE_DISC:
57 default:
58 view->drawArc (getStyle()->color, core::style::Color::SHADING_NORMAL,
59 filled, x + l/2, y + l/2, l, l, 0, 360);
60 }
61 }
62
63 core::Iterator *Bullet::iterator (core::Content::Type mask, bool atEnd)
64 {
65 //return new core::TextIterator (this, mask, atEnd, "*");
66 /** \bug Not implemented. */
67 return new core::EmptyIterator (this, mask, atEnd);
68 }
69
70 } // namespace dw
0 #ifndef __BULLET_HH__
1 #define __BULLET_HH__
2
3 #include "core.hh"
4
5 namespace dw {
6
7 /**
8 * \brief Displays different kind of bullets.
9 *
10 * Perhaps, in the future, Unicode characters are used for bullets, so this
11 * widget is not used anymore.
12 */
13 class Bullet: public core::Widget
14 {
15 protected:
16 void sizeRequestImpl (core::Requisition *requisition);
17 void draw (core::View *view, core::Rectangle *area);
18 core::Iterator *iterator (core::Content::Type mask, bool atEnd);
19
20 public:
21 Bullet ();
22 };
23
24 } // namespace dw
25
26 #endif // __BULLET_HH__
0 #ifndef __DW_CORE_HH__
1 #define __DW_CORE_HH__
2
3 #define __INCLUDED_FROM_DW_CORE_HH__
4
5 /**
6 * \brief Dw is in this namespace, or sub namespaces of this one.
7 *
8 * The core can be found in dw::core, widgets are defined directly here.
9 *
10 * \sa \ref dw-overview
11 */
12 namespace dw {
13
14 /**
15 * \brief The core of Dw is defined in this namespace.
16 *
17 * \sa \ref dw-overview
18 */
19 namespace core {
20
21 typedef unsigned char byte;
22
23 class Layout;
24 class View;
25 class Widget;
26 class Iterator;
27
28 namespace ui {
29
30 class ResourceFactory;
31
32 } // namespace ui
33
34
35 } // namespace dw
36 } // namespace core
37
38 #include "../lout/object.hh"
39 #include "../lout/container.hh"
40 #include "../lout/signal.hh"
41
42 #include "types.hh"
43 #include "events.hh"
44 #include "imgbuf.hh"
45 #include "style.hh"
46 #include "view.hh"
47 #include "platform.hh"
48 #include "iterator.hh"
49 #include "findtext.hh"
50 #include "selection.hh"
51 #include "layout.hh"
52 #include "widget.hh"
53 #include "ui.hh"
54
55 #undef __INCLUDED_FROM_DW_CORE_HH__
56
57 #endif // __DW_CORE_HH__
0 #ifndef __DW_EVENTS_HH__
1 #define __DW_EVENTS_HH__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 namespace dw {
8 namespace core {
9
10 /**
11 * \brief Platform independent representation.
12 */
13 enum ButtonState
14 {
15 /* We won't use more than these ones. */
16 SHIFT_MASK = 1 << 0,
17 CONTROL_MASK = 1 << 1,
18 META_MASK = 1 << 2,
19 BUTTON1_MASK = 1 << 3,
20 BUTTON2_MASK = 1 << 4,
21 BUTTON3_MASK = 1 << 5
22 };
23
24 /**
25 * \brief Base class for all events.
26 *
27 * The dw::core::Event hierarchy describes events in a platform independent
28 * way.
29 */
30 class Event: public lout::object::Object
31 {
32 public:
33 };
34
35 /**
36 * \brief Base class for all mouse events.
37 */
38 class MouseEvent: public Event
39 {
40 public:
41 ButtonState state;
42 };
43
44 /**
45 * \brief Base class for all mouse events related to a specific position.
46 */
47 class MousePositionEvent: public MouseEvent
48 {
49 public:
50 int xCanvas, yCanvas, xWidget, yWidget;
51 };
52
53 /**
54 * \brief Represents a button press or release event.
55 */
56 class EventButton: public MousePositionEvent
57 {
58 public:
59 int numPressed; /* 1 for simple click, 2 for double click, etc. */
60 int button;
61 };
62
63 /**
64 * \brief Represents a mouse motion event.
65 */
66 class EventMotion: public MousePositionEvent
67 {
68 };
69
70 /**
71 * \brief Represents a enter or leave notify event.
72 */
73 class EventCrossing: public MouseEvent
74 {
75 public:
76 Widget *lastWidget, *currentWidget;
77 };
78
79 } // namespace dw
80 } // namespace core
81
82 #endif // __DW_EVENTS_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "core.hh"
22 #include "../lout/msg.h"
23
24 namespace dw {
25 namespace core {
26
27 FindtextState::FindtextState ()
28 {
29 key = NULL;
30 nexttab = NULL;
31 widget = NULL;
32 iterator = NULL;
33 hlIterator = NULL;
34 }
35
36 FindtextState::~FindtextState ()
37 {
38 if (key)
39 free(key);
40 if (nexttab)
41 delete[] nexttab;
42 if (iterator)
43 delete iterator;
44 if (hlIterator)
45 delete hlIterator;
46 }
47
48 void FindtextState::setWidget (Widget *widget)
49 {
50 this->widget = widget;
51
52 // A widget change will restart the search.
53 if (key)
54 free(key);
55 key = NULL;
56 if (nexttab)
57 delete[] nexttab;
58 nexttab = NULL;
59
60 if (iterator)
61 delete iterator;
62 iterator = NULL;
63 if (hlIterator)
64 delete hlIterator;
65 hlIterator = NULL;
66 }
67
68 FindtextState::Result FindtextState::search (const char *key, bool caseSens,
69 bool backwards)
70 {
71 if (!widget || *key == 0) // empty keys are not found
72 return NOT_FOUND;
73
74 bool wasHighlighted = unhighlight ();
75 bool newKey;
76
77 // If the key (or the widget) changes (including case sensitivity),
78 // the search is started from the beginning.
79 if (this->key == NULL || this->caseSens != caseSens ||
80 strcmp (this->key, key) != 0) {
81 newKey = true;
82 if (this->key)
83 free(this->key);
84 this->key = strdup (key);
85 this->caseSens = caseSens;
86
87 if (nexttab)
88 delete[] nexttab;
89 nexttab = createNexttab (key, caseSens, backwards);
90
91 if (iterator)
92 delete iterator;
93 iterator = new CharIterator (widget);
94
95 if (backwards) {
96 /* Go to end */
97 while (iterator->next () ) ;
98 iterator->prev (); //We don't want to be at CharIterator::END.
99 } else {
100 iterator->next ();
101 }
102 } else
103 newKey = false;
104
105 bool firstTrial = !wasHighlighted || newKey;
106
107 if (search0 (backwards, firstTrial)) {
108 // Highlighting is done with a clone.
109 hlIterator = iterator->cloneCharIterator ();
110 for (int i = 0; key[i]; i++)
111 hlIterator->next ();
112 CharIterator::highlight (iterator, hlIterator, HIGHLIGHT_FINDTEXT);
113 CharIterator::scrollTo (iterator, hlIterator,
114 HPOS_INTO_VIEW, VPOS_CENTER);
115
116 // The search will continue from the word after the found position.
117 iterator->next ();
118 return SUCCESS;
119 } else {
120 if (firstTrial) {
121 return NOT_FOUND;
122 } else {
123 // Nothing found anymore, reset the state for the next trial.
124 delete iterator;
125 iterator = new CharIterator (widget);
126 if (backwards) {
127 /* Go to end */
128 while (iterator->next ()) ;
129 iterator->prev (); //We don't want to be at CharIterator::END.
130 } else {
131 iterator->next ();
132 }
133 // We expect a success.
134 Result result2 = search (key, caseSens, backwards);
135 assert (result2 == SUCCESS);
136 return RESTART;
137 }
138 }
139 }
140
141 /**
142 * \brief This method is called when the user closes the "find text" dialog.
143 */
144 void FindtextState::resetSearch ()
145 {
146 unhighlight ();
147
148 if (key)
149 free(key);
150 key = NULL;
151 }
152
153 /*
154 * Return a new string: with the reverse of the original.
155 */
156 const char* FindtextState::rev(const char *str)
157 {
158 if (!str)
159 return NULL;
160
161 int len = strlen(str);
162 char *nstr = new char[len+1];
163 for (int i = 0; i < len; ++i)
164 nstr[i] = str[len-1 -i];
165 nstr[len] = 0;
166
167 return nstr;
168 }
169
170 int *FindtextState::createNexttab (const char *needle, bool caseSens,
171 bool backwards)
172 {
173 const char* key;
174
175 key = (backwards) ? rev(needle) : needle;
176 int i = 0;
177 int j = -1;
178 int l = strlen (key);
179 int *nexttab = new int[l + 1]; // + 1 is necessary for l == 1 case
180 nexttab[0] = -1;
181
182 do {
183 if (j == -1 || charsEqual (key[i], key[j], caseSens)) {
184 i++;
185 j++;
186 nexttab[i] = j;
187 //_MSG ("nexttab[%d] = %d\n", i, j);
188 } else
189 j = nexttab[j];
190 } while (i < l - 1);
191
192 if (backwards)
193 delete [] key;
194
195 return nexttab;
196 }
197
198 /**
199 * \brief Unhighlight, and return whether a region was highlighted.
200 */
201 bool FindtextState::unhighlight ()
202 {
203 if (hlIterator) {
204 CharIterator *start = hlIterator->cloneCharIterator ();
205 for (int i = 0; key[i]; i++)
206 start->prev ();
207
208 CharIterator::unhighlight (start, hlIterator, HIGHLIGHT_FINDTEXT);
209 delete start;
210 delete hlIterator;
211 hlIterator = NULL;
212
213 return true;
214 } else
215 return false;
216 }
217
218 bool FindtextState::search0 (bool backwards, bool firstTrial)
219 {
220 if (iterator->getChar () == CharIterator::END)
221 return false;
222
223 bool ret = false;
224 const char* searchKey = (backwards) ? rev(key) : key;
225 int j = 0;
226 bool nextit = true;
227 int l = strlen (key);
228
229 if (backwards && !firstTrial) {
230 _MSG("Having to do.");
231 /* Position correctly */
232 /* In order to achieve good results (i.e: find a word that ends within
233 * the previously searched word's limit) we have to position the
234 * iterator in the semilast character of the previously searched word.
235 *
236 * Since we know that if a word was found before it was exactly the
237 * same word as the one we are searching for now, we can apply the
238 * following expression:
239 *
240 * Where l=length of the key and n=num of positions to move:
241 *
242 * n = l - 3
243 *
244 * If n is negative, we have to move backwards, but if it is
245 * positive, we have to move forward. So, when l>=4, we start moving
246 * the iterator forward. */
247
248 if (l==1) {
249 iterator->prev();
250 iterator->prev();
251 } else if (l==2) {
252 iterator->prev();
253 } else if (l>=4) {
254 for (int i=0; i<l-3; i++) {
255 iterator->next();
256 }
257 }
258
259 } else if (backwards && l==1) {
260 /* Particular case where we can't find the last character */
261 iterator->next();
262 }
263
264 do {
265 if (j == -1 || charsEqual (iterator->getChar(),searchKey[j],caseSens)) {
266 j++;
267 nextit = backwards ? iterator->prev () : iterator->next ();
268 } else
269 j = nexttab[j];
270 } while (nextit && j < l);
271
272 if (j >= l) {
273 if (backwards) {
274 //This is the location of the key
275 iterator->next();
276 } else {
277 // Go back to where the key was found.
278 for (int i = 0; i < l; i++)
279 iterator->prev ();
280 }
281 ret = true;
282 }
283
284 if (backwards)
285 delete [] searchKey;
286
287 return ret;
288 }
289
290 } // namespace dw
291 } // namespace core
0 #ifndef __DW_FINDTEXT_STATE_H__
1 #define __DW_FINDTEXT_STATE_H__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 #include <ctype.h>
8
9 namespace dw {
10 namespace core {
11
12 class FindtextState
13 {
14 public:
15 typedef enum {
16 /** \brief The next occurrence of the pattern has been found. */
17 SUCCESS,
18
19 /**
20 * \brief There is no further occurrence of the pattern, instead, the
21 * first occurrence has been selected.
22 */
23 RESTART,
24
25 /** \brief The patten does not at all occur in the text. */
26 NOT_FOUND
27 } Result;
28
29 private:
30 /**
31 * \brief The key used for the last search.
32 *
33 * If dw::core::Findtext::search is called with the same key, the search
34 * is continued, otherwise it is restarted.
35 */
36 char *key;
37
38 /** \brief Whether the last search was case sensitive. */
39 bool caseSens;
40
41 /** \brief The table used for KMP search. */
42 int *nexttab;
43
44 /** \brief The top of the widget tree, in which the search is done.
45 *
46 * From this, the iterator will be constructed. Set by
47 * dw::core::Findtext::widget
48 */
49 Widget *widget;
50
51 /** \brief The position from where the next search will start. */
52 CharIterator *iterator;
53
54 /**
55 * \brief The position from where the characters are highlighted.
56 *
57 * NULL, when no text is highlighted.
58 */
59 CharIterator *hlIterator;
60
61 static const char* rev(const char* _str); /* reverse a C string */
62
63 static int *createNexttab (const char *needle,bool caseSens,bool backwards);
64 bool unhighlight ();
65 bool search0 (bool backwards, bool firstTrial);
66
67 inline static bool charsEqual (char c1, char c2, bool caseSens)
68 { return caseSens ? c1 == c2 : tolower (c1) == tolower (c2) ||
69 (isspace (c1) && isspace (c2)); }
70
71 public:
72 FindtextState ();
73 ~FindtextState ();
74
75 void setWidget (Widget *widget);
76 Result search (const char *key, bool caseSens, bool backwards);
77 void resetSearch ();
78 };
79
80 } // namespace dw
81 } // namespace core
82
83 #endif // __DW_FINDTEXT_STATE_H__
0 // fltkcomplexbutton.cc contains code from FLTK 1.3's src/Fl_Button.cxx
1 // that is Copyright 1998-2010 by Bill Spitzak and others.
2
3 /*
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program 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
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <FL/Fl.H>
19 #include <FL/Fl_Button.H>
20 #include <FL/Fl_Group.H>
21 #include <FL/Fl_Window.H>
22
23 #include "fltkcomplexbutton.hh"
24
25 using namespace dw::fltk::ui;
26
27 /**
28 Sets the current value of the button.
29 A non-zero value sets the button to 1 (ON), and zero sets it to 0 (OFF).
30 \param[in] v button value.
31 */
32 int ComplexButton::value(int v) {
33 v = v ? 1 : 0;
34 oldval = v;
35 clear_changed();
36 if (value_ != v) {
37 value_ = v;
38 if (box()) redraw();
39 return 1;
40 } else {
41 return 0;
42 }
43 }
44
45 void ComplexButton::draw() {
46 Fl_Color col = value() ? selection_color() : color();
47 draw_box(value() ? (down_box()?down_box():fl_down(box())) : box(), col);
48 if (Fl::focus() == this) draw_focus();
49
50 // ComplexButton is a Group; draw its children
51 for (int i = children () - 1; i >= 0; i--) {
52 // set absolute coordinates for fltk-1.3 --jcid
53 child (i)->position(x()+(w()-child(i)->w())/2,y()+(h()-child(i)->h())/2);
54 draw_child (*child (i));
55 }
56 }
57
58 int ComplexButton::handle(int event) {
59 int newval;
60 switch (event) {
61 case FL_ENTER: /* FALLTHROUGH */
62 case FL_LEAVE:
63 return 1;
64 case FL_PUSH:
65 if (Fl::visible_focus() && handle(FL_FOCUS)) Fl::focus(this);
66 case FL_DRAG:
67 if (Fl::event_inside(this)) {
68 newval = !oldval;
69 } else
70 {
71 clear_changed();
72 newval = oldval;
73 }
74 if (newval != value_) {
75 value_ = newval;
76 set_changed();
77 redraw();
78 if (when() & FL_WHEN_CHANGED) do_callback();
79 }
80 return 1;
81 case FL_RELEASE:
82 if (value_ == oldval) {
83 if (when() & FL_WHEN_NOT_CHANGED) do_callback();
84 return 1;
85 }
86 set_changed();
87 value(oldval);
88 set_changed();
89 if (when() & FL_WHEN_CHANGED) {
90 Fl_Widget_Tracker wp(this);
91 do_callback();
92 if (wp.deleted()) return 1;
93 }
94 if (when() & FL_WHEN_RELEASE) do_callback();
95 return 1;
96 case FL_FOCUS : /* FALLTHROUGH */
97 case FL_UNFOCUS :
98 if (Fl::visible_focus()) {
99 if (box() == FL_NO_BOX) {
100 // Widgets with the FL_NO_BOX boxtype need a parent to
101 // redraw, since it is responsible for redrawing the
102 // background...
103 int X = x() > 0 ? x() - 1 : 0;
104 int Y = y() > 0 ? y() - 1 : 0;
105 if (window()) window()->damage(FL_DAMAGE_ALL, X, Y, w() + 2, h() + 2);
106 } else redraw();
107 return 1;
108 } else return 0;
109 case FL_KEYBOARD :
110 if (Fl::focus() == this && Fl::event_key() == ' ' &&
111 !(Fl::event_state() & (FL_SHIFT | FL_CTRL | FL_ALT | FL_META))) {
112 set_changed();
113 Fl_Widget_Tracker wp(this);
114 if (wp.deleted()) return 1;
115 if (when() & FL_WHEN_RELEASE) do_callback();
116 return 1;
117 }
118 default:
119 return 0;
120 }
121 }
122
123 /**
124 The constructor creates the button using the given position, size and label.
125 \param[in] X, Y, W, H position and size of the widget
126 \param[in] L widget label, default is no label
127 */
128 ComplexButton::ComplexButton(int X, int Y, int W, int H, const char *L)
129 : Fl_Group(X,Y,W,H,L) {
130 Fl_Group::current(0);
131 box(FL_UP_BOX);
132 down_box(FL_NO_BOX);
133 value_ = oldval = 0;
134 }
135
136 ComplexButton::~ComplexButton() {
137 /*
138 * The Fl_Group destructor clear()s the children, but layout expects
139 * the flat view to be around until it deletes it.
140 */
141 remove(0);
142 }
0
1 // fltkcomplexbutton.hh contains code from FLTK 1.3's FL/Fl_Button.H
2 // that is Copyright 1998-2010 by Bill Spitzak and others.
3
4 /*
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifndef __FLTK_COMPLEX_BUTTON_HH__
20 #define __FLTK_COMPLEX_BUTTON_HH__
21
22 #include <FL/Fl_Group.H>
23
24 extern FL_EXPORT Fl_Shortcut fl_old_shortcut(const char*);
25
26 namespace dw {
27 namespace fltk {
28 namespace ui {
29
30 class FL_EXPORT ComplexButton : public Fl_Group {
31
32 int shortcut_;
33 char value_;
34 char oldval;
35 uchar down_box_;
36
37 protected:
38 virtual void draw();
39
40 public:
41 virtual int handle(int);
42
43 ComplexButton(int X, int Y, int W, int H, const char *L = 0);
44 ~ComplexButton();
45
46 int value(int v);
47
48 /**
49 Returns the current value of the button (0 or 1).
50 */
51 char value() const {return value_;}
52
53 /**
54 Returns the current down box type, which is drawn when value() is non-zero.
55 \retval Fl_Boxtype
56 */
57 Fl_Boxtype down_box() const {return (Fl_Boxtype)down_box_;}
58
59 /**
60 Sets the down box type. The default value of 0 causes FLTK to figure out
61 the correct matching down version of box().
62 \param[in] b down box type
63 */
64 void down_box(Fl_Boxtype b) {down_box_ = b;}
65 };
66
67 } // namespace ui
68 } // namespace fltk
69 } // namespace dw
70
71 #endif
72
73 //
74 //
0 #ifndef __DW_FLTK_CORE_HH__
1 #define __DW_FLTK_CORE_HH__
2
3 #define __INCLUDED_FROM_DW_FLTK_CORE_HH__
4
5 namespace dw {
6 namespace fltk {
7 namespace ui {
8
9 class FltkResource;
10
11 } // namespace ui
12 } // namespace fltk
13 } // namespace core
14
15 #include <FL/Fl_Widget.H>
16
17 #include "core.hh"
18 #include "fltkimgbuf.hh"
19 #include "fltkplatform.hh"
20 #include "fltkui.hh"
21
22 #undef __INCLUDED_FROM_DW_FLTK_CORE_HH__
23
24 #endif // __DW_FLTK_CORE_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "fltkflatview.hh"
22
23 #include <stdio.h>
24
25 using namespace lout::container::typed;
26
27 namespace dw {
28 namespace fltk {
29
30 FltkFlatView::FltkFlatView (int x, int y, int w, int h, const char *label):
31 FltkWidgetView (x, y, w, h, label)
32 {
33 }
34
35 FltkFlatView::~FltkFlatView ()
36 {
37 }
38
39 void FltkFlatView::setCanvasSize (int width, int ascent, int descent)
40 {
41 /**
42 * \bug It has to be clarified, who is responsible for setting the
43 * FLTK widget size. In the only used context (complex buttons),
44 * it is done elsewhere.
45 */
46
47 #if 0
48 FltkWidgetView::setCanvasSize (width, ascent, descent);
49
50 w (width);
51 h (ascent + descent);
52 #endif
53 }
54
55 bool FltkFlatView::usesViewport ()
56 {
57 return false;
58 }
59
60 int FltkFlatView::getHScrollbarThickness ()
61 {
62 return 0;
63 }
64
65 int FltkFlatView::getVScrollbarThickness ()
66 {
67 return 0;
68 }
69
70 void FltkFlatView::scrollTo (int x, int y)
71 {
72 }
73
74 void FltkFlatView::setViewportSize (int width, int height,
75 int hScrollbarThickness,
76 int vScrollbarThickness)
77 {
78 }
79
80 int FltkFlatView::translateViewXToCanvasX (int X)
81 {
82 return X - x ();
83 }
84
85 int FltkFlatView::translateViewYToCanvasY (int Y)
86 {
87 return Y - y ();
88 }
89
90 int FltkFlatView::translateCanvasXToViewX (int X)
91 {
92 return X + x ();
93 }
94
95 int FltkFlatView::translateCanvasYToViewY (int Y)
96 {
97 return Y + y ();
98 }
99
100
101 } // namespace fltk
102 } // namespace dw
0 #ifndef __DW_FLTKFLATVIEW_HH__
1 #define __DW_FLTKFLATVIEW_HH__
2
3 #include "core.hh"
4 #include "fltkcore.hh"
5 #include "fltkviewbase.hh"
6
7 namespace dw {
8 namespace fltk {
9
10 class FltkFlatView: public FltkWidgetView
11 {
12 protected:
13 int translateViewXToCanvasX (int x);
14 int translateViewYToCanvasY (int y);
15 int translateCanvasXToViewX (int x);
16 int translateCanvasYToViewY (int y);
17
18 public:
19 FltkFlatView (int x, int y, int w, int h, const char *label = 0);
20 ~FltkFlatView ();
21
22 void setCanvasSize (int width, int ascent, int descent);
23
24 bool usesViewport ();
25 int getHScrollbarThickness ();
26 int getVScrollbarThickness ();
27 void scrollTo (int x, int y);
28 void setViewportSize (int width, int height,
29 int hScrollbarThickness, int vScrollbarThickness);
30 };
31
32 } // namespace fltk
33 } // namespace dw
34
35 #endif // __DW_FLTKFLATVIEW_HH__
36
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "fltkcore.hh"
22 #include "../lout/msg.h"
23 #include "../lout/misc.hh"
24
25 #include <FL/fl_draw.H>
26
27 #define IMAGE_MAX_AREA (6000 * 6000)
28
29 namespace dw {
30 namespace fltk {
31
32 using namespace lout::container::typed;
33
34 FltkImgbuf::FltkImgbuf (Type type, int width, int height)
35 {
36 _MSG("FltkImgbuf: new root %p\n", this);
37 init (type, width, height, NULL);
38 }
39
40 FltkImgbuf::FltkImgbuf (Type type, int width, int height, FltkImgbuf *root)
41 {
42 _MSG("FltkImgbuf: new scaled %p, root is %p\n", this, root);
43 init (type, width, height, root);
44 }
45
46 void FltkImgbuf::init (Type type, int width, int height, FltkImgbuf *root)
47 {
48 this->root = root;
49 this->type = type;
50 this->width = width;
51 this->height = height;
52
53 // TODO: Maybe this is only for root buffers
54 switch (type) {
55 case RGBA: bpp = 4; break;
56 case RGB: bpp = 3; break;
57 default: bpp = 1; break;
58 }
59 _MSG("FltkImgbuf::init width=%d height=%d bpp=%d\n", width, height, bpp);
60 rawdata = new uchar[bpp * width * height];
61 // Set light-gray as interim background color.
62 memset(rawdata, 222, width*height*bpp);
63
64 refCount = 1;
65 deleteOnUnref = true;
66 copiedRows = new lout::misc::BitSet (height);
67
68 // The list is only used for root buffers.
69 if (isRoot())
70 scaledBuffers = new lout::container::typed::List <FltkImgbuf> (true);
71 else
72 scaledBuffers = NULL;
73
74 if (!isRoot()) {
75 // Scaling
76 for (int row = 0; row < root->height; row++) {
77 if (root->copiedRows->get (row))
78 scaleRow (row, root->rawdata + row*root->width*root->bpp);
79 }
80 }
81 }
82
83 FltkImgbuf::~FltkImgbuf ()
84 {
85 _MSG("~FltkImgbuf[%s %p] deleted\n", isRoot() ? "root":"scaled", this);
86
87 if (!isRoot())
88 root->detachScaledBuf (this);
89
90 delete[] rawdata;
91 delete copiedRows;
92
93 if (scaledBuffers)
94 delete scaledBuffers;
95 }
96
97 /**
98 * \brief This method is called for the root buffer, when a scaled buffer
99 * removed.
100 */
101 void FltkImgbuf::detachScaledBuf (FltkImgbuf *scaledBuf)
102 {
103 scaledBuffers->detachRef (scaledBuf);
104
105 _MSG("FltkImgbuf[root %p]: scaled buffer %p is detached, %d left\n",
106 this, scaledBuf, scaledBuffers->size ());
107
108 if (refCount == 0 && scaledBuffers->isEmpty () && deleteOnUnref)
109 // If the root buffer is not used anymore, but this is the last scaled
110 // buffer.
111 // See also: FltkImgbuf::unref().
112 delete this;
113 }
114
115 void FltkImgbuf::setCMap (int *colors, int num_colors)
116 {
117 }
118
119 inline void FltkImgbuf::scaleRow (int row, const core::byte *data)
120 {
121 int sr1 = scaledY (row);
122 int sr2 = scaledY (row + 1);
123
124 for (int sr = sr1; sr < sr2; sr++) {
125 // Avoid multiple passes.
126 if (copiedRows->get(sr)) continue;
127
128 copiedRows->set (sr, true);
129 if (sr == sr1) {
130 for (int px = 0; px < root->width; px++) {
131 int px1 = px * width / root->width;
132 int px2 = (px+1) * width / root->width;
133 for (int sp = px1; sp < px2; sp++) {
134 memcpy(rawdata + (sr*width + sp)*bpp, data + px*bpp, bpp);
135 }
136 }
137 } else {
138 memcpy(rawdata + sr*width*bpp, rawdata + sr1*width*bpp, width*bpp);
139 }
140 }
141 }
142
143 void FltkImgbuf::copyRow (int row, const core::byte *data)
144 {
145 assert (isRoot());
146
147 // Flag the row done and copy its data.
148 copiedRows->set (row, true);
149 memcpy(rawdata + row * width * bpp, data, width * bpp);
150
151 // Update all the scaled buffers of this root image.
152 for (Iterator <FltkImgbuf> it = scaledBuffers->iterator(); it.hasNext(); ) {
153 FltkImgbuf *sb = it.getNext ();
154 sb->scaleRow(row, data);
155 }
156 }
157
158 void FltkImgbuf::newScan ()
159 {
160 if (isRoot()) {
161 for (Iterator<FltkImgbuf> it = scaledBuffers->iterator(); it.hasNext();){
162 FltkImgbuf *sb = it.getNext ();
163 sb->copiedRows->clear();
164 }
165 }
166 }
167
168 core::Imgbuf* FltkImgbuf::getScaledBuf (int width, int height)
169 {
170 if (!isRoot())
171 return root->getScaledBuf (width, height);
172
173 if (width == this->width && height == this->height) {
174 ref ();
175 return this;
176 }
177
178 for (Iterator <FltkImgbuf> it = scaledBuffers->iterator(); it.hasNext(); ) {
179 FltkImgbuf *sb = it.getNext ();
180 if (sb->width == width && sb->height == height) {
181 sb->ref ();
182 return sb;
183 }
184 }
185
186 /* Check for excessive image sizes which would cause crashes due to
187 * too big allocations for the image buffer.
188 * In this case we return a pointer to the unscaled image buffer.
189 */
190 if (width <= 0 || height <= 0 ||
191 width > IMAGE_MAX_AREA / height) {
192 MSG("FltkImgbuf::getScaledBuf: suspicious image size request %d x %d\n",
193 width, height);
194 ref ();
195 return this;
196 }
197
198 /* This size is not yet used, so a new buffer has to be created. */
199 FltkImgbuf *sb = new FltkImgbuf (type, width, height, this);
200 scaledBuffers->append (sb);
201 return sb;
202 }
203
204 void FltkImgbuf::getRowArea (int row, dw::core::Rectangle *area)
205 {
206 // TODO: May have to be adjusted.
207
208 if (isRoot()) {
209 /* root buffer */
210 area->x = 0;
211 area->y = row;
212 area->width = width;
213 area->height = 1;
214 _MSG("::getRowArea: area x=%d y=%d width=%d height=%d\n",
215 area->x, area->y, area->width, area->height);
216 } else {
217 // scaled buffer
218 int sr1 = scaledY (row);
219 int sr2 = scaledY (row + 1);
220
221 area->x = 0;
222 area->y = sr1;
223 area->width = width;
224 area->height = sr2 - sr1;
225 _MSG("::getRowArea: area x=%d y=%d width=%d height=%d\n",
226 area->x, area->y, area->width, area->height);
227 }
228 }
229
230 int FltkImgbuf::getRootWidth ()
231 {
232 return root ? root->width : width;
233 }
234
235 int FltkImgbuf::getRootHeight ()
236 {
237 return root ? root->height : height;
238 }
239
240 void FltkImgbuf::ref ()
241 {
242 refCount++;
243
244 //if (root)
245 // MSG("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n",
246 // this, root, refCount);
247 //else
248 // MSG("FltkImgbuf[root %p]: ref() => %d\n", this, refCount);
249 }
250
251 void FltkImgbuf::unref ()
252 {
253 //if (root)
254 // MSG("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n",
255 // this, root, refCount - 1);
256 //else
257 // MSG("FltkImgbuf[root %p]: ref() => %d\n", this, refCount - 1);
258
259 if (--refCount == 0) {
260 if (isRoot ()) {
261 // Root buffer, it must be ensured that no scaled buffers are left.
262 // See also FltkImgbuf::detachScaledBuf().
263 if (scaledBuffers->isEmpty () && deleteOnUnref) {
264 delete this;
265 } else {
266 _MSG("FltkImgbuf[root %p]: not deleted. numScaled=%d\n",
267 this, scaledBuffers->size ());
268 }
269 } else
270 // Scaled buffer buffer, simply delete it.
271 delete this;
272 }
273 }
274
275 bool FltkImgbuf::lastReference ()
276 {
277 return refCount == 1 &&
278 (scaledBuffers == NULL || scaledBuffers->isEmpty ());
279 }
280
281 void FltkImgbuf::setDeleteOnUnref (bool deleteOnUnref)
282 {
283 assert (isRoot ());
284 this->deleteOnUnref = deleteOnUnref;
285 }
286
287 bool FltkImgbuf::isReferred ()
288 {
289 return refCount != 0 ||
290 (scaledBuffers != NULL && !scaledBuffers->isEmpty ());
291 }
292
293
294 int FltkImgbuf::scaledY(int ySrc)
295 {
296 // TODO: May have to be adjusted.
297 assert (root != NULL);
298 return ySrc * height / root->height;
299 }
300
301 void FltkImgbuf::draw (Fl_Widget *target, int xRoot, int yRoot,
302 int x, int y, int width, int height)
303 {
304 // TODO: Clarify the question, whether "target" is the current widget
305 // (and so has not to be passed at all).
306
307 _MSG("::draw: xRoot=%d x=%d yRoot=%d y=%d width=%d height=%d\n"
308 " this->width=%d this->height=%d\n",
309 xRoot, x, yRoot, y, width, height, this->width, this->height);
310
311 if (x > this->width || y > this->height) {
312 return;
313 }
314
315 if (x + width > this->width) {
316 width = this->width - x;
317 }
318
319 if (y + height > this->height) {
320 height = this->height - y;
321 }
322
323 fl_draw_image(rawdata+bpp*(y*this->width + x), xRoot + x, yRoot + y, width,
324 height, bpp, this->width * bpp);
325
326 }
327
328 } // namespace dw
329 } // namespace fltk
0 #ifndef __DW_FLTKIMGBUF_HH__
1 #define __DW_FLTKIMGBUF_HH__
2
3 #ifndef __INCLUDED_FROM_DW_FLTK_CORE_HH__
4 # error Do not include this file directly, use "fltkcore.hh" instead.
5 #endif
6
7 namespace dw {
8 namespace fltk {
9
10 class FltkImgbuf: public core::Imgbuf
11 {
12 private:
13 FltkImgbuf *root;
14 int refCount;
15 bool deleteOnUnref;
16 lout::container::typed::List <FltkImgbuf> *scaledBuffers;
17
18 int width, height;
19 Type type;
20
21 //{
22 int bpp;
23 uchar *rawdata;
24 //}
25
26 // This is just for testing drawing, it has to be replaced by
27 // the image buffer.
28 lout::misc::BitSet *copiedRows;
29
30 FltkImgbuf (Type type, int width, int height, FltkImgbuf *root);
31 void init (Type type, int width, int height, FltkImgbuf *root);
32 int scaledY(int ySrc);
33 int isRoot() { return (root == NULL); }
34 void detachScaledBuf (FltkImgbuf *scaledBuf);
35
36 protected:
37 ~FltkImgbuf ();
38
39 public:
40 FltkImgbuf (Type type, int width, int height);
41
42 void setCMap (int *colors, int num_colors);
43 inline void scaleRow (int row, const core::byte *data);
44 void newScan ();
45 void copyRow (int row, const core::byte *data);
46 core::Imgbuf* getScaledBuf (int width, int height);
47 void getRowArea (int row, dw::core::Rectangle *area);
48 int getRootWidth ();
49 int getRootHeight ();
50 void ref ();
51 void unref ();
52
53 bool lastReference ();
54 void setDeleteOnUnref (bool deleteOnUnref);
55 bool isReferred ();
56
57 void draw (Fl_Widget *target, int xRoot, int yRoot,
58 int x, int y, int width, int height);
59 };
60
61 } // namespace dw
62 } // namespace fltk
63
64 #endif // __DW_FLTK_IMGBUF_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20 #include "../lout/msg.h"
21 #include "fltkmisc.hh"
22
23 #include <FL/Fl.H>
24 #include <stdio.h>
25
26 namespace dw {
27 namespace fltk {
28 namespace misc {
29
30 int screenWidth ()
31 {
32 return Fl::w ();
33 }
34
35 int screenHeight ()
36 {
37 return Fl::h ();
38 }
39
40 void warpPointer (int x, int y)
41 {
42 MSG_ERR("no warpPointer mechanism available.\n");
43 }
44
45 } // namespace misc
46 } // namespace fltk
47 } // namespace dw
0 #ifndef __FLTKMISC_HH__
1 #define __FLTKMISC_HH__
2
3 namespace dw {
4 namespace fltk {
5
6 /**
7 * \brief Miscellaneous FLTK stuff.
8 */
9 namespace misc {
10
11 int screenWidth ();
12 int screenHeight ();
13
14 void warpPointer (int x, int y);
15
16 } // namespace misc
17 } // namespace fltk
18 } // namespace dw
19
20
21 #endif // __FLTKMISC_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdio.h>
20
21 #include "../lout/msg.h"
22 #include "fltkcore.hh"
23
24 #include <FL/fl_draw.H>
25 #include <FL/Fl_Box.H>
26 #include <FL/Fl_Tooltip.H>
27 #include <FL/Fl_Menu_Window.H>
28 #include <FL/Fl_Paged_Device.H>
29
30 /*
31 * Local data
32 */
33
34 /* Use of Fl_Text_Display links in a lot of printer code that we don't have
35 * any need for currently. This stub prevents that. */
36 class FL_EXPORT Fl_Printer : public Fl_Paged_Device {
37 public:
38 static const char *class_id;
39 Fl_Printer(void) {};
40 };
41 const char *Fl_Printer::class_id = "Fl_Printer";
42
43 /* Tooltips */
44 static Fl_Menu_Window *tt_window = NULL;
45 static int in_tooltip = 0, req_tooltip = 0;
46
47 namespace dw {
48 namespace fltk {
49
50 using namespace lout;
51
52 /**
53 * \todo Distinction between italics and oblique would be nice.
54 */
55
56 container::typed::HashTable <dw::core::style::FontAttrs,
57 FltkFont> *FltkFont::fontsTable =
58 new container::typed::HashTable <dw::core::style::FontAttrs,
59 FltkFont> (false, false);
60
61 container::typed::HashTable <lout::object::ConstString,
62 FltkFont::FontFamily> *FltkFont::systemFonts =
63 NULL;
64
65 FltkFont::FontFamily FltkFont::standardFontFamily (FL_HELVETICA,
66 FL_HELVETICA_BOLD,
67 FL_HELVETICA_ITALIC,
68 FL_HELVETICA_BOLD_ITALIC);
69
70 FltkFont::FontFamily::FontFamily (Fl_Font fontNormal, Fl_Font fontBold,
71 Fl_Font fontItalic, Fl_Font fontBoldItalic)
72 {
73 font[0] = fontNormal;
74 font[1] = fontBold;
75 font[2] = fontItalic;
76 font[3] = fontBoldItalic;
77 }
78
79 void FltkFont::FontFamily::set (Fl_Font f, int attrs)
80 {
81 int idx = 0;
82 if (attrs & FL_BOLD)
83 idx += 1;
84 if (attrs & FL_ITALIC)
85 idx += 2;
86 font[idx] = f;
87 }
88
89 Fl_Font FltkFont::FontFamily::get (int attrs)
90 {
91 int idx = 0;
92 if (attrs & FL_BOLD)
93 idx += 1;
94 if (attrs & FL_ITALIC)
95 idx += 2;
96
97 // should the desired font style not exist, we
98 // return the normal font of the fontFamily
99 return font[idx] >= 0 ? font[idx] : font[0];
100 }
101
102
103
104 FltkFont::FltkFont (core::style::FontAttrs *attrs)
105 {
106 if (!systemFonts)
107 initSystemFonts ();
108
109 copyAttrs (attrs);
110
111 int fa = 0;
112 if (weight >= 500)
113 fa |= FL_BOLD;
114 if (style != core::style::FONT_STYLE_NORMAL)
115 fa |= FL_ITALIC;
116
117 object::ConstString nameString (name);
118 FontFamily *family = systemFonts->get (&nameString);
119 if (!family)
120 family = &standardFontFamily;
121
122 font = family->get (fa);
123
124 fl_font(font, size);
125 spaceWidth = misc::max(0, (int)fl_width(' ') + letterSpacing);
126 int xx, xy, xw, xh;
127 fl_text_extents("x", xx, xy, xw, xh);
128 xHeight = xh;
129 descent = fl_descent();
130 ascent = fl_height() - descent;
131 }
132
133 FltkFont::~FltkFont ()
134 {
135 fontsTable->remove (this);
136 }
137
138 static void strstrip(char *big, const char *little)
139 {
140 if (strlen(big) >= strlen(little) &&
141 strcasecmp(big + strlen(big) - strlen(little), little) == 0)
142 *(big + strlen(big) - strlen(little)) = '\0';
143 }
144
145 void FltkFont::initSystemFonts ()
146 {
147 systemFonts = new container::typed::HashTable
148 <lout::object::ConstString, FontFamily> (true, true);
149
150 int k = Fl::set_fonts ("-*-iso10646-1");
151 for (int i = 0; i < k; i++) {
152 int t;
153 char *name = strdup (Fl::get_font_name ((Fl_Font) i, &t));
154
155 // normalize font family names (strip off "bold", "italic")
156 if (t & FL_ITALIC)
157 strstrip(name, " italic");
158 if (t & FL_BOLD)
159 strstrip(name, " bold");
160
161 MSG("Found font: %s%s%s\n", name, t & FL_BOLD ? " bold" : "",
162 t & FL_ITALIC ? " italic" : "");
163
164 object::String *familyName = new object::String(name);
165 free (name);
166 FontFamily *family = systemFonts->get (familyName);
167
168 if (family) {
169 family->set ((Fl_Font) i, t);
170 delete familyName;
171 } else {
172 // set first font of family also as normal font in case there
173 // is no normal (non-bold, non-italic) font
174 family = new FontFamily ((Fl_Font) i, -1, -1, -1);
175 family->set ((Fl_Font) i, t);
176 systemFonts->put (familyName, family);
177 }
178 }
179 }
180
181 bool
182 FltkFont::fontExists (const char *name)
183 {
184 if (!systemFonts)
185 initSystemFonts ();
186 object::ConstString familyName (name);
187 return systemFonts->get (&familyName) != NULL;
188 }
189
190 Fl_Font
191 FltkFont::get (const char *name, int attrs)
192 {
193 if (!systemFonts)
194 initSystemFonts ();
195 object::ConstString familyName (name);
196 FontFamily *family = systemFonts->get (&familyName);
197 if (family)
198 return family->get (attrs);
199 else
200 return FL_HELVETICA;
201 }
202
203 bool
204 FltkPlatform::fontExists (const char *name)
205 {
206 return FltkFont::fontExists (name);
207 }
208
209 FltkFont*
210 FltkFont::create (core::style::FontAttrs *attrs)
211 {
212 FltkFont *font = fontsTable->get (attrs);
213
214 if (font == NULL) {
215 font = new FltkFont (attrs);
216 fontsTable->put (font, font);
217 }
218
219 return font;
220 }
221
222 container::typed::HashTable <dw::core::style::ColorAttrs,
223 FltkColor>
224 *FltkColor::colorsTable =
225 new container::typed::HashTable <dw::core::style::ColorAttrs,
226 FltkColor> (false, false);
227
228 FltkColor::FltkColor (int color): Color (color)
229 {
230 this->color = color;
231
232 colors[SHADING_NORMAL] = shadeColor (color, SHADING_NORMAL) << 8;
233 colors[SHADING_INVERSE] = shadeColor (color, SHADING_INVERSE) << 8;
234 colors[SHADING_DARK] = shadeColor (color, SHADING_DARK) << 8;
235 colors[SHADING_LIGHT] = shadeColor (color, SHADING_LIGHT) << 8;
236 }
237
238 FltkColor::~FltkColor ()
239 {
240 colorsTable->remove (this);
241 }
242
243 FltkColor * FltkColor::create (int col)
244 {
245 ColorAttrs attrs(col);
246 FltkColor *color = colorsTable->get (&attrs);
247
248 if (color == NULL) {
249 color = new FltkColor (col);
250 colorsTable->put (color, color);
251 }
252
253 return color;
254 }
255
256 FltkTooltip::FltkTooltip (const char *text) : Tooltip(text)
257 {
258 }
259
260 FltkTooltip::~FltkTooltip ()
261 {
262 if (in_tooltip || req_tooltip)
263 cancel(); /* cancel tooltip window */
264 }
265
266 FltkTooltip *FltkTooltip::create (const char *text)
267 {
268 return new FltkTooltip(text);
269 }
270
271 /*
272 * Tooltip callback: used to delay it a bit
273 * INVARIANT: Only one instance of this function is requested.
274 */
275 static void tooltip_tcb(void *data)
276 {
277 req_tooltip = 2;
278 ((FltkTooltip *)data)->onEnter();
279 req_tooltip = 0;
280 }
281
282 void FltkTooltip::onEnter()
283 {
284 _MSG("FltkTooltip::onEnter\n");
285 if (!str || !*str)
286 return;
287 if (req_tooltip == 0) {
288 Fl::remove_timeout(tooltip_tcb);
289 Fl::add_timeout(1.0, tooltip_tcb, this);
290 req_tooltip = 1;
291 return;
292 }
293
294 if (!tt_window) {
295 tt_window = new Fl_Menu_Window(0,0,100,24);
296 tt_window->set_override();
297 tt_window->box(FL_NO_BOX);
298 Fl_Box *b = new Fl_Box(0,0,100,24);
299 b->box(FL_BORDER_BOX);
300 b->color(fl_color_cube(FL_NUM_RED-1, FL_NUM_GREEN-1, FL_NUM_BLUE-2));
301 b->labelfont(FL_HELVETICA);
302 b->labelsize(14);
303 b->align(FL_ALIGN_WRAP|FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
304 tt_window->resizable(b);
305 tt_window->end();
306 }
307
308 /* prepare tooltip window */
309 int x, y;
310 Fl_Box *box = (Fl_Box*)tt_window->child(0);
311 box->label(str);
312 Fl::get_mouse(x,y); y += 6;
313 /* calculate window size */
314 int ww, hh;
315 ww = 800; // max width;
316 box->measure_label(ww, hh);
317 ww += 6 + 2 * Fl::box_dx(box->box());
318 hh += 6 + 2 * Fl::box_dy(box->box());
319 tt_window->resize(x,y,ww,hh);
320 tt_window->show();
321 in_tooltip = 1;
322 }
323
324 /*
325 * Leaving the widget cancels the tooltip
326 */
327 void FltkTooltip::onLeave()
328 {
329 _MSG(" FltkTooltip::onLeave in_tooltip=%d\n", in_tooltip);
330 cancel();
331 }
332
333 void FltkPlatform::cancelTooltip()
334 {
335 FltkTooltip::cancel();
336 }
337
338 /*
339 * Remove a shown tooltip or cancel a pending one
340 */
341 void FltkTooltip::cancel()
342 {
343 if (req_tooltip) {
344 Fl::remove_timeout(tooltip_tcb);
345 req_tooltip = 0;
346 }
347 if (!in_tooltip) return;
348 in_tooltip = 0;
349 tt_window->hide();
350
351 /* WORKAROUND: (Black magic here)
352 * Hiding a tooltip with the keyboard or mousewheel doesn't work.
353 * The code below "fixes" the problem */
354 Fl_Widget *widget = Fl::belowmouse();
355 if (widget && widget->window()) {
356 widget->window()->damage(FL_DAMAGE_EXPOSE,0,0,1,1);
357 }
358 }
359
360 void FltkTooltip::onMotion()
361 {
362 }
363
364 void FltkView::addFltkWidget (Fl_Widget *widget,
365 core::Allocation *allocation)
366 {
367 }
368
369 void FltkView::removeFltkWidget (Fl_Widget *widget)
370 {
371 }
372
373 void FltkView::allocateFltkWidget (Fl_Widget *widget,
374 core::Allocation *allocation)
375 {
376 }
377
378 void FltkView::drawFltkWidget (Fl_Widget *widget, core::Rectangle *area)
379 {
380 }
381
382
383 core::ui::LabelButtonResource *
384 FltkPlatform::FltkResourceFactory::createLabelButtonResource (const char
385 *label)
386 {
387 return new ui::FltkLabelButtonResource (platform, label);
388 }
389
390 core::ui::ComplexButtonResource *
391 FltkPlatform::FltkResourceFactory::createComplexButtonResource (core::Widget
392 *widget,
393 bool relief)
394 {
395 return new ui::FltkComplexButtonResource (platform, widget, relief);
396 }
397
398 core::ui::ListResource *
399 FltkPlatform::FltkResourceFactory::createListResource (core::ui
400 ::ListResource
401 ::SelectionMode
402 selectionMode, int rows)
403 {
404 return new ui::FltkListResource (platform, selectionMode, rows);
405 }
406
407 core::ui::OptionMenuResource *
408 FltkPlatform::FltkResourceFactory::createOptionMenuResource ()
409 {
410 return new ui::FltkOptionMenuResource (platform);
411 }
412
413 core::ui::EntryResource *
414 FltkPlatform::FltkResourceFactory::createEntryResource (int maxLength,
415 bool password,
416 const char *label)
417 {
418 return new ui::FltkEntryResource (platform, maxLength, password, label);
419 }
420
421 core::ui::MultiLineTextResource *
422 FltkPlatform::FltkResourceFactory::createMultiLineTextResource (int cols,
423 int rows)
424 {
425 return new ui::FltkMultiLineTextResource (platform, cols, rows);
426 }
427
428 core::ui::CheckButtonResource *
429 FltkPlatform::FltkResourceFactory::createCheckButtonResource (bool activated)
430 {
431 return new ui::FltkCheckButtonResource (platform, activated);
432 }
433
434 core::ui::RadioButtonResource
435 *FltkPlatform::FltkResourceFactory::createRadioButtonResource
436 (core::ui::RadioButtonResource *groupedWith, bool activated)
437 {
438 return
439 new ui::FltkRadioButtonResource (platform,
440 (ui::FltkRadioButtonResource*)
441 groupedWith,
442 activated);
443 }
444
445 // ----------------------------------------------------------------------
446
447 FltkPlatform::FltkPlatform ()
448 {
449 layout = NULL;
450 idleQueue = new container::typed::List <IdleFunc> (true);
451 idleFuncRunning = false;
452 idleFuncId = 0;
453
454 view = NULL;
455 resources = new container::typed::List <ui::FltkResource> (false);
456
457 resourceFactory.setPlatform (this);
458 }
459
460 FltkPlatform::~FltkPlatform ()
461 {
462 if (idleFuncRunning)
463 Fl::remove_idle (generalStaticIdle, (void*)this);
464 delete idleQueue;
465 delete resources;
466 }
467
468 void FltkPlatform::setLayout (core::Layout *layout)
469 {
470 this->layout = layout;
471 }
472
473
474 void FltkPlatform::attachView (core::View *view)
475 {
476 if (this->view)
477 MSG_ERR("FltkPlatform::attachView: multiple views!\n");
478 this->view = (FltkView*)view;
479
480 for (container::typed::Iterator <ui::FltkResource> it =
481 resources->iterator (); it.hasNext (); ) {
482 ui::FltkResource *resource = it.getNext ();
483 resource->attachView (this->view);
484 }
485 }
486
487
488 void FltkPlatform::detachView (core::View *view)
489 {
490 if (this->view != view)
491 MSG_ERR("FltkPlatform::detachView: this->view: %p view: %p\n",
492 this->view, view);
493
494 for (container::typed::Iterator <ui::FltkResource> it =
495 resources->iterator (); it.hasNext (); ) {
496 ui::FltkResource *resource = it.getNext ();
497 resource->detachView ((FltkView*)view);
498 }
499 this->view = NULL;
500 }
501
502
503 int FltkPlatform::textWidth (core::style::Font *font, const char *text,
504 int len)
505 {
506 char chbuf[4];
507 int c, cu;
508 int width = 0;
509 FltkFont *ff = (FltkFont*) font;
510 int curr = 0, next = 0, nb;
511
512 if (font->fontVariant == core::style::FONT_VARIANT_SMALL_CAPS) {
513 int sc_fontsize = lout::misc::roundInt(ff->size * 0.78);
514 for (curr = 0; next < len; curr = next) {
515 next = nextGlyph(text, curr);
516 c = fl_utf8decode(text + curr, text + next, &nb);
517 if ((cu = fl_toupper(c)) == c) {
518 /* already uppercase, just draw the character */
519 fl_font(ff->font, ff->size);
520 width += font->letterSpacing;
521 width += (int)fl_width(text + curr, next - curr);
522 } else {
523 /* make utf8 string for converted char */
524 nb = fl_utf8encode(cu, chbuf);
525 fl_font(ff->font, sc_fontsize);
526 width += font->letterSpacing;
527 width += (int)fl_width(chbuf, nb);
528 }
529 }
530 } else {
531 fl_font (ff->font, ff->size);
532 width = (int) fl_width (text, len);
533
534 if (font->letterSpacing) {
535 int curr = 0, next = 0;
536
537 while (next < len) {
538 next = nextGlyph(text, curr);
539 width += font->letterSpacing;
540 curr = next;
541 }
542 }
543 }
544
545 return width;
546 }
547
548 int FltkPlatform::nextGlyph (const char *text, int idx)
549 {
550 return fl_utf8fwd (&text[idx + 1], text, &text[strlen (text)]) - text;
551 }
552
553 int FltkPlatform::prevGlyph (const char *text, int idx)
554 {
555 return fl_utf8back (&text[idx - 1], text, &text[strlen (text)]) - text;
556 }
557
558 float FltkPlatform::dpiX ()
559 {
560 float horizontal, vertical;
561
562 Fl::screen_dpi(horizontal, vertical);
563 return horizontal;
564 }
565
566 float FltkPlatform::dpiY ()
567 {
568 float horizontal, vertical;
569
570 Fl::screen_dpi(horizontal, vertical);
571 return vertical;
572 }
573
574 void FltkPlatform::generalStaticIdle (void *data)
575 {
576 ((FltkPlatform*)data)->generalIdle();
577 }
578
579 void FltkPlatform::generalIdle ()
580 {
581 IdleFunc *idleFunc;
582
583 if (!idleQueue->isEmpty ()) {
584 /* Execute the first function in the list. */
585 idleFunc = idleQueue->getFirst ();
586 (layout->*(idleFunc->func)) ();
587
588 /* Remove this function. */
589 idleQueue->removeRef(idleFunc);
590 }
591
592 if (idleQueue->isEmpty()) {
593 idleFuncRunning = false;
594 Fl::remove_idle (generalStaticIdle, (void*)this);
595 }
596 }
597
598 /**
599 * \todo Incomplete comments.
600 */
601 int FltkPlatform::addIdle (void (core::Layout::*func) ())
602 {
603 /*
604 * Since ... (todo) we have to wrap around fltk_add_idle. There is only one
605 * idle function, the passed idle function is put into a queue.
606 */
607 if (!idleFuncRunning) {
608 Fl::add_idle (generalStaticIdle, (void*)this);
609 idleFuncRunning = true;
610 }
611
612 idleFuncId++;
613
614 IdleFunc *idleFunc = new IdleFunc();
615 idleFunc->id = idleFuncId;
616 idleFunc->func = func;
617 idleQueue->append (idleFunc);
618
619 return idleFuncId;
620 }
621
622 void FltkPlatform::removeIdle (int idleId)
623 {
624 bool found;
625 container::typed::Iterator <IdleFunc> it;
626 IdleFunc *idleFunc;
627
628 for (found = false, it = idleQueue->iterator(); !found && it.hasNext(); ) {
629 idleFunc = it.getNext();
630 if (idleFunc->id == idleId) {
631 idleQueue->removeRef (idleFunc);
632 found = true;
633 }
634 }
635
636 if (idleFuncRunning && idleQueue->isEmpty())
637 Fl::remove_idle (generalStaticIdle, (void*)this);
638 }
639
640 core::style::Font *FltkPlatform::createFont (core::style::FontAttrs
641 *attrs,
642 bool tryEverything)
643 {
644 return FltkFont::create (attrs);
645 }
646
647 core::style::Color *FltkPlatform::createColor (int color)
648 {
649 return FltkColor::create (color);
650 }
651
652 core::style::Tooltip *FltkPlatform::createTooltip (const char *text)
653 {
654 return FltkTooltip::create (text);
655 }
656
657 void FltkPlatform::copySelection(const char *text)
658 {
659 Fl::copy(text, strlen(text), 0);
660 }
661
662 core::Imgbuf *FltkPlatform::createImgbuf (core::Imgbuf::Type type,
663 int width, int height)
664 {
665 return new FltkImgbuf (type, width, height);
666 }
667
668 core::ui::ResourceFactory *FltkPlatform::getResourceFactory ()
669 {
670 return &resourceFactory;
671 }
672
673
674 void FltkPlatform::attachResource (ui::FltkResource *resource)
675 {
676 resources->append (resource);
677 resource->attachView (view);
678 }
679
680 void FltkPlatform::detachResource (ui::FltkResource *resource)
681 {
682 resources->removeRef (resource);
683 }
684
685 } // namespace fltk
686 } // namespace dw
0 #ifndef __DW_FLTKPLATFORM_HH__
1 #define __DW_FLTKPLATFORM_HH__
2
3 #ifndef __INCLUDED_FROM_DW_FLTK_CORE_HH__
4 # error Do not include this file directly, use "fltkcore.hh" instead.
5 #endif
6
7 namespace dw {
8
9 /**
10 * \brief This namespace contains FLTK implementations of Dw interfaces.
11 */
12 namespace fltk {
13
14 class FltkFont: public core::style::Font
15 {
16 class FontFamily: public lout::object::Object {
17 Fl_Font font[4];
18 public:
19 FontFamily (Fl_Font fontNormal, Fl_Font fontBold,
20 Fl_Font fontItalic, Fl_Font fontBoldItalic);
21 void set (Fl_Font, int attrs);
22 Fl_Font get (int attrs);
23 };
24
25 static FontFamily standardFontFamily;
26
27 static lout::container::typed::HashTable <lout::object::ConstString,
28 FontFamily> *systemFonts;
29 static lout::container::typed::HashTable <dw::core::style::FontAttrs,
30 FltkFont> *fontsTable;
31
32 FltkFont (core::style::FontAttrs *attrs);
33 ~FltkFont ();
34
35 static void initSystemFonts ();
36
37 public:
38 Fl_Font font;
39
40 static FltkFont *create (core::style::FontAttrs *attrs);
41 static bool fontExists (const char *name);
42 static Fl_Font get (const char *name, int attrs);
43 };
44
45
46 class FltkColor: public core::style::Color
47 {
48 static lout::container::typed::HashTable <dw::core::style::ColorAttrs,
49 FltkColor> *colorsTable;
50
51 FltkColor (int color);
52 ~FltkColor ();
53
54 public:
55 int colors[SHADING_NUM];
56
57 static FltkColor *create(int color);
58 };
59
60 class FltkTooltip: public core::style::Tooltip
61 {
62 private:
63 FltkTooltip (const char *text);
64 ~FltkTooltip ();
65 public:
66 static FltkTooltip *create(const char *text);
67 static void cancel();
68 void onEnter();
69 void onLeave();
70 void onMotion();
71 };
72
73
74 /**
75 * \brief This interface adds some more methods for all flkt-based views.
76 */
77 class FltkView: public core::View
78 {
79 public:
80 virtual bool usesFltkWidgets () = 0;
81
82 virtual void addFltkWidget (Fl_Widget *widget,
83 core::Allocation *allocation);
84 virtual void removeFltkWidget (Fl_Widget *widget);
85 virtual void allocateFltkWidget (Fl_Widget *widget,
86 core::Allocation *allocation);
87 virtual void drawFltkWidget (Fl_Widget *widget, core::Rectangle *area);
88 };
89
90
91 class FltkPlatform: public core::Platform
92 {
93 private:
94 class FltkResourceFactory: public core::ui::ResourceFactory
95 {
96 private:
97 FltkPlatform *platform;
98
99 public:
100 inline void setPlatform (FltkPlatform *platform) {
101 this->platform = platform; }
102
103 core::ui::LabelButtonResource *createLabelButtonResource (const char
104 *label);
105 core::ui::ComplexButtonResource *
106 createComplexButtonResource (core::Widget *widget, bool relief);
107 core::ui::ListResource *
108 createListResource (core::ui::ListResource::SelectionMode selectionMode,
109 int rows);
110 core::ui::OptionMenuResource *createOptionMenuResource ();
111 core::ui::EntryResource *createEntryResource (int maxLength,
112 bool password,
113 const char *label);
114 core::ui::MultiLineTextResource *createMultiLineTextResource (int cols,
115 int rows);
116 core::ui::CheckButtonResource *createCheckButtonResource (bool
117 activated);
118 core::ui::RadioButtonResource *
119 createRadioButtonResource (core::ui::RadioButtonResource
120 *groupedWith, bool activated);
121 };
122
123 FltkResourceFactory resourceFactory;
124
125 class IdleFunc: public lout::object::Object
126 {
127 public:
128 int id;
129 void (core::Layout::*func) ();
130 };
131
132 core::Layout *layout;
133
134 lout::container::typed::List <IdleFunc> *idleQueue;
135 bool idleFuncRunning;
136 int idleFuncId;
137
138 static void generalStaticIdle(void *data);
139 void generalIdle();
140
141 FltkView *view;
142 lout::container::typed::List <ui::FltkResource> *resources;
143
144 public:
145 FltkPlatform ();
146 ~FltkPlatform ();
147
148 void setLayout (core::Layout *layout);
149
150 void attachView (core::View *view);
151
152 void detachView (core::View *view);
153
154 int textWidth (core::style::Font *font, const char *text, int len);
155 int nextGlyph (const char *text, int idx);
156 int prevGlyph (const char *text, int idx);
157 float dpiX ();
158 float dpiY ();
159
160 int addIdle (void (core::Layout::*func) ());
161 void removeIdle (int idleId);
162
163 core::style::Font *createFont (core::style::FontAttrs *attrs,
164 bool tryEverything);
165 bool fontExists (const char *name);
166 core::style::Color *createColor (int color);
167 core::style::Tooltip *createTooltip (const char *text);
168 void cancelTooltip();
169
170 core::Imgbuf *createImgbuf (core::Imgbuf::Type type, int width, int height);
171
172 void copySelection(const char *text);
173
174 core::ui::ResourceFactory *getResourceFactory ();
175
176 void attachResource (ui::FltkResource *resource);
177 void detachResource (ui::FltkResource *resource);
178 };
179
180 } // namespace fltk
181 } // namespace dw
182
183 #endif // __DW_FLTKPLATFORM_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "../lout/msg.h"
20
21 #include "fltkpreview.hh"
22 #include "fltkmisc.hh"
23
24 #include <FL/Fl.H>
25 #include <FL/Fl_Bitmap.H>
26 #include <FL/fl_draw.H>
27 #include <stdio.h>
28
29 #include "preview.xbm"
30
31 namespace dw {
32 namespace fltk {
33
34 FltkPreview::FltkPreview (int x, int y, int w, int h,
35 dw::core::Layout *layout, const char *label):
36 FltkViewBase (x, y, w, h, label)
37 {
38 layout->attachView (this);
39
40 scrollX = 0;
41 scrollY = 0;
42 scrollWidth = 1;
43 scrollHeight = 1;
44 }
45
46 FltkPreview::~FltkPreview ()
47 {
48 }
49
50 int FltkPreview::handle (int event)
51 {
52 return FltkViewBase::handle (event);
53 }
54
55 int FltkPreview::translateViewXToCanvasX (int x)
56 {
57 return x * canvasWidth / w ();
58 }
59
60 int FltkPreview::translateViewYToCanvasY (int y)
61 {
62 return y * canvasHeight / h ();
63 }
64
65 int FltkPreview::translateCanvasXToViewX (int x)
66 {
67 return x * w () / canvasWidth;
68 }
69
70 int FltkPreview::translateCanvasYToViewY (int y)
71 {
72 return y * h () / canvasHeight;
73 }
74
75 void FltkPreview::setCanvasSize (int width, int ascent, int descent)
76 {
77 FltkViewBase::setCanvasSize (width, ascent, descent);
78 if (parent() && parent()->visible ())
79 ((FltkPreviewWindow*)parent())->reallocate ();
80 }
81
82 bool FltkPreview::usesViewport ()
83 {
84 return true;
85 }
86
87 int FltkPreview::getHScrollbarThickness ()
88 {
89 return 0;
90 }
91
92 int FltkPreview::getVScrollbarThickness ()
93 {
94 return 0;
95 }
96
97 void FltkPreview::scrollTo (int x, int y)
98 {
99 scrollX = x;
100 scrollY = y;
101 }
102
103 void FltkPreview::scroll (dw::core::ScrollCommand cmd)
104 {
105 MSG_ERR("FltkPreview::scroll not implemented\n");
106 }
107
108 void FltkPreview::setViewportSize (int width, int height,
109 int hScrollbarThickness,
110 int vScrollbarThickness)
111 {
112 scrollWidth = width - vScrollbarThickness;
113 scrollHeight = height - hScrollbarThickness;
114 }
115
116 void FltkPreview::drawText (core::style::Font *font,
117 core::style::Color *color,
118 core::style::Color::Shading shading,
119 int x, int y, const char *text, int len)
120 {
121 /*
122 * We must call setfont() before calling getwidth() (or anything
123 * else that measures text).
124 */
125 FltkFont *ff = (FltkFont*)font;
126 Fl::set_font(ff->font, translateCanvasXToViewX (ff->size));
127 #if 0
128 /**
129 * \todo Normally, this should already be known, maybe it
130 * should be passed?
131 */
132 int width = (int)getwidth (text, len);
133 int height = font->ascent; // No descent, this would look to "bold".
134
135 int x1 = translateCanvasXToViewX (x);
136 int y1 = translateCanvasYToViewY (y);
137 int x2 = translateCanvasXToViewX (x + width);
138 int y2 = translateCanvasYToViewY (y + height);
139 Rectangle rect (x1, y1, x2 - x1, y2 - y1);
140
141 setcolor(((FltkColor*)color)->colors[shading]);
142 fillrect (rect);
143 #endif
144 fl_color(((FltkColor*)color)->colors[shading]);
145 fl_draw(text, len, translateCanvasXToViewX (x), translateCanvasYToViewY(y));
146 }
147
148 void FltkPreview::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot,
149 int x, int y, int width, int height)
150 {
151 }
152
153 bool FltkPreview::usesFltkWidgets ()
154 {
155 return false;
156 }
157
158 void FltkPreview::drawFltkWidget (Fl_Widget *widget,
159 core::Rectangle *area)
160 {
161 }
162
163 // ----------------------------------------------------------------------
164
165 FltkPreviewWindow::FltkPreviewWindow (dw::core::Layout *layout):
166 Fl_Menu_Window (1, 1)
167 {
168 box (FL_EMBOSSED_BOX);
169
170 begin ();
171 preview = new FltkPreview (BORDER_WIDTH, BORDER_WIDTH, 1, 1, layout);
172 end ();
173
174 hide ();
175 }
176
177 FltkPreviewWindow::~FltkPreviewWindow ()
178 {
179 }
180
181 void FltkPreviewWindow::showWindow ()
182 {
183 reallocate ();
184 show ();
185 }
186
187 void FltkPreviewWindow::reallocate ()
188 {
189 int maxWidth = misc::screenWidth () / 2;
190 int maxHeight = misc::screenHeight () * 4 / 5;
191 int mx, my, width, height;
192 bool warp = false;
193
194 if (preview->canvasHeight * maxWidth > maxHeight * preview->canvasWidth) {
195 // Expand to maximal height (most likely case).
196 width = preview->canvasWidth * maxHeight / preview->canvasHeight;
197 height = maxHeight;
198 } else {
199 // Expand to maximal width.
200 width = maxWidth;
201 height = preview->canvasHeight * maxWidth / preview->canvasWidth;
202 }
203
204 Fl::get_mouse(mx, my);
205
206 posX = mx - preview->translateCanvasXToViewX (preview->scrollX
207 + preview->scrollWidth / 2);
208 posY = my - preview->translateCanvasYToViewY (preview->scrollY
209 + preview->scrollHeight / 2);
210
211 if (posX < 0) {
212 mx -= posX;
213 posX = 0;
214 warp = true;
215 } else if (posX + width > misc::screenWidth ()) {
216 mx -= (posX - (misc::screenWidth () - width));
217 posX = misc::screenWidth () - width;
218 warp = true;
219 }
220
221 if (posY < 0) {
222 my -= posY;
223 posY = 0;
224 warp = true;
225 } else if (posY + height > misc::screenHeight ()) {
226 my -= (posY - (misc::screenHeight () - height));
227 posY = misc::screenHeight () - height;
228 warp = true;
229 }
230
231 if (warp)
232 misc::warpPointer (mx, my);
233
234 resize (posX, posY, width, height);
235
236 preview->size(w () - 2 * BORDER_WIDTH, h () - 2 * BORDER_WIDTH);
237 }
238
239 void FltkPreviewWindow::hideWindow ()
240 {
241 Fl_Window::hide ();
242 }
243
244 void FltkPreviewWindow::scrollTo (int mouseX, int mouseY)
245 {
246 preview->scrollX =
247 preview->translateViewXToCanvasX (mouseX - posX - BORDER_WIDTH)
248 - preview->scrollWidth / 2;
249 preview->scrollY =
250 preview->translateViewYToCanvasY (mouseY - posY - BORDER_WIDTH)
251 - preview->scrollHeight / 2;
252 preview->theLayout->scrollPosChanged (preview,
253 preview->scrollX, preview->scrollY);
254 }
255
256 // ----------------------------------------------------------------------
257
258 FltkPreviewButton::FltkPreviewButton (int x, int y, int w, int h,
259 dw::core::Layout *layout,
260 const char *label):
261 Fl_Button (x, y, w, h, label)
262 {
263 image (new Fl_Bitmap (preview_bits, preview_width, preview_height));
264 window = new FltkPreviewWindow (layout);
265 }
266
267 FltkPreviewButton::~FltkPreviewButton ()
268 {
269 }
270
271 int FltkPreviewButton::handle (int event)
272 {
273 /** \bug Some parts are missing. */
274
275 switch (event) {
276 case FL_PUSH:
277 window->showWindow ();
278 return Fl_Button::handle (event);
279
280 case FL_DRAG:
281 if (window->visible ()) {
282 window->scrollTo (Fl::event_x_root (), Fl::event_y_root ());
283 return 1;
284 }
285 return Fl_Button::handle (event);
286
287 case FL_RELEASE:
288 window->hideWindow ();
289 return Fl_Button::handle (event);
290
291 default:
292 return Fl_Button::handle (event);
293 }
294 }
295
296 } // namespace fltk
297 } // namespace dw
0 #ifndef __FlTKPREVIEW_HH__
1 #define __FlTKPREVIEW_HH__
2
3 #include <FL/Fl_Button.H>
4 #include <FL/Fl_Menu_Window.H>
5 #include "fltkviewbase.hh"
6
7 namespace dw {
8 namespace fltk {
9
10 class FltkPreview: public FltkViewBase
11 {
12 friend class FltkPreviewWindow;
13
14 private:
15 int scrollX, scrollY, scrollWidth, scrollHeight;
16
17 protected:
18 int translateViewXToCanvasX (int x);
19 int translateViewYToCanvasY (int y);
20 int translateCanvasXToViewX (int x);
21 int translateCanvasYToViewY (int y);
22
23 public:
24 FltkPreview (int x, int y, int w, int h, dw::core::Layout *layout,
25 const char *label = 0);
26 ~FltkPreview ();
27
28 int handle (int event);
29
30 void setCanvasSize (int width, int ascent, int descent);
31
32 bool usesViewport ();
33 int getHScrollbarThickness ();
34 int getVScrollbarThickness ();
35 void scrollTo (int x, int y);
36 void scroll (dw::core::ScrollCommand cmd);
37 void setViewportSize (int width, int height,
38 int hScrollbarThickness, int vScrollbarThickness);
39
40 void drawText (core::style::Font *font,
41 core::style::Color *color,
42 core::style::Color::Shading shading,
43 int x, int y, const char *text, int len);
44 void drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot,
45 int x, int y, int width, int height);
46
47 bool usesFltkWidgets ();
48 void drawFltkWidget (Fl_Widget *widget, core::Rectangle *area);
49 };
50
51
52 class FltkPreviewWindow: public Fl_Menu_Window
53 {
54 private:
55 enum { BORDER_WIDTH = 2 };
56
57 FltkPreview *preview;
58 int posX, posY;
59
60 public:
61 FltkPreviewWindow (dw::core::Layout *layout);
62 ~FltkPreviewWindow ();
63
64 void reallocate ();
65
66 void showWindow ();
67 void hideWindow ();
68
69 void scrollTo (int mouseX, int mouseY);
70 };
71
72
73 class FltkPreviewButton: public Fl_Button
74 {
75 private:
76 FltkPreviewWindow *window;
77
78 public:
79 FltkPreviewButton (int x, int y, int w, int h,
80 dw::core::Layout *layout, const char *label = 0);
81 ~FltkPreviewButton ();
82
83 int handle (int event);
84 };
85
86 } // namespace fltk
87 } // namespace dw
88
89 #endif // __FlTKPREVIEW_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "fltkcore.hh"
22 #include "fltkflatview.hh"
23 #include "fltkcomplexbutton.hh"
24 #include "../lout/msg.h"
25 #include "../lout/misc.hh"
26
27 #include <FL/Fl.H>
28 #include <FL/fl_draw.H>
29 #include <FL/Fl_Input.H>
30 #include <FL/Fl_Text_Editor.H>
31 #include <FL/Fl_Check_Button.H>
32 #include <FL/Fl_Round_Button.H>
33 #include <FL/Fl_Choice.H>
34 #include <FL/Fl_Tree.H>
35
36 #include <stdio.h>
37
38 //----------------------------------------------------------------------------
39 /*
40 * Local sub classes
41 */
42
43 /*
44 * Used to enable CTRL+{a,e,d,k} in form inputs (for start,end,del,cut)
45 */
46 class CustInput2 : public Fl_Input {
47 public:
48 CustInput2 (int x, int y, int w, int h, const char* l=0) :
49 Fl_Input(x,y,w,h,l) {};
50 int handle(int e);
51 };
52
53 int CustInput2::handle(int e)
54 {
55 int k = Fl::event_key();
56
57 _MSG("CustInput2::handle event=%d\n", e);
58
59 // We're only interested in some flags
60 unsigned modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL | FL_ALT);
61
62 if (e == FL_KEYBOARD && modifier == FL_CTRL) {
63 if (k == 'a' || k == 'e') {
64 position(k == 'a' ? 0 : size());
65 return 1;
66 } else if (k == 'k') {
67 cut(position(), size());
68 return 1;
69 } else if (k == 'd') {
70 cut(position(), position()+1);
71 return 1;
72 }
73 }
74 return Fl_Input::handle(e);
75 }
76
77 //----------------------------------------------------------------------------
78
79 namespace dw {
80 namespace fltk {
81 namespace ui {
82
83 enum { RELIEF_X_THICKNESS = 3, RELIEF_Y_THICKNESS = 3 };
84
85 using namespace lout::object;
86 using namespace lout::container::typed;
87
88 FltkResource::FltkResource (FltkPlatform *platform)
89 {
90 this->platform = platform;
91
92 allocation.x = 0;
93 allocation.y = 0;
94 allocation.width = 1;
95 allocation.ascent = 1;
96 allocation.descent = 0;
97
98 style = NULL;
99
100 enabled = true;
101 }
102
103 /**
104 * This is not a constructor, since it calls some virtual methods, which
105 * should not be done in a C++ base constructor.
106 */
107 void FltkResource::init (FltkPlatform *platform)
108 {
109 view = NULL;
110 widget = NULL;
111 platform->attachResource (this);
112 }
113
114 FltkResource::~FltkResource ()
115 {
116 platform->detachResource (this);
117 if (widget) {
118 if (view) {
119 view->removeFltkWidget(widget);
120 }
121 delete widget;
122 }
123 if (style)
124 style->unref ();
125 }
126
127 void FltkResource::attachView (FltkView *view)
128 {
129 if (this->view)
130 MSG_ERR("FltkResource::attachView: multiple views!\n");
131
132 if (view->usesFltkWidgets ()) {
133 this->view = view;
134
135 widget = createNewWidget (&allocation);
136 view->addFltkWidget (widget, &allocation);
137 if (style)
138 setWidgetStyle (widget, style);
139 if (! enabled)
140 widget->deactivate ();
141 }
142 }
143
144 void FltkResource::detachView (FltkView *view)
145 {
146 if (this->view != view)
147 MSG_ERR("FltkResource::detachView: this->view: %p view: %p\n",
148 this->view, view);
149 this->view = NULL;
150 }
151
152 void FltkResource::sizeAllocate (core::Allocation *allocation)
153 {
154 this->allocation = *allocation;
155 view->allocateFltkWidget (widget, allocation);
156 }
157
158 void FltkResource::draw (core::View *view, core::Rectangle *area)
159 {
160 FltkView *fltkView = (FltkView*)view;
161 if (fltkView->usesFltkWidgets () && this->view == fltkView) {
162 fltkView->drawFltkWidget (widget, area);
163 }
164 }
165
166 void FltkResource::setStyle (core::style::Style *style)
167 {
168 if (this->style)
169 this->style->unref ();
170
171 this->style = style;
172 style->ref ();
173
174 setWidgetStyle (widget, style);
175 }
176
177 void FltkResource::setWidgetStyle (Fl_Widget *widget,
178 core::style::Style *style)
179 {
180 FltkFont *font = (FltkFont*)style->font;
181 widget->labelsize (font->size);
182 widget->labelfont (font->font);
183
184 FltkColor *bg = (FltkColor*)style->backgroundColor;
185 if (bg) {
186 int normal_bg = bg->colors[FltkColor::SHADING_NORMAL];
187
188 if (style->color) {
189 int style_fg = ((FltkColor*)style->color)->colors
190 [FltkColor::SHADING_NORMAL];
191 Fl_Color fg = fl_contrast(style_fg, normal_bg);
192
193 widget->labelcolor(fg);
194 widget->selection_color(fg);
195 }
196
197 widget->color(normal_bg);
198 }
199 }
200
201 void FltkResource::setDisplayed(bool displayed)
202 {
203 if (displayed)
204 widget->show();
205 else
206 widget->hide();
207 }
208
209 bool FltkResource::displayed()
210 {
211 bool ret = false;
212
213 if (widget) {
214 // visible() is not the same thing as being show()n exactly, but
215 // show()/hide() set it appropriately for our purposes.
216 ret = widget->visible();
217 }
218 return ret;
219 }
220
221 bool FltkResource::isEnabled ()
222 {
223 return enabled;
224 }
225
226 void FltkResource::setEnabled (bool enabled)
227 {
228 this->enabled = enabled;
229
230 if (enabled)
231 widget->activate ();
232 else
233 widget->deactivate ();
234 }
235
236 // ----------------------------------------------------------------------
237
238 template <class I> void FltkSpecificResource<I>::sizeAllocate (core::Allocation
239 *allocation)
240 {
241 FltkResource::sizeAllocate (allocation);
242 }
243
244 template <class I> void FltkSpecificResource<I>::draw (core::View *view,
245 core::Rectangle *area)
246 {
247 FltkResource::draw (view, area);
248 }
249
250 template <class I> void FltkSpecificResource<I>::setStyle (core::style::Style
251 *style)
252 {
253 FltkResource::setStyle (style);
254 }
255
256 template <class I> bool FltkSpecificResource<I>::isEnabled ()
257 {
258 return FltkResource::isEnabled ();
259 }
260
261 template <class I> void FltkSpecificResource<I>::setEnabled (bool enabled)
262 {
263 FltkResource::setEnabled (enabled);
264 }
265
266 // ----------------------------------------------------------------------
267
268 FltkLabelButtonResource::FltkLabelButtonResource (FltkPlatform *platform,
269 const char *label):
270 FltkSpecificResource <dw::core::ui::LabelButtonResource> (platform)
271 {
272 this->label = strdup (label);
273 init (platform);
274 }
275
276 FltkLabelButtonResource::~FltkLabelButtonResource ()
277 {
278 free((char *)label);
279 }
280
281 Fl_Widget *FltkLabelButtonResource::createNewWidget (core::Allocation
282 *allocation)
283 {
284 Fl_Button *button =
285 new Fl_Button (allocation->x, allocation->y, allocation->width,
286 allocation->ascent + allocation->descent, label);
287 button->callback (widgetCallback, this);
288 button->when (FL_WHEN_RELEASE);
289 return button;
290 }
291
292 void FltkLabelButtonResource::sizeRequest (core::Requisition *requisition)
293 {
294 if (style) {
295 FltkFont *font = (FltkFont*)style->font;
296 fl_font(font->font,font->size);
297 requisition->width =
298 (int)fl_width (label, strlen (label))
299 + 2 * RELIEF_X_THICKNESS;
300 requisition->ascent = font->ascent + RELIEF_Y_THICKNESS;
301 requisition->descent = font->descent + RELIEF_Y_THICKNESS;
302 } else {
303 requisition->width = 1;
304 requisition->ascent = 1;
305 requisition->descent = 0;
306 }
307 }
308
309 /*
310 * Get FLTK state and translate to dw
311 *
312 * TODO: find a good home for this and the fltkviewbase.cc original.
313 */
314 static core::ButtonState getDwButtonState ()
315 {
316 int s1 = Fl::event_state ();
317 int s2 = (core::ButtonState)0;
318
319 if (s1 & FL_SHIFT) s2 |= core::SHIFT_MASK;
320 if (s1 & FL_CTRL) s2 |= core::CONTROL_MASK;
321 if (s1 & FL_ALT) s2 |= core::META_MASK;
322 if (s1 & FL_BUTTON1) s2 |= core::BUTTON1_MASK;
323 if (s1 & FL_BUTTON2) s2 |= core::BUTTON2_MASK;
324 if (s1 & FL_BUTTON3) s2 |= core::BUTTON3_MASK;
325
326 return (core::ButtonState)s2;
327 }
328
329 static void setButtonEvent(dw::core::EventButton *event)
330 {
331 event->xCanvas = Fl::event_x();
332 event->yCanvas = Fl::event_y();
333 event->state = getDwButtonState();
334 event->button = Fl::event_button();
335 event->numPressed = Fl::event_clicks() + 1;
336 }
337
338 void FltkLabelButtonResource::widgetCallback (Fl_Widget *widget,
339 void *data)
340 {
341 if (!Fl::event_button3()) {
342 FltkLabelButtonResource *lbr = (FltkLabelButtonResource*) data;
343 dw::core::EventButton event;
344 setButtonEvent(&event);
345 lbr->emitClicked(&event);
346 }
347 }
348
349 const char *FltkLabelButtonResource::getLabel ()
350 {
351 return label;
352 }
353
354
355 void FltkLabelButtonResource::setLabel (const char *label)
356 {
357 delete this->label;
358 this->label = strdup (label);
359
360 widget->label (this->label);
361 queueResize (true);
362 }
363
364 // ----------------------------------------------------------------------
365
366 FltkComplexButtonResource::FltkComplexButtonResource (FltkPlatform *platform,
367 dw::core::Widget
368 *widget, bool relief):
369 FltkSpecificResource <dw::core::ui::ComplexButtonResource> (platform)
370 {
371 flatView = topView = NULL;
372 this->relief = relief;
373 FltkResource::init (platform);
374 ComplexButtonResource::init (widget);
375 }
376
377 FltkComplexButtonResource::~FltkComplexButtonResource ()
378 {
379 }
380
381 void FltkComplexButtonResource::widgetCallback (Fl_Widget *widget,
382 void *data)
383 {
384 FltkComplexButtonResource *res = (FltkComplexButtonResource*)data;
385
386 if (!Fl::event_button3()) {
387 res->click_x = Fl::event_x();
388 res->click_y = Fl::event_y();
389 dw::core::EventButton event;
390 setButtonEvent(&event);
391 res->emitClicked(&event);
392 }
393 }
394
395 dw::core::Platform *FltkComplexButtonResource::createPlatform ()
396 {
397 return new FltkPlatform ();
398 }
399
400 void FltkComplexButtonResource::attachView (FltkView *view)
401 {
402 FltkResource::attachView (view);
403
404 if (view->usesFltkWidgets ())
405 topView = view;
406 }
407
408 void FltkComplexButtonResource::detachView (FltkView *view)
409 {
410 FltkResource::detachView (view);
411 }
412
413 void FltkComplexButtonResource::sizeAllocate (core::Allocation *allocation)
414 {
415 FltkResource::sizeAllocate (allocation);
416
417 ((FltkFlatView*)flatView)->resize (
418 reliefXThickness (), reliefYThickness (),
419 allocation->width - 2 * reliefXThickness (),
420 allocation->ascent + allocation->descent - 2 * reliefYThickness ());
421
422 ((FltkFlatView*)flatView)->parent ()->init_sizes ();
423 }
424
425 void FltkComplexButtonResource::setLayout (dw::core::Layout *layout)
426 {
427 layout->attachView (flatView);
428 }
429
430 int FltkComplexButtonResource::reliefXThickness ()
431 {
432 return relief ? RELIEF_X_THICKNESS : 0;
433 }
434
435 int FltkComplexButtonResource::reliefYThickness ()
436 {
437 return relief ? RELIEF_Y_THICKNESS : 0;
438 }
439
440
441 Fl_Widget *FltkComplexButtonResource::createNewWidget (core::Allocation
442 *allocation)
443 {
444 ComplexButton *button =
445 new ComplexButton (allocation->x, allocation->y, allocation->width,
446 allocation->ascent + allocation->descent);
447 button->callback (widgetCallback, this);
448 button->when (FL_WHEN_RELEASE);
449 if (!relief)
450 button->box(FL_FLAT_BOX);
451
452 flatView = new FltkFlatView (allocation->x + reliefXThickness (),
453 allocation->y + reliefYThickness (),
454 allocation->width - 2 * reliefXThickness (),
455 allocation->ascent + allocation->descent
456 - 2 * reliefYThickness ());
457 button->add ((FltkFlatView *)flatView);
458
459 if (layout)
460 layout->attachView (flatView);
461 return button;
462 }
463
464 // ----------------------------------------------------------------------
465
466 FltkEntryResource::FltkEntryResource (FltkPlatform *platform, int maxLength,
467 bool password, const char *label):
468 FltkSpecificResource <dw::core::ui::EntryResource> (platform)
469 {
470 this->maxLength = maxLength;
471 this->password = password;
472 this->label = label ? strdup(label) : NULL;
473 this->label_w = 0;
474
475 initText = NULL;
476 editable = false;
477
478 init (platform);
479 }
480
481 FltkEntryResource::~FltkEntryResource ()
482 {
483 if (initText)
484 free((char *)initText);
485 if (label)
486 free(label);
487 }
488
489 Fl_Widget *FltkEntryResource::createNewWidget (core::Allocation
490 *allocation)
491 {
492 Fl_Input *input =
493 new CustInput2(allocation->x, allocation->y, allocation->width,
494 allocation->ascent + allocation->descent);
495 if (password)
496 input->type(FL_SECRET_INPUT);
497 input->callback (widgetCallback, this);
498 input->when (FL_WHEN_ENTER_KEY_ALWAYS);
499
500 if (label) {
501 input->label(label);
502 input->align(FL_ALIGN_LEFT);
503 }
504 if (initText)
505 input->value (initText);
506
507 return input;
508 }
509
510 void FltkEntryResource::setWidgetStyle (Fl_Widget *widget,
511 core::style::Style *style)
512 {
513 Fl_Input *in = (Fl_Input *)widget;
514
515 FltkResource::setWidgetStyle(widget, style);
516
517 in->textcolor(widget->labelcolor());
518 in->cursor_color(in->textcolor());
519 in->textsize(in->labelsize());
520 in->textfont(in->labelfont());
521
522 if (label) {
523 int h;
524 label_w = 0;
525 widget->measure_label(label_w, h);
526 label_w += RELIEF_X_THICKNESS;
527 }
528 }
529
530 void FltkEntryResource::setDisplayed(bool displayed)
531 {
532 FltkResource::setDisplayed(displayed);
533 queueResize(true);
534 }
535
536 void FltkEntryResource::sizeRequest (core::Requisition *requisition)
537 {
538 if (displayed() && style) {
539 FltkFont *font = (FltkFont*)style->font;
540 fl_font(font->font,font->size);
541 requisition->width =
542 (int)fl_width ('n')
543 * (maxLength == UNLIMITED_MAX_LENGTH ? 10 : maxLength)
544 + label_w + (2 * RELIEF_X_THICKNESS);
545 requisition->ascent = font->ascent + RELIEF_Y_THICKNESS;
546 requisition->descent = font->descent + RELIEF_Y_THICKNESS;
547 } else {
548 requisition->width = 0;
549 requisition->ascent = 0;
550 requisition->descent = 0;
551 }
552 }
553
554 void FltkEntryResource::sizeAllocate (core::Allocation *allocation)
555 {
556 if (!label) {
557 FltkResource::sizeAllocate(allocation);
558 } else {
559 this->allocation = *allocation;
560
561 /* push the Fl_Input over to the right of the label */
562 core::Allocation a = this->allocation;
563 a.x += this->label_w;
564 a.width -= this->label_w;
565 view->allocateFltkWidget (widget, &a);
566 }
567 }
568
569 void FltkEntryResource::widgetCallback (Fl_Widget *widget, void *data)
570 {
571 ((FltkEntryResource*)data)->emitActivate ();
572 }
573
574 const char *FltkEntryResource::getText ()
575 {
576 return ((Fl_Input*)widget)->value ();
577 }
578
579 void FltkEntryResource::setText (const char *text)
580 {
581 if (initText)
582 free((char *)initText);
583 initText = strdup (text);
584
585 ((Fl_Input*)widget)->value (initText);
586 }
587
588 bool FltkEntryResource::isEditable ()
589 {
590 return editable;
591 }
592
593 void FltkEntryResource::setEditable (bool editable)
594 {
595 this->editable = editable;
596 }
597
598 // ----------------------------------------------------------------------
599
600 FltkMultiLineTextResource::FltkMultiLineTextResource (FltkPlatform *platform,
601 int cols, int rows):
602 FltkSpecificResource <dw::core::ui::MultiLineTextResource> (platform)
603 {
604 buffer = new Fl_Text_Buffer;
605 editable = false;
606
607 numCols = cols;
608 numRows = rows;
609
610 // Check values. Upper bound check is left to the caller.
611 if (numCols < 1) {
612 MSG_WARN("numCols = %d is set to 1.\n", numCols);
613 numCols = 1;
614 }
615 if (numRows < 1) {
616 MSG_WARN("numRows = %d is set to 1.\n", numRows);
617 numRows = 1;
618 }
619
620 init (platform);
621 }
622
623 FltkMultiLineTextResource::~FltkMultiLineTextResource ()
624 {
625 /* Free memory avoiding a double-free of text buffers */
626 ((Fl_Text_Editor *) widget)->buffer (0);
627 delete buffer;
628 }
629
630 Fl_Widget *FltkMultiLineTextResource::createNewWidget (core::Allocation
631 *allocation)
632 {
633 Fl_Text_Editor *text =
634 new Fl_Text_Editor (allocation->x, allocation->y, allocation->width,
635 allocation->ascent + allocation->descent);
636 text->wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
637 text->buffer (buffer);
638 return text;
639 }
640
641 void FltkMultiLineTextResource::setWidgetStyle (Fl_Widget *widget,
642 core::style::Style *style)
643 {
644 Fl_Text_Editor *ed = (Fl_Text_Editor *)widget;
645
646 FltkResource::setWidgetStyle(widget, style);
647
648 ed->textcolor(widget->labelcolor());
649 ed->cursor_color(ed->textcolor());
650 ed->textsize(ed->labelsize());
651 ed->textfont(ed->labelfont());
652 }
653
654 void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition)
655 {
656 if (style) {
657 FltkFont *font = (FltkFont*)style->font;
658 fl_font(font->font,font->size);
659 requisition->width =
660 (int)fl_width ('n') * numCols + 2 * RELIEF_X_THICKNESS;
661 requisition->ascent =
662 RELIEF_Y_THICKNESS + font->ascent +
663 (font->ascent + font->descent) * (numRows - 1);
664 requisition->descent =
665 font->descent +
666 RELIEF_Y_THICKNESS;
667 } else {
668 requisition->width = 1;
669 requisition->ascent = 1;
670 requisition->descent = 0;
671 }
672 }
673
674 const char *FltkMultiLineTextResource::getText ()
675 {
676 return buffer->text ();
677 }
678
679 void FltkMultiLineTextResource::setText (const char *text)
680 {
681 buffer->text (text);
682 }
683
684 bool FltkMultiLineTextResource::isEditable ()
685 {
686 return editable;
687 }
688
689 void FltkMultiLineTextResource::setEditable (bool editable)
690 {
691 this->editable = editable;
692 }
693
694 // ----------------------------------------------------------------------
695
696 template <class I>
697 FltkToggleButtonResource<I>::FltkToggleButtonResource (FltkPlatform *platform,
698 bool activated):
699 FltkSpecificResource <I> (platform)
700 {
701 initActivated = activated;
702 }
703
704
705 template <class I>
706 FltkToggleButtonResource<I>::~FltkToggleButtonResource ()
707 {
708 }
709
710
711 template <class I>
712 Fl_Widget *FltkToggleButtonResource<I>::createNewWidget (core::Allocation
713 *allocation)
714 {
715 Fl_Button *button = createNewButton (allocation);
716 button->value (initActivated);
717 return button;
718 }
719
720 template <class I>
721 void FltkToggleButtonResource<I>::setWidgetStyle (Fl_Widget *widget,
722 core::style::Style *style)
723 {
724 FltkResource::setWidgetStyle(widget, style);
725
726 widget->selection_color(FL_BLACK);
727 }
728
729
730 template <class I>
731 void FltkToggleButtonResource<I>::sizeRequest (core::Requisition *requisition)
732 {
733 FltkFont *font = (FltkFont *)
734 (this->FltkResource::style ? this->FltkResource::style->font : NULL);
735
736 if (font) {
737 fl_font(font->font, font->size);
738 requisition->width = font->ascent + font->descent + 2*RELIEF_X_THICKNESS;
739 requisition->ascent = font->ascent + RELIEF_Y_THICKNESS;
740 requisition->descent = font->descent + RELIEF_Y_THICKNESS;
741 } else {
742 requisition->width = 1;
743 requisition->ascent = 1;
744 requisition->descent = 0;
745 }
746 }
747
748
749 template <class I>
750 bool FltkToggleButtonResource<I>::FltkToggleButtonResource::isActivated ()
751 {
752 return ((Fl_Button*)this->widget)->value ();
753 }
754
755
756 template <class I>
757 void FltkToggleButtonResource<I>::setActivated (bool activated)
758 {
759 initActivated = activated;
760 ((Fl_Button*)this->widget)->value (initActivated);
761 }
762
763 // ----------------------------------------------------------------------
764
765 FltkCheckButtonResource::FltkCheckButtonResource (FltkPlatform *platform,
766 bool activated):
767 FltkToggleButtonResource<dw::core::ui::CheckButtonResource> (platform,
768 activated)
769 {
770 init (platform);
771 }
772
773
774 FltkCheckButtonResource::~FltkCheckButtonResource ()
775 {
776 }
777
778
779 Fl_Button *FltkCheckButtonResource::createNewButton (core::Allocation
780 *allocation)
781 {
782 Fl_Check_Button *cb =
783 new Fl_Check_Button (allocation->x, allocation->y, allocation->width,
784 allocation->ascent + allocation->descent);
785 return cb;
786 }
787
788 // ----------------------------------------------------------------------
789
790 bool FltkRadioButtonResource::Group::FltkGroupIterator::hasNext ()
791 {
792 return it.hasNext ();
793 }
794
795 dw::core::ui::RadioButtonResource
796 *FltkRadioButtonResource::Group::FltkGroupIterator::getNext ()
797 {
798 return (dw::core::ui::RadioButtonResource*)it.getNext ();
799 }
800
801 void FltkRadioButtonResource::Group::FltkGroupIterator::unref ()
802 {
803 delete this;
804 }
805
806
807 FltkRadioButtonResource::Group::Group (FltkRadioButtonResource
808 *radioButtonResource)
809 {
810 list = new lout::container::typed::List <FltkRadioButtonResource> (false);
811 connect (radioButtonResource);
812 }
813
814 FltkRadioButtonResource::Group::~Group ()
815 {
816 delete list;
817 }
818
819 void FltkRadioButtonResource::Group::connect (FltkRadioButtonResource
820 *radioButtonResource)
821 {
822 list->append (radioButtonResource);
823 }
824
825 void FltkRadioButtonResource::Group::unconnect (FltkRadioButtonResource
826 *radioButtonResource)
827 {
828 list->removeRef (radioButtonResource);
829 if (list->isEmpty ())
830 delete this;
831 }
832
833
834 FltkRadioButtonResource::FltkRadioButtonResource (FltkPlatform *platform,
835 FltkRadioButtonResource
836 *groupedWith,
837 bool activated):
838 FltkToggleButtonResource<dw::core::ui::RadioButtonResource> (platform,
839 activated)
840 {
841 init (platform);
842
843 if (groupedWith) {
844 group = groupedWith->group;
845 group->connect (this);
846 } else
847 group = new Group (this);
848 }
849
850
851 FltkRadioButtonResource::~FltkRadioButtonResource ()
852 {
853 group->unconnect (this);
854 }
855
856 dw::core::ui::RadioButtonResource::GroupIterator
857 *FltkRadioButtonResource::groupIterator ()
858 {
859 return group->groupIterator ();
860 }
861
862 void FltkRadioButtonResource::widgetCallback (Fl_Widget *widget,
863 void *data)
864 {
865 if (widget->when () & FL_WHEN_CHANGED)
866 ((FltkRadioButtonResource*)data)->buttonClicked ();
867 }
868
869 void FltkRadioButtonResource::buttonClicked ()
870 {
871 for (Iterator <FltkRadioButtonResource> it = group->iterator ();
872 it.hasNext (); ) {
873 FltkRadioButtonResource *other = it.getNext ();
874 other->setActivated (other == this);
875 }
876 }
877
878 Fl_Button *FltkRadioButtonResource::createNewButton (core::Allocation
879 *allocation)
880 {
881 /*
882 * Groups of Fl_Radio_Button must be added to one Fl_Group, which is
883 * not possible in this context. For this, we do the grouping ourself,
884 * based on FltkRadioButtonResource::Group.
885 *
886 * What we actually need for this, is a widget, which behaves like a
887 * check button, but looks like a radio button. The first depends on the
888 * type, the second on the style. Since the type is simpler to change
889 * than the style, we create a radio button, and then change the type
890 * (instead of creating a check button, and changing the style).
891 */
892
893 Fl_Button *button =
894 new Fl_Round_Button (allocation->x, allocation->y, allocation->width,
895 allocation->ascent + allocation->descent);
896 button->when (FL_WHEN_CHANGED);
897 button->callback (widgetCallback, this);
898 button->type (FL_TOGGLE_BUTTON);
899
900 return button;
901 }
902
903 // ----------------------------------------------------------------------
904
905 template <class I> dw::core::Iterator *
906 FltkSelectionResource<I>::iterator (dw::core::Content::Type mask, bool atEnd)
907 {
908 /** \bug Implementation. */
909 return new core::EmptyIterator (this->getEmbed (), mask, atEnd);
910 }
911
912 // ----------------------------------------------------------------------
913
914 FltkOptionMenuResource::FltkOptionMenuResource (FltkPlatform *platform):
915 FltkSelectionResource <dw::core::ui::OptionMenuResource> (platform)
916 {
917 /* Fl_Menu_ does not like multiple menu items with the same label, and
918 * insert() treats some characters specially unless escaped, so let's
919 * do our own menu handling.
920 */
921 itemsAllocated = 0x10;
922 menu = new Fl_Menu_Item[itemsAllocated];
923 memset(menu, 0, itemsAllocated * sizeof(Fl_Menu_Item));
924 itemsUsed = 1; // menu[0].text == NULL, which is an end-of-menu marker.
925 visibleItems = 0;
926
927 init (platform);
928 }
929
930 FltkOptionMenuResource::~FltkOptionMenuResource ()
931 {
932 for (int i = 0; i < itemsUsed; i++) {
933 if (menu[i].text)
934 free((char *) menu[i].text);
935 }
936 delete[] menu;
937 }
938
939 void FltkOptionMenuResource::setWidgetStyle (Fl_Widget *widget,
940 core::style::Style *style)
941 {
942 Fl_Choice *ch = (Fl_Choice *)widget;
943
944 FltkResource::setWidgetStyle(widget, style);
945
946 ch->textcolor(widget->labelcolor());
947 ch->textfont(ch->labelfont());
948 ch->textsize(ch->labelsize());
949 }
950
951 Fl_Widget *FltkOptionMenuResource::createNewWidget (core::Allocation
952 *allocation)
953 {
954 Fl_Choice *choice =
955 new Fl_Choice (allocation->x, allocation->y,
956 allocation->width,
957 allocation->ascent + allocation->descent);
958 choice->menu(menu);
959 return choice;
960 }
961
962 void FltkOptionMenuResource::widgetCallback (Fl_Widget *widget,
963 void *data)
964 {
965 }
966
967 int FltkOptionMenuResource::getMaxItemWidth()
968 {
969 int i, max = 0;
970
971 for (i = 0; i < itemsUsed; i++) {
972 int width = 0;
973 const char *str = menu[i].text;
974
975 if (str) {
976 width = fl_width(str);
977 if (width > max)
978 max = width;
979 }
980 }
981 return max;
982 }
983
984 void FltkOptionMenuResource::sizeRequest (core::Requisition *requisition)
985 {
986 if (style) {
987 FltkFont *font = (FltkFont*)style->font;
988 fl_font(font->font, font->size);
989 int maxItemWidth = getMaxItemWidth ();
990 requisition->ascent = font->ascent + RELIEF_Y_THICKNESS;
991 requisition->descent = font->descent + RELIEF_Y_THICKNESS;
992 requisition->width = maxItemWidth
993 + (requisition->ascent + requisition->descent)
994 + 2 * RELIEF_X_THICKNESS;
995 } else {
996 requisition->width = 1;
997 requisition->ascent = 1;
998 requisition->descent = 0;
999 }
1000 }
1001
1002 void FltkOptionMenuResource::enlargeMenu ()
1003 {
1004 Fl_Choice *ch = (Fl_Choice *)widget;
1005 int selected = ch->value();
1006 Fl_Menu_Item *newMenu;
1007
1008 itemsAllocated += 0x10;
1009 newMenu = new Fl_Menu_Item[itemsAllocated];
1010 memcpy(newMenu, menu, itemsUsed * sizeof(Fl_Menu_Item));
1011 memset(newMenu + itemsUsed, 0, 0x10 * sizeof(Fl_Menu_Item));
1012 delete[] menu;
1013 menu = newMenu;
1014 ch->menu(menu);
1015 ch->value(selected);
1016 }
1017
1018 Fl_Menu_Item *FltkOptionMenuResource::newItem()
1019 {
1020 Fl_Menu_Item *item;
1021
1022 if (itemsUsed == itemsAllocated)
1023 enlargeMenu();
1024
1025 item = menu + itemsUsed - 1;
1026 itemsUsed++;
1027
1028 return item;
1029 }
1030
1031 void FltkOptionMenuResource::addItem (const char *str,
1032 bool enabled, bool selected)
1033 {
1034 Fl_Menu_Item *item = newItem();
1035
1036 item->text = strdup(str);
1037 item->argument(visibleItems++);
1038
1039 if (enabled == false)
1040 item->flags = FL_MENU_INACTIVE;
1041
1042 if (selected)
1043 ((Fl_Choice *)widget)->value(item);
1044
1045 queueResize (true);
1046 }
1047
1048 void FltkOptionMenuResource::pushGroup (const char *name, bool enabled)
1049 {
1050 Fl_Menu_Item *item = newItem();
1051
1052 item->text = strdup(name);
1053 item->argument(visibleItems++);
1054
1055 if (enabled == false)
1056 item->flags = FL_MENU_INACTIVE;
1057
1058 item->flags |= FL_SUBMENU;
1059
1060 queueResize (true);
1061 }
1062
1063 void FltkOptionMenuResource::popGroup ()
1064 {
1065 /* Item with NULL text field closes the submenu */
1066 newItem();
1067 queueResize (true);
1068 }
1069
1070 bool FltkOptionMenuResource::isSelected (int index)
1071 {
1072 return index == (long) ((Fl_Choice *)widget)->mvalue()->user_data();
1073 }
1074
1075 int FltkOptionMenuResource::getNumberOfItems()
1076 {
1077 return ((Fl_Choice*)widget)->size();
1078 }
1079
1080 // ----------------------------------------------------------------------
1081
1082 FltkListResource::FltkListResource (FltkPlatform *platform,
1083 core::ui::ListResource::SelectionMode
1084 selectionMode, int rowCount):
1085 FltkSelectionResource <dw::core::ui::ListResource> (platform),
1086 itemsSelected(8)
1087 {
1088 mode = selectionMode;
1089 showRows = rowCount;
1090 init (platform);
1091 }
1092
1093 FltkListResource::~FltkListResource ()
1094 {
1095 }
1096
1097
1098 Fl_Widget *FltkListResource::createNewWidget (core::Allocation *allocation)
1099 {
1100 Fl_Tree *tree =
1101 new Fl_Tree (allocation->x, allocation->y, allocation->width,
1102 allocation->ascent + allocation->descent);
1103
1104 tree->selectmode((mode == SELECTION_MULTIPLE) ? FL_TREE_SELECT_MULTI
1105 : FL_TREE_SELECT_SINGLE);
1106 tree->showroot(0);
1107 tree->connectorstyle(FL_TREE_CONNECTOR_NONE);
1108 tree->marginleft(-14);
1109 tree->callback(widgetCallback,this);
1110 tree->when(FL_WHEN_CHANGED);
1111
1112 currParent = tree->root();
1113 return tree;
1114 }
1115
1116 void FltkListResource::setWidgetStyle (Fl_Widget *widget,
1117 core::style::Style *style)
1118 {
1119 Fl_Tree *t = (Fl_Tree *)widget;
1120
1121 FltkResource::setWidgetStyle(widget, style);
1122
1123 t->item_labelfont(widget->labelfont());
1124 t->item_labelsize(widget->labelsize());
1125 t->item_labelfgcolor(widget->labelcolor());
1126 t->item_labelbgcolor(widget->color());
1127 }
1128
1129 void FltkListResource::widgetCallback (Fl_Widget *widget, void *data)
1130 {
1131 Fl_Tree_Item *fltkItem = ((Fl_Tree *) widget)->callback_item ();
1132 int index = -1;
1133 if (fltkItem)
1134 index = (long) (fltkItem->user_data ());
1135 if (index > -1) {
1136 FltkListResource *res = (FltkListResource *) data;
1137 bool selected = fltkItem->is_selected ();
1138 res->itemsSelected.set (index, selected);
1139 }
1140 }
1141
1142 void *FltkListResource::newItem (const char *str, bool enabled, bool selected)
1143 {
1144 Fl_Tree *tree = (Fl_Tree *) widget;
1145 Fl_Tree_Item *parent = (Fl_Tree_Item *)currParent;
1146 Fl_Tree_Item *item = tree->add(parent, str);
1147 int index = itemsSelected.size();
1148
1149 enabled &= parent->is_active();
1150 item->activate(enabled);
1151 item->user_data((void*)(long)index);
1152 itemsSelected.increase ();
1153 itemsSelected.set (itemsSelected.size() - 1, selected);
1154
1155 return item;
1156 }
1157
1158 void FltkListResource::addItem (const char *str, bool enabled, bool selected)
1159 {
1160 Fl_Tree *tree = (Fl_Tree *) widget;
1161 Fl_Tree_Item *item = (Fl_Tree_Item *) newItem(str, enabled, selected);
1162
1163 if (selected) {
1164 if (mode == SELECTION_MULTIPLE) {
1165 item->select(selected);
1166 } else {
1167 const bool do_callback = true;
1168 tree->select_only(item, do_callback);
1169 }
1170 }
1171 queueResize (true);
1172 }
1173
1174 void FltkListResource::pushGroup (const char *name, bool enabled)
1175 {
1176 bool selected = false;
1177
1178 /* TODO: make it impossible to select a group */
1179 currParent = (Fl_Tree_Item *) newItem(name, enabled, selected);
1180 queueResize (true);
1181 }
1182
1183 void FltkListResource::popGroup ()
1184 {
1185 Fl_Tree_Item *p = (Fl_Tree_Item *)currParent;
1186
1187 if (p->parent())
1188 currParent = p->parent();
1189 }
1190
1191 int FltkListResource::getMaxItemWidth()
1192 {
1193 Fl_Tree *tree = (Fl_Tree *)widget;
1194 int max = 0;
1195
1196 for (Fl_Tree_Item *i = tree->first(); i; i = tree->next(i)) {
1197 int width = 0;
1198
1199 if (i == tree->root())
1200 continue;
1201
1202 for (Fl_Tree_Item *p = i->parent(); p != tree->root(); p = p->parent())
1203 width += tree->connectorwidth();
1204
1205 if (i->label())
1206 width += fl_width(i->label());
1207
1208 if (width > max)
1209 max = width;
1210 }
1211 return max;
1212 }
1213
1214 void FltkListResource::sizeRequest (core::Requisition *requisition)
1215 {
1216 if (style) {
1217 FltkFont *font = (FltkFont*)style->font;
1218 fl_font(font->font,font->size);
1219 int rows = getNumberOfItems();
1220 if (showRows < rows) {
1221 rows = showRows;
1222 }
1223 requisition->width = getMaxItemWidth() + 5 + Fl::scrollbar_size();;
1224 requisition->ascent = font->ascent + 5 +
1225 (rows - 1) * (font->ascent + font->descent + 1);
1226 requisition->descent = font->descent + 3;
1227 } else {
1228 requisition->width = 1;
1229 requisition->ascent = 1;
1230 requisition->descent = 0;
1231 }
1232 }
1233
1234 bool FltkListResource::isSelected (int index)
1235 {
1236 return itemsSelected.get (index);
1237 }
1238
1239 } // namespace ui
1240 } // namespace fltk
1241 } // namespace dw
1242
0 #ifndef __DW_FLTK_UI_HH__
1 #define __DW_FLTK_UI_HH__
2
3 #ifndef __INCLUDED_FROM_DW_FLTK_CORE_HH__
4 # error Do not include this file directly, use "fltkcore.hh" instead.
5 #endif
6
7 #include <FL/Fl_Button.H>
8 #include <FL/Fl_Menu.H>
9 #include <FL/Fl_Text_Buffer.H>
10
11 namespace dw {
12 namespace fltk {
13
14 /**
15 * \brief FLTK implementation of dw::core::ui.
16 *
17 * The design should be like this:
18 *
19 * \dot
20 * digraph G {
21 * node [shape=record, fontname=Helvetica, fontsize=10];
22 * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
23 * labelfontsize=10, color="#404040", labelfontcolor="#000080"];
24 * fontname=Helvetica; fontsize=10;
25 *
26 * subgraph cluster_core {
27 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
28 * label="dw::core::ui";
29 *
30 * Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"];
31 * LabelButtonResource [color="#a0a0a0",
32 * URL="\ref dw::core::ui::LabelButtonResource"];
33 * EntryResource [color="#a0a0a0",
34 * URL="\ref dw::core::ui::EntryResource"];
35 * }
36 *
37 * subgraph cluster_fltk {
38 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
39 * label="dw::fltk::ui";
40 *
41 * FltkResource [color="#a0a0a0", URL="\ref dw::fltk::ui::FltkResource"];
42 * FltkLabelButtonResource
43 * [URL="\ref dw::fltk::ui::FltkLabelButtonResource"];
44 * FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"];
45 * }
46 *
47 * Resource -> LabelButtonResource;
48 * Resource -> EntryResource;
49 * FltkResource -> FltkLabelButtonResource;
50 * FltkResource -> FltkEntryResource;
51 * Resource -> FltkResource;
52 * LabelButtonResource -> FltkLabelButtonResource;
53 * EntryResource -> FltkEntryResource;
54 * }
55 * \enddot
56 *
57 * <center>[\ref uml-legend "legend"]</center>
58 *
59 * where dw::fltk::ui::FltkResource provides some base funtionality for all
60 * conctrete FLTK implementations of sub-interfaces of dw::core::ui::Resource.
61 * However, this is not directly possible in C++, since the base class
62 * dw::core::ui::Resource is ambiguous for
63 * dw::fltk::ui::FltkLabelButtonResource.
64 *
65 * To solve this, we have to remove the dependency between
66 * dw::fltk::ui::FltkResource and dw::core::ui::Resource, instead, the part
67 * of dw::core::ui::Resource, which is implemented in
68 * dw::fltk::ui::FltkResource, must be explicitly delegated from
69 * dw::fltk::ui::FltkLabelButtonResourceto dw::fltk::ui::FltkResource:
70 *
71 * \dot
72 * digraph G {
73 * node [shape=record, fontname=Helvetica, fontsize=10];
74 * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
75 * labelfontsize=10, color="#404040", labelfontcolor="#000080"];
76 * fontname=Helvetica; fontsize=10;
77 *
78 * subgraph cluster_core {
79 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
80 * label="dw::core::ui";
81 *
82 * Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"];
83 * LabelButtonResource [color="#a0a0a0",
84 * URL="\ref dw::core::ui::LabelButtonResource"];
85 * EntryResource [color="#a0a0a0",
86 * URL="\ref dw::core::ui::EntryResource"];
87 * }
88 *
89 * subgraph cluster_fltk {
90 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
91 * label="dw::fltk::ui";
92 *
93 * FltkResource [color="#a0a0a0", URL="\ref dw::fltk::ui::FltkResource"];
94 * FltkLabelButtonResource
95 * [URL="\ref dw::fltk::ui::FltkLabelButtonResource"];
96 * FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"];
97 * }
98 *
99 * Resource -> LabelButtonResource;
100 * Resource -> EntryResource;
101 * FltkResource -> FltkLabelButtonResource;
102 * FltkResource -> FltkEntryResource;
103 * LabelButtonResource -> FltkLabelButtonResource;
104 * EntryResource -> FltkEntryResource;
105 * }
106 * \enddot
107 *
108 * <center>[\ref uml-legend "legend"]</center>
109 *
110 * To make this a bit simpler, we use templates:
111 *
112 * \dot
113 * digraph G {
114 * node [shape=record, fontname=Helvetica, fontsize=10];
115 * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
116 * labelfontsize=10, color="#404040", labelfontcolor="#000080"];
117 * fontname=Helvetica; fontsize=10;
118 *
119 * subgraph cluster_core {
120 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
121 * label="dw::core::ui";
122 *
123 * Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"];
124 * LabelButtonResource [color="#a0a0a0",
125 * URL="\ref dw::core::ui::LabelButtonResource"];
126 * EntryResource [color="#a0a0a0",
127 * URL="\ref dw::core::ui::EntryResource"];
128 * }
129 *
130 * subgraph cluster_fltk {
131 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
132 * label="dw::fltk::ui";
133 *
134 * FltkResource [color="#a0a0a0", URL="\ref dw::fltk::ui::FltkResource"];
135 * FltkSpecificResource [color="#a0a0a0",
136 * fillcolor="#ffffc0", style="filled"
137 * URL="\ref dw::fltk::ui::FltkSpecificResource"];
138 * FltkSpecificResource_button [color="#a0a0a0",
139 * label="FltkSpecificResource \<LabelButtonResource\>"];
140 * FltkSpecificResource_entry [color="#a0a0a0",
141 * label="FltkSpecificResource \<EntryResource\>"];
142 * FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"];
143 * FltkLabelButtonResource
144 * [URL="\ref dw::fltk::ui::FltkLabelButtonResource"];
145 * }
146 *
147 * Resource -> LabelButtonResource;
148 * Resource -> EntryResource;
149 * FltkResource -> FltkSpecificResource;
150 * FltkSpecificResource -> FltkSpecificResource_button [arrowhead="open",
151 * arrowtail="none",
152 * style="dashed",
153 * color="#808000"];
154 * FltkSpecificResource -> FltkSpecificResource_entry [arrowhead="open",
155 * arrowtail="none",
156 * style="dashed",
157 * color="#808000"];
158 * LabelButtonResource -> FltkSpecificResource_button;
159 * EntryResource -> FltkSpecificResource_entry;
160 * FltkSpecificResource_button -> FltkLabelButtonResource;
161 * FltkSpecificResource_entry -> FltkEntryResource;
162 * }
163 * \enddot
164 *
165 * <center>[\ref uml-legend "legend"]</center>
166 */
167 namespace ui {
168
169 /**
170 * ...
171 */
172 class FltkResource: public lout::object::Object
173 {
174 private:
175 bool enabled;
176
177 protected:
178 FltkView *view;
179 Fl_Widget *widget;
180 core::Allocation allocation;
181 FltkPlatform *platform;
182
183 core::style::Style *style;
184
185 FltkResource (FltkPlatform *platform);
186 void init (FltkPlatform *platform);
187 virtual Fl_Widget *createNewWidget (core::Allocation *allocation) = 0;
188
189 virtual void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
190 void setDisplayed (bool displayed);
191 bool displayed();
192 public:
193 ~FltkResource ();
194
195 virtual void attachView (FltkView *view);
196 virtual void detachView (FltkView *view);
197
198 void sizeAllocate (core::Allocation *allocation);
199 void draw (core::View *view, core::Rectangle *area);
200
201 void setStyle (core::style::Style *style);
202
203 bool isEnabled ();
204 void setEnabled (bool enabled);
205 };
206
207
208 template <class I> class FltkSpecificResource: public I, public FltkResource
209 {
210 public:
211 inline FltkSpecificResource (FltkPlatform *platform) :
212 FltkResource (platform) { }
213
214 void sizeAllocate (core::Allocation *allocation);
215 void draw (core::View *view, core::Rectangle *area);
216 void setStyle (core::style::Style *style);
217
218 bool isEnabled ();
219 void setEnabled (bool enabled);
220 };
221
222
223 class FltkLabelButtonResource:
224 public FltkSpecificResource <dw::core::ui::LabelButtonResource>
225 {
226 private:
227 const char *label;
228
229 static void widgetCallback (Fl_Widget *widget, void *data);
230
231 protected:
232 Fl_Widget *createNewWidget (core::Allocation *allocation);
233
234 public:
235 FltkLabelButtonResource (FltkPlatform *platform, const char *label);
236 ~FltkLabelButtonResource ();
237
238 void sizeRequest (core::Requisition *requisition);
239
240 const char *getLabel ();
241 void setLabel (const char *label);
242 };
243
244
245 class FltkComplexButtonResource:
246 public FltkSpecificResource <dw::core::ui::ComplexButtonResource>
247 {
248 private:
249 bool relief;
250
251 static void widgetCallback (Fl_Widget *widget, void *data);
252
253 protected:
254 FltkView *topView, *flatView;
255
256 void attachView (FltkView *view);
257 void detachView (FltkView *view);
258
259 void sizeAllocate (core::Allocation *allocation);
260
261 dw::core::Platform *createPlatform ();
262 void setLayout (dw::core::Layout *layout);
263
264 int reliefXThickness ();
265 int reliefYThickness ();
266
267 Fl_Widget *createNewWidget (core::Allocation *allocation);
268
269 public:
270 FltkComplexButtonResource (FltkPlatform *platform, dw::core::Widget *widget,
271 bool relief);
272 ~FltkComplexButtonResource ();
273 };
274
275
276 /**
277 * \bug Maximal length not supported yet.
278 * \todo Text values are not synchronized (not needed in dillo).
279 */
280 class FltkEntryResource:
281 public FltkSpecificResource <dw::core::ui::EntryResource>
282 {
283 private:
284 int maxLength;
285 bool password;
286 const char *initText;
287 char *label;
288 int label_w;
289 bool editable;
290
291 static void widgetCallback (Fl_Widget *widget, void *data);
292 void setDisplayed (bool displayed);
293
294 protected:
295 Fl_Widget *createNewWidget (core::Allocation *allocation);
296 void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
297
298 public:
299 FltkEntryResource (FltkPlatform *platform, int maxLength, bool password,
300 const char *label);
301 ~FltkEntryResource ();
302
303 void sizeRequest (core::Requisition *requisition);
304 void sizeAllocate (core::Allocation *allocation);
305
306 const char *getText ();
307 void setText (const char *text);
308 bool isEditable ();
309 void setEditable (bool editable);
310 };
311
312
313 class FltkMultiLineTextResource:
314 public FltkSpecificResource <dw::core::ui::MultiLineTextResource>
315 {
316 private:
317 Fl_Text_Buffer *buffer;
318 bool editable;
319 int numCols, numRows;
320
321 protected:
322 Fl_Widget *createNewWidget (core::Allocation *allocation);
323 void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
324
325 public:
326 FltkMultiLineTextResource (FltkPlatform *platform, int cols, int rows);
327 ~FltkMultiLineTextResource ();
328
329 void sizeRequest (core::Requisition *requisition);
330
331 const char *getText ();
332 void setText (const char *text);
333 bool isEditable ();
334 void setEditable (bool editable);
335 };
336
337
338 template <class I> class FltkToggleButtonResource:
339 public FltkSpecificResource <I>
340 {
341 private:
342 bool initActivated;
343
344 protected:
345 virtual Fl_Button *createNewButton (core::Allocation *allocation) = 0;
346 Fl_Widget *createNewWidget (core::Allocation *allocation);
347 void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
348
349 public:
350 FltkToggleButtonResource (FltkPlatform *platform,
351 bool activated);
352 ~FltkToggleButtonResource ();
353
354 void sizeRequest (core::Requisition *requisition);
355
356 bool isActivated ();
357 void setActivated (bool activated);
358 };
359
360
361 class FltkCheckButtonResource:
362 public FltkToggleButtonResource <dw::core::ui::CheckButtonResource>
363 {
364 protected:
365 Fl_Button *createNewButton (core::Allocation *allocation);
366
367 public:
368 FltkCheckButtonResource (FltkPlatform *platform,
369 bool activated);
370 ~FltkCheckButtonResource ();
371 };
372
373
374 class FltkRadioButtonResource:
375 public FltkToggleButtonResource <dw::core::ui::RadioButtonResource>
376 {
377 private:
378 class Group
379 {
380 private:
381 class FltkGroupIterator:
382 public dw::core::ui::RadioButtonResource::GroupIterator
383 {
384 private:
385 lout::container::typed::Iterator <FltkRadioButtonResource> it;
386
387 public:
388 inline FltkGroupIterator (lout::container::typed::List
389 <FltkRadioButtonResource>
390 *list)
391 { it = list->iterator (); }
392
393 bool hasNext ();
394 dw::core::ui::RadioButtonResource *getNext ();
395 void unref ();
396 };
397
398 lout::container::typed::List <FltkRadioButtonResource> *list;
399
400 protected:
401 ~Group ();
402
403 public:
404 Group (FltkRadioButtonResource *radioButtonResource);
405
406 inline lout::container::typed::Iterator <FltkRadioButtonResource>
407 iterator ()
408 {
409 return list->iterator ();
410 }
411
412 inline dw::core::ui::RadioButtonResource::GroupIterator
413 *groupIterator ()
414 {
415 return new FltkGroupIterator (list);
416 }
417
418 void connect (FltkRadioButtonResource *radioButtonResource);
419 void unconnect (FltkRadioButtonResource *radioButtonResource);
420 };
421
422 Group *group;
423
424 static void widgetCallback (Fl_Widget *widget, void *data);
425 void buttonClicked ();
426
427 protected:
428 Fl_Button *createNewButton (core::Allocation *allocation);
429
430 public:
431 FltkRadioButtonResource (FltkPlatform *platform,
432 FltkRadioButtonResource *groupedWith,
433 bool activated);
434 ~FltkRadioButtonResource ();
435
436 GroupIterator *groupIterator ();
437 };
438
439
440 template <class I> class FltkSelectionResource:
441 public FltkSpecificResource <I>
442 {
443 protected:
444 virtual bool setSelectedItems() { return false; }
445 virtual void addItem (const char *str, bool enabled, bool selected) = 0;
446 virtual void pushGroup (const char *name, bool enabled) = 0;
447 virtual void popGroup () = 0;
448 public:
449 FltkSelectionResource (FltkPlatform *platform) :
450 FltkSpecificResource<I> (platform) {};
451 dw::core::Iterator *iterator (dw::core::Content::Type mask, bool atEnd);
452 };
453
454
455 class FltkOptionMenuResource:
456 public FltkSelectionResource <dw::core::ui::OptionMenuResource>
457 {
458 protected:
459 Fl_Widget *createNewWidget (core::Allocation *allocation);
460 virtual bool setSelectedItems() { return true; }
461 void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
462 int getNumberOfItems();
463 int getMaxItemWidth ();
464 private:
465 static void widgetCallback (Fl_Widget *widget, void *data);
466 void enlargeMenu();
467 Fl_Menu_Item *newItem();
468 Fl_Menu_Item *menu;
469 int itemsAllocated, itemsUsed;
470 int visibleItems; /* not counting the invisible ones that close a group */
471 public:
472 FltkOptionMenuResource (FltkPlatform *platform);
473 ~FltkOptionMenuResource ();
474
475 void addItem (const char *str, bool enabled, bool selected);
476 void pushGroup (const char *name, bool enabled);
477 void popGroup ();
478
479 void sizeRequest (core::Requisition *requisition);
480 bool isSelected (int index);
481 };
482
483 class FltkListResource:
484 public FltkSelectionResource <dw::core::ui::ListResource>
485 {
486 protected:
487 Fl_Widget *createNewWidget (core::Allocation *allocation);
488 void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
489
490 int getNumberOfItems () {return itemsSelected.size();};
491 int getMaxItemWidth ();
492
493 private:
494 static void widgetCallback (Fl_Widget *widget, void *data);
495 void *newItem (const char *str, bool enabled, bool selected);
496 void *currParent;
497 lout::misc::SimpleVector <bool> itemsSelected;
498 int showRows;
499 ListResource::SelectionMode mode;
500 public:
501 FltkListResource (FltkPlatform *platform,
502 core::ui::ListResource::SelectionMode selectionMode,
503 int rows);
504 ~FltkListResource ();
505
506 void addItem (const char *str, bool enabled, bool selected);
507 void pushGroup (const char *name, bool enabled);
508 void popGroup ();
509
510 void sizeRequest (core::Requisition *requisition);
511 bool isSelected (int index);
512 };
513
514
515 } // namespace ui
516 } // namespace fltk
517 } // namespace dw
518
519
520 #endif // __DW_FLTK_UI_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "fltkviewport.hh"
22
23 #include <FL/Fl.H>
24 #include <FL/fl_draw.H>
25
26 #include <stdio.h>
27 #include "../lout/msg.h"
28
29 extern Fl_Widget* fl_oldfocus;
30
31 using namespace lout::object;
32 using namespace lout::container::typed;
33
34 namespace dw {
35 namespace fltk {
36
37 FltkViewBase::BackBuffer::BackBuffer ()
38 {
39 w = 0;
40 h = 0;
41 created = false;
42 }
43
44 FltkViewBase::BackBuffer::~BackBuffer ()
45 {
46 if (created)
47 fl_delete_offscreen (offscreen);
48 }
49
50 void FltkViewBase::BackBuffer::setSize (int w, int h)
51 {
52 if (!created || w > this->w || h > this->h) {
53 this->w = w;
54 this->h = h;
55 if (created)
56 fl_delete_offscreen (offscreen);
57 offscreen = fl_create_offscreen (w, h);
58 created = true;
59 }
60 }
61
62 FltkViewBase::BackBuffer *FltkViewBase::backBuffer;
63 bool FltkViewBase::backBufferInUse;
64
65 FltkViewBase::FltkViewBase (int x, int y, int w, int h, const char *label):
66 Fl_Group (x, y, w, h, label)
67 {
68 Fl_Group::current(0);
69 canvasWidth = 1;
70 canvasHeight = 1;
71 bgColor = FL_WHITE;
72 mouse_x = mouse_y = 0;
73 focused_child = NULL;
74 exposeArea = NULL;
75 if (backBuffer == NULL) {
76 backBuffer = new BackBuffer ();
77 }
78 box(FL_NO_BOX);
79 resizable(NULL);
80 }
81
82 FltkViewBase::~FltkViewBase ()
83 {
84 cancelQueueDraw ();
85 }
86
87 void FltkViewBase::setBufferedDrawing (bool b) {
88 if (b && backBuffer == NULL) {
89 backBuffer = new BackBuffer ();
90 } else if (!b && backBuffer != NULL) {
91 delete backBuffer;
92 backBuffer = NULL;
93 }
94 }
95
96 void FltkViewBase::draw ()
97 {
98 int d = damage ();
99
100 if ((d & FL_DAMAGE_USER1) && !(d & FL_DAMAGE_EXPOSE)) {
101 lout::container::typed::Iterator <core::Rectangle> it;
102
103 for (it = drawRegion.rectangles (); it.hasNext (); ) {
104 draw (it.getNext (), DRAW_BUFFERED);
105 }
106
107 drawRegion.clear ();
108 d &= ~FL_DAMAGE_USER1;
109 }
110
111 if (d & FL_DAMAGE_CHILD) {
112 drawChildWidgets ();
113 d &= ~FL_DAMAGE_CHILD;
114 }
115
116 if (d) {
117 dw::core::Rectangle rect (
118 translateViewXToCanvasX (x ()),
119 translateViewYToCanvasY (y ()),
120 w (),
121 h ());
122
123 if (d == FL_DAMAGE_SCROLL) {
124 // a clipping rectangle has already been set by fltk::scrollrect ()
125 draw (&rect, DRAW_PLAIN);
126 } else {
127 draw (&rect, DRAW_CLIPPED);
128 drawRegion.clear ();
129 }
130 }
131 }
132
133 void FltkViewBase::draw (const core::Rectangle *rect,
134 DrawType type)
135 {
136 int X = translateCanvasXToViewX (rect->x);
137 int Y = translateCanvasYToViewY (rect->y);
138 int W, H;
139
140 // fl_clip_box() can't handle values greater than SHRT_MAX!
141 if (X > x () + w () || Y > y () + h ())
142 return;
143
144 W = X + rect->width > x () + w () ? x () + w () - X : rect->width;
145 H = Y + rect->height > y () + h () ? y () + h () - Y : rect->height;
146
147 fl_clip_box(X, Y, W, H, X, Y, W, H);
148
149 core::Rectangle r (translateViewXToCanvasX (X),
150 translateViewYToCanvasY (Y), W, H);
151
152 if (r.isEmpty ())
153 return;
154
155 exposeArea = &r;
156
157 if (type == DRAW_BUFFERED && backBuffer && !backBufferInUse) {
158 backBufferInUse = true;
159 backBuffer->setSize (X + W, Y + H); // would be nicer to use (W, H)...
160 fl_begin_offscreen (backBuffer->offscreen);
161 fl_push_matrix ();
162 fl_color (bgColor);
163 fl_rectf (X, Y, W, H);
164 theLayout->expose (this, &r);
165 fl_pop_matrix ();
166 fl_end_offscreen ();
167 fl_copy_offscreen (X, Y, W, H, backBuffer->offscreen, X, Y);
168 backBufferInUse = false;
169 } else if (type == DRAW_BUFFERED || type == DRAW_CLIPPED) {
170 // if type == DRAW_BUFFERED but we do not have backBuffer available
171 // we fall back to clipped drawing
172 fl_push_clip (X, Y, W, H);
173 fl_color (bgColor);
174 fl_rectf (X, Y, W, H);
175 theLayout->expose (this, &r);
176 fl_pop_clip ();
177 } else {
178 fl_color (bgColor);
179 fl_rectf (X, Y, W, H);
180 theLayout->expose (this, &r);
181 }
182 // DEBUG:
183 //fl_color(FL_RED);
184 //fl_rect(X, Y, W, H);
185
186 exposeArea = NULL;
187 }
188
189 void FltkViewBase::drawChildWidgets () {
190 for (int i = children () - 1; i >= 0; i--) {
191 Fl_Widget& w = *child(i);
192 #if 0
193 PORT1.3
194 if (w.damage() & DAMAGE_CHILD_LABEL) {
195 draw_outside_label(w);
196 w.set_damage(w.damage() & ~DAMAGE_CHILD_LABEL);
197 }
198 #endif
199 update_child(w);
200 }
201 }
202
203 core::ButtonState getDwButtonState ()
204 {
205 int s1 = Fl::event_state ();
206 int s2 = (core::ButtonState)0;
207
208 if (s1 & FL_SHIFT) s2 |= core::SHIFT_MASK;
209 if (s1 & FL_CTRL) s2 |= core::CONTROL_MASK;
210 if (s1 & FL_ALT) s2 |= core::META_MASK;
211 if (s1 & FL_BUTTON1) s2 |= core::BUTTON1_MASK;
212 if (s1 & FL_BUTTON2) s2 |= core::BUTTON2_MASK;
213 if (s1 & FL_BUTTON3) s2 |= core::BUTTON3_MASK;
214
215 return (core::ButtonState)s2;
216 }
217
218 int FltkViewBase::handle (int event)
219 {
220 bool processed;
221
222 /**
223 * \todo Consider, whether this from the FLTK documentation has any
224 * impacts: "To receive fltk::RELEASE events you must return non-zero
225 * when passed a fltk::PUSH event. "
226 */
227 switch(event) {
228 case FL_PUSH:
229 /* Hide the tooltip */
230 theLayout->cancelTooltip();
231
232 processed =
233 theLayout->buttonPress (this, Fl::event_clicks () + 1,
234 translateViewXToCanvasX (Fl::event_x ()),
235 translateViewYToCanvasY (Fl::event_y ()),
236 getDwButtonState (), Fl::event_button ());
237 _MSG("PUSH => %s\n", processed ? "true" : "false");
238 if (processed) {
239 /* pressed dw content; give focus to the view */
240 Fl::focus(this);
241 return true;
242 }
243 break;
244 case FL_RELEASE:
245 processed =
246 theLayout->buttonRelease (this, Fl::event_clicks () + 1,
247 translateViewXToCanvasX (Fl::event_x ()),
248 translateViewYToCanvasY (Fl::event_y ()),
249 getDwButtonState (), Fl::event_button ());
250 _MSG("RELEASE => %s\n", processed ? "true" : "false");
251 if (processed)
252 return true;
253 break;
254 case FL_MOVE:
255 mouse_x = Fl::event_x();
256 mouse_y = Fl::event_y();
257 processed =
258 theLayout->motionNotify (this,
259 translateViewXToCanvasX (mouse_x),
260 translateViewYToCanvasY (mouse_y),
261 getDwButtonState ());
262 _MSG("MOVE => %s\n", processed ? "true" : "false");
263 if (processed)
264 return true;
265 break;
266 case FL_DRAG:
267 processed =
268 theLayout->motionNotify (this,
269 translateViewXToCanvasX (Fl::event_x ()),
270 translateViewYToCanvasY (Fl::event_y ()),
271 getDwButtonState ());
272 _MSG("DRAG => %s\n", processed ? "true" : "false");
273 if (processed)
274 return true;
275 break;
276 case FL_ENTER:
277 theLayout->enterNotify (this,
278 translateViewXToCanvasX (Fl::event_x ()),
279 translateViewYToCanvasY (Fl::event_y ()),
280 getDwButtonState ());
281 break;
282 case FL_HIDE:
283 /* WORKAROUND: strangely, the tooltip window is not automatically hidden
284 * with its parent. Here we fake a LEAVE to achieve it. */
285 case FL_LEAVE:
286 theLayout->leaveNotify (this, getDwButtonState ());
287 break;
288 case FL_FOCUS:
289 if (focused_child && find(focused_child) < children()) {
290 /* strangely, find() == children() if the child is not found */
291 focused_child->take_focus();
292 }
293 return 1;
294 case FL_UNFOCUS:
295 focused_child = fl_oldfocus;
296 return 0;
297 default:
298 break;
299 }
300 return Fl_Group::handle (event);
301 }
302
303 // ----------------------------------------------------------------------
304
305 void FltkViewBase::setLayout (core::Layout *layout)
306 {
307 theLayout = layout;
308 if (usesViewport())
309 theLayout->viewportSizeChanged(this, w(), h());
310 }
311
312 void FltkViewBase::setCanvasSize (int width, int ascent, int descent)
313 {
314 canvasWidth = width;
315 canvasHeight = ascent + descent;
316 }
317
318 void FltkViewBase::setCursor (core::style::Cursor cursor)
319 {
320 static Fl_Cursor mapDwToFltk[] = {
321 FL_CURSOR_CROSS,
322 FL_CURSOR_DEFAULT,
323 FL_CURSOR_HAND,
324 FL_CURSOR_MOVE,
325 FL_CURSOR_WE,
326 FL_CURSOR_NESW,
327 FL_CURSOR_NWSE,
328 FL_CURSOR_NS,
329 FL_CURSOR_NWSE,
330 FL_CURSOR_NESW,
331 FL_CURSOR_NS,
332 FL_CURSOR_WE,
333 FL_CURSOR_INSERT,
334 FL_CURSOR_WAIT,
335 FL_CURSOR_HELP
336 };
337
338 fl_cursor (mapDwToFltk[cursor]);
339 }
340
341 void FltkViewBase::setBgColor (core::style::Color *color)
342 {
343 bgColor = color ?
344 ((FltkColor*)color)->colors[dw::core::style::Color::SHADING_NORMAL] :
345 FL_WHITE;
346 }
347
348 void FltkViewBase::startDrawing (core::Rectangle *area)
349 {
350 }
351
352 void FltkViewBase::finishDrawing (core::Rectangle *area)
353 {
354 }
355
356 void FltkViewBase::queueDraw (core::Rectangle *area)
357 {
358 drawRegion.addRectangle (area);
359 /** DAMAGE_VALUE is just an arbitrary value other than DAMAGE_EXPOSE here */
360 damage (FL_DAMAGE_USER1);
361 }
362
363 void FltkViewBase::queueDrawTotal ()
364 {
365 damage (FL_DAMAGE_EXPOSE);
366 }
367
368 void FltkViewBase::cancelQueueDraw ()
369 {
370 }
371
372 void FltkViewBase::drawPoint (core::style::Color *color,
373 core::style::Color::Shading shading,
374 int x, int y)
375 {
376 }
377
378 void FltkViewBase::drawLine (core::style::Color *color,
379 core::style::Color::Shading shading,
380 int x1, int y1, int x2, int y2)
381 {
382 fl_color(((FltkColor*)color)->colors[shading]);
383 // we clip with a large border (5000px), as clipping causes artefacts
384 // with non-solid line styles.
385 // However it's still better than no clipping at all.
386 clipPoint (&x1, &y1, 5000);
387 clipPoint (&x2, &y2, 5000);
388 fl_line (translateCanvasXToViewX (x1),
389 translateCanvasYToViewY (y1),
390 translateCanvasXToViewX (x2),
391 translateCanvasYToViewY (y2));
392 }
393
394 void FltkViewBase::drawTypedLine (core::style::Color *color,
395 core::style::Color::Shading shading,
396 core::style::LineType type, int width,
397 int x1, int y1, int x2, int y2)
398 {
399 char dashes[3], w, ng, d, gap, len;
400 const int f = 2;
401
402 w = (width == 1) ? 0 : width;
403 if (type == core::style::LINE_DOTTED) {
404 /* customized drawing for dotted lines */
405 len = (x2 == x1) ? y2 - y1 + 1 : (y2 == y1) ? x2 - x1 + 1 : 0;
406 ng = len / f*width;
407 d = len % f*width;
408 gap = ng ? d/ng + (w > 3 ? 2 : 0) : 0;
409 dashes[0] = 1; dashes[1] = f*width-gap; dashes[2] = 0;
410 fl_line_style(FL_DASH + FL_CAP_ROUND, w, dashes);
411
412 /* These formulas also work, but ain't pretty ;)
413 * fl_line_style(FL_DOT + FL_CAP_ROUND, w);
414 * dashes[0] = 1; dashes[1] = 3*width-2; dashes[2] = 0;
415 */
416 } else if (type == core::style::LINE_DASHED) {
417 fl_line_style(FL_DASH + FL_CAP_ROUND, w);
418 }
419
420 fl_color(((FltkColor*)color)->colors[shading]);
421 drawLine (color, shading, x1, y1, x2, y2);
422
423 if (type != core::style::LINE_NORMAL)
424 fl_line_style(FL_SOLID);
425 }
426
427 void FltkViewBase::drawRectangle (core::style::Color *color,
428 core::style::Color::Shading shading,
429 bool filled,
430 int X, int Y, int width, int height)
431 {
432 fl_color(((FltkColor*)color)->colors[shading]);
433 if (width < 0) {
434 X += width;
435 width = -width;
436 }
437 if (height < 0) {
438 Y += height;
439 height = -height;
440 }
441
442 int x1 = X;
443 int y1 = Y;
444 int x2 = X + width;
445 int y2 = Y + height;
446
447 // We only support rectangles with line width 1px, so we clip with
448 // a rectangle 1px wider and higher than what we actually expose.
449 // This is only really necessary for non-filled rectangles.
450 clipPoint (&x1, &y1, 1);
451 clipPoint (&x2, &y2, 1);
452
453 x1 = translateCanvasXToViewX (x1);
454 y1 = translateCanvasYToViewY (y1);
455 x2 = translateCanvasXToViewX (x2);
456 y2 = translateCanvasYToViewY (y2);
457
458 if (filled)
459 fl_rectf (x1, y1, x2 - x1, y2 - y1);
460 else
461 fl_rect (x1, y1, x2 - x1, y2 - y1);
462 }
463
464 void FltkViewBase::drawArc (core::style::Color *color,
465 core::style::Color::Shading shading, bool filled,
466 int centerX, int centerY, int width, int height,
467 int angle1, int angle2)
468 {
469 fl_color(((FltkColor*)color)->colors[shading]);
470 int x = translateCanvasXToViewX (centerX) - width / 2;
471 int y = translateCanvasYToViewY (centerY) - height / 2;
472
473 fl_arc(x, y, width, height, 0.0, 360.0);
474 if (filled)
475 fl_pie(x, y, width, height, 0.0, 360.0);
476 }
477
478 void FltkViewBase::drawPolygon (core::style::Color *color,
479 core::style::Color::Shading shading,
480 bool filled, bool convex, int points[][2],
481 int npoints)
482 {
483 if (npoints > 0) {
484 fl_color(((FltkColor*)color)->colors[shading]);
485
486 if (filled) {
487 if (convex)
488 fl_begin_polygon();
489 else
490 fl_begin_complex_polygon();
491 } else
492 fl_begin_loop();
493
494 for (int i = 0; i < npoints; i++) {
495 fl_vertex(translateCanvasXToViewX(points[i][0]),
496 translateCanvasYToViewY(points[i][1]));
497 }
498 if (filled) {
499 if (convex)
500 fl_end_polygon();
501 else
502 fl_end_complex_polygon();
503 } else
504 fl_end_loop();
505 }
506 }
507
508 core::View *FltkViewBase::getClippingView (int x, int y, int width, int height)
509 {
510 fl_push_clip (translateCanvasXToViewX (x), translateCanvasYToViewY (y),
511 width, height);
512 return this;
513 }
514
515 void FltkViewBase::mergeClippingView (core::View *clippingView)
516 {
517 fl_pop_clip ();
518 }
519
520 // ----------------------------------------------------------------------
521
522 FltkWidgetView::FltkWidgetView (int x, int y, int w, int h,
523 const char *label):
524 FltkViewBase (x, y, w, h, label)
525 {
526 }
527
528 FltkWidgetView::~FltkWidgetView ()
529 {
530 }
531
532 void FltkWidgetView::drawText (core::style::Font *font,
533 core::style::Color *color,
534 core::style::Color::Shading shading,
535 int X, int Y, const char *text, int len)
536 {
537 FltkFont *ff = (FltkFont*)font;
538 fl_font(ff->font, ff->size);
539 fl_color(((FltkColor*)color)->colors[shading]);
540
541 if (!font->letterSpacing && !font->fontVariant) {
542 fl_draw(text, len,
543 translateCanvasXToViewX (X), translateCanvasYToViewY (Y));
544 } else {
545 /* Nonzero letter spacing adjustment, draw each glyph individually */
546 int viewX = translateCanvasXToViewX (X),
547 viewY = translateCanvasYToViewY (Y);
548 int curr = 0, next = 0, nb;
549 char chbuf[4];
550 int c, cu;
551
552 if (font->fontVariant == core::style::FONT_VARIANT_SMALL_CAPS) {
553 int sc_fontsize = lout::misc::roundInt(ff->size * 0.78);
554 for (curr = 0; next < len; curr = next) {
555 next = theLayout->nextGlyph(text, curr);
556 c = fl_utf8decode(text + curr, text + next, &nb);
557 if ((cu = fl_toupper(c)) == c) {
558 /* already uppercase, just draw the character */
559 fl_font(ff->font, ff->size);
560 fl_draw(text + curr, next - curr, viewX, viewY);
561 viewX += font->letterSpacing;
562 viewX += (int)fl_width(text + curr, next - curr);
563 } else {
564 /* make utf8 string for converted char */
565 nb = fl_utf8encode(cu, chbuf);
566 fl_font(ff->font, sc_fontsize);
567 fl_draw(chbuf, nb, viewX, viewY);
568 viewX += font->letterSpacing;
569 viewX += (int)fl_width(chbuf, nb);
570 }
571 }
572 } else {
573 while (next < len) {
574 next = theLayout->nextGlyph(text, curr);
575 fl_draw(text + curr, next - curr, viewX, viewY);
576 viewX += font->letterSpacing +
577 (int)fl_width(text + curr,next - curr);
578 curr = next;
579 }
580 }
581 }
582 }
583
584 void FltkWidgetView::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot,
585 int X, int Y, int width, int height)
586 {
587 ((FltkImgbuf*)imgbuf)->draw (this,
588 translateCanvasXToViewX (xRoot),
589 translateCanvasYToViewY (yRoot),
590 X, Y, width, height);
591 }
592
593 bool FltkWidgetView::usesFltkWidgets ()
594 {
595 return true;
596 }
597
598 void FltkWidgetView::addFltkWidget (Fl_Widget *widget,
599 core::Allocation *allocation)
600 {
601 allocateFltkWidget (widget, allocation);
602 add (widget);
603 }
604
605 void FltkWidgetView::removeFltkWidget (Fl_Widget *widget)
606 {
607 remove (widget);
608 }
609
610 void FltkWidgetView::allocateFltkWidget (Fl_Widget *widget,
611 core::Allocation *allocation)
612 {
613 widget->resize (translateCanvasXToViewX (allocation->x),
614 translateCanvasYToViewY (allocation->y),
615 allocation->width,
616 allocation->ascent + allocation->descent);
617 }
618
619 void FltkWidgetView::drawFltkWidget (Fl_Widget *widget,
620 core::Rectangle *area)
621 {
622 draw_child (*widget);
623 draw_outside_label(*widget);
624 }
625
626 } // namespace fltk
627 } // namespace dw
0 #ifndef __DW_FLTKVIEWBASE_HH__
1 #define __DW_FLTKVIEWBASE_HH__
2
3 #include <time.h> // for time_t
4 #include <sys/time.h> // for time_t in FreeBSD
5
6 #include <FL/Fl_Group.H>
7 #include <FL/x.H>
8
9 #include "fltkcore.hh"
10
11 namespace dw {
12 namespace fltk {
13
14 class FltkViewBase: public FltkView, public Fl_Group
15 {
16 private:
17 class BackBuffer {
18 private:
19 int w;
20 int h;
21 bool created;
22
23 public:
24 Fl_Offscreen offscreen;
25
26 BackBuffer ();
27 ~BackBuffer ();
28 void setSize(int w, int h);
29 };
30
31 typedef enum { DRAW_PLAIN, DRAW_CLIPPED, DRAW_BUFFERED } DrawType;
32
33 int bgColor;
34 core::Region drawRegion;
35 core::Rectangle *exposeArea;
36 static BackBuffer *backBuffer;
37 static bool backBufferInUse;
38
39 void draw (const core::Rectangle *rect, DrawType type);
40 void drawChildWidgets ();
41 inline void clipPoint (int *x, int *y, int border) {
42 if (exposeArea) {
43 if (*x < exposeArea->x - border)
44 *x = exposeArea->x - border;
45 if (*x > exposeArea->x + exposeArea->width + border)
46 *x = exposeArea->x + exposeArea->width + border;
47 if (*y < exposeArea->y - border)
48 *y = exposeArea->y - border;
49 if (*y > exposeArea->y + exposeArea->height + border)
50 *y = exposeArea->y + exposeArea->height + border;
51 }
52 }
53 protected:
54 core::Layout *theLayout;
55 int canvasWidth, canvasHeight;
56 int mouse_x, mouse_y;
57 Fl_Widget *focused_child;
58
59 virtual int translateViewXToCanvasX (int x) = 0;
60 virtual int translateViewYToCanvasY (int y) = 0;
61 virtual int translateCanvasXToViewX (int x) = 0;
62 virtual int translateCanvasYToViewY (int y) = 0;
63
64 public:
65 FltkViewBase (int x, int y, int w, int h, const char *label = 0);
66 ~FltkViewBase ();
67
68 void draw();
69 int handle (int event);
70
71 void setLayout (core::Layout *layout);
72 void setCanvasSize (int width, int ascent, int descent);
73 void setCursor (core::style::Cursor cursor);
74 void setBgColor (core::style::Color *color);
75
76 void startDrawing (core::Rectangle *area);
77 void finishDrawing (core::Rectangle *area);
78 void queueDraw (core::Rectangle *area);
79 void queueDrawTotal ();
80 void cancelQueueDraw ();
81 void drawPoint (core::style::Color *color,
82 core::style::Color::Shading shading,
83 int x, int y);
84 void drawLine (core::style::Color *color,
85 core::style::Color::Shading shading,
86 int x1, int y1, int x2, int y2);
87 void drawTypedLine (core::style::Color *color,
88 core::style::Color::Shading shading,
89 core::style::LineType type, int width,
90 int x1, int y1, int x2, int y2);
91 void drawRectangle (core::style::Color *color,
92 core::style::Color::Shading shading, bool filled,
93 int x, int y, int width, int height);
94 void drawArc (core::style::Color *color,
95 core::style::Color::Shading shading, bool filled,
96 int centerX, int centerY, int width, int height,
97 int angle1, int angle2);
98 void drawPolygon (core::style::Color *color,
99 core::style::Color::Shading shading,
100 bool filled, bool convex, int points[][2], int npoints);
101
102 core::View *getClippingView (int x, int y, int width, int height);
103 void mergeClippingView (core::View *clippingView);
104 void setBufferedDrawing (bool b);
105 };
106
107
108 class FltkWidgetView: public FltkViewBase
109 {
110 public:
111 FltkWidgetView (int x, int y, int w, int h, const char *label = 0);
112 ~FltkWidgetView ();
113
114 void drawText (core::style::Font *font,
115 core::style::Color *color,
116 core::style::Color::Shading shading,
117 int x, int y, const char *text, int len);
118 void drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot,
119 int x, int y, int width, int height);
120
121 bool usesFltkWidgets ();
122 void addFltkWidget (Fl_Widget *widget, core::Allocation *allocation);
123 void removeFltkWidget (Fl_Widget *widget);
124 void allocateFltkWidget (Fl_Widget *widget,
125 core::Allocation *allocation);
126 void drawFltkWidget (Fl_Widget *widget, core::Rectangle *area);
127 };
128
129 } // namespace fltk
130 } // namespace dw
131
132 #endif // __DW_FLTKVIEWBASE_HH__
133
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "fltkviewport.hh"
22
23 #include <FL/Fl.H>
24 #include <FL/fl_draw.H>
25 #include <FL/names.h>
26
27 #include <stdio.h>
28 #include "../lout/msg.h"
29
30 using namespace lout;
31 using namespace lout::object;
32 using namespace lout::container::typed;
33
34 namespace dw {
35 namespace fltk {
36
37 /*
38 * Lets SHIFT+{Left,Right} go to the parent
39 */
40 class CustScrollbar : public Fl_Scrollbar
41 {
42 public:
43 CustScrollbar(int x, int y, int w, int h) : Fl_Scrollbar(x,y,w,h) {};
44 int handle(int e) {
45 if (e == FL_SHORTCUT && Fl::event_state() == FL_SHIFT &&
46 (Fl::event_key() == FL_Left || Fl::event_key() == FL_Right))
47 return 0;
48 return Fl_Scrollbar::handle(e);
49 }
50 };
51
52 FltkViewport::FltkViewport (int X, int Y, int W, int H, const char *label):
53 FltkWidgetView (X, Y, W, H, label)
54 {
55 hscrollbar = new CustScrollbar (x (), y (), 1, 1);
56 hscrollbar->type(FL_HORIZONTAL);
57 hscrollbar->callback (hscrollbarCallback, this);
58 hscrollbar->hide();
59 add (hscrollbar);
60
61 vscrollbar = new Fl_Scrollbar (x (), y(), 1, 1);
62 vscrollbar->type(FL_VERTICAL);
63 vscrollbar->callback (vscrollbarCallback, this);
64 vscrollbar->hide();
65 add (vscrollbar);
66
67 hasDragScroll = 1;
68 scrollX = scrollY = scrollDX = scrollDY = 0;
69 horScrolling = verScrolling = dragScrolling = 0;
70
71 gadgetOrientation[0] = GADGET_HORIZONTAL;
72 gadgetOrientation[1] = GADGET_HORIZONTAL;
73 gadgetOrientation[2] = GADGET_VERTICAL;
74 gadgetOrientation[3] = GADGET_HORIZONTAL;
75
76 gadgets =
77 new container::typed::List <object::TypedPointer < Fl_Widget> >
78 (true);
79 }
80
81 FltkViewport::~FltkViewport ()
82 {
83 delete gadgets;
84 }
85
86 void FltkViewport::adjustScrollbarsAndGadgetsAllocation ()
87 {
88 int hdiff = 0, vdiff = 0;
89 int visibility = 0;
90
91 _MSG(" >>FltkViewport::adjustScrollbarsAndGadgetsAllocation\n");
92 if (hscrollbar->visible ())
93 visibility |= 1;
94 if (vscrollbar->visible ())
95 visibility |= 2;
96
97 if (gadgets->size () > 0) {
98 switch (gadgetOrientation [visibility]) {
99 case GADGET_VERTICAL:
100 hdiff = SCROLLBAR_THICKNESS;
101 vdiff = SCROLLBAR_THICKNESS * gadgets->size ();
102 break;
103
104 case GADGET_HORIZONTAL:
105 hdiff = SCROLLBAR_THICKNESS * gadgets->size ();
106 vdiff = SCROLLBAR_THICKNESS;
107 break;
108 }
109 } else {
110 hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
111 vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
112 }
113
114 hscrollbar->resize(x (), y () + h () - SCROLLBAR_THICKNESS,
115 w () - hdiff, SCROLLBAR_THICKNESS);
116 vscrollbar->resize(x () + w () - SCROLLBAR_THICKNESS, y (),
117 SCROLLBAR_THICKNESS, h () - vdiff);
118
119 int X = x () + w () - SCROLLBAR_THICKNESS;
120 int Y = y () + h () - SCROLLBAR_THICKNESS;
121 for (Iterator <TypedPointer < Fl_Widget> > it = gadgets->iterator ();
122 it.hasNext (); ) {
123 Fl_Widget *widget = it.getNext()->getTypedValue ();
124 widget->resize(x (), y (), SCROLLBAR_THICKNESS, SCROLLBAR_THICKNESS);
125
126 switch (gadgetOrientation [visibility]) {
127 case GADGET_VERTICAL:
128 Y -= SCROLLBAR_THICKNESS;
129 break;
130
131 case GADGET_HORIZONTAL:
132 X -= SCROLLBAR_THICKNESS;
133 break;
134 }
135 }
136 }
137
138 void FltkViewport::adjustScrollbarValues ()
139 {
140 hscrollbar->value (scrollX, hscrollbar->w (), 0, canvasWidth);
141 vscrollbar->value (scrollY, vscrollbar->h (), 0, canvasHeight);
142 }
143
144 void FltkViewport::hscrollbarChanged ()
145 {
146 scroll (hscrollbar->value () - scrollX, 0);
147 }
148
149 void FltkViewport::vscrollbarChanged ()
150 {
151 scroll (0, vscrollbar->value () - scrollY);
152 }
153
154 void FltkViewport::vscrollbarCallback (Fl_Widget *vscrollbar,void *viewportPtr)
155 {
156 ((FltkViewport*)viewportPtr)->vscrollbarChanged ();
157 }
158
159 void FltkViewport::hscrollbarCallback (Fl_Widget *hscrollbar,void *viewportPtr)
160 {
161 ((FltkViewport*)viewportPtr)->hscrollbarChanged ();
162 }
163
164 // ----------------------------------------------------------------------
165
166 void FltkViewport::resize(int X, int Y, int W, int H)
167 {
168 bool dimension_changed = W != w() || H != h();
169
170 Fl_Group::resize(X, Y, W, H);
171 if (dimension_changed) {
172 theLayout->viewportSizeChanged (this, W, H);
173 adjustScrollbarsAndGadgetsAllocation ();
174 }
175 }
176
177 void FltkViewport::draw_area (void *data, int x, int y, int w, int h)
178 {
179 FltkViewport *vp = (FltkViewport*) data;
180 fl_push_clip(x, y, w, h);
181
182 vp->FltkWidgetView::draw ();
183
184 for (Iterator <TypedPointer < Fl_Widget> > it = vp->gadgets->iterator();
185 it.hasNext (); ) {
186 Fl_Widget *widget = it.getNext()->getTypedValue ();
187 vp->draw_child (*widget);
188 }
189
190 fl_pop_clip();
191
192 }
193
194 void FltkViewport::draw ()
195 {
196 int hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
197 int vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
198 int d = damage();
199
200 if (d & FL_DAMAGE_SCROLL) {
201 clear_damage (FL_DAMAGE_SCROLL);
202 fl_scroll(x(), y(), w() - hdiff, h() - vdiff,
203 -scrollDX, -scrollDY, draw_area, this);
204 clear_damage (d & ~FL_DAMAGE_SCROLL);
205 }
206
207 if (d) {
208 draw_area(this, x(), y(), w () - hdiff, h () - vdiff);
209
210 if (d == FL_DAMAGE_CHILD) {
211 if (hscrollbar->damage ())
212 draw_child (*hscrollbar);
213 if (vscrollbar->damage ())
214 draw_child (*vscrollbar);
215 } else {
216 draw_child (*hscrollbar);
217 draw_child (*vscrollbar);
218 }
219 }
220
221 scrollDX = 0;
222 scrollDY = 0;
223 }
224
225 int FltkViewport::handle (int event)
226 {
227 _MSG("FltkViewport::handle %s\n", fl_eventnames[event]);
228
229 switch(event) {
230 case FL_KEYBOARD:
231 /* When the viewport has focus (and not one of its children), FLTK
232 * sends the event here. Returning zero tells FLTK to resend the
233 * event as SHORTCUT, which we finally route to the parent. */
234
235 /* As we don't know the exact keybindings set by the user, we ask
236 * for all of them (except Tab to keep form navigation). */
237 if (Fl::event_key() != FL_Tab)
238 return 0;
239 break;
240
241 case FL_SHORTCUT:
242 /* send it to the parent (UI) */
243 return 0;
244
245 case FL_FOCUS:
246 /** \bug Draw focus box. */
247 break;
248
249 case FL_UNFOCUS:
250 /** \bug Undraw focus box. */
251 break;
252
253 case FL_PUSH:
254 if (vscrollbar->visible() && Fl::event_inside(vscrollbar)) {
255 if (vscrollbar->handle(event))
256 verScrolling = 1;
257 } else if (hscrollbar->visible() && Fl::event_inside(hscrollbar)) {
258 if (hscrollbar->handle(event))
259 horScrolling = 1;
260 } else if (FltkWidgetView::handle(event) == 0 &&
261 Fl::event_button() == FL_MIDDLE_MOUSE) {
262 if (!hasDragScroll) {
263 /* let the parent widget handle it... */
264 return 0;
265 } else {
266 /* receive FL_DRAG and FL_RELEASE */
267 dragScrolling = 1;
268 dragX = Fl::event_x();
269 dragY = Fl::event_y();
270 setCursor (core::style::CURSOR_MOVE);
271 }
272 }
273 return 1;
274 break;
275
276 case FL_DRAG:
277 if (dragScrolling && Fl::event_button() == FL_MIDDLE_MOUSE) {
278 scroll(dragX - Fl::event_x(), dragY - Fl::event_y());
279 dragX = Fl::event_x();
280 dragY = Fl::event_y();
281 return 1;
282 } else if (verScrolling) {
283 vscrollbar->handle(event);
284 return 1;
285 } else if (horScrolling) {
286 hscrollbar->handle(event);
287 return 1;
288 }
289 break;
290
291 case FL_MOUSEWHEEL:
292 return (Fl::event_dx() ? hscrollbar : vscrollbar)->handle(event);
293 break;
294
295 case FL_RELEASE:
296 if (Fl::event_button() == FL_MIDDLE_MOUSE) {
297 dragScrolling = 0;
298 setCursor (core::style::CURSOR_DEFAULT);
299 } else if (verScrolling && vscrollbar->handle(event)) {
300 verScrolling = 0;
301 return 1;
302 } else if (horScrolling && hscrollbar->handle(event)) {
303 horScrolling = 0;
304 return 1;
305 }
306 break;
307
308 case FL_ENTER:
309 /* could be the result of, e.g., closing another window. */
310 mouse_x = Fl::event_x();
311 mouse_y = Fl::event_y();
312 positionChanged();
313 break;
314
315 case FL_LEAVE:
316 mouse_x = mouse_y = -1;
317 break;
318 }
319
320 return FltkWidgetView::handle (event);
321 }
322
323 // ----------------------------------------------------------------------
324
325 void FltkViewport::setCanvasSize (int width, int ascent, int descent)
326 {
327 FltkWidgetView::setCanvasSize (width, ascent, descent);
328 adjustScrollbarValues ();
329 }
330
331 /*
332 * This is used to simulate mouse motion (e.g., when scrolling).
333 */
334 void FltkViewport::positionChanged ()
335 {
336 if (mouse_x != -1 && dragScrolling == false)
337 (void)theLayout->motionNotify (this,
338 translateViewXToCanvasX (mouse_x),
339 translateViewYToCanvasY (mouse_y),
340 (core::ButtonState)0);
341 }
342
343 /*
344 * For scrollbars, this currently sets the same step to both vertical and
345 * horizontal. It may me differentiated if necessary.
346 */
347 void FltkViewport::setScrollStep(int step)
348 {
349 vscrollbar->linesize(step);
350 hscrollbar->linesize(step);
351 }
352
353 bool FltkViewport::usesViewport ()
354 {
355 return true;
356 }
357
358 int FltkViewport::getHScrollbarThickness ()
359 {
360 return SCROLLBAR_THICKNESS;
361 }
362
363 int FltkViewport::getVScrollbarThickness ()
364 {
365 return SCROLLBAR_THICKNESS;
366 }
367
368 void FltkViewport::scrollTo (int x, int y)
369 {
370 int hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
371 int vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
372
373 x = misc::min (x, canvasWidth - w() + hdiff);
374 x = misc::max (x, 0);
375
376 y = misc::min (y, canvasHeight - h() + vdiff);
377 y = misc::max (y, 0);
378
379 if (x == scrollX && y == scrollY) {
380 return;
381 }
382
383 /* multiple calls to scroll can happen before a redraw occurs.
384 * scrollDX / scrollDY can therefore be non-zero here.
385 */
386 updateCanvasWidgets (x - scrollX, y - scrollY);
387 scrollDX += x - scrollX;
388 scrollDY += y - scrollY;
389
390 scrollX = x;
391 scrollY = y;
392
393 adjustScrollbarValues ();
394 damage(FL_DAMAGE_SCROLL);
395 theLayout->scrollPosChanged (this, scrollX, scrollY);
396 positionChanged();
397 }
398
399 void FltkViewport::scroll (int dx, int dy)
400 {
401 scrollTo (scrollX + dx, scrollY + dy);
402 }
403
404 void FltkViewport::scroll (core::ScrollCommand cmd)
405 {
406 if (cmd == core::SCREEN_UP_CMD) {
407 scroll (0, -h () + vscrollbar->linesize ());
408 } else if (cmd == core::SCREEN_DOWN_CMD) {
409 scroll (0, h () - vscrollbar->linesize ());
410 } else if (cmd == core::LINE_UP_CMD) {
411 scroll (0, (int) -vscrollbar->linesize ());
412 } else if (cmd == core::LINE_DOWN_CMD) {
413 scroll (0, (int) vscrollbar->linesize ());
414 } else if (cmd == core::LEFT_CMD) {
415 scroll ((int) -hscrollbar->linesize (), 0);
416 } else if (cmd == core::RIGHT_CMD) {
417 scroll ((int) hscrollbar->linesize (), 0);
418 } else if (cmd == core::TOP_CMD) {
419 scrollTo (scrollX, 0);
420 } else if (cmd == core::BOTTOM_CMD) {
421 scrollTo (scrollX, canvasHeight); /* gets adjusted in scrollTo () */
422 }
423 }
424
425 void FltkViewport::setViewportSize (int width, int height,
426 int hScrollbarThickness,
427 int vScrollbarThickness)
428 {
429 int adjustReq =
430 (hscrollbar->visible() ? !hScrollbarThickness : hScrollbarThickness) ||
431 (vscrollbar->visible() ? !vScrollbarThickness : vScrollbarThickness);
432
433 _MSG("FltkViewport::setViewportSize old_w,old_h=%dx%d -> w,h=%dx%d\n"
434 "\t hThick=%d hVis=%d, vThick=%d vVis=%d, adjustReq=%d\n",
435 w(),h(),width,height,
436 hScrollbarThickness,hscrollbar->visible(),
437 vScrollbarThickness,vscrollbar->visible(), adjustReq);
438
439 (hScrollbarThickness > 0) ? hscrollbar->show () : hscrollbar->hide ();
440 (vScrollbarThickness > 0) ? vscrollbar->show () : vscrollbar->hide ();
441
442 /* If no scrollbar, go to the beginning */
443 scroll(hScrollbarThickness ? 0 : -scrollX,
444 vScrollbarThickness ? 0 : -scrollY);
445
446 /* Adjust when scrollbar visibility changes */
447 if (adjustReq)
448 adjustScrollbarsAndGadgetsAllocation ();
449 }
450
451 void FltkViewport::updateCanvasWidgets (int dx, int dy)
452 {
453 // scroll all child widgets except scroll bars
454 for (int i = children () - 1; i > 0; i--) {
455 Fl_Widget *widget = child (i);
456
457 if (widget == hscrollbar || widget == vscrollbar)
458 continue;
459
460 widget->position(widget->x () - dx, widget->y () - dy);
461 }
462 }
463
464 int FltkViewport::translateViewXToCanvasX (int X)
465 {
466 return X - x () + scrollX;
467 }
468
469 int FltkViewport::translateViewYToCanvasY (int Y)
470 {
471 return Y - y () + scrollY;
472 }
473
474 int FltkViewport::translateCanvasXToViewX (int X)
475 {
476 return X + x () - scrollX;
477 }
478
479 int FltkViewport::translateCanvasYToViewY (int Y)
480 {
481 return Y + y () - scrollY;
482 }
483
484 // ----------------------------------------------------------------------
485
486 void FltkViewport::setGadgetOrientation (bool hscrollbarVisible,
487 bool vscrollbarVisible,
488 FltkViewport::GadgetOrientation
489 gadgetOrientation)
490 {
491 this->gadgetOrientation[(hscrollbarVisible ? 0 : 1) |
492 (vscrollbarVisible ? 0 : 2)] = gadgetOrientation;
493 adjustScrollbarsAndGadgetsAllocation ();
494 }
495
496 void FltkViewport::addGadget (Fl_Widget *gadget)
497 {
498 /** \bug Reparent? */
499
500 gadgets->append (new TypedPointer < Fl_Widget> (gadget));
501 adjustScrollbarsAndGadgetsAllocation ();
502 }
503
504
505 } // namespace fltk
506 } // namespace dw
0 #ifndef __DW_FLTKVIEWPORT_HH__
1 #define __DW_FLTKVIEWPORT_HH__
2
3 #include <FL/Fl_Group.H>
4 #include <FL/Fl_Scrollbar.H>
5
6 #include "core.hh"
7 #include "fltkcore.hh"
8 #include "fltkviewbase.hh"
9
10 namespace dw {
11 namespace fltk {
12
13 class FltkViewport: public FltkWidgetView
14 {
15 public:
16 enum GadgetOrientation { GADGET_VERTICAL, GADGET_HORIZONTAL };
17
18 private:
19 enum { SCROLLBAR_THICKNESS = 15 };
20
21 int scrollX, scrollY;
22 int scrollDX, scrollDY;
23 int hasDragScroll, dragScrolling, dragX, dragY;
24 int horScrolling, verScrolling;
25
26 Fl_Scrollbar *vscrollbar, *hscrollbar;
27
28 GadgetOrientation gadgetOrientation[4];
29 lout::container::typed::List <lout::object::TypedPointer < Fl_Widget> >
30 *gadgets;
31
32 void adjustScrollbarsAndGadgetsAllocation ();
33 void adjustScrollbarValues ();
34 void hscrollbarChanged ();
35 void vscrollbarChanged ();
36 void positionChanged ();
37
38 static void hscrollbarCallback (Fl_Widget *hscrollbar, void *viewportPtr);
39 static void vscrollbarCallback (Fl_Widget *vscrollbar, void *viewportPtr);
40
41 void updateCanvasWidgets (int oldScrollX, int oldScrollY);
42 static void draw_area (void *data, int x, int y, int w, int h);
43
44 protected:
45 int translateViewXToCanvasX (int x);
46 int translateViewYToCanvasY (int y);
47 int translateCanvasXToViewX (int x);
48 int translateCanvasYToViewY (int y);
49
50 public:
51 FltkViewport (int x, int y, int w, int h, const char *label = 0);
52 ~FltkViewport ();
53
54 void resize(int x, int y, int w, int h);
55 void draw ();
56 int handle (int event);
57
58 void setCanvasSize (int width, int ascent, int descent);
59
60 bool usesViewport ();
61 int getHScrollbarThickness ();
62 int getVScrollbarThickness ();
63 void scroll(int dx, int dy);
64 void scroll(dw::core::ScrollCommand cmd);
65 void scrollTo (int x, int y);
66 void setViewportSize (int width, int height,
67 int hScrollbarThickness, int vScrollbarThickness);
68 void setScrollStep(int step);
69
70 void setGadgetOrientation (bool hscrollbarVisible, bool vscrollbarVisible,
71 GadgetOrientation gadgetOrientation);
72 void setDragScroll (bool enable) { hasDragScroll = enable ? 1 : 0; }
73 void addGadget (Fl_Widget *gadget);
74 };
75
76 } // namespace fltk
77 } // namespace dw
78
79 #endif // __DW_FLTKVIEWPORT_HH__
80
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "image.hh"
22 #include "../lout/msg.h"
23 #include "../lout/misc.hh"
24
25 namespace dw {
26
27 using namespace lout;
28
29 ImageMapsList::ImageMap::ImageMap ()
30 {
31 shapesAndLinks = new container::typed::List <ShapeAndLink> (true);
32 defaultLink = -1;
33 }
34
35 ImageMapsList::ImageMap::~ImageMap ()
36 {
37 delete shapesAndLinks;
38 }
39
40 void ImageMapsList::ImageMap::draw (core::View *view,core::style::Style *style,
41 int x, int y)
42 {
43 container::typed::Iterator <ShapeAndLink> it;
44
45 for (it = shapesAndLinks->iterator (); it.hasNext (); ) {
46 ShapeAndLink *shapeAndLink = it.getNext ();
47
48 shapeAndLink->shape->draw(view, style, x, y);
49 }
50 }
51
52 void ImageMapsList::ImageMap::add (core::Shape *shape, int link) {
53 ShapeAndLink *shapeAndLink = new ShapeAndLink ();
54 shapeAndLink->shape = shape;
55 shapeAndLink->link = link;
56 shapesAndLinks->append (shapeAndLink);
57 }
58
59 int ImageMapsList::ImageMap::link (int x, int y) {
60 container::typed::Iterator <ShapeAndLink> it;
61 int link = defaultLink;
62
63 for (it = shapesAndLinks->iterator (); it.hasNext (); ) {
64 ShapeAndLink *shapeAndLink = it.getNext ();
65
66 if (shapeAndLink->shape->isPointWithin (x, y)) {
67 link = shapeAndLink->link;
68 break;
69 }
70 }
71
72 return link;
73 }
74
75 ImageMapsList::ImageMapsList ()
76 {
77 imageMaps = new container::typed::HashTable <object::Object, ImageMap>
78 (true, true);
79 currentMap = NULL;
80 }
81
82 ImageMapsList::~ImageMapsList ()
83 {
84 delete imageMaps;
85 }
86
87 /**
88 * \brief Start a new map and make it the current one.
89 *
90 * This has to be called before dw::ImageMapsList::addShapeToCurrentMap.
91 * "key" is owned by the image map list, so a copy should be passed, when
92 * necessary.
93 */
94 void ImageMapsList::startNewMap (object::Object *key)
95 {
96 currentMap = new ImageMap ();
97 imageMaps->put (key, currentMap);
98 }
99
100 /**
101 * \brief Add a shape to the current map-
102 *
103 * "shape" is owned by the image map list, so a copy should be passed, when
104 * necessary.
105 */
106 void ImageMapsList::addShapeToCurrentMap (core::Shape *shape, int link)
107 {
108 currentMap->add (shape, link);
109 }
110
111 /**
112 * \brief Set default link for current map-
113 */
114 void ImageMapsList::setCurrentMapDefaultLink (int link)
115 {
116 currentMap->setDefaultLink (link);
117 }
118
119 void ImageMapsList::drawMap (lout::object::Object *key, core::View *view,
120 core::style::Style *style, int x, int y)
121 {
122 ImageMap *map = imageMaps->get (key);
123
124 if (map)
125 map->draw(view, style, x, y);
126 }
127
128 int ImageMapsList::link (object::Object *key, int x, int y)
129 {
130 int link = -1;
131 ImageMap *map = imageMaps->get (key);
132
133 if (map)
134 link = map->link (x, y);
135
136 return link;
137 }
138
139 // ----------------------------------------------------------------------
140
141 int Image::CLASS_ID = -1;
142
143 Image::Image(const char *altText)
144 {
145 registerName ("dw::Image", &CLASS_ID);
146 this->altText = altText ? strdup (altText) : NULL;
147 altTextWidth = -1; // not yet calculated
148 buffer = NULL;
149 clicking = false;
150 currLink = -1;
151 mapList = NULL;
152 mapKey = NULL;
153 isMap = false;
154 }
155
156 Image::~Image()
157 {
158 if (altText)
159 free(altText);
160 if (buffer)
161 buffer->unref ();
162 if (mapKey)
163 delete mapKey;
164 }
165
166 void Image::sizeRequestImpl (core::Requisition *requisition)
167 {
168 if (buffer) {
169 if (getStyle ()->height == core::style::LENGTH_AUTO &&
170 core::style::isAbsLength (getStyle ()->width) &&
171 buffer->getRootWidth () > 0) {
172 // preserve aspect ratio when only width is given
173 requisition->width = core::style::absLengthVal (getStyle ()->width);
174 requisition->ascent = buffer->getRootHeight () *
175 requisition->width / buffer->getRootWidth ();
176 } else if (getStyle ()->width == core::style::LENGTH_AUTO &&
177 core::style::isAbsLength (getStyle ()->height) &&
178 buffer->getRootHeight () > 0) {
179 // preserve aspect ratio when only height is given
180 requisition->ascent = core::style::absLengthVal (getStyle ()->height);
181 requisition->width = buffer->getRootWidth () *
182 requisition->ascent / buffer->getRootHeight ();
183 } else {
184 requisition->width = buffer->getRootWidth ();
185 requisition->ascent = buffer->getRootHeight ();
186 }
187 requisition->descent = 0;
188 } else {
189 if (altText && altText[0]) {
190 if (altTextWidth == -1)
191 altTextWidth =
192 layout->textWidth (getStyle()->font, altText, strlen (altText));
193
194 requisition->width = altTextWidth;
195 requisition->ascent = getStyle()->font->ascent;
196 requisition->descent = getStyle()->font->descent;
197 } else {
198 requisition->width = 0;
199 requisition->ascent = 0;
200 requisition->descent = 0;
201 }
202 }
203
204 requisition->width += getStyle()->boxDiffWidth ();
205 requisition->ascent += getStyle()->boxOffsetY ();
206 requisition->descent += getStyle()->boxRestHeight ();
207 }
208
209 void Image::sizeAllocateImpl (core::Allocation *allocation)
210 {
211 core::Imgbuf *oldBuffer;
212 int dx, dy;
213
214 /* if image is moved only */
215 if (allocation->width == this->allocation.width &&
216 allocation->ascent + allocation->descent == getHeight ())
217 return;
218
219 dx = getStyle()->boxDiffWidth ();
220 dy = getStyle()->boxDiffHeight ();
221 #if 0
222 MSG("boxDiffHeight = %d + %d, buffer=%p\n",
223 getStyle()->boxOffsetY(), getStyle()->boxRestHeight(), buffer);
224 MSG("getContentWidth() = allocation.width - style->boxDiffWidth ()"
225 " = %d - %d = %d\n",
226 this->allocation.width, getStyle()->boxDiffWidth(),
227 this->allocation.width - getStyle()->boxDiffWidth());
228 MSG("getContentHeight() = getHeight() - style->boxDiffHeight ()"
229 " = %d - %d = %d\n", this->getHeight(), getStyle()->boxDiffHeight(),
230 this->getHeight() - getStyle()->boxDiffHeight());
231 #endif
232 if (buffer &&
233 (allocation->width - dx > 0 ||
234 allocation->ascent + allocation->descent - dy > 0)) {
235 // Zero content size : simply wait...
236 // Only one dimension: naturally scale
237 oldBuffer = buffer;
238 buffer = oldBuffer->getScaledBuf (allocation->width - dx,
239 allocation->ascent
240 + allocation->descent - dy);
241 oldBuffer->unref ();
242 }
243 }
244
245 void Image::enterNotifyImpl (core::EventCrossing *event)
246 {
247 // BUG: this is wrong for image maps, but the cursor position is unknown.
248 currLink = getStyle()->x_link;
249
250 if (currLink != -1) {
251 (void) layout->emitLinkEnter (this, currLink, -1, -1, -1);
252 }
253 Widget::enterNotifyImpl(event);
254 }
255
256 void Image::leaveNotifyImpl (core::EventCrossing *event)
257 {
258 clicking = false;
259
260 if (currLink != -1) {
261 currLink = -1;
262 (void) layout->emitLinkEnter (this, -1, -1, -1, -1);
263 }
264 Widget::leaveNotifyImpl(event);
265 }
266
267 /*
268 * Return the coordinate relative to the contents.
269 * If the event occurred in the surrounding box, return the value at the
270 * edge of the contents instead.
271 */
272 int Image::contentX (core::MousePositionEvent *event)
273 {
274 int ret = event->xWidget - getStyle()->boxOffsetX();
275
276 ret = misc::min(getContentWidth(), misc::max(ret, 0));
277 return ret;
278 }
279
280 int Image::contentY (core::MousePositionEvent *event)
281 {
282 int ret = event->yWidget - getStyle()->boxOffsetY();
283
284 ret = misc::min(getContentHeight(), misc::max(ret, 0));
285 return ret;
286 }
287
288 bool Image::motionNotifyImpl (core::EventMotion *event)
289 {
290 if (mapList || isMap) {
291 int x = contentX(event);
292 int y = contentY(event);
293
294 if (mapList) {
295 /* client-side image map */
296 int newLink = mapList->link (mapKey, x, y);
297 if (newLink != currLink) {
298 currLink = newLink;
299 clicking = false;
300 /* \todo Using MAP/AREA styles would probably be best */
301 setCursor(newLink == -1 ? getStyle()->cursor :
302 core::style::CURSOR_POINTER);
303 (void) layout->emitLinkEnter (this, newLink, -1, -1, -1);
304 }
305 } else if (isMap && currLink != -1) {
306 /* server-side image map */
307 (void) layout->emitLinkEnter (this, currLink, -1, x, y);
308 }
309 }
310 return true;
311 }
312
313 bool Image::buttonPressImpl (core::EventButton *event)
314 {
315 bool ret = false;
316
317 currLink = mapList ? mapList->link(mapKey,contentX(event),contentY(event)) :
318 getStyle()->x_link;
319 if (event->button == 3){
320 (void)layout->emitLinkPress(this, currLink, getStyle()->x_img, -1, -1,
321 event);
322 ret = true;
323 } else if (event->button == 1 || currLink != -1){
324 clicking = true;
325 ret = true;
326 }
327 return ret;
328 }
329
330 bool Image::buttonReleaseImpl (core::EventButton *event)
331 {
332 currLink = mapList ? mapList->link(mapKey,contentX(event),contentY(event)) :
333 getStyle()->x_link;
334 if (clicking) {
335 int x = isMap ? contentX(event) : -1;
336 int y = isMap ? contentY(event) : -1;
337 clicking = false;
338 layout->emitLinkClick (this, currLink, getStyle()->x_img, x, y, event);
339 return true;
340 }
341 return false;
342 }
343
344 void Image::draw (core::View *view, core::Rectangle *area)
345 {
346 int dx, dy;
347 core::Rectangle content, intersection;
348
349 drawWidgetBox (view, area, false);
350
351 if (buffer) {
352 dx = getStyle()->boxOffsetX ();
353 dy = getStyle()->boxOffsetY ();
354 content.x = dx;
355 content.y = dy;
356 content.width = getContentWidth ();
357 content.height = getContentHeight ();
358
359 if (area->intersectsWith (&content, &intersection))
360 view->drawImage (buffer,
361 allocation.x + dx, allocation.y + dy,
362 intersection.x - dx, intersection.y - dy,
363 intersection.width, intersection.height);
364 } else {
365 core::View *clippingView;
366
367 if (altText && altText[0]) {
368 core::View *usedView = view;
369
370 clippingView = NULL;
371
372 if (altTextWidth == -1)
373 altTextWidth =
374 layout->textWidth (getStyle()->font, altText, strlen (altText));
375
376 if ((getContentWidth() < altTextWidth) ||
377 (getContentHeight() <
378 getStyle()->font->ascent + getStyle()->font->descent)) {
379 clippingView = usedView =
380 view->getClippingView (allocation.x + getStyle()->boxOffsetX (),
381 allocation.y + getStyle()->boxOffsetY (),
382 getContentWidth(),
383 getContentHeight());
384 }
385
386 usedView->drawText (getStyle()->font, getStyle()->color,
387 core::style::Color::SHADING_NORMAL,
388 allocation.x + getStyle()->boxOffsetX (),
389 allocation.y + getStyle()->boxOffsetY ()
390 + getStyle()->font->ascent,
391 altText, strlen(altText));
392
393 if (clippingView)
394 view->mergeClippingView (clippingView);
395 }
396 if (mapKey) {
397 clippingView = view->getClippingView (allocation.x +
398 getStyle()->boxOffsetX (),
399 allocation.y +
400 getStyle()->boxOffsetY (),
401 getContentWidth(),
402 getContentHeight());
403 mapList->drawMap(mapKey, clippingView, getStyle(),
404 allocation.x + getStyle()->boxOffsetX (),
405 allocation.y + getStyle()->boxOffsetY ());
406 view->mergeClippingView (clippingView);
407 }
408 }
409
410 /** TODO: draw selection */
411 }
412
413 core::Iterator *Image::iterator (core::Content::Type mask, bool atEnd)
414 {
415 //return new core::TextIterator (this, mask, atEnd, altText);
416 /** \bug Not implemented. */
417 return new core::EmptyIterator (this, mask, atEnd);
418 }
419
420 void Image::setBuffer (core::Imgbuf *buffer, bool resize)
421 {
422 core::Imgbuf *oldBuf = this->buffer;
423
424 if (resize)
425 queueResize (0, true);
426
427 if (wasAllocated () && getContentWidth () > 0 && getContentHeight () > 0) {
428 // Only scale when both dimensions are known.
429 this->buffer =
430 buffer->getScaledBuf (getContentWidth (), getContentHeight ());
431 } else {
432 this->buffer = buffer;
433 buffer->ref ();
434 }
435
436 if (oldBuf)
437 oldBuf->unref ();
438 }
439
440 void Image::drawRow (int row)
441 {
442 core::Rectangle area;
443
444 assert (buffer != NULL);
445
446 buffer->getRowArea (row, &area);
447 if (area.width && area.height)
448 queueDrawArea (area.x + getStyle()->boxOffsetX (),
449 area.y + getStyle()->boxOffsetY (),
450 area.width, area.height);
451 }
452
453
454 /**
455 * \brief Sets image as server side image map.
456 */
457 void Image::setIsMap ()
458 {
459 isMap = true;
460 }
461
462
463 /**
464 * \brief Sets image as client side image map.
465 *
466 * "list" is not owned by the image, the caller has to free it. "key"
467 * is owned by the image, if it is used by the caller afterwards, a copy
468 * should be passed.
469 */
470 void Image::setUseMap (ImageMapsList *list, object::Object *key)
471 {
472 mapList = list;
473 if (mapKey && mapKey != key)
474 delete mapKey;
475 mapKey = key;
476 }
477
478 } // namespace dw
0 #ifndef __DW_IMAGE_HH__
1 #define __DW_IMAGE_HH__
2
3 #include "core.hh"
4
5 namespace dw {
6
7 /**
8 * \brief Represents a list of client-side image maps.
9 *
10 * All image maps of a HTML page (in the future, also image maps from
11 * different HTML pages) are stored in a list, which is passed to the
12 * image, so that it is possible to deal with maps, which are defined
13 * after the image within the HTML page.
14 *
15 * Maps are referred by instances of object::Object. These keys are
16 * typically URLs, so the type representing URLS should be derived from
17 * object::Object.
18 *
19 * \todo Some methods within the key class have to be implemented, this
20 * is not clear at this time.
21 */
22 class ImageMapsList
23 {
24 private:
25 class ImageMap: public lout::object::Object {
26 private:
27 class ShapeAndLink: public lout::object::Object {
28 public:
29 core::Shape *shape;
30 int link;
31
32 ~ShapeAndLink () { if (shape) delete shape; };
33 };
34
35 lout::container::typed::List <ShapeAndLink> *shapesAndLinks;
36 int defaultLink;
37 public:
38 ImageMap ();
39 ~ImageMap ();
40
41 void draw (core::View *view, core::style::Style *style, int x, int y);
42 void add (core::Shape *shape, int link);
43 void setDefaultLink (int link) { defaultLink = link; };
44 int link (int x, int y);
45 };
46
47 lout::container::typed::HashTable <lout::object::Object, ImageMap>
48 *imageMaps;
49 ImageMap *currentMap;
50
51 public:
52 ImageMapsList ();
53 ~ImageMapsList ();
54
55 void startNewMap (lout::object::Object *key);
56 void addShapeToCurrentMap (core::Shape *shape, int link);
57 void setCurrentMapDefaultLink (int link);
58 void drawMap(lout::object::Object *key, core::View *view,
59 core::style::Style *style, int x, int y);
60 int link (lout::object::Object *key, int x, int y);
61 };
62
63 /**
64 * \brief Displays an instance of dw::core::Imgbuf.
65 *
66 * The dw::core::Imgbuf is automatically scaled, when needed, but dw::Image
67 * does not keep a reference on the root buffer.
68 *
69 *
70 * <h3>Signals</h3>
71 *
72 * For image maps, dw::Image uses the signals defined in
73 * dw::core::Layout::LinkReceiver. For client side image maps, -1 is
74 * passed for the coordinates, for server side image maps, the respective
75 * coordinates are used. See section "Image Maps" below.
76 *
77 *
78 * <h3>%Image Maps</h3>
79 *
80 * <h4>Client Side %Image Maps</h4>
81 *
82 * You must first create a list of image maps (dw::ImageMapList), which can
83 * be used for multiple images. The caller is responsible for freeing the
84 * dw::ImageMapList.
85 *
86 * Adding a map is done by dw::ImageMapsList::startNewMap. The key is an
87 * instance of a sub class of object::Object. In the context of HTML, this is
88 * a URL, which defines this map globally, by combining the URL of the
89 * document, this map is defined in, with the value of the attribute "name" of
90 * the \<MAP\> element, as a fragment.
91 *
92 * dw::ImageMapsList::addShapeToCurrentMap adds a shape to the current
93 * map. The \em link argument is a number, which is later passed to
94 * the dw::core::Layout::LinkReceiver.
95 *
96 * This map list is then, together with the key for the image, passed to
97 * dw::Image::setUseMap. For HTML, a URL with the value of the "ismap"
98 * attribute of \<IMG\> should be used.
99 *
100 * dw::Image will search the correct map, when needed. If it is not found
101 * at this time, but later defined, it will be found and used later. This is
102 * the case, when an HTML \<MAP\> is defined below the \<IMG\> in the
103 * document.
104 *
105 * Currently, only maps defined in the same document as the image may be
106 * used, since the dw::ImageMapsList is stored in the HTML link block, and
107 * contains only the image maps defined in the document.
108 *
109 * <h4>Server Side %Image Maps</h4>
110 *
111 * To use images for server side image maps, you must call
112 * dw::Image::setIsMap, and the dw::Image::style must contain a valid link
113 * (dw::core::style::Style::x_link). After this, motions and clicks are
114 * delegated to dw::core::Layout::LinkReceiver.
115 *
116 * \sa\ref dw-images-and-backgrounds
117 */
118 class Image: public core::Widget
119 {
120 private:
121 char *altText;
122 core::Imgbuf *buffer;
123 int altTextWidth;
124 bool clicking;
125 int currLink;
126 ImageMapsList *mapList;
127 Object *mapKey;
128 bool isMap;
129
130 protected:
131 void sizeRequestImpl (core::Requisition *requisition);
132 void sizeAllocateImpl (core::Allocation *allocation);
133
134 void draw (core::View *view, core::Rectangle *area);
135
136 bool buttonPressImpl (core::EventButton *event);
137 bool buttonReleaseImpl (core::EventButton *event);
138 void enterNotifyImpl (core::EventCrossing *event);
139 void leaveNotifyImpl (core::EventCrossing *event);
140 bool motionNotifyImpl (core::EventMotion *event);
141 int contentX (core::MousePositionEvent *event);
142 int contentY (core::MousePositionEvent *event);
143
144 //core::Iterator *iterator (Content::Type mask, bool atEnd);
145
146 public:
147 static int CLASS_ID;
148
149 Image(const char *altText);
150 ~Image();
151
152 core::Iterator *iterator (core::Content::Type mask, bool atEnd);
153
154 inline core::Imgbuf *getBuffer () { return buffer; }
155 void setBuffer (core::Imgbuf *buffer, bool resize = false);
156
157 void drawRow (int row);
158
159 void setIsMap ();
160 void setUseMap (ImageMapsList *list, Object *key);
161
162 /* This is a hack for the perhaps frivolous feature of drawing image map
163 * shapes when there is no image to display. If the map is defined after
164 * an image using an image map, and the actual image data has not been
165 * loaded, tell the image to redraw.
166 */
167 void forceMapRedraw () { if (mapKey && ! buffer) queueDraw (); };
168 };
169
170 } // namespace dw
171
172 #endif // __DW_IMAGE_HH__
0 #ifndef __DW_IMGBUF_HH__
1 #define __DW_IMGBUF_HH__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 namespace dw {
8 namespace core {
9
10 /**
11 * \brief The platform independent interface for image buffers.
12 *
13 * %Image buffers depend on the platform (see \ref dw-images-and-backgrounds),
14 * but have this general, platform independent interface. The purpose of
15 * an image buffer is
16 *
17 * <ol>
18 * <li> storing the image data,
19 * <li> handling scaled versions of this buffer, and
20 * <li> drawing.
21 * </ol>
22 *
23 * The latter must be done independently from the window.
24 *
25 * <h3>Creating</h3>
26 *
27 * %Image buffers are created by calling dw::core::Platform::createImgbuf.
28 *
29 * <h3>Storing %Image Data</h3>
30 *
31 * dw::core::Imgbuf supports five image types, which are listed in the table
32 * below. The representation defines, how the colors are stored within
33 * the data, which is passed to dw::core::Imgbuf::copyRow.
34 *
35 * <table>
36 * <tr><th>Type (dw::core::Imgbuf::Type) <th>Bytes per
37 * Pixel <th>Representation
38 * <tr><td>dw::core::Imgbuf::RGB <td>3 <td>red, green, blue
39 * <tr><td>dw::core::Imgbuf::RGBA <td>4 <td>red, green, blue, alpha
40 * <tr><td>dw::core::Imgbuf::GRAY <td>1 <td>gray value
41 * <tr><td>dw::core::Imgbuf::INDEXED <td>1 <td>index to colormap
42 * <tr><td>dw::core::Imgbuf::INDEXED_ALPHA <td>1 <td>index to colormap
43 * </table>
44 *
45 * The last two types need a colormap, which is set by
46 * dw::core::Imgbuf::setCMap, which must be called before
47 * dw::core::Imgbuf::copyRow. This function expects the colors as 32 bit
48 * unsigned integers, which have the format 0xrrbbgg (for indexed
49 * images), or 0xaarrggbb (for indexed alpha), respectively.
50 *
51 *
52 * <h3>Scaling</h3>
53 *
54 * The buffer with the original size, which was created by
55 * dw::core::Platform::createImgbuf, is called root buffer. Imgbuf provides
56 * the ability to scale buffers. Generally, both root buffers, as well as
57 * scaled buffers, may be shared, memory management is done by reference
58 * counters.
59 *
60 * Via dw::core::Imgbuf::getScaledBuf, you can retrieve a scaled buffer.
61 * Generally, something like this must work always, in an efficient way:
62 *
63 * \code
64 * dw::core::Imgbuf *curBuf, *oldBuf;
65 * int width, heigt,
66 * // ...
67 * oldBuf = curBuf;
68 * curBuf = oldBuf->getScaledBuf(oldBuf, width, height);
69 * oldBuf->unref();
70 * \endcode
71 *
72 * \em oldBuf may both be a root buffer, or a scaled buffer.
73 *
74 * The root buffer keeps a list of all children, and all methods
75 * operating on the image data (dw::core::Imgbuf::copyRow and
76 * dw::core::Imgbuf::setCMap) are delegated to the scaled buffers, when
77 * processed, and inherited, when a new scaled buffer is created. This
78 * means, that they must only be performed for the root buffer.
79 *
80 * A possible implementation could be (dw::fltk::FltkImgbuf does it this way):
81 *
82 * <ul>
83 * <li> If the method is called with an already scaled image buffer, this is
84 * delegated to the root buffer.
85 *
86 * <li> If the given size is the original size, the root buffer is
87 * returned, with an increased reference counter.
88 *
89 * <li> Otherwise, if this buffer has already been scaled to the given
90 * size, return this scaled buffer, with an increased reference
91 * counter.
92 *
93 * <li> Otherwise, return a new scaled buffer with reference counter 1.
94 * </ul>
95 *
96 * Special care is to be taken, when the root buffer is not used anymore,
97 * i.e. after dw::core::Imgbuf::unref the reference counter is 0, but there
98 * are still scaled buffers. Since all methods operating on the image data
99 * (dw::core::Imgbuf::copyRow and dw::core::Imgbuf::setCMap) are called for
100 * the root buffer, the root buffer is still needed, and so must not be
101 * deleted at this point. This is, how dw::fltk::FltkImgbuf solves this
102 * problem:
103 *
104 * <ul>
105 * <li> dw::fltk::FltkImgbuf::unref does, for root buffers, check, not only
106 * whether dw::fltk::FltkImgbuf::refCount is 0, but also, whether
107 * there are children left. When the latter is the case, the buffer
108 * is not deleted.
109 *
110 * <li> There is an additional check in dw::fltk::FltkImgbuf::detachScaledBuf,
111 * which deals with the case, that dw::fltk::FltkImgbuf::refCount is 0,
112 * and the last scaled buffer is removed.
113 * </ul>
114 *
115 * In the following example:
116 *
117 * \code
118 * dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform ();
119 * dw::core::Layout *layout = new dw::core::Layout (platform);
120 *
121 * dw::core::Imgbuf *rootbuf =
122 * layout->createImgbuf (dw::core::Imgbuf::RGB, 100, 100);
123 * dw::core::Imgbuf *scaledbuf = rootbuf->getScaledBuf (50, 50);
124 * rootbuf->unref ();
125 * scaledbuf->unref ();
126 * \endcode
127 *
128 * the root buffer is not deleted, when dw::core::Imgbuf::unref is called,
129 * since a scaled buffer is left. After calling dw::core::Imgbuf::unref for
130 * the scaled buffer, it is deleted, and after it, the root buffer.
131 *
132 * <h3>Drawing</h3>
133 *
134 * dw::core::Imgbuf provides no methods for drawing, instead, this is
135 * done by the views (implementation of dw::core::View).
136 *
137 * There are two situations, when drawing is necessary:
138 *
139 * <ol>
140 * <li> To react on expose events, the function dw::core::View::drawImage
141 * should be used, with the following parameters:
142 * <ul>
143 * <li> of course, the image buffer,
144 * <li> where the root of the image would be displayed (as \em xRoot
145 * and \em yRoot), and
146 * <li> the region within the image, which should be displayed (\em x,
147 * \em y, \em width, \em height).
148 * </ul>
149 *
150 * <li> When a row has been copied, it has to be drawn. To determine the
151 * area, which has to be drawn, the dw::core::Imgbuf::getRowArea
152 * should be used. The result can then passed
153 * to dw::core::View::drawImage.
154 * </ol>
155 *
156 * \sa \ref dw-images-and-backgrounds
157 */
158 class Imgbuf: public lout::object::Object, public lout::signal::ObservedObject
159 {
160 public:
161 enum Type { RGB, RGBA, GRAY, INDEXED, INDEXED_ALPHA };
162
163 /*
164 * Methods called from the image decoding
165 */
166
167 virtual void setCMap (int *colors, int num_colors) = 0;
168 virtual void copyRow (int row, const byte *data) = 0;
169 virtual void newScan () = 0;
170
171 /*
172 * Methods called from dw::Image
173 */
174
175 virtual Imgbuf* getScaledBuf (int width, int height) = 0;
176 virtual void getRowArea (int row, dw::core::Rectangle *area) = 0;
177 virtual int getRootWidth () = 0;
178 virtual int getRootHeight () = 0;
179
180 /*
181 * Reference counting.
182 */
183
184 virtual void ref () = 0;
185 virtual void unref () = 0;
186
187 /**
188 * \todo Comment
189 */
190 virtual bool lastReference () = 0;
191
192
193 /**
194 * \todo Comment
195 */
196 virtual void setDeleteOnUnref (bool deleteOnUnref) = 0;
197
198 /**
199 * \todo Comment
200 */
201 virtual bool isReferred () = 0;
202 };
203
204 } // namespace dw
205 } // namespace core
206
207 #endif // __DW_IMGBUF_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "core.hh"
22 #include <limits.h>
23
24 using namespace lout;
25
26 namespace dw {
27 namespace core {
28
29 // --------------
30 // Iterator
31 // --------------
32
33 Iterator::Iterator(Widget *widget, Content::Type mask, bool atEnd)
34 {
35 this->widget = widget;
36 this->mask = mask;
37 }
38
39 Iterator::Iterator(Iterator &it): object::Object (), misc::Comparable ()
40 {
41 widget = it.widget;
42 content = it.content;
43 }
44
45 Iterator::~Iterator()
46 {
47 }
48
49 bool Iterator::equals (Object *other)
50 {
51 Iterator *otherIt = (Iterator*)other;
52 return
53 this == otherIt ||
54 (getWidget() == otherIt->getWidget() && compareTo(otherIt) == 0);
55 }
56
57 /**
58 * \brief Delete the iterator.
59 *
60 * The destructor is hidden, implementations may use optimizations for
61 * the allocation. (Will soon be the case for dw::core::EmptyIteratorFactory.)
62 */
63 void Iterator::unref ()
64 {
65 delete this;
66 }
67
68 /**
69 * \brief Scrolls the viewport, so that the region between \em it1 and
70 * \em it2 is seen, according to \em hpos and \em vpos.
71 *
72 * The parameters \em start and \em end have the same meaning as in
73 * dw::core::Iterator::getAllocation, \em start refers
74 * to \em it1, while \em end rerers to \em it2.
75 *
76 * If \em it1 and \em it2 point to the same location (see code), only
77 * \em it1 is regarded, and both belowstart and belowend refer to it.
78 */
79 void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end,
80 HPosition hpos, VPosition vpos)
81 {
82 Allocation alloc1, alloc2, alloc;
83 int x1, x2, y1, y2;
84 DeepIterator *eit1, *eit2, *eit3;
85 int curStart, curEnd, cmp;
86 bool atStart;
87
88 if (it1->equals(it2)) {
89 it1->getAllocation (start, end, &alloc);
90 it1->getWidget()->getLayout()->scrollTo (hpos, vpos, alloc.x, alloc.y,
91 alloc.width,
92 alloc.ascent + alloc.descent);
93 } else {
94 // First, determine the rectangle all iterators from it1 and it2
95 // allocate, i.e. the smallest rectangle containing all allocations of
96 // these iterators.
97 eit1 = new DeepIterator (it1);
98 eit2 = new DeepIterator (it2);
99
100 x1 = INT_MAX;
101 x2 = INT_MIN;
102 y1 = INT_MAX;
103 y2 = INT_MIN;
104
105 for (eit3 = (DeepIterator*)eit1->clone (), atStart = true;
106 (cmp = eit3->compareTo (eit2)) <= 0;
107 eit3->next (), atStart = false) {
108 if (atStart)
109 curStart = start;
110 else
111 curStart = 0;
112
113 if (cmp == 0)
114 curEnd = end;
115 else
116 curEnd = INT_MAX;
117
118 eit3->getAllocation (curStart, curEnd, &alloc);
119 x1 = misc::min (x1, alloc.x);
120 x2 = misc::max (x2, alloc.x + alloc.width);
121 y1 = misc::min (y1, alloc.y);
122 y2 = misc::max (y2, alloc.y + alloc.ascent + alloc.descent);
123 }
124
125 delete eit3;
126 delete eit2;
127 delete eit1;
128
129 it1->getAllocation (start, INT_MAX, &alloc1);
130 it2->getAllocation (0, end, &alloc2);
131
132 if (alloc1.x > alloc2.x) {
133 //
134 // This is due to a line break within the region. When the line is
135 // longer than the viewport, and the region is actually quite short,
136 // the user would not see anything of the region, as in this figure
137 // (with region marked as "#"):
138 //
139 // +----------+ ,-- alloc1
140 // | | V
141 // | | ### ###
142 // ### ### | |
143 // ^ | | <-- viewport
144 // | +----------+
145 // `-- alloc2
146 // |----------------------------|
147 // width
148 //
149 // Therefore, we make the region smaller, so that the region will be
150 // displayed like this:
151 //
152 // ,-- alloc1
153 // +----|-----+
154 // | V |
155 // | ### ###|
156 // ### ### | |
157 // ^ | | <-- viewport
158 // `-- alloc2 +----------+
159 // |----------|
160 // width
161 //
162
163 /** \todo Changes in the viewport size, until the idle function is
164 * called, are not regarded. */
165
166 if (it1->getWidget()->getLayout()->getUsesViewport() &&
167 x2 - x1 > it1->getWidget()->getLayout()->getWidthViewport()) {
168 x1 = x2 - it1->getWidget()->getLayout()->getWidthViewport();
169 x2 = x1 + it1->getWidget()->getLayout()->getWidthViewport();
170 }
171 }
172
173 if (alloc1.y > alloc2.y) {
174 // This is similar to the case above, e.g. if the region ends in
175 // another table column.
176 if (it1->getWidget()->getLayout()->getUsesViewport() &&
177 y2 - y1 > it1->getWidget()->getLayout()->getHeightViewport()) {
178 y1 = y2 - it1->getWidget()->getLayout()->getHeightViewport();
179 y2 = y1 + it1->getWidget()->getLayout()->getHeightViewport();
180 }
181 }
182
183 it1->getWidget()->getLayout()->scrollTo (hpos, vpos,
184 x1, y1, x2 - x1, y2 - y1);
185 }
186 }
187
188 // -------------------
189 // EmptyIterator
190 // -------------------
191
192 EmptyIterator::EmptyIterator (Widget *widget, Content::Type mask, bool atEnd):
193 Iterator (widget, mask, atEnd)
194 {
195 this->content.type = (atEnd ? Content::END : Content::START);
196 }
197
198 EmptyIterator::EmptyIterator (EmptyIterator &it): Iterator (it)
199 {
200 }
201
202 object::Object *EmptyIterator::clone ()
203 {
204 return new EmptyIterator (*this);
205 }
206
207 int EmptyIterator::compareTo (misc::Comparable *other)
208 {
209 EmptyIterator *otherIt = (EmptyIterator*)other;
210
211 if (content.type == otherIt->content.type)
212 return 0;
213 else if (content.type == Content::START)
214 return -1;
215 else
216 return +1;
217 }
218
219 bool EmptyIterator::next ()
220 {
221 content.type = Content::END;
222 return false;
223 }
224
225 bool EmptyIterator::prev ()
226 {
227 content.type = Content::START;
228 return false;
229 }
230
231 void EmptyIterator::highlight (int start, int end, HighlightLayer layer)
232 {
233 }
234
235 void EmptyIterator::unhighlight (int direction, HighlightLayer layer)
236 {
237 }
238
239 void EmptyIterator::getAllocation (int start, int end, Allocation *allocation)
240 {
241 }
242
243 // ------------------
244 // TextIterator
245 // ------------------
246
247 TextIterator::TextIterator (Widget *widget, Content::Type mask, bool atEnd,
248 const char *text): Iterator (widget, mask, atEnd)
249 {
250 this->content.type = (atEnd ? Content::END : Content::START);
251 this->text = (mask & Content::TEXT) ? text : NULL;
252 }
253
254 TextIterator::TextIterator (TextIterator &it): Iterator (it)
255 {
256 text = it.text;
257 }
258
259 int TextIterator::compareTo (misc::Comparable *other)
260 {
261 TextIterator *otherIt = (TextIterator*)other;
262
263 if (content.type == otherIt->content.type)
264 return 0;
265
266 switch (content.type) {
267 case Content::START:
268 return -1;
269
270 case Content::TEXT:
271 if (otherIt->content.type == Content::START)
272 return +1;
273 else
274 return -1;
275
276 case Content::END:
277 return +1;
278
279 default:
280 misc::assertNotReached();
281 return 0;
282 }
283 }
284
285 bool TextIterator::next ()
286 {
287 if (content.type == Content::START && text != NULL) {
288 content.type = Content::TEXT;
289 content.text = text;
290 return true;
291 } else {
292 content.type = Content::END;
293 return false;
294 }
295 }
296
297 bool TextIterator::prev ()
298 {
299 if (content.type == Content::END && text != NULL) {
300 content.type = Content::TEXT;
301 content.text = text;
302 return true;
303 } else {
304 content.type = Content::START;
305 return false;
306 }
307 }
308
309 void TextIterator::getAllocation (int start, int end, Allocation *allocation)
310 {
311 // Return the allocation of the widget.
312 *allocation = *(getWidget()->getAllocation ());
313 }
314
315 // ------------------
316 // DeepIterator
317 // ------------------
318
319 DeepIterator::Stack::~Stack ()
320 {
321 for (int i = 0; i < size (); i++)
322 get(i)->unref ();
323 }
324
325 /*
326 * The following two methods are used by dw::core::DeepIterator::DeepIterator,
327 * when the passed dw::core::Iterator points to a widget. Since a
328 * dw::core::DeepIterator never returns a widget, the dw::core::Iterator has
329 * to be corrected, by searching for the next content downwards (within the
330 * widget pointed to), forwards, and backwards (in the traversed tree).
331 */
332
333 /*
334 * Search downwards. If fromEnd is true, start search at the end,
335 * otherwise at the beginning.
336 */
337 Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask,
338 bool fromEnd)
339 {
340 Iterator *it2, *it3;
341
342 //DEBUG_MSG (1, "%*smoving down (%swards) from %s\n",
343 // indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
344
345 assert (it->getContent()->type == Content::WIDGET);
346 it2 = it->getContent()->widget->iterator (mask, fromEnd);
347
348 if (it2 == NULL) {
349 // Moving downwards failed.
350 //DEBUG_MSG (1, "%*smoving down failed\n", indent, "");
351 return NULL;
352 }
353
354 while (fromEnd ? it2->prev () : it2->next ()) {
355 //DEBUG_MSG (1, "%*sexamining %s\n",
356 // indent, "", a_Dw_iterator_text (it2));
357
358 if (it2->getContent()->type == Content::WIDGET) {
359 // Another widget. Search in it downwards.
360 it3 = searchDownward (it2, mask, fromEnd);
361 if (it3 != NULL) {
362 it2->unref ();
363 return it3;
364 }
365 // Else continue in this widget.
366 } else {
367 // Success!
368 //DEBUG_MSG (1, "%*smoving down succeeded: %s\n",
369 // indent, "", a_Dw_iterator_text (it2));
370 return it2;
371 }
372 }
373
374 // Nothing found.
375 it2->unref ();
376 //DEBUG_MSG (1, "%*smoving down failed (nothing found)\n", indent, "");
377 return NULL;
378 }
379
380 /*
381 * Search sidewards. fromEnd specifies the direction, false means forwards,
382 * true means backwards.
383 */
384 Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
385 bool fromEnd)
386 {
387 Iterator *it2, *it3;
388
389 //DEBUG_MSG (1, "%*smoving %swards from %s\n",
390 // indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
391
392 assert (it->getContent()->type == Content::WIDGET);
393 it2 = it->cloneIterator ();
394
395 while (fromEnd ? it2->prev () : it2->next ()) {
396 if (it2->getContent()->type == Content::WIDGET) {
397 // Search downwards in this widget.
398 it3 = searchDownward (it2, mask, fromEnd);
399 if (it3 != NULL) {
400 it2->unref ();
401 //DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
402 // indent, "", from_end ? "back" : "for",
403 // a_Dw_iterator_text (it3));
404 return it3;
405 }
406 // Else continue in this widget.
407 } else {
408 // Success!
409 // DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
410 // indent, "", from_end ? "back" : "for",
411 // a_Dw_iterator_text (it2));
412 return it2;
413 }
414 }
415
416 /* Nothing found, go upwards in the tree (if possible). */
417 it2->unref ();
418 if (it->getWidget()->getParent ()) {
419 it2 = it->getWidget()->getParent()->iterator (mask, false);
420 while (true) {
421 if (!it2->next ())
422 misc::assertNotReached ();
423
424 if (it2->getContent()->type == Content::WIDGET &&
425 it2->getContent()->widget == it->getWidget ()) {
426 it3 = searchSideward (it2, mask, fromEnd);
427 it2->unref ();
428 //DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
429 // indent, "", from_end ? "back" : "for",
430 // a_Dw_iterator_text (it3));
431 return it3;
432 }
433 }
434 }
435
436 // Nothing found at all.
437 // DEBUG_MSG (1, "%*smoving %swards failed (nothing found)\n",
438 // indent, "", from_end ? "back" : "for");
439 return NULL;
440 }
441
442 /**
443 * \brief Create a new deep iterator from an existing dw::core::Iterator.
444 *
445 * The content of the return value will be the content of \em it. If within
446 * the widget tree, there is no non-widget content, the resulting deep
447 * iterator is empty (denoted by dw::core::DeepIterator::stack == NULL).
448 *
449 * Notes:
450 *
451 * <ol>
452 * <li> The mask of \em i" must include DW_CONTENT_WIDGET, but
453 * dw::core::DeepIterator::next will never return widgets.
454 * </ol>
455 */
456 DeepIterator::DeepIterator (Iterator *it)
457 {
458 //DEBUG_MSG (1, "a_Dw_ext_iterator_new: %s\n", a_Dw_iterator_text (it));
459
460 // Clone input iterator, so the iterator passed as parameter
461 // remains untouched.
462 it = it->cloneIterator ();
463 this->mask = it->getMask ();
464
465 hasContents = true;
466
467 // If it points to a widget, find a near non-widget content,
468 // since an DeepIterator should never return widgets.
469 if (it->getContent()->type == Content::WIDGET) {
470 Iterator *it2;
471
472 // The second argument of searchDownward is actually a matter of
473 // taste :-)
474 if ((it2 = searchDownward (it, mask, false)) ||
475 (it2 = searchSideward (it, mask, false)) ||
476 (it2 = searchSideward (it, mask, true))) {
477 it->unref ();
478 it = it2;
479 } else {
480 // This may happen, when a page does not contain any non-widget
481 // content.
482 //DEBUG_MSG (1, "a_Dw_ext_iterator_new got totally helpless!\n");
483 it->unref ();
484 hasContents = false;
485 }
486 }
487
488 //DEBUG_MSG (1, " => %s\n", a_Dw_iterator_text (it));
489
490 if (hasContents) {
491 // If this widget has parents, we must construct appropriate iterators.
492 //
493 // \todo There may be a faster way instead of iterating through the
494 // parent widgets.
495
496 // Construct the iterators.
497 int thisLevel = it->getWidget()->getLevel (), level;
498 Widget *w;
499 for (w = it->getWidget (), level = thisLevel; w->getParent() != NULL;
500 w = w->getParent (), level--) {
501 Iterator *it = w->getParent()->iterator (mask, false);
502 stack.put (it, level - 1);
503 while (true) {
504 bool hasNext = it->next();
505 assert (hasNext);
506
507 if (it->getContent()->type == Content::WIDGET &&
508 it->getContent()->widget == w)
509 break;
510 }
511 }
512
513 stack.put (it, thisLevel);
514 content = *(it->getContent());
515 }
516 }
517
518
519 DeepIterator::~DeepIterator ()
520 {
521 }
522
523 object::Object *DeepIterator::clone ()
524 {
525 DeepIterator *it = new DeepIterator ();
526
527 for (int i = 0; i < stack.size (); i++)
528 it->stack.put (stack.get(i)->cloneIterator (), i);
529
530 it->mask = mask;
531 it->content = content;
532 it->hasContents = hasContents;
533
534 return it;
535 }
536
537 int DeepIterator::compareTo (misc::Comparable *other)
538 {
539 DeepIterator *otherDeepIterator = (DeepIterator*)other;
540
541 // Search the highest level, where the widgets are the same.
542 int level = 0;
543
544 while (stack.get(level)->getWidget ()
545 == otherDeepIterator->stack.get(level)->getWidget ()) {
546 if (level == stack.size() - 1 ||
547 level == otherDeepIterator->stack.size() - 1)
548 break;
549 level++;
550 }
551
552 while (stack.get(level)->getWidget ()
553 != otherDeepIterator->stack.get(level)->getWidget ())
554 level--;
555
556 return stack.get(level)->compareTo (otherDeepIterator->stack.get(level));
557 }
558
559 DeepIterator *DeepIterator::createVariant(Iterator *it)
560 {
561 /** \todo Not yet implemented, and actually not yet needed very much. */
562 return new DeepIterator (it);
563 }
564
565 bool DeepIterator::isEmpty () {
566 return !hasContents;
567 }
568
569 /**
570 * \brief Move iterator forward and store content it.
571 *
572 * Returns true on success.
573 */
574 bool DeepIterator::next ()
575 {
576 Iterator *it = stack.getTop ();
577
578 if (it->next ()) {
579 if (it->getContent()->type == Content::WIDGET) {
580 // Widget: new iterator on stack, to search in this widget.
581 stack.push (it->getContent()->widget->iterator (mask, false));
582 return next ();
583 } else {
584 // Simply return the content of the iterartor.
585 content = *(it->getContent ());
586 return true;
587 }
588 } else {
589 // No more data in the top-most widget.
590 if (stack.size () > 1) {
591 // Pop iterator from stack, and move to next item in the old one.
592 stack.pop ();
593 return next ();
594 } else {
595 // Stack is empty.
596 content.type = Content::END;
597 return false;
598 }
599 }
600 }
601
602 /**
603 * \brief Move iterator backward and store content it.
604 *
605 * Returns true on success.
606 */
607 bool DeepIterator::prev ()
608 {
609 Iterator *it = stack.getTop ();
610
611 if (it->prev ()) {
612 if (it->getContent()->type == Content::WIDGET) {
613 // Widget: new iterator on stack, to search in this widget.
614 stack.push (it->getContent()->widget->iterator (mask, true));
615 return prev ();
616 } else {
617 // Simply return the content of the iterartor.
618 content = *(it->getContent ());
619 return true;
620 }
621 } else {
622 // No more data in the top-most widget.
623 if (stack.size () > 1) {
624 // Pop iterator from stack, and move to next item in the old one.
625 stack.pop ();
626 return prev ();
627 } else {
628 // Stack is empty.
629 content.type = Content::START;
630 return false;
631 }
632 }
633 }
634
635 // -----------------
636 // CharIterator
637 // -----------------
638
639 CharIterator::CharIterator ()
640 {
641 it = NULL;
642 }
643
644 CharIterator::CharIterator (Widget *widget)
645 {
646 Iterator *i = widget->iterator (Content::SELECTION_CONTENT, false);
647 it = new DeepIterator (i);
648 i->unref ();
649 ch = START;
650 }
651
652 CharIterator::~CharIterator ()
653 {
654 if (it)
655 delete it;
656 }
657
658 object::Object *CharIterator::clone()
659 {
660 CharIterator *cloned = new CharIterator ();
661 cloned->it = it->cloneDeepIterator ();
662 cloned->ch = ch;
663 cloned->pos = pos;
664 return cloned;
665 }
666
667 int CharIterator::compareTo(misc::Comparable *other)
668 {
669 CharIterator *otherIt = (CharIterator*)other;
670 int c = it->compareTo(otherIt->it);
671 if (c != 0)
672 return c;
673 else
674 return pos - otherIt->pos;
675 }
676
677 bool CharIterator::next ()
678 {
679 if (ch == START || it->getContent()->type == Content::BREAK ||
680 (it->getContent()->type == Content::TEXT &&
681 it->getContent()->text[pos] == 0)) {
682 if (it->next()) {
683 if (it->getContent()->type == Content::BREAK)
684 ch = '\n';
685 else { // if (it->getContent()->type == Content::TEXT)
686 pos = 0;
687 ch = it->getContent()->text[pos];
688 if (ch == 0)
689 // should not happen, actually
690 return next ();
691 }
692 return true;
693 }
694 else {
695 ch = END;
696 return false;
697 }
698 } else if (ch == END)
699 return false;
700 else {
701 // at this point, it->getContent()->type == Content::TEXT
702 pos++;
703 ch = it->getContent()->text[pos];
704 if (ch == 0) {
705 if (it->getContent()->space) {
706 ch = ' ';
707 } else {
708 return next ();
709 }
710 }
711
712 return true;
713 }
714 }
715
716 bool CharIterator::prev ()
717 {
718 if (ch == END || it->getContent()->type == Content::BREAK ||
719 (it->getContent()->type == Content::TEXT && pos == 0)) {
720 if (it->prev()) {
721 if (it->getContent()->type == Content::BREAK)
722 ch = '\n';
723 else { // if (it->getContent()->type == Content::TEXT)
724 if (it->getContent()->text[0] == 0)
725 return prev ();
726 else {
727 pos = strlen (it->getContent()->text);
728 if (it->getContent()->space) {
729 ch = ' ';
730 } else {
731 pos--;
732 ch = it->getContent()->text[pos];
733 }
734 }
735 }
736 return true;
737 }
738 else {
739 ch = START;
740 return false;
741 }
742 } else if (ch == START)
743 return false;
744 else {
745 // at this point, it->getContent()->type == Content::TEXT
746 pos--;
747 ch = it->getContent()->text[pos];
748 return true;
749 }
750 }
751
752 void CharIterator::highlight (CharIterator *it1, CharIterator *it2,
753 HighlightLayer layer)
754 {
755 if (it2->getChar () == CharIterator::END)
756 it2->prev ();
757
758 if (it1->it->compareTo (it2->it) == 0)
759 // Only one content => highlight part of it.
760 it1->it->highlight (it1->pos, it2->pos, layer);
761 else {
762 DeepIterator *it = it1->it->cloneDeepIterator ();
763 int c;
764 bool start;
765 for (start = true;
766 (c = it->compareTo (it2->it)) <= 0;
767 it->next (), start = false) {
768 int endOfWord =
769 it->getContent()->type == Content::TEXT ?
770 strlen (it->getContent()->text) : 1;
771 if (start) // first iteration
772 it->highlight (it1->pos, endOfWord, layer);
773 else if (c == 0) // last iteration
774 it->highlight (0, it2->pos, layer);
775 else
776 it->highlight (0, endOfWord, layer);
777 }
778 delete it;
779 }
780 }
781
782 void CharIterator::unhighlight (CharIterator *it1, CharIterator *it2,
783 HighlightLayer layer)
784 {
785 if (it1->it->compareTo (it2->it) == 0)
786 // Only one content => unhighlight it (only for efficiency).
787 it1->it->unhighlight (0, layer);
788 else {
789 DeepIterator *it = it1->it->cloneDeepIterator ();
790 for (; it->compareTo (it2->it) <= 0; it->next ())
791 it->unhighlight (-1, layer);
792 delete it;
793 }
794 }
795
796 } // namespace dw
797 } // namespace core
0 #ifndef __ITERATOR_HH__
1 #define __ITERATOR_HH__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 namespace dw {
8 namespace core {
9
10 /**
11 * \brief Iterators are used to iterate through the contents of a widget.
12 *
13 * When using iterators, you should care about the results of
14 * dw::core::Widget::hasContents.
15 *
16 * \sa dw::core::Widget::iterator
17 */
18 class Iterator: public lout::object::Object, public lout::misc::Comparable
19 {
20 protected:
21 Iterator(Widget *widget, Content::Type mask, bool atEnd);
22 Iterator(Iterator &it);
23 ~Iterator();
24
25 Content content;
26
27 private:
28 Widget *widget;
29 Content::Type mask;
30
31 public:
32 bool equals (Object *other);
33
34 inline Widget *getWidget () { return widget; }
35 inline Content *getContent () { return &content; }
36 inline Content::Type getMask () { return mask; }
37
38 virtual void unref ();
39
40 /**
41 * \brief Move iterator forward and store content it.
42 *
43 * Returns true on success.
44 */
45 virtual bool next () = 0;
46
47 /**
48 * \brief Move iterator backward and store content it.
49 *
50 * Returns true on success.
51 */
52 virtual bool prev () = 0;
53
54 /**
55 * \brief Extend highlighted region to contain part of the current content.
56 *
57 * For text, start and end define the
58 * characters, otherwise, the shape is defined as [0, 1], i.e. for
59 * highlighting a whole dw::core::Content, pass 0 and >= 1.
60 * To unhighlight see also dw::core::Iterator::unhighlight.
61 */
62 virtual void highlight (int start, int end, HighlightLayer layer) = 0;
63
64 /**
65 * \brief Shrink highlighted region to no longer contain the
66 * current content.
67 *
68 * The direction parameter indicates whether the highlighted region should
69 * be reduced from the start (direction > 0) or from the end
70 * (direction < 0). If direction is 0 all content is unhighlighted.
71 */
72 virtual void unhighlight (int direction, HighlightLayer layer) = 0;
73
74 /**
75 * \brief Return the shape, which a part of the item, the iterator points
76 * on, allocates.
77 *
78 * The parameters start and end have the same meaning as in
79 * DwIterator::highlight().
80 */
81 virtual void getAllocation (int start, int end, Allocation *allocation) = 0;
82
83 inline Iterator *cloneIterator () { return (Iterator*)clone(); }
84
85 static void scrollTo (Iterator *it1, Iterator *it2, int start, int end,
86 HPosition hpos, VPosition vpos);
87 };
88
89
90 /**
91 * \brief This implementation of dw::core::Iterator can be used by widgets
92 * with no contents.
93 */
94 class EmptyIterator: public Iterator
95 {
96 private:
97 EmptyIterator (EmptyIterator &it);
98
99 public:
100 EmptyIterator (Widget *widget, Content::Type mask, bool atEnd);
101
102 lout::object::Object *clone();
103 int compareTo(lout::misc::Comparable *other);
104 bool next ();
105 bool prev ();
106 void highlight (int start, int end, HighlightLayer layer);
107 void unhighlight (int direction, HighlightLayer layer);
108 void getAllocation (int start, int end, Allocation *allocation);
109 };
110
111
112 /**
113 * \brief This implementation of dw::core::Iterator can be used by widgets
114 * having one text word as contents
115 */
116 class TextIterator: public Iterator
117 {
118 private:
119 /** May be NULL, in this case, the next is skipped. */
120 const char *text;
121
122 TextIterator (TextIterator &it);
123
124 public:
125 TextIterator (Widget *widget, Content::Type mask, bool atEnd,
126 const char *text);
127
128 int compareTo(lout::misc::Comparable *other);
129
130 bool next ();
131 bool prev ();
132 void getAllocation (int start, int end, Allocation *allocation);
133 };
134
135
136 /**
137 * \brief A stack of iterators, to iterate recursively through a widget tree.
138 *
139 * This class is similar to dw::core::Iterator, but not
140 * created by a widget, but explicitly from another iterator. Deep
141 * iterators do not have the limitation, that iteration is only done within
142 * a widget, instead, child widgets are iterated through recursively.
143 */
144 class DeepIterator: public lout::object::Object, public lout::misc::Comparable
145 {
146 private:
147 class Stack: public lout::container::typed::Vector<Iterator>
148 {
149 public:
150 inline Stack (): lout::container::typed::Vector<Iterator> (4, false) { }
151 ~Stack ();
152 inline Iterator *getTop () { return get (size () - 1); }
153 inline void push (Iterator *it) { put(it, -1); }
154 inline void pop() { getTop()->unref (); remove (size () - 1); }
155 };
156
157 Stack stack;
158
159 static Iterator *searchDownward (Iterator *it, Content::Type mask,
160 bool fromEnd);
161 static Iterator *searchSideward (Iterator *it, Content::Type mask,
162 bool fromEnd);
163
164 Content::Type mask;
165 Content content;
166 bool hasContents;
167
168 inline DeepIterator () { }
169
170 public:
171 DeepIterator(Iterator *it);
172 ~DeepIterator();
173
174 lout::object::Object *clone ();
175
176 DeepIterator *createVariant(Iterator *it);
177 inline Iterator *getTopIterator () { return stack.getTop(); }
178 inline Content *getContent () { return &content; }
179
180 bool isEmpty ();
181
182 bool next ();
183 bool prev ();
184 inline DeepIterator *cloneDeepIterator() { return (DeepIterator*)clone(); }
185 int compareTo(lout::misc::Comparable *other);
186
187 /**
188 * \brief Highlight a part of the current content.
189 *
190 * Unhighlight the current content by passing -1 as start (see also
191 * (dw::core::Iterator::unhighlight). For text, start and end define the
192 * characters, otherwise, the shape is defined as [0, 1], i.e. for
193 * highlighting a whole dw::core::Content, pass 0 and >= 1.
194 */
195 inline void highlight (int start, int end, HighlightLayer layer)
196 { stack.getTop()->highlight (start, end, layer); }
197
198 /**
199 * \brief Return the shape, which a part of the item, the iterator points
200 * on, allocates.
201 *
202 * The parameters start and end have the same meaning as in
203 * DwIterator::highlight().
204 */
205 inline void getAllocation (int start, int end, Allocation *allocation)
206 { stack.getTop()->getAllocation (start, end, allocation); }
207
208 inline void unhighlight (int direction, HighlightLayer layer)
209 { stack.getTop()->unhighlight (direction, layer); }
210
211 inline static void scrollTo (DeepIterator *it1, DeepIterator *it2,
212 int start, int end,
213 HPosition hpos, VPosition vpos)
214 { Iterator::scrollTo(it1->stack.getTop(), it2->stack.getTop(),
215 start, end, hpos, vpos); }
216 };
217
218 class CharIterator: public lout::object::Object, public lout::misc::Comparable
219 {
220 public:
221 // START and END must not clash with any char value
222 // neither for signed nor unsigned char.
223 enum { START = 257, END = 258 };
224
225 private:
226 DeepIterator *it;
227 int pos, ch;
228
229 CharIterator ();
230
231 public:
232 CharIterator (Widget *widget);
233 ~CharIterator ();
234
235 lout::object::Object *clone();
236 int compareTo(lout::misc::Comparable *other);
237
238 bool next ();
239 bool prev ();
240 inline int getChar() { return ch; }
241 inline CharIterator *cloneCharIterator() { return (CharIterator*)clone(); }
242
243 static void highlight (CharIterator *it1, CharIterator *it2,
244 HighlightLayer layer);
245 static void unhighlight (CharIterator *it1, CharIterator *it2,
246 HighlightLayer layer);
247
248 inline static void scrollTo (CharIterator *it1, CharIterator *it2,
249 HPosition hpos, VPosition vpos)
250 { DeepIterator::scrollTo(it1->it, it2->it, it1->pos, it2->pos,
251 hpos, vpos); }
252 };
253
254 } // namespace dw
255 } // namespace core
256
257 #endif // __ITERATOR_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "core.hh"
22
23 #include "../lout/msg.h"
24 #include "../lout/debug.hh"
25 #include "../lout/misc.hh"
26
27 using namespace lout;
28 using namespace lout::container;
29 using namespace lout::object;
30
31 namespace dw {
32 namespace core {
33
34 void Layout::Receiver::canvasSizeChanged (int width, int ascent, int descent)
35 {
36 }
37
38 // ----------------------------------------------------------------------
39
40 bool Layout::Emitter::emitToReceiver (lout::signal::Receiver *receiver,
41 int signalNo, int argc,
42 lout::object::Object **argv)
43 {
44 Receiver *layoutReceiver = (Receiver*)receiver;
45
46 switch (signalNo) {
47 case CANVAS_SIZE_CHANGED:
48 layoutReceiver->canvasSizeChanged (((Integer*)argv[0])->getValue (),
49 ((Integer*)argv[1])->getValue (),
50 ((Integer*)argv[2])->getValue ());
51 break;
52
53 default:
54 misc::assertNotReached ();
55 }
56
57 return false;
58 }
59
60 void Layout::Emitter::emitCanvasSizeChanged (int width,
61 int ascent, int descent)
62 {
63 Integer w (width), a (ascent), d (descent);
64 Object *argv[3] = { &w, &a, &d };
65 emitVoid (CANVAS_SIZE_CHANGED, 3, argv);
66 }
67
68 // ----------------------------------------------------------------------
69
70 bool Layout::LinkReceiver::enter (Widget *widget, int link, int img,
71 int x, int y)
72 {
73 return false;
74 }
75
76 bool Layout::LinkReceiver::press (Widget *widget, int link, int img,
77 int x, int y, EventButton *event)
78 {
79 return false;
80 }
81
82 bool Layout::LinkReceiver::release (Widget *widget, int link, int img,
83 int x, int y, EventButton *event)
84 {
85 return false;
86 }
87
88 bool Layout::LinkReceiver::click (Widget *widget, int link, int img,
89 int x, int y, EventButton *event)
90 {
91 return false;
92 }
93
94 // ----------------------------------------------------------------------
95
96 bool Layout::LinkEmitter::emitToReceiver (lout::signal::Receiver *receiver,
97 int signalNo, int argc,
98 lout::object::Object **argv)
99 {
100 LinkReceiver *linkReceiver = (LinkReceiver*)receiver;
101
102 switch (signalNo) {
103 case ENTER:
104 return linkReceiver->enter ((Widget*)argv[0],
105 ((Integer*)argv[1])->getValue (),
106 ((Integer*)argv[2])->getValue (),
107 ((Integer*)argv[3])->getValue (),
108 ((Integer*)argv[4])->getValue ());
109
110 case PRESS:
111 return linkReceiver->press ((Widget*)argv[0],
112 ((Integer*)argv[1])->getValue (),
113 ((Integer*)argv[2])->getValue (),
114 ((Integer*)argv[3])->getValue (),
115 ((Integer*)argv[4])->getValue (),
116 (EventButton*)argv[5]);
117
118 case RELEASE:
119 return linkReceiver->release ((Widget*)argv[0],
120 ((Integer*)argv[1])->getValue (),
121 ((Integer*)argv[2])->getValue (),
122 ((Integer*)argv[3])->getValue (),
123 ((Integer*)argv[4])->getValue (),
124 (EventButton*)argv[5]);
125
126 case CLICK:
127 return linkReceiver->click ((Widget*)argv[0],
128 ((Integer*)argv[1])->getValue (),
129 ((Integer*)argv[2])->getValue (),
130 ((Integer*)argv[3])->getValue (),
131 ((Integer*)argv[4])->getValue (),
132 (EventButton*)argv[5]);
133
134 default:
135 misc::assertNotReached ();
136 }
137 return false;
138 }
139
140 bool Layout::LinkEmitter::emitEnter (Widget *widget, int link, int img,
141 int x, int y)
142 {
143 Integer ilink (link), iimg (img), ix (x), iy (y);
144 Object *argv[5] = { widget, &ilink, &iimg, &ix, &iy };
145 return emitBool (ENTER, 5, argv);
146 }
147
148 bool Layout::LinkEmitter::emitPress (Widget *widget, int link, int img,
149 int x, int y, EventButton *event)
150 {
151 Integer ilink (link), iimg (img), ix (x), iy (y);
152 Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
153 return emitBool (PRESS, 6, argv);
154 }
155
156 bool Layout::LinkEmitter::emitRelease (Widget *widget, int link, int img,
157 int x, int y, EventButton *event)
158 {
159 Integer ilink (link), iimg (img), ix (x), iy (y);
160 Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
161 return emitBool (RELEASE, 6, argv);
162 }
163
164 bool Layout::LinkEmitter::emitClick (Widget *widget, int link, int img,
165 int x, int y, EventButton *event)
166 {
167 Integer ilink (link), iimg (img), ix (x), iy (y);
168 Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
169 return emitBool (CLICK, 6, argv);
170 }
171
172 // ---------------------------------------------------------------------
173
174 Layout::Anchor::~Anchor ()
175 {
176 free(name);
177 }
178
179 // ---------------------------------------------------------------------
180
181 Layout::Layout (Platform *platform)
182 {
183 this->platform = platform;
184 view = NULL;
185 topLevel = NULL;
186 widgetAtPoint = NULL;
187
188 DBG_OBJ_CREATE (this, "DwRenderLayout");
189
190 bgColor = NULL;
191 cursor = style::CURSOR_DEFAULT;
192
193 canvasWidth = canvasAscent = canvasDescent = 0;
194
195 usesViewport = false;
196 scrollX = scrollY = 0;
197 viewportWidth = viewportHeight = 0;
198 hScrollbarThickness = vScrollbarThickness = 0;
199
200 requestedAnchor = NULL;
201 scrollIdleId = -1;
202 scrollIdleNotInterrupted = false;
203
204 anchorsTable =
205 new container::typed::HashTable <object::String, Anchor> (true, true);
206
207 resizeIdleId = -1;
208
209 textZone = new misc::ZoneAllocator (16 * 1024);
210
211 DBG_OBJ_ASSOC (&findtextState, this);
212 DBG_OBJ_ASSOC (&selectionState, this);
213
214 platform->setLayout (this);
215
216 selectionState.setLayout(this);
217 }
218
219 Layout::~Layout ()
220 {
221 widgetAtPoint = NULL;
222
223 if (scrollIdleId != -1)
224 platform->removeIdle (scrollIdleId);
225 if (resizeIdleId != -1)
226 platform->removeIdle (resizeIdleId);
227 if (bgColor)
228 bgColor->unref ();
229 if (topLevel) {
230 Widget *w = topLevel;
231 topLevel = NULL;
232 delete w;
233 }
234 delete platform;
235 delete view;
236 delete anchorsTable;
237 delete textZone;
238 }
239
240 void Layout::addWidget (Widget *widget)
241 {
242 if (topLevel) {
243 MSG_WARN("widget already set\n");
244 return;
245 }
246
247 topLevel = widget;
248 widget->layout = this;
249
250 findtextState.setWidget (widget);
251
252 canvasHeightGreater = false;
253 setSizeHints ();
254 queueResize ();
255 }
256
257 void Layout::removeWidget ()
258 {
259 /**
260 * \bug Some more attributes must be reset here.
261 */
262 topLevel = NULL;
263 widgetAtPoint = NULL;
264 canvasWidth = canvasAscent = canvasDescent = 0;
265 scrollX = scrollY = 0;
266
267 view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent);
268 if (view->usesViewport ())
269 view->setViewportSize (viewportWidth, viewportHeight, 0, 0);
270 view->queueDrawTotal ();
271
272 setAnchor (NULL);
273 updateAnchor ();
274
275 emitter.emitCanvasSizeChanged (canvasWidth, canvasAscent, canvasDescent);
276
277 findtextState.setWidget (NULL);
278 selectionState.reset ();
279
280 updateCursor ();
281 }
282
283 void Layout::setWidget (Widget *widget)
284 {
285 widgetAtPoint = NULL;
286 if (topLevel) {
287 Widget *w = topLevel;
288 topLevel = NULL;
289 delete w;
290 }
291 textZone->zoneFree ();
292 addWidget (widget);
293
294 updateCursor ();
295 }
296
297 /**
298 * \brief Attach a view to the layout.
299 *
300 * It will become a child of the layout,
301 * and so it will be destroyed, when the layout will be destroyed.
302 */
303 void Layout::attachView (View *view)
304 {
305 if (this->view)
306 MSG_ERR("attachView: Multiple views for layout!\n");
307
308 this->view = view;
309 platform->attachView (view);
310
311 /*
312 * The layout of the view is set later, first, we "project" the current
313 * state of the layout into the new view. A view must handle this without
314 * a layout. See also at the end of this function.
315 */
316 if (bgColor)
317 view->setBgColor (bgColor);
318 view->setCursor (cursor);
319 view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent);
320
321 if (view->usesViewport ()) {
322 if (usesViewport) {
323 view->scrollTo (scrollX, scrollY);
324 view->setViewportSize (viewportWidth, viewportHeight,
325 hScrollbarThickness, vScrollbarThickness);
326 hScrollbarThickness = misc::max (hScrollbarThickness,
327 view->getHScrollbarThickness ());
328 vScrollbarThickness = misc::max (vScrollbarThickness,
329 view->getVScrollbarThickness ());
330 }
331 else {
332 usesViewport = true;
333 scrollX = scrollY = 0;
334 viewportWidth = viewportHeight = 100; // random values
335 hScrollbarThickness = view->getHScrollbarThickness ();
336 vScrollbarThickness = view->getVScrollbarThickness ();
337 }
338 }
339
340 /*
341 * This is the last call within this function, so that it is safe for
342 * the implementation of dw::core::View::setLayout, to call methods
343 * of dw::core::Layout.
344 */
345 view->setLayout (this);
346 }
347
348 void Layout::detachView (View *view)
349 {
350 if (this->view != view)
351 MSG_ERR("detachView: this->view: %p view %p\n", this->view, view);
352
353 view->setLayout (NULL);
354 platform->detachView (view);
355 this->view = NULL;
356 /**
357 * \todo Actually, viewportMarkerWidthDiff and
358 * viewportMarkerHeightDiff have to be recalculated here, since the
359 * effective (i.e. maximal) values may change, after the view has been
360 * detached. Same applies to the usage of viewports.
361 */
362 }
363
364 void Layout::scroll(ScrollCommand cmd)
365 {
366 if (view->usesViewport ())
367 view->scroll(cmd);
368 }
369
370 /**
371 * \brief Scrolls all viewports, so that the region [x, y, width, height]
372 * is seen, according to hpos and vpos.
373 */
374 void Layout::scrollTo (HPosition hpos, VPosition vpos,
375 int x, int y, int width, int height)
376 {
377 scrollTo0 (hpos, vpos, x, y, width, height, true);
378 }
379
380 void Layout::scrollTo0 (HPosition hpos, VPosition vpos,
381 int x, int y, int width, int height,
382 bool scrollingInterrupted)
383 {
384 if (usesViewport) {
385 _MSG("scrollTo (%d, %d, %s)\n",
386 x, y, scrollingInterrupted ? "true" : "false");
387
388 scrollTargetHpos = hpos;
389 scrollTargetVpos = vpos;
390 scrollTargetX = x;
391 scrollTargetY = y;
392 scrollTargetWidth = width;
393 scrollTargetHeight = height;
394
395 if (scrollIdleId == -1) {
396 scrollIdleId = platform->addIdle (&Layout::scrollIdle);
397 scrollIdleNotInterrupted = true;
398 }
399
400 scrollIdleNotInterrupted =
401 scrollIdleNotInterrupted || !scrollingInterrupted;
402 }
403 }
404
405 void Layout::scrollIdle ()
406 {
407 bool xChanged = true;
408 switch (scrollTargetHpos) {
409 case HPOS_LEFT:
410 scrollX = scrollTargetX;
411 break;
412 case HPOS_CENTER:
413 scrollX =
414 scrollTargetX
415 - (viewportWidth - vScrollbarThickness - scrollTargetWidth) / 2;
416 break;
417 case HPOS_RIGHT:
418 scrollX =
419 scrollTargetX
420 - (viewportWidth - vScrollbarThickness - scrollTargetWidth);
421 break;
422 case HPOS_INTO_VIEW:
423 xChanged = calcScrollInto (scrollTargetX, scrollTargetWidth, &scrollX,
424 viewportWidth - vScrollbarThickness);
425 break;
426 case HPOS_NO_CHANGE:
427 xChanged = false;
428 break;
429 }
430
431 bool yChanged = true;
432 switch (scrollTargetVpos) {
433 case VPOS_TOP:
434 scrollY = scrollTargetY;
435 break;
436 case VPOS_CENTER:
437 scrollY =
438 scrollTargetY
439 - (viewportHeight - hScrollbarThickness - scrollTargetHeight) / 2;
440 break;
441 case VPOS_BOTTOM:
442 scrollY =
443 scrollTargetY
444 - (viewportHeight - hScrollbarThickness - scrollTargetHeight);
445 break;
446 case VPOS_INTO_VIEW:
447 yChanged = calcScrollInto (scrollTargetY, scrollTargetHeight, &scrollY,
448 viewportHeight - hScrollbarThickness);
449 break;
450 case VPOS_NO_CHANGE:
451 yChanged = false;
452 break;
453 }
454
455 if (xChanged || yChanged) {
456 adjustScrollPos ();
457 view->scrollTo (scrollX, scrollY);
458 }
459
460 scrollIdleId = -1;
461 }
462
463 void Layout::adjustScrollPos ()
464 {
465 scrollX = misc::min (scrollX,
466 canvasWidth - (viewportWidth - vScrollbarThickness));
467 scrollX = misc::max (scrollX, 0);
468
469 scrollY = misc::min (scrollY,
470 canvasAscent + canvasDescent - (viewportHeight - hScrollbarThickness));
471 scrollY = misc::max (scrollY, 0);
472
473 _MSG("adjustScrollPos: scrollX=%d scrollY=%d\n", scrollX, scrollY);
474 }
475
476 bool Layout::calcScrollInto (int requestedValue, int requestedSize,
477 int *value, int viewportSize)
478 {
479 if (requestedSize > viewportSize) {
480 // The viewport size is smaller than the size of the region which will
481 // be shown. If the region is already visible, do not change the
482 // position. Otherwise, show the left/upper border, this is most likely
483 // what is needed.
484 if (*value >= requestedValue &&
485 *value + viewportSize < requestedValue + requestedSize)
486 return false;
487 else
488 requestedSize = viewportSize;
489 }
490
491 if (requestedValue < *value) {
492 *value = requestedValue;
493 return true;
494 } else if (requestedValue + requestedSize > *value + viewportSize) {
495 *value = requestedValue - viewportSize + requestedSize;
496 return true;
497 } else
498 return false;
499 }
500
501 void Layout::draw (View *view, Rectangle *area)
502 {
503 Rectangle widgetArea, intersection, widgetDrawArea;
504
505 if (topLevel) {
506 /* Draw the top level widget. */
507 widgetArea.x = topLevel->allocation.x;
508 widgetArea.y = topLevel->allocation.y;
509 widgetArea.width = topLevel->allocation.width;
510 widgetArea.height = topLevel->getHeight ();
511
512 if (area->intersectsWith (&widgetArea, &intersection)) {
513 view->startDrawing (&intersection);
514
515 /* Intersection in widget coordinates. */
516 widgetDrawArea.x = intersection.x - topLevel->allocation.x;
517 widgetDrawArea.y = intersection.y - topLevel->allocation.y;
518 widgetDrawArea.width = intersection.width;
519 widgetDrawArea.height = intersection.height;
520
521 topLevel->draw (view, &widgetDrawArea);
522
523 view->finishDrawing (&intersection);
524 }
525 }
526 }
527
528 /**
529 * Sets the anchor to scroll to.
530 */
531 void Layout::setAnchor (const char *anchor)
532 {
533 _MSG("setAnchor (%s)\n", anchor);
534
535 if (requestedAnchor)
536 free(requestedAnchor);
537 requestedAnchor = anchor ? strdup (anchor) : NULL;
538 updateAnchor ();
539 }
540
541 /**
542 * Used, when the widget is not allocated yet.
543 */
544 char *Layout::addAnchor (Widget *widget, const char* name)
545 {
546 return addAnchor (widget, name, -1);
547 }
548
549 char *Layout::addAnchor (Widget *widget, const char* name, int y)
550 {
551 String key (name);
552 if (anchorsTable->contains (&key))
553 return NULL;
554 else {
555 Anchor *anchor = new Anchor ();
556 anchor->name = strdup (name);
557 anchor->widget = widget;
558 anchor->y = y;
559
560 anchorsTable->put (new String (name), anchor);
561 updateAnchor ();
562
563 return anchor->name;
564 }
565 }
566
567 void Layout::changeAnchor (Widget *widget, char* name, int y)
568 {
569 String key (name);
570 Anchor *anchor = anchorsTable->get (&key);
571 assert (anchor);
572 assert (anchor->widget == widget);
573 anchor->y = y;
574 updateAnchor ();
575 }
576
577 void Layout::removeAnchor (Widget *widget, char* name)
578 {
579 String key (name);
580 anchorsTable->remove (&key);
581 }
582
583 void Layout::updateAnchor ()
584 {
585 Anchor *anchor;
586 if (requestedAnchor) {
587 String key (requestedAnchor);
588 anchor = anchorsTable->get (&key);
589 } else
590 anchor = NULL;
591
592 if (anchor == NULL) {
593 /** \todo Copy comment from old docs. */
594 if (scrollIdleId != -1 && !scrollIdleNotInterrupted) {
595 platform->removeIdle (scrollIdleId);
596 scrollIdleId = -1;
597 }
598 } else
599 if (anchor->y != -1)
600 scrollTo0 (HPOS_NO_CHANGE, VPOS_TOP, 0, anchor->y, 0, 0, false);
601 }
602
603 void Layout::setCursor (style::Cursor cursor)
604 {
605 if (cursor != this->cursor) {
606 this->cursor = cursor;
607 view->setCursor (cursor);
608 }
609 }
610
611 void Layout::updateCursor ()
612 {
613 if (widgetAtPoint && widgetAtPoint->style)
614 setCursor (widgetAtPoint->style->cursor);
615 else
616 setCursor (style::CURSOR_DEFAULT);
617 }
618
619 void Layout::setBgColor (style::Color *color)
620 {
621 color->ref ();
622
623 if (bgColor)
624 bgColor->unref ();
625
626 bgColor = color;
627
628 if (view)
629 view->setBgColor (bgColor);
630 }
631
632 void Layout::resizeIdle ()
633 {
634 //static int calls = 0;
635 //MSG(" Layout::resizeIdle calls = %d\n", ++calls);
636
637 while (resizeIdleId != -1) {
638 // Reset already here, since in this function, queueResize() may be
639 // called again.
640 resizeIdleId = -1;
641
642 if (topLevel) {
643 Requisition requisition;
644 Allocation allocation;
645
646 topLevel->sizeRequest (&requisition);
647
648 allocation.x = allocation.y = 0;
649 allocation.width = requisition.width;
650 allocation.ascent = requisition.ascent;
651 allocation.descent = requisition.descent;
652 topLevel->sizeAllocate (&allocation);
653
654 canvasWidth = requisition.width;
655 canvasAscent = requisition.ascent;
656 canvasDescent = requisition.descent;
657
658 emitter.emitCanvasSizeChanged (
659 canvasWidth, canvasAscent, canvasDescent);
660
661 // Tell the view about the new world size.
662 view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent);
663 // view->queueDrawTotal (false);
664
665 if (usesViewport) {
666 int actualHScrollbarThickness =
667 (canvasWidth > viewportWidth) ? hScrollbarThickness : 0;
668 int actualVScrollbarThickness =
669 (canvasAscent + canvasDescent > viewportHeight) ?
670 vScrollbarThickness : 0;
671
672 if (!canvasHeightGreater &&
673 canvasAscent + canvasDescent
674 > viewportHeight - actualHScrollbarThickness) {
675 canvasHeightGreater = true;
676 setSizeHints ();
677 /* May queue a new resize. */
678 }
679
680 // Set viewport sizes.
681 view->setViewportSize (viewportWidth, viewportHeight,
682 actualHScrollbarThickness,
683 actualVScrollbarThickness);
684 }
685 }
686
687 // views are redrawn via Widget::resizeDrawImpl ()
688
689 }
690
691 updateAnchor ();
692 }
693
694 void Layout::setSizeHints ()
695 {
696 if (topLevel) {
697 topLevel->setWidth (viewportWidth
698 - (canvasHeightGreater ? vScrollbarThickness : 0));
699 topLevel->setAscent (viewportHeight - hScrollbarThickness);
700 topLevel->setDescent (0);
701 }
702 }
703
704 void Layout::queueDraw (int x, int y, int width, int height)
705 {
706 Rectangle area;
707 area.x = x;
708 area.y = y;
709 area.width = width;
710 area.height = height;
711
712 if (area.isEmpty ()) return;
713
714 view->queueDraw (&area);
715 }
716
717 void Layout::queueDrawExcept (int x, int y, int width, int height,
718 int ex, int ey, int ewidth, int eheight) {
719
720 if (x == ex && y == ey && width == ewidth && height == eheight)
721 return;
722
723 // queueDraw() the four rectangles within rectangle (x, y, width, height)
724 // around rectangle (ex, ey, ewidth, eheight).
725 // Some or all of these may be empty.
726
727 // upper left corner of the intersection rectangle
728 int ix1 = misc::max (x, ex);
729 int iy1 = misc::max (y, ey);
730 // lower right corner of the intersection rectangle
731 int ix2 = misc::min (x + width, ex + ewidth);
732 int iy2 = misc::min (y + height, ey + eheight);
733
734 queueDraw (x, y, width, iy1 - y);
735 queueDraw (x, iy2, width, y + height - iy2);
736 queueDraw (x, iy1, ix1 - x, iy2 - iy1);
737 queueDraw (ix2, iy1, x + width - ix2, iy2 - iy1);
738 }
739
740 void Layout::queueResize ()
741 {
742 if (resizeIdleId == -1) {
743 view->cancelQueueDraw ();
744
745 resizeIdleId = platform->addIdle (&Layout::resizeIdle);
746 }
747 }
748
749
750 // Views
751
752 bool Layout::buttonEvent (ButtonEventType type, View *view, int numPressed,
753 int x, int y, ButtonState state, int button)
754
755 {
756 EventButton event;
757
758 moveToWidgetAtPoint (x, y, state);
759
760 event.xCanvas = x;
761 event.yCanvas = y;
762 event.state = state;
763 event.button = button;
764 event.numPressed = numPressed;
765
766 return processMouseEvent (&event, type, true);
767 }
768
769 /**
770 * \brief This function is called by a view, to delegate a motion notify
771 * event.
772 *
773 * Arguments are similar to dw::core::Layout::buttonPress.
774 */
775 bool Layout::motionNotify (View *view, int x, int y, ButtonState state)
776 {
777 EventButton event;
778
779 moveToWidgetAtPoint (x, y, state);
780
781 event.xCanvas = x;
782 event.yCanvas = y;
783 event.state = state;
784
785 return processMouseEvent (&event, MOTION_NOTIFY, true);
786 }
787
788 /**
789 * \brief This function is called by a view, to delegate a enter notify event.
790 *
791 * Arguments are similar to dw::core::Layout::buttonPress.
792 */
793 void Layout::enterNotify (View *view, int x, int y, ButtonState state)
794 {
795 Widget *lastWidget;
796 EventCrossing event;
797
798 lastWidget = widgetAtPoint;
799 moveToWidgetAtPoint (x, y, state);
800
801 if (widgetAtPoint) {
802 event.state = state;
803 event.lastWidget = lastWidget;
804 event.currentWidget = widgetAtPoint;
805 widgetAtPoint->enterNotify (&event);
806 }
807 }
808
809 /**
810 * \brief This function is called by a view, to delegate a leave notify event.
811 *
812 * Arguments are similar to dw::core::Layout::buttonPress.
813 */
814 void Layout::leaveNotify (View *view, ButtonState state)
815 {
816 Widget *lastWidget;
817 EventCrossing event;
818
819 lastWidget = widgetAtPoint;
820 moveOutOfView (state);
821
822 if (lastWidget) {
823 event.state = state;
824 event.lastWidget = lastWidget;
825 event.currentWidget = widgetAtPoint;
826 lastWidget->leaveNotify (&event);
827 }
828 }
829
830 /*
831 * Return the widget at position (x, y). Return NULL, if there is no widget.
832 */
833 Widget *Layout::getWidgetAtPoint (int x, int y)
834 {
835 _MSG ("------------------------------------------------------------\n");
836 _MSG ("widget at (%d, %d)\n", x, y);
837 if (topLevel)
838 return topLevel->getWidgetAtPoint (x, y, 0);
839 else
840 return NULL;
841 }
842
843
844 /*
845 * Emit the necessary crossing events, when the mouse pointer has moved to
846 * the given widget.
847 */
848 void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state)
849 {
850 Widget *ancestor, *w;
851 Widget **track;
852 int trackLen, i, i_a;
853 EventCrossing crossingEvent;
854
855 _MSG("moveToWidget: wap=%p nwap=%p\n",widgetAtPoint,newWidgetAtPoint);
856 if (newWidgetAtPoint != widgetAtPoint) {
857 // The mouse pointer has been moved into another widget.
858 if (newWidgetAtPoint && widgetAtPoint)
859 ancestor =
860 newWidgetAtPoint->getNearestCommonAncestor (widgetAtPoint);
861 else if (newWidgetAtPoint)
862 ancestor = newWidgetAtPoint->getTopLevel ();
863 else
864 ancestor = widgetAtPoint->getTopLevel ();
865
866 // Construct the track.
867 trackLen = 0;
868 if (widgetAtPoint)
869 // first part
870 for (w = widgetAtPoint; w != ancestor; w = w->getParent ())
871 trackLen++;
872 trackLen++; // for the ancestor
873 if (newWidgetAtPoint)
874 // second part
875 for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ())
876 trackLen++;
877
878 track = new Widget* [trackLen];
879 i = 0;
880 if (widgetAtPoint)
881 /* first part */
882 for (w = widgetAtPoint; w != ancestor; w = w->getParent ())
883 track[i++] = w;
884 i_a = i;
885 track[i++] = ancestor;
886 if (newWidgetAtPoint) {
887 /* second part */
888 i = trackLen - 1;
889 for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ())
890 track[i--] = w;
891 }
892
893 /* Send events to the widgets on the track */
894 for (i = 0; i < trackLen; i++) {
895 crossingEvent.state = state;
896 crossingEvent.currentWidget = widgetAtPoint; // ???
897 crossingEvent.lastWidget = widgetAtPoint; // ???
898 if (i < i_a) {
899 track[i]->leaveNotify (&crossingEvent);
900 } else if (i == i_a) { /* ancestor */
901 if (!widgetAtPoint)
902 track[i]->enterNotify (&crossingEvent);
903 else if (!newWidgetAtPoint)
904 track[i]->leaveNotify (&crossingEvent);
905 } else {
906 track[i]->enterNotify (&crossingEvent);
907 }
908 }
909
910 delete[] track;
911
912 widgetAtPoint = newWidgetAtPoint;
913 updateCursor ();
914 }
915 }
916
917 /**
918 * \brief Common processing of press, release and motion events.
919 *
920 * This function depends on that move_to_widget_at_point()
921 * has been called before.
922 */
923 bool Layout::processMouseEvent (MousePositionEvent *event,
924 ButtonEventType type, bool mayBeSuppressed)
925 {
926 Widget *widget;
927
928 for (widget = widgetAtPoint; widget; widget = widget->getParent ()) {
929 if (!mayBeSuppressed || widget->isButtonSensitive ()) {
930 event->xWidget = event->xCanvas - widget->getAllocation()->x;
931 event->yWidget = event->yCanvas - widget->getAllocation()->y;
932
933 switch (type) {
934 case BUTTON_PRESS:
935 return widget->buttonPress ((EventButton*)event);
936
937 case BUTTON_RELEASE:
938 return widget->buttonRelease ((EventButton*)event);
939
940 case MOTION_NOTIFY:
941 return widget->motionNotify ((EventMotion*)event);
942
943 default:
944 misc::assertNotReached ();
945 }
946 }
947 }
948 if (type == BUTTON_PRESS)
949 return emitLinkPress (NULL, -1, -1, -1, -1, (EventButton*)event);
950 else if (type == BUTTON_RELEASE)
951 return emitLinkRelease(NULL, -1, -1, -1, -1, (EventButton*)event);
952
953 return false;
954 }
955
956 /*
957 * This function must be called by a view, when the user has manually changed
958 * the viewport position. It is *not* called, when the layout has requested the
959 * position change.
960 */
961 void Layout::scrollPosChanged (View *view, int x, int y)
962 {
963 if (x != scrollX || y != scrollY) {
964 scrollX = x;
965 scrollY = y;
966
967 setAnchor (NULL);
968 updateAnchor ();
969 }
970 }
971
972 /*
973 * This function must be called by a viewport view, when its viewport size has
974 * changed. It is *not* called, when the layout has requested the size change.
975 */
976 void Layout::viewportSizeChanged (View *view, int width, int height)
977 {
978 _MSG("Layout::viewportSizeChanged w=%d h=%d new_w=%d new_h=%d\n",
979 viewportWidth, viewportHeight, width, height);
980
981 /* If the width has become higher, we test again, whether the vertical
982 * scrollbar (so to speak) can be hidden again. */
983 if (usesViewport && width > viewportWidth)
984 canvasHeightGreater = false;
985
986 /* if size changes, redraw this view.
987 * TODO: this is a resize call (redraw/resize code needs a review). */
988 if (viewportWidth != width || viewportHeight != height)
989 queueResize();
990
991 viewportWidth = width;
992 viewportHeight = height;
993
994 setSizeHints ();
995 }
996
997 } // namespace dw
998 } // namespace core
999
0 #ifndef __DW_LAYOUT_HH__
1 #define __DW_LAYOUT_HH__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 namespace dw {
8 namespace core {
9
10 /**
11 * \brief The central class for managing and drawing a widget tree.
12 *
13 * \sa\ref dw-overview, \ref dw-layout-widgets, \ref dw-layout-views
14 */
15 class Layout: public lout::object::Object
16 {
17 friend class Widget;
18
19 public:
20 /**
21 * \brief Receiver interface different signals.
22 *
23 * May be extended
24 */
25 class Receiver: public lout::signal::Receiver
26 {
27 public:
28 virtual void canvasSizeChanged (int width, int ascent, int descent);
29 };
30
31 class LinkReceiver: public lout::signal::Receiver
32 {
33 public:
34 /**
35 * \brief Called, when a link is entered, left, or the position has
36 * changed.
37 *
38 * When a link is entered, this method is called with the respective
39 * arguments. When a link is left, this method is called with all
40 * three arguments (\em link, \em x, \em y) set to -1.
41 *
42 * When coordinates are supported, a change of the coordinates also
43 * causes emitting this signal.
44 */
45 virtual bool enter (Widget *widget, int link, int img, int x, int y);
46
47 /**
48 * \brief Called, when the user has pressed the mouse button on a
49 * link (but not yet released).
50 *
51 * The causing event is passed as \em event.
52 */
53 virtual bool press (Widget *widget, int link, int img, int x, int y,
54 EventButton *event);
55
56 /**
57 * \brief Called, when the user has released the mouse button on a
58 * link.
59 *
60 * The causing event is passed as \em event.
61 */
62 virtual bool release (Widget *widget, int link, int img, int x, int y,
63 EventButton *event);
64
65 /**
66 * \brief Called, when the user has clicked on a link.
67 *
68 * For mouse interaction, this is equivalent to "press" and "release"
69 * on the same link. In this case, \em event contains the "release"
70 * event.
71 *
72 *
73 * When activating links via keyboard is supported, only a "clicked"
74 * signal will be emitted, and \em event will be NULL.
75 */
76 virtual bool click (Widget *widget, int link, int img, int x, int y,
77 EventButton *event);
78 };
79
80 class LinkEmitter: public lout::signal::Emitter
81 {
82 private:
83 enum { ENTER, PRESS, RELEASE, CLICK };
84
85 protected:
86 bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
87 int argc, lout::object::Object **argv);
88
89 public:
90 inline void connectLink (LinkReceiver *receiver) { connect (receiver); }
91
92 bool emitEnter (Widget *widget, int link, int img, int x, int y);
93 bool emitPress (Widget *widget, int link, int img, int x, int y,
94 EventButton *event);
95 bool emitRelease (Widget *widget, int link, int img, int x, int y,
96 EventButton *event);
97 bool emitClick (Widget *widget, int link, int img, int x, int y,
98 EventButton *event);
99 };
100
101 LinkEmitter linkEmitter;
102
103 private:
104 class Emitter: public lout::signal::Emitter
105 {
106 private:
107 enum { CANVAS_SIZE_CHANGED };
108
109 protected:
110 bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
111 int argc, lout::object::Object **argv);
112
113 public:
114 inline void connectLayout (Receiver *receiver) { connect (receiver); }
115
116 void emitCanvasSizeChanged (int width, int ascent, int descent);
117 };
118
119 Emitter emitter;
120
121 class Anchor: public lout::object::Object
122 {
123 public:
124 char *name;
125 Widget *widget;
126 int y;
127
128 ~Anchor ();
129 };
130
131 Platform *platform;
132 View *view;
133 Widget *topLevel, *widgetAtPoint;
134
135 /* The state, which must be projected into the view. */
136 style::Color *bgColor;
137 style::Cursor cursor;
138 int canvasWidth, canvasAscent, canvasDescent;
139
140 bool usesViewport;
141 int scrollX, scrollY, viewportWidth, viewportHeight;
142 bool canvasHeightGreater;
143 int hScrollbarThickness, vScrollbarThickness;
144
145 HPosition scrollTargetHpos;
146 VPosition scrollTargetVpos;
147 int scrollTargetX, scrollTargetY, scrollTargetWidth, scrollTargetHeight;
148
149 char *requestedAnchor;
150 int scrollIdleId, resizeIdleId;
151 bool scrollIdleNotInterrupted;
152
153 /* Anchors of the widget tree */
154 lout::container::typed::HashTable <lout::object::String, Anchor>
155 *anchorsTable;
156
157 SelectionState selectionState;
158 FindtextState findtextState;
159
160 enum ButtonEventType { BUTTON_PRESS, BUTTON_RELEASE, MOTION_NOTIFY };
161
162 Widget *getWidgetAtPoint (int x, int y);
163 void moveToWidget (Widget *newWidgetAtPoint, ButtonState state);
164
165 /**
166 * \brief Emit the necessary crossing events, when the mouse pointer has
167 * moved to position (\em x, \em );
168 */
169 void moveToWidgetAtPoint (int x, int y, ButtonState state)
170 { moveToWidget (getWidgetAtPoint (x, y), state); }
171
172 /**
173 * \brief Emit the necessary crossing events, when the mouse pointer
174 * has moved out of the view.
175 */
176 void moveOutOfView (ButtonState state) { moveToWidget (NULL, state); }
177
178 bool processMouseEvent (MousePositionEvent *event, ButtonEventType type,
179 bool mayBeSuppressed);
180 bool buttonEvent (ButtonEventType type, View *view,
181 int numPressed, int x, int y, ButtonState state,
182 int button);
183 void resizeIdle ();
184 void setSizeHints ();
185 void draw (View *view, Rectangle *area);
186
187 void scrollTo0(HPosition hpos, VPosition vpos,
188 int x, int y, int width, int height,
189 bool scrollingInterrupted);
190 void scrollIdle ();
191 void adjustScrollPos ();
192 static bool calcScrollInto (int targetValue, int requestedSize,
193 int *value, int viewportSize);
194
195 void updateAnchor ();
196
197 /* Widget */
198
199 char *addAnchor (Widget *widget, const char* name);
200 char *addAnchor (Widget *widget, const char* name, int y);
201 void changeAnchor (Widget *widget, char* name, int y);
202 void removeAnchor (Widget *widget, char* name);
203 void setCursor (style::Cursor cursor);
204 void updateCursor ();
205 void queueDraw (int x, int y, int width, int height);
206 void queueDrawExcept (int x, int y, int width, int height,
207 int ex, int ey, int ewidth, int eheight);
208 void queueResize ();
209 void removeWidget ();
210
211 public:
212 Layout (Platform *platform);
213 ~Layout ();
214
215 inline void connectLink (LinkReceiver *receiver)
216 { linkEmitter.connectLink (receiver); }
217
218 inline bool emitLinkEnter (Widget *w, int link, int img, int x, int y)
219 { return linkEmitter.emitEnter (w, link, img, x, y); }
220
221 inline bool emitLinkPress (Widget *w, int link, int img,
222 int x, int y, EventButton *event)
223 { return linkEmitter.emitPress (w, link, img, x, y, event); }
224
225 inline bool emitLinkRelease (Widget *w, int link, int img,
226 int x, int y, EventButton *event)
227 { return linkEmitter.emitRelease (w, link, img, x, y, event); }
228
229 inline bool emitLinkClick (Widget *w, int link, int img,
230 int x, int y, EventButton *event)
231 { return linkEmitter.emitClick (w, link, img, x, y, event); }
232
233 lout::misc::ZoneAllocator *textZone;
234
235 void addWidget (Widget *widget);
236 void setWidget (Widget *widget);
237
238 void attachView (View *view);
239 void detachView (View *view);
240
241 inline bool getUsesViewport () { return usesViewport; }
242 inline int getWidthViewport () { return viewportWidth; }
243 inline int getHeightViewport () { return viewportHeight; }
244 inline int getScrollPosX () { return scrollX; }
245 inline int getScrollPosY () { return scrollY; }
246
247 /* public */
248
249 void scrollTo (HPosition hpos, VPosition vpos,
250 int x, int y, int width, int height);
251 void scroll (ScrollCommand);
252 void setAnchor (const char *anchor);
253
254 /* View */
255
256 inline void expose (View *view, Rectangle *area) { draw (view, area); }
257
258 /**
259 * \brief This function is called by a view, to delegate a button press
260 * event.
261 *
262 * \em numPressed is 1 for simple presses, 2 for double presses etc. (more
263 * that 2 is never needed), \em x and \em y the world coordinates, and
264 * \em button the number of the button pressed.
265 */
266 inline bool buttonPress (View *view, int numPressed, int x, int y,
267 ButtonState state, int button)
268 {
269 return buttonEvent (BUTTON_PRESS, view, numPressed, x, y, state, button);
270 }
271
272 /**
273 * \brief This function is called by a view, to delegate a button press
274 * event.
275 *
276 * Arguments are similar to dw::core::Layout::buttonPress.
277 */
278 inline bool buttonRelease (View *view, int numPressed, int x, int y,
279 ButtonState state, int button)
280 {
281 return buttonEvent (BUTTON_RELEASE, view, numPressed, x, y, state,
282 button);
283 }
284
285 bool motionNotify (View *view, int x, int y, ButtonState state);
286 void enterNotify (View *view, int x, int y, ButtonState state);
287 void leaveNotify (View *view, ButtonState state);
288
289 void scrollPosChanged (View *view, int x, int y);
290 void viewportSizeChanged (View *view, int width, int height);
291
292 /* delegated */
293
294 inline int textWidth (style::Font *font, const char *text, int len)
295 {
296 return platform->textWidth (font, text, len);
297 }
298
299 inline int nextGlyph (const char *text, int idx)
300 {
301 return platform->nextGlyph (text, idx);
302 }
303
304 inline int prevGlyph (const char *text, int idx)
305 {
306 return platform->prevGlyph (text, idx);
307 }
308
309 inline float dpiX ()
310 {
311 return platform->dpiX ();
312 }
313
314 inline float dpiY ()
315 {
316 return platform->dpiY ();
317 }
318
319 inline style::Font *createFont (style::FontAttrs *attrs, bool tryEverything)
320 {
321 return platform->createFont (attrs, tryEverything);
322 }
323
324 inline bool fontExists (const char *name)
325 {
326 return platform->fontExists (name);
327 }
328
329 inline style::Color *createColor (int color)
330 {
331 return platform->createColor (color);
332 }
333
334 inline style::Tooltip *createTooltip (const char *text)
335 {
336 return platform->createTooltip (text);
337 }
338
339 inline void cancelTooltip ()
340 {
341 return platform->cancelTooltip ();
342 }
343
344 inline Imgbuf *createImgbuf (Imgbuf::Type type, int width, int height)
345 {
346 return platform->createImgbuf (type, width, height);
347 }
348
349 inline void copySelection(const char *text)
350 {
351 platform->copySelection(text);
352 }
353
354 inline ui::ResourceFactory *getResourceFactory ()
355 {
356 return platform->getResourceFactory ();
357 }
358
359 inline void connect (Receiver *receiver) {
360 emitter.connectLayout (receiver); }
361
362 /** \brief See dw::core::FindtextState::search. */
363 inline FindtextState::Result search (const char *str, bool caseSens,
364 int backwards)
365 { return findtextState.search (str, caseSens, backwards); }
366
367 /** \brief See dw::core::FindtextState::resetSearch. */
368 inline void resetSearch () { findtextState.resetSearch (); }
369
370 void setBgColor (style::Color *color);
371
372 inline style::Color* getBgColor () { return bgColor; }
373 };
374
375 } // namespace dw
376 } // namespace core
377
378 #endif // __DW_LAYOUT_HH__
379
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "listitem.hh"
22 #include <stdio.h>
23
24 namespace dw {
25
26 int ListItem::CLASS_ID = -1;
27
28 ListItem::ListItem (ListItem *ref, bool limitTextWidth):
29 AlignedTextblock (limitTextWidth)
30 {
31 registerName ("dw::ListItem", &CLASS_ID);
32 setRefTextblock (ref);
33 }
34
35 ListItem::~ListItem()
36 {
37 }
38
39 void ListItem::initWithWidget (core::Widget *widget,
40 core::style::Style *style)
41 {
42 hasListitemValue = true;
43 addWidget (widget, style);
44 addSpace (style);
45 if (style->listStylePosition == core::style::LIST_STYLE_POSITION_OUTSIDE)
46 updateValue ();
47 }
48
49 void ListItem::initWithText (const char *text, core::style::Style *style)
50 {
51 hasListitemValue = true;
52 addText (text, style);
53 addSpace (style);
54 if (style->listStylePosition == core::style::LIST_STYLE_POSITION_OUTSIDE)
55 updateValue ();
56 }
57
58 int ListItem::getValue ()
59 {
60 if (words->size () == 0)
61 return 0;
62 else
63 return words->getRef(0)->size.width + words->getRef(0)->origSpace;
64 }
65
66 void ListItem::setMaxValue (int maxValue, int value)
67 {
68 innerPadding = maxValue;
69 line1Offset = - value;
70 redrawY = 0;
71 queueResize (0, true);
72 }
73
74 } // namespace dw
0 #ifndef __DW_LISTITEM_HH__
1 #define __DW_LISTITEM_HH__
2
3 #include "core.hh"
4 #include "alignedtextblock.hh"
5
6 namespace dw {
7
8 class ListItem: public AlignedTextblock
9 {
10 protected:
11 int getValue ();
12 void setMaxValue (int maxValue, int value);
13
14 public:
15 static int CLASS_ID;
16
17 ListItem(ListItem *ref, bool limitTextWidth);
18 ~ListItem();
19
20 void initWithWidget (core::Widget *widget, core::style::Style *style);
21 void initWithText (const char *text, core::style::Style *style);
22 };
23
24 } // namespace dw
25
26 #endif // __DW_LISTITEM_HH__
0 #ifndef __DW_PLATFORM_HH__
1 #define __DW_PLATFORM_HH__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 namespace dw {
8 namespace core {
9
10 /**
11 * \brief An interface to encapsulate some platform dependencies.
12 *
13 * \sa\ref dw-overview
14 */
15 class Platform: public lout::object::Object
16 {
17 public:
18 /*
19 * -----------------------------------
20 * General
21 * -----------------------------------
22 */
23
24 /**
25 * \brief This methods notifies the platform, that it has been attached to
26 * a layout.
27 */
28 virtual void setLayout (Layout *layout) = 0;
29
30 /*
31 * -------------------------
32 * Operations on views
33 * -------------------------
34 */
35
36 /**
37 * \brief This methods notifies the platform, that a view has been attached
38 * to the related layout.
39 */
40 virtual void attachView (View *view) = 0;
41
42 /**
43 * \brief This methods notifies the platform, that a view has been detached
44 * from the related layout.
45 */
46 virtual void detachView (View *view) = 0;
47
48 /*
49 * -----------------------------------
50 * Platform dependent properties
51 * -----------------------------------
52 */
53
54 /**
55 * \brief Return the width of a text, with a given length and font.
56 */
57 virtual int textWidth (style::Font *font, const char *text, int len) = 0;
58
59 /**
60 * \brief Return the index of the next glyph in string text.
61 */
62 virtual int nextGlyph (const char *text, int idx) = 0;
63
64 /**
65 * \brief Return the index of the previous glyph in string text.
66 */
67 virtual int prevGlyph (const char *text, int idx) = 0;
68
69 /**
70 * \brief Return screen resolution in x-direction.
71 */
72 virtual float dpiX () = 0;
73
74 /**
75 * \brief Return screen resolution in y-direction.
76 */
77 virtual float dpiY () = 0;
78
79 /*
80 * ---------------------------------------------------------
81 * These are to encapsulate some platform dependencies
82 * ---------------------------------------------------------
83 */
84
85 /**
86 * \brief Add an idle function.
87 *
88 * An idle function is called once, when no other
89 * tasks are to be done (e.g. there are no events to process), and then
90 * removed from the queue. The return value is a number, which can be
91 * used in removeIdle below.
92 */
93 virtual int addIdle (void (Layout::*func) ()) = 0;
94
95 /**
96 * \brief Remove an idle function, which has not been processed yet.
97 */
98 virtual void removeIdle (int idleId) = 0;
99
100 /*
101 * ---------------------
102 * Style Resources
103 * ---------------------
104 */
105
106 /**
107 * \brief Create a (platform dependent) font.
108 *
109 * Typically, within a platform, a sub class of dw::core::style::Font
110 * is defined, which holds more platform dependent data.
111 *
112 * Also, this method must fill the attributes "font" (when needed),
113 * "ascent", "descent", "spaceSidth" and "xHeight". If "tryEverything"
114 * is true, several methods should be used to use another font, when
115 * the requested font is not available. Passing false is typically done,
116 * if the caller wants to test different variations.
117 */
118 virtual style::Font *createFont (style::FontAttrs *attrs,
119 bool tryEverything) = 0;
120
121 virtual bool fontExists (const char *name) = 0;
122
123 /**
124 * \brief Create a color resource for a given 0xrrggbb value.
125 */
126 virtual style::Color *createColor (int color) = 0;
127
128 /**
129 * \brief Create a tooltip
130 */
131 virtual style::Tooltip *createTooltip (const char *text) = 0;
132
133 /**
134 * \brief Cancel a tooltip (either shown or requested)
135 */
136 virtual void cancelTooltip () = 0;
137
138 /*
139 * --------------------
140 * Image Buffers
141 * --------------------
142 */
143 virtual Imgbuf *createImgbuf (Imgbuf::Type type, int width, int height) = 0;
144
145 /**
146 * \brief Copy selected text (0-terminated).
147 */
148 virtual void copySelection(const char *text) = 0;
149
150 /**
151 * ...
152 */
153 virtual ui::ResourceFactory *getResourceFactory () = 0;
154 };
155
156 } // namespace dw
157 } // namespace core
158
159 #endif // __DW_PLATFORM_HH__
0 #define preview_width 11
1 #define preview_height 11
2 static unsigned char preview_bits[] = {
3 0x20, 0x00, 0x70, 0x00, 0x20, 0x00, 0x20, 0x00, 0x22, 0x02, 0xff, 0x07,
4 0x22, 0x02, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, 0x20, 0x00};
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "ruler.hh"
22 #include "../lout/misc.hh"
23
24 #include <stdio.h>
25
26 namespace dw {
27
28 Ruler::Ruler ()
29 {
30 setFlags (BLOCK_LEVEL);
31 unsetFlags (HAS_CONTENTS);
32 }
33
34 void Ruler::sizeRequestImpl (core::Requisition *requisition)
35 {
36 requisition->width = getStyle()->boxDiffWidth ();
37 requisition->ascent = getStyle()->boxOffsetY ();
38 requisition->descent = getStyle()->boxRestHeight ();
39 }
40
41 void Ruler::draw (core::View *view, core::Rectangle *area)
42 {
43 drawWidgetBox (view, area, false);
44 }
45
46 core::Iterator *Ruler::iterator (core::Content::Type mask, bool atEnd)
47 {
48 /** \todo TextIterator? */
49 return new core::EmptyIterator (this, mask, atEnd);
50 }
51
52 } // namespace dw
0 #ifndef __RULER_HH__
1 #define __RULER_HH__
2
3 #include "core.hh"
4
5 namespace dw {
6
7 /**
8 * \brief Widget for drawing (horizontal) rules.
9 *
10 * This is really an empty widget, the HTML parser puts a border
11 * around it, and drawing is done in dw::core::Widget::drawWidgetBox.
12 * The only remarkable point is that the HAS_CONTENT flag is
13 * cleared.
14 */
15 class Ruler: public core::Widget
16 {
17 protected:
18 void sizeRequestImpl (core::Requisition *requisition);
19 void draw (core::View *view, core::Rectangle *area);
20
21 public:
22 Ruler ();
23
24 core::Iterator *iterator (core::Content::Type mask, bool atEnd);
25 };
26
27 } // namespace dw
28
29 #endif // __RULER_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "core.hh"
22
23 #include <string.h>
24
25 using namespace lout;
26
27 /*
28 * strndup() is a GNU extension.
29 */
30 extern "C" char *strndup(const char *s, size_t size)
31 {
32 char *r = (char *) malloc (size + 1);
33
34 if (r) {
35 strncpy (r, s, size);
36 r[size] = 0;
37 }
38
39 return r;
40 }
41
42 namespace dw {
43 namespace core {
44
45 SelectionState::SelectionState ()
46 {
47 layout = NULL;
48
49 selectionState = NONE;
50 from = NULL;
51 to = NULL;
52
53 linkState = LINK_NONE;
54 link = NULL;
55 }
56
57 SelectionState::~SelectionState ()
58 {
59 reset ();
60 }
61
62 void SelectionState::reset ()
63 {
64 resetSelection ();
65 resetLink ();
66 }
67
68 void SelectionState::resetSelection ()
69 {
70 if (from)
71 delete from;
72 from = NULL;
73 if (to)
74 delete to;
75 to = NULL;
76 selectionState = NONE;
77 }
78
79
80 void SelectionState::resetLink ()
81 {
82 if (link)
83 delete link;
84 link = NULL;
85 linkState = LINK_NONE;
86 }
87
88 bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo,
89 EventButton *event)
90 {
91 Widget *itWidget = it->getWidget ();
92 bool ret = false;
93
94 if (!event) return ret;
95
96 if (event->button == 3) {
97 // menu popup
98 layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event);
99 ret = true;
100 } else if (linkNo != -1) {
101 // link handling
102 (void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event);
103 resetLink ();
104 linkState = LINK_PRESSED;
105 linkButton = event->button;
106 DeepIterator *newLink = new DeepIterator (it);
107 if (newLink->isEmpty ()) {
108 delete newLink;
109 resetLink ();
110 } else {
111 link = newLink;
112 // It may be that the user has pressed on something activatable
113 // (linkNo != -1), but there is no contents, e.g. with images
114 // without ALTernative text.
115 if (link) {
116 linkChar = correctCharPos (link, charPos);
117 linkNumber = linkNo;
118 }
119 }
120 // We do not return the value of the signal method,
121 // but we do actually process this event.
122 ret = true;
123 } else if (event->button == 1) {
124 // normal selection handling
125 highlight (false, 0);
126 resetSelection ();
127
128 selectionState = SELECTING;
129 DeepIterator *newFrom = new DeepIterator (it);
130 if (newFrom->isEmpty ()) {
131 delete newFrom;
132 resetSelection ();
133 } else {
134 from = newFrom;
135 fromChar = correctCharPos (from, charPos);
136 to = from->cloneDeepIterator ();
137 toChar = correctCharPos (to, charPos);
138 }
139 ret = true;
140 }
141
142 return ret;
143 }
144
145 bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo,
146 EventButton *event)
147 {
148 Widget *itWidget = it->getWidget ();
149 bool ret = false;
150
151 if (linkState == LINK_PRESSED && event && event->button == linkButton) {
152 // link handling
153 ret = true;
154 if (linkNo != -1)
155 (void) layout->emitLinkRelease (itWidget, linkNo, -1, -1, -1, event);
156
157 // The link where the user clicked the mouse button?
158 if (linkNo == linkNumber) {
159 resetLink ();
160 (void) layout->emitLinkClick (itWidget, linkNo, -1, -1, -1, event);
161 } else {
162 if (event->button == 1)
163 // Reset links and switch to selection mode. The selection
164 // state will be set to SELECTING, which is handled some lines
165 // below.
166 switchLinkToSelection (it, charPos);
167 }
168 }
169
170 if (selectionState == SELECTING && event && event->button == 1) {
171 // normal selection
172 ret = true;
173 adjustSelection (it, charPos);
174
175 if (from->compareTo (to) == 0 && fromChar == toChar)
176 // nothing selected
177 resetSelection ();
178 else {
179 copy ();
180 selectionState = SELECTED;
181 }
182 }
183
184 return ret;
185 }
186
187 bool SelectionState::buttonMotion (Iterator *it, int charPos, int linkNo,
188 EventMotion *event)
189 {
190 if (linkState == LINK_PRESSED) {
191 //link handling
192 if (linkNo != linkNumber)
193 // No longer the link where the user clicked the mouse button.
194 // Reset links and switch to selection mode.
195 switchLinkToSelection (it, charPos);
196 // Still in link: do nothing.
197 } else if (selectionState == SELECTING) {
198 // selection
199 adjustSelection (it, charPos);
200 }
201
202 return true;
203 }
204
205 /**
206 * \brief General form of dw::core::SelectionState::buttonPress,
207 * dw::core::SelectionState::buttonRelease and
208 * dw::core::SelectionState::buttonMotion.
209 */
210 bool SelectionState::handleEvent (EventType eventType, Iterator *it,
211 int charPos, int linkNo,
212 MousePositionEvent *event)
213 {
214 switch (eventType) {
215 case BUTTON_PRESS:
216 return buttonPress (it, charPos, linkNo, (EventButton*)event);
217
218 case BUTTON_RELEASE:
219 return buttonRelease (it, charPos, linkNo, (EventButton*)event);
220
221 case BUTTON_MOTION:
222 return buttonMotion (it, charPos, linkNo, (EventMotion*)event);
223
224
225 default:
226 misc::assertNotReached ();
227 }
228
229 return false;
230 }
231
232
233 /**
234 * \brief This method is called when the user decides not to activate a link,
235 * but instead select text.
236 */
237 void SelectionState::switchLinkToSelection (Iterator *it, int charPos)
238 {
239 // It may be that selection->link is NULL, see a_Selection_button_press.
240 if (link) {
241 // Reset old selection.
242 highlight (false, 0);
243 resetSelection ();
244
245 // Transfer link state into selection state.
246 from = link->cloneDeepIterator ();
247 fromChar = linkChar;
248 to = from->createVariant (it);
249 toChar = correctCharPos (to, charPos);
250 selectionState = SELECTING;
251
252 // Reset link status.
253 resetLink ();
254
255 highlight (true, 0);
256
257 } else {
258 // A link was pressed on, but there is nothing to select. Reset
259 // everything.
260 resetSelection ();
261 resetLink ();
262 }
263 }
264
265 /**
266 * \brief This method is used by core::dw::SelectionState::buttonMotion and
267 * core::dw::SelectionState::buttonRelease, and changes the second limit of
268 * the already existing selection region.
269 */
270 void SelectionState::adjustSelection (Iterator *it, int charPos)
271 {
272 DeepIterator *newTo;
273 int newToChar, cmpOld, cmpNew, cmpDiff, len;
274 bool bruteHighlighting = false;
275
276 newTo = to->createVariant (it);
277 newToChar = correctCharPos (newTo, charPos);
278
279 cmpOld = to->compareTo (from);
280 cmpNew = newTo->compareTo (from);
281
282 if (cmpOld == 0 || cmpNew == 0) {
283 // Either before, or now, the limits differ only by the character
284 // position.
285 bruteHighlighting = true;
286 } else if (cmpOld * cmpNew < 0) {
287 // The selection order has changed, i.e. the user moved the selection
288 // end again beyond the position he started.
289 bruteHighlighting = true;
290 } else {
291 // Here, cmpOld and cmpNew are equivalent and != 0.
292 cmpDiff = newTo->compareTo (to);
293
294 if (cmpOld * cmpDiff > 0) {
295 // The user has enlarged the selection. Highlight the difference.
296 if (cmpDiff < 0) {
297 len = correctCharPos (to, END_OF_WORD);
298 highlight0 (true, newTo, newToChar, to, len + 1, 1);
299 } else {
300 highlight0 (true, to, 0, newTo, newToChar, -1);
301 }
302 } else {
303 if (cmpOld * cmpDiff < 0) {
304 // The user has reduced the selection. Unhighlight the difference.
305 highlight0 (false, to, 0, newTo, 0, cmpDiff);
306 }
307
308 // Otherwise, the user has changed the position only slightly.
309 // In both cases, re-highlight the new position.
310 if (cmpOld < 0) {
311 len = correctCharPos (newTo, END_OF_WORD);
312 newTo->highlight (newToChar, len + 1, HIGHLIGHT_SELECTION);
313 } else
314 newTo->highlight (0, newToChar, HIGHLIGHT_SELECTION);
315 }
316 }
317
318 if (bruteHighlighting)
319 highlight (false, 0);
320
321 delete to;
322 to = newTo;
323 toChar = newToChar;
324
325 if (bruteHighlighting)
326 highlight (true, 0);
327 }
328
329 /**
330 * \brief This method deals especially with the case that a widget passes
331 * dw::core::SelectionState::END_OF_WORD.
332 */
333 int SelectionState::correctCharPos (DeepIterator *it, int charPos)
334 {
335 Iterator *top = it->getTopIterator ();
336 int len;
337
338 if (top->getContent()->type == Content::TEXT)
339 len = strlen(top->getContent()->text);
340 else
341 len = 1;
342
343 return misc::min(charPos, len);
344 }
345
346 void SelectionState::highlight0 (bool fl, DeepIterator *from, int fromChar,
347 DeepIterator *to, int toChar, int dir)
348 {
349 DeepIterator *a, *b, *i;
350 int cmp, aChar, bChar;
351 bool start;
352
353 if (from && to) {
354 cmp = from->compareTo (to);
355 if (cmp == 0) {
356 if (fl) {
357 if (fromChar < toChar)
358 from->highlight (fromChar, toChar, HIGHLIGHT_SELECTION);
359 else
360 from->highlight (toChar, fromChar, HIGHLIGHT_SELECTION);
361 } else
362 from->unhighlight (0, HIGHLIGHT_SELECTION);
363 return;
364 }
365
366 if (cmp < 0) {
367 a = from;
368 aChar = fromChar;
369 b = to;
370 bChar = toChar;
371 } else {
372 a = to;
373 aChar = toChar;
374 b = from;
375 bChar = fromChar;
376 }
377
378 for (i = a->cloneDeepIterator (), start = true;
379 (cmp = i->compareTo (b)) <= 0;
380 i->next (), start = false) {
381 if (i->getContent()->type == Content::TEXT) {
382 if (fl) {
383 if (start) {
384 i->highlight (aChar, strlen (i->getContent()->text) + 1,
385 HIGHLIGHT_SELECTION);
386 } else if (cmp == 0) {
387 // the end
388 i->highlight (0, bChar, HIGHLIGHT_SELECTION);
389 } else {
390 i->highlight (0, strlen (i->getContent()->text) + 1,
391 HIGHLIGHT_SELECTION);
392 }
393 } else {
394 i->unhighlight (dir, HIGHLIGHT_SELECTION);
395 }
396 }
397 }
398 delete i;
399 }
400 }
401
402 void SelectionState::copy()
403 {
404 if (from && to) {
405 Iterator *si;
406 DeepIterator *a, *b, *i;
407 int cmp, aChar, bChar;
408 bool start;
409 char *tmp;
410 misc::StringBuffer strbuf;
411
412 cmp = from->compareTo (to);
413 if (cmp == 0) {
414 if (from->getContent()->type == Content::TEXT) {
415 si = from->getTopIterator ();
416 if (fromChar < toChar)
417 tmp = strndup (si->getContent()->text + fromChar,
418 toChar - fromChar);
419 else
420 tmp = strndup (si->getContent()->text + toChar,
421 fromChar - toChar);
422 strbuf.appendNoCopy (tmp);
423 }
424 } else {
425 if (cmp < 0) {
426 a = from;
427 aChar = fromChar;
428 b = to;
429 bChar = toChar;
430 } else {
431 a = to;
432 aChar = toChar;
433 b = from;
434 bChar = fromChar;
435 }
436
437 for (i = a->cloneDeepIterator (), start = true;
438 (cmp = i->compareTo (b)) <= 0;
439 i->next (), start = false) {
440 si = i->getTopIterator ();
441 switch (si->getContent()->type) {
442 case Content::TEXT:
443 if (start) {
444 tmp = strndup (si->getContent()->text + aChar,
445 strlen (i->getContent()->text) - aChar);
446 strbuf.appendNoCopy (tmp);
447 } else if (cmp == 0) {
448 // the end
449 tmp = strndup (si->getContent()->text, bChar);
450 strbuf.appendNoCopy (tmp);
451 } else
452 strbuf.append (si->getContent()->text);
453
454 if (si->getContent()->space && cmp != 0)
455 strbuf.append (" ");
456
457 break;
458
459 case Content::BREAK:
460 if (si->getContent()->breakSpace > 0)
461 strbuf.append ("\n\n");
462 else
463 strbuf.append ("\n");
464 break;
465 default:
466 // Make pedantic compilers happy. Especially
467 // DW_CONTENT_WIDGET is never returned by a DwDeepIterator.
468 break;
469 }
470 }
471 delete i;
472 }
473
474 layout->copySelection(strbuf.getChars());
475 }
476 }
477
478 } // namespace dw
479 } // namespace core
0 #ifndef __DW_SELECTION_H__
1 #define __DW_SELECTION_H__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 namespace dw {
8 namespace core {
9
10 /**
11 * \brief This class handles selections, as well as activation of links,
12 * which is closely related.
13 *
14 * <h3>General Overview</h3>
15 *
16 * dw::core::SelectionState is associated with dw::core::Layout. The selection
17 * state is controlled by "abstract events", which are sent by single
18 * widgets by calling one of the following methods:
19 *
20 * <ul>
21 * <li> dw::core::SelectionState::buttonPress for button press events,
22 * <li> dw::core::SelectionState::buttonRelease for button release events, and
23 * <li> dw::core::SelectionState::buttonMotion for motion events (with pressed
24 * mouse button).
25 * </ul>
26 *
27 * The widget must construct simple iterators (dw::core::Iterator), which will
28 * be transferred to deep iterators (dw::core::DeepIterator), see below for
29 * more details. All event handling methods have the same signature, the
30 * arguments in detail are:
31 *
32 * <table>
33 * <tr><td>dw::core::Iterator *it <td>the iterator pointing on the item
34 * under the mouse pointer; this
35 * iterator \em must be created with
36 * dw::core::Content::SELECTION_CONTENT
37 * as mask
38 * <tr><td>int charPos <td>the exact (character) position
39 * within the iterator,
40 * <tr><td>int linkNo <td>if this item is associated with a
41 * link, its number (see
42 * dw::core::Layout::LinkReceiver),
43 * otherwise -1
44 * <tr><td>dw::core::EventButton *event <td>the event itself; only the button
45 * is used
46 * </table>
47 *
48 * Look also at dw::core::SelectionState::handleEvent, which may be useful
49 * in some circumstances.
50 *
51 * In some cases, \em charPos would be difficult to determine. E.g., when
52 * the dw::Textblock widget decides that the user is pointing on a position
53 * <i>at the end</i> of an image (DwImage), it constructs a simple iterator
54 * pointing on this image widget. In a simple iterator, that fact that
55 * the pointer is at the end, would be represented by \em charPos == 1. But
56 * when transferring this simple iterator into an deep iterator, this
57 * simple iterator is discarded and instead the stack has an iterator
58 * pointing to text at the top. As a result, only the first letter of the
59 * ALT text would be copied.
60 *
61 * To avoid this problem, widgets should in this case pass
62 * dw::core::SelectionState::END_OF_WORD as \em charPos, which is then
63 * automatically reduced to the actual length of the deep(!) iterator.
64 *
65 * The return value is the same as in DwWidget event handling methods.
66 * I.e., in most cases, they should simply return it. The events
67 * dw::core::Layout::LinkReceiver::press,
68 * dw::core::Layout::LinkReceiver::release and
69 * dw::core::Layout::LinkReceiver::click (but not
70 * dw::core::Layout::LinkReceiver::enter) are emitted by these methods, so
71 * that widgets which let dw::core::SelectionState handle links, should only
72 * emit dw::core::Layout::LinkReceiver::enter for themselves.
73 *
74 * <h3>Selection State</h3>
75 *
76 * Selection interferes with handling the activation of links, so the
77 * latter is also handled by the dw::core::SelectionState. Details are based on
78 * following guidelines:
79 *
80 * <ol>
81 * <li> It should be simple to select links and to start selection in
82 * links. The rule to distinguish between link activation and
83 * selection is that the selection starts as soon as the user leaves
84 * the link. (This is, IMO, a useful feature. Even after drag and
85 * drop has been implemented in dillo, this should be somehow
86 * preserved.)
87 *
88 * <li> The selection should stay as long as possible, i.e., the old
89 * selection is only cleared when a new selection is started.
90 * </ol>
91 *
92 * The latter leads to a model with two states: the selection state and
93 * the link handling state.
94 *
95 * The general selection works, for events not pointing on links, like
96 * this (numbers in parantheses after the event denote the button, "n"
97 * means arbitrary button):
98 *
99 * \dot
100 * digraph G {
101 * node [shape=ellipse, fontname=Helvetica, fontsize=10];
102 * edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
103 * color="#404040", labelfontcolor="#000080",
104 * fontname=Helvetica, fontsize=10, fontcolor="#000080"];
105 * fontname=Helvetica; fontsize=10;
106 *
107 * NONE;
108 * SELECTING;
109 * q [label="Anything selected?", shape=plaintext];
110 * SELECTED;
111 *
112 * NONE -> SELECTING [label="press(1)\non non-link"];
113 * SELECTING -> SELECTING [label="motion(1)"];
114 * SELECTING -> q [label="release(1)"];
115 * q -> SELECTED [label="yes"];
116 * q -> NONE [label="no"];
117 * SELECTED -> SELECTING [label="press(1)"];
118 *
119 * }
120 * \enddot
121 *
122 * The selected region is represented by two instances of
123 * dw::core::DeepIterator.
124 *
125 * Links are handled by a different state machine:
126 *
127 * \dot
128 * digraph G {
129 * node [shape=ellipse, fontname=Helvetica, fontsize=10];
130 * edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
131 * color="#404040", labelfontcolor="#000080",
132 * fontname=Helvetica, fontsize=10, fontcolor="#000080"];
133 * fontname=Helvetica; fontsize=10;
134 *
135 * LINK_NONE;
136 * LINK_PRESSED;
137 * click [label="Emit \"click\" signal.", shape=record];
138 * q11 [label="Still the same link?", shape=plaintext];
139 * q21 [label="Still the same link?", shape=plaintext];
140 * q22 [label="n == 1?", shape=plaintext];
141 * SELECTED [label="Switch selection\nto SELECTED", shape=record];
142 * q12 [label="n == 1?", shape=plaintext];
143 * SELECTING [label="Switch selection\nto SELECTING", shape=record];
144 *
145 * LINK_NONE -> LINK_PRESSED [label="press(n)\non link"];
146 * LINK_PRESSED -> q11 [label="motion(n)"];
147 * q11 -> LINK_PRESSED [label="yes"];
148 * q11 -> q12 [label="no"];
149 * q12 -> SELECTING [label="yes"];
150 * SELECTING -> LINK_NONE;
151 * q12 -> LINK_NONE [label="no"];
152 * LINK_PRESSED -> q21 [label="release(n)"];
153 * q21 -> click [label="yes"];
154 * click -> LINK_NONE;
155 * q21 -> q22 [label="no"];
156 * q22 -> SELECTED [label="yes"];
157 * SELECTED -> LINK_NONE;
158 * q22 -> LINK_NONE [label="no"];
159 * }
160 * \enddot
161 *
162 * Switching selection simply means that the selection state will
163 * eventually be SELECTED/SELECTING, with the original and the current
164 * position making up the selection region. This happens for button 1,
165 * events with buttons other than 1 do not affect selection at all.
166 *
167 *
168 * \todo dw::core::SelectionState::buttonMotion currently always assumes
169 * that button 1 has been pressed (since otherwise it would not do
170 * anything). This should be made a bit cleaner.
171 *
172 * \todo The selection should be cleared, when the user selects something
173 * somewhere else (perhaps switched into "non-active" mode, as e.g. Gtk+
174 * does).
175 *
176 */
177 class SelectionState
178 {
179 public:
180 enum { END_OF_WORD = 1 << 30 };
181
182 private:
183 Layout *layout;
184
185 // selection
186 enum {
187 NONE,
188 SELECTING,
189 SELECTED
190 } selectionState;
191
192 DeepIterator *from, *to;
193 int fromChar, toChar;
194
195 // link handling
196 enum {
197 LINK_NONE,
198 LINK_PRESSED
199 } linkState;
200
201 int linkButton;
202 DeepIterator *link;
203 int linkChar, linkNumber;
204
205 void resetSelection ();
206 void resetLink ();
207 void switchLinkToSelection (Iterator *it, int charPos);
208 void adjustSelection (Iterator *it, int charPos);
209 static int correctCharPos (DeepIterator *it, int charPos);
210
211 void highlight (bool fl, int dir)
212 { highlight0 (fl, from, fromChar, to, toChar, dir); }
213
214 void highlight0 (bool fl, DeepIterator *from, int fromChar,
215 DeepIterator *to, int toChar, int dir);
216 void copy ();
217
218 public:
219 enum EventType { BUTTON_PRESS, BUTTON_RELEASE, BUTTON_MOTION };
220
221 SelectionState ();
222 ~SelectionState ();
223
224 inline void setLayout (Layout *layout) { this->layout = layout; }
225 void reset ();
226 bool buttonPress (Iterator *it, int charPos, int linkNo,
227 EventButton *event);
228 bool buttonRelease (Iterator *it, int charPos, int linkNo,
229 EventButton *event);
230 bool buttonMotion (Iterator *it, int charPos, int linkNo,
231 EventMotion *event);
232
233 bool handleEvent (EventType eventType, Iterator *it, int charPos,
234 int linkNo, MousePositionEvent *event);
235 };
236
237 } // namespace dw
238 } // namespace core
239
240 #endif // __DW_SELECTION_H__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <ctype.h>
25 #include <math.h>
26
27 #include "core.hh"
28 #include "../lout/msg.h"
29
30 using namespace lout;
31
32 namespace dw {
33 namespace core {
34 namespace style {
35
36 void StyleAttrs::initValues ()
37 {
38 x_link = -1;
39 x_img = -1;
40 x_tooltip = NULL;
41 textDecoration = TEXT_DECORATION_NONE;
42 textAlign = TEXT_ALIGN_LEFT;
43 textAlignChar = '.';
44 listStylePosition = LIST_STYLE_POSITION_OUTSIDE;
45 listStyleType = LIST_STYLE_TYPE_DISC;
46 valign = VALIGN_BASELINE;
47 backgroundColor = NULL;
48 width = height = lineHeight = LENGTH_AUTO;
49 textIndent = 0;
50 margin.setVal (0);
51 borderWidth.setVal (0);
52 padding.setVal (0);
53 borderCollapse = BORDER_MODEL_SEPARATE;
54 setBorderColor (NULL);
55 setBorderStyle (BORDER_NONE);
56 hBorderSpacing = 0;
57 vBorderSpacing = 0;
58 wordSpacing = 0;
59
60 display = DISPLAY_INLINE;
61 whiteSpace = WHITE_SPACE_NORMAL;
62 cursor = CURSOR_DEFAULT;
63 }
64
65 /**
66 * \brief Reset those style attributes to their standard values, which are
67 * not inherited, according to CSS.
68 */
69 void StyleAttrs::resetValues ()
70 {
71 x_img = -1;
72
73 valign = VALIGN_BASELINE;
74 textAlignChar = '.';
75 backgroundColor = NULL;
76 width = LENGTH_AUTO;
77 height = LENGTH_AUTO;
78
79 margin.setVal (0);
80 borderWidth.setVal (0);
81 padding.setVal (0);
82 setBorderColor (NULL);
83 setBorderStyle (BORDER_NONE);
84 hBorderSpacing = 0;
85 vBorderSpacing = 0;
86
87 display = DISPLAY_INLINE;
88 }
89
90 /**
91 * \brief This method returns whether something may change its size, when
92 * its style changes from this style to \em otherStyle.
93 *
94 * It is mainly for optimizing style changes where only colors etc change
95 * (where false would be returned), in some cases it may return true, although
96 * a size change does not actually happen (e.g. when in a certain
97 * context a particular attribute is ignored).
98 *
99 * \todo Should for CSS implemented properly. Currently, size changes are
100 * not needed, so always false is returned. See also
101 * dw::core::Widget::setStyle.
102 */
103 bool StyleAttrs::sizeDiffs (StyleAttrs *otherStyle)
104 {
105 return false;
106 }
107
108 bool StyleAttrs::equals (object::Object *other) {
109 StyleAttrs *otherAttrs = (StyleAttrs *) other;
110
111 return this == otherAttrs ||
112 (font == otherAttrs->font &&
113 textDecoration == otherAttrs->textDecoration &&
114 color == otherAttrs->color &&
115 backgroundColor == otherAttrs->backgroundColor &&
116 textAlign == otherAttrs->textAlign &&
117 valign == otherAttrs->valign &&
118 textAlignChar == otherAttrs->textAlignChar &&
119 hBorderSpacing == otherAttrs->hBorderSpacing &&
120 vBorderSpacing == otherAttrs->vBorderSpacing &&
121 wordSpacing == otherAttrs->wordSpacing &&
122 width == otherAttrs->width &&
123 height == otherAttrs->height &&
124 lineHeight == otherAttrs->lineHeight &&
125 textIndent == otherAttrs->textIndent &&
126 margin.equals (&otherAttrs->margin) &&
127 borderWidth.equals (&otherAttrs->borderWidth) &&
128 padding.equals (&otherAttrs->padding) &&
129 borderCollapse == otherAttrs->borderCollapse &&
130 borderColor.top == otherAttrs->borderColor.top &&
131 borderColor.right == otherAttrs->borderColor.right &&
132 borderColor.bottom == otherAttrs->borderColor.bottom &&
133 borderColor.left == otherAttrs->borderColor.left &&
134 borderStyle.top == otherAttrs->borderStyle.top &&
135 borderStyle.right == otherAttrs->borderStyle.right &&
136 borderStyle.bottom == otherAttrs->borderStyle.bottom &&
137 borderStyle.left == otherAttrs->borderStyle.left &&
138 display == otherAttrs->display &&
139 whiteSpace == otherAttrs->whiteSpace &&
140 listStylePosition == otherAttrs->listStylePosition &&
141 listStyleType == otherAttrs->listStyleType &&
142 cursor == otherAttrs->cursor &&
143 x_link == otherAttrs->x_link &&
144 x_img == otherAttrs->x_img &&
145 x_tooltip == otherAttrs->x_tooltip);
146 }
147
148 int StyleAttrs::hashValue () {
149 return (intptr_t) font +
150 textDecoration +
151 (intptr_t) color +
152 (intptr_t) backgroundColor +
153 textAlign +
154 valign +
155 textAlignChar +
156 hBorderSpacing +
157 vBorderSpacing +
158 wordSpacing +
159 width +
160 height +
161 lineHeight +
162 textIndent +
163 margin.hashValue () +
164 borderWidth.hashValue () +
165 padding.hashValue () +
166 borderCollapse +
167 (intptr_t) borderColor.top +
168 (intptr_t) borderColor.right +
169 (intptr_t) borderColor.bottom +
170 (intptr_t) borderColor.left +
171 borderStyle.top +
172 borderStyle.right +
173 borderStyle.bottom +
174 borderStyle.left +
175 display +
176 whiteSpace +
177 listStylePosition +
178 listStyleType +
179 cursor +
180 x_link +
181 x_img +
182 (intptr_t) x_tooltip;
183 }
184
185 int Style::totalRef = 0;
186 container::typed::HashTable <StyleAttrs, Style> * Style::styleTable =
187 new container::typed::HashTable <StyleAttrs, Style> (false, false, 1024);
188
189 Style::Style (StyleAttrs *attrs)
190 {
191 copyAttrs (attrs);
192
193 refCount = 1;
194
195 font->ref ();
196 if (color)
197 color->ref ();
198 if (backgroundColor)
199 backgroundColor->ref ();
200 if (borderColor.top)
201 borderColor.top->ref();
202 if (borderColor.bottom)
203 borderColor.bottom->ref();
204 if (borderColor.left)
205 borderColor.left->ref();
206 if (borderColor.right)
207 borderColor.right->ref();
208 if (x_tooltip)
209 x_tooltip->ref();
210
211 totalRef++;
212 }
213
214 Style::~Style ()
215 {
216 font->unref ();
217
218 if (color)
219 color->unref ();
220 if (backgroundColor)
221 backgroundColor->unref ();
222 if (borderColor.top)
223 borderColor.top->unref();
224 if (borderColor.bottom)
225 borderColor.bottom->unref();
226 if (borderColor.left)
227 borderColor.left->unref();
228 if (borderColor.right)
229 borderColor.right->unref();
230 if (x_tooltip)
231 x_tooltip->unref();
232
233 styleTable->remove (this);
234 totalRef--;
235 }
236
237 void Style::copyAttrs (StyleAttrs *attrs)
238 {
239 font = attrs->font;
240 textDecoration = attrs->textDecoration;
241 color = attrs->color;
242 backgroundColor = attrs->backgroundColor;
243 textAlign = attrs->textAlign;
244 valign = attrs->valign;
245 textAlignChar = attrs->textAlignChar;
246 hBorderSpacing = attrs->hBorderSpacing;
247 vBorderSpacing = attrs->vBorderSpacing;
248 wordSpacing = attrs->wordSpacing;
249 width = attrs->width;
250 height = attrs->height;
251 lineHeight = attrs->lineHeight;
252 textIndent = attrs->textIndent;
253 margin = attrs->margin;
254 borderWidth = attrs->borderWidth;
255 padding = attrs->padding;
256 borderCollapse = attrs->borderCollapse;
257 borderColor = attrs->borderColor;
258 borderStyle = attrs->borderStyle;
259 display = attrs->display;
260 whiteSpace = attrs->whiteSpace;
261 listStylePosition = attrs->listStylePosition;
262 listStyleType = attrs->listStyleType;
263 cursor = attrs->cursor;
264 x_link = attrs->x_link;
265 x_img = attrs->x_img;
266 x_tooltip = attrs->x_tooltip;
267 }
268
269 // ----------------------------------------------------------------------
270
271 bool FontAttrs::equals(object::Object *other)
272 {
273 FontAttrs *otherAttrs = (FontAttrs*)other;
274 return
275 this == otherAttrs ||
276 (size == otherAttrs->size &&
277 weight == otherAttrs->weight &&
278 style == otherAttrs->style &&
279 letterSpacing == otherAttrs->letterSpacing &&
280 fontVariant == otherAttrs->fontVariant &&
281 strcmp (name, otherAttrs->name) == 0);
282 }
283
284 int FontAttrs::hashValue()
285 {
286 int h = object::String::hashValue (name);
287 h = (h << 5) - h + size;
288 h = (h << 5) - h + weight;
289 h = (h << 5) - h + style;
290 h = (h << 5) - h + letterSpacing;
291 h = (h << 5) - h + fontVariant;
292 return h;
293 }
294
295 Font::~Font ()
296 {
297 free ((char*)name);
298 }
299
300 void Font::copyAttrs (FontAttrs *attrs)
301 {
302 name = strdup (attrs->name);
303 size = attrs->size;
304 weight = attrs->weight;
305 style = attrs->style;
306 letterSpacing = attrs->letterSpacing;
307 fontVariant = attrs->fontVariant;
308 }
309
310 Font *Font::create0 (Layout *layout, FontAttrs *attrs,
311 bool tryEverything)
312 {
313 return layout->createFont (attrs, tryEverything);
314 }
315
316 Font *Font::create (Layout *layout, FontAttrs *attrs)
317 {
318 return create0 (layout, attrs, false);
319 }
320
321 bool Font::exists (Layout *layout, const char *name)
322 {
323 return layout->fontExists (name);
324 }
325
326 // ----------------------------------------------------------------------
327
328 bool ColorAttrs::equals(object::Object *other)
329 {
330 ColorAttrs *oc = (ColorAttrs*)other;
331 return this == oc || (color == oc->color);
332 }
333
334 int ColorAttrs::hashValue()
335 {
336 return color;
337 }
338
339 Color::~Color ()
340 {
341 }
342
343 int Color::shadeColor (int color, int d)
344 {
345 int red = (color >> 16) & 255;
346 int green = (color >> 8) & 255;
347 int blue = color & 255;
348
349 double oldLightness = ((double) misc::max (red, green, blue)) / 255;
350 double newLightness;
351
352 if (oldLightness > 0.8) {
353 if (d > 0)
354 newLightness = oldLightness - 0.2;
355 else
356 newLightness = oldLightness - 0.4;
357 } else if (oldLightness < 0.2) {
358 if (d > 0)
359 newLightness = oldLightness + 0.4;
360 else
361 newLightness = oldLightness + 0.2;
362 } else
363 newLightness = oldLightness + d * 0.2;
364
365 if (oldLightness) {
366 double f = (newLightness / oldLightness);
367 red = (int)(red * f);
368 green = (int)(green * f);
369 blue = (int)(blue * f);
370 } else {
371 red = green = blue = (int)(newLightness * 255);
372 }
373
374 return (red << 16) | (green << 8) | blue;
375 }
376
377 int Color::shadeColor (int color, Shading shading)
378 {
379 switch (shading) {
380 case SHADING_NORMAL:
381 return color;
382
383 case SHADING_LIGHT:
384 return shadeColor(color, +1);
385
386 case SHADING_INVERSE:
387 return color ^ 0xffffff;
388
389 case SHADING_DARK:
390 return shadeColor(color, -1);
391
392 default:
393 // compiler happiness
394 misc::assertNotReached ();
395 return -1;
396 }
397 }
398
399
400 Color *Color::create (Layout *layout, int col)
401 {
402 ColorAttrs attrs(col);
403
404 return layout->createColor (col);
405 }
406
407 Tooltip *Tooltip::create (Layout *layout, const char *text)
408 {
409 return layout->createTooltip (text);
410 }
411
412 // ----------------------------------------------------------------------
413
414 /*
415 * The drawBorder{Top,Bottom,Left,Right} functions are similar. They
416 * use a trapezium as draw polygon, or drawTypedLine() for dots and dashes.
417 * Although the concept is simple, achieving pixel accuracy is laborious [1].
418 *
419 * [1] http://www.dillo.org/css_compat/tests/border-style.html
420 */
421 static void drawBorderTop(View *view, Style *style,
422 int x1, int y1, int x2, int y2)
423
424 {
425 int points[4][2], d, w;
426 const bool filled = true, convex = true;
427 bool ridge = false, inset = false, dotted = false;
428 Color::Shading shading = Color::SHADING_NORMAL;
429
430 if (!style->borderColor.top || style->borderWidth.top == 0)
431 return;
432
433 switch (style->borderStyle.top) {
434 case BORDER_NONE:
435 case BORDER_HIDDEN:
436 break;
437 case BORDER_DOTTED:
438 dotted = true;
439 case BORDER_DASHED:
440 w = style->borderWidth.top;
441 view->drawTypedLine(style->borderColor.top, shading,
442 dotted ? LINE_DOTTED : LINE_DASHED,
443 w, x1+w/2, y1+w/2, x2-w/2, y2+w/2);
444 break;
445 case BORDER_SOLID:
446 case BORDER_INSET:
447 inset = true;
448 case BORDER_OUTSET:
449 if (style->borderStyle.top != BORDER_SOLID)
450 shading = (inset) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
451
452 if (style->borderWidth.top == 1) {
453 view->drawLine(style->borderColor.top, shading, x1, y1, x2, y2);
454 } else {
455 points[0][0] = x1;
456 points[1][0] = x2 + 1;
457 points[0][1] = points[1][1] = y1;
458 points[2][0] = points[1][0] - style->borderWidth.right;
459 points[3][0] = x1 + style->borderWidth.left;
460 points[2][1] = points[3][1] = points[0][1] + style->borderWidth.top;
461 view->drawPolygon (style->borderColor.top, shading, filled, convex,
462 points, 4);
463 }
464 break;
465 case BORDER_RIDGE:
466 ridge = true;
467 case BORDER_GROOVE:
468 d = style->borderWidth.top & 1;
469 points[0][0] = x1;
470 points[1][0] = x2 + 1;
471 points[0][1] = points[1][1] = y1;
472 points[2][0] = x2 - style->borderWidth.right / 2;
473 points[3][0] = x1 + style->borderWidth.left / 2;
474 points[2][1] = points[3][1] = y1 + style->borderWidth.top / 2 + d;
475 shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK;
476 view->drawPolygon (style->borderColor.top, shading, filled, convex,
477 points, 4);
478 points[0][0] = x1 + style->borderWidth.left / 2 + d;
479 points[1][0] = x2 - style->borderWidth.right / 2 + 1 - d;
480 points[0][1] = points[1][1] = y1 + style->borderWidth.top / 2 + d;
481 points[2][0] = x2 - style->borderWidth.right + 1 - d;
482 points[3][0] = x1 + style->borderWidth.left;
483 points[2][1] = points[3][1] = y1 + style->borderWidth.top;
484 shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
485 view->drawPolygon (style->borderColor.top, shading, filled, convex,
486 points, 4);
487 break;
488 case BORDER_DOUBLE:
489 w = (int) rint(style->borderWidth.top / 3.0);
490 d = w ? style->borderWidth.top - 2 * w : 0;
491 int w_l = (int) rint(style->borderWidth.left / 3.0);
492 int w_r = (int) rint(style->borderWidth.right / 3.0);
493 if (style->borderWidth.top == 1) {
494 view->drawLine(style->borderColor.top, shading, x1, y1, x2, y2);
495 break;
496 }
497 points[0][0] = x1;
498 points[1][0] = x2 + 1;
499 points[0][1] = points[1][1] = y1;
500 points[2][0] = points[1][0] - w_r;
501 points[3][0] = points[0][0] + w_l;
502 points[2][1] = points[3][1] = points[0][1] + w;
503 view->drawPolygon (style->borderColor.top, shading, filled, convex,
504 points, 4);
505 points[0][0] = x1 + style->borderWidth.left - w_l;
506 points[1][0] = x2 + 1 - style->borderWidth.right + w_r;
507 points[0][1] = points[1][1] = y1 + w + d;
508 points[2][0] = x2 + 1 - style->borderWidth.right;
509 points[3][0] = x1 + style->borderWidth.left;
510 points[2][1] = points[3][1] = y1 + style->borderWidth.top;
511 view->drawPolygon (style->borderColor.top, shading, filled, convex,
512 points, 4);
513 break;
514 }
515 }
516
517 static void drawBorderBottom(View *view, Style *style,
518 int x1, int y1, int x2, int y2)
519
520 {
521 int points[4][2], d, w;
522 const bool filled = true, convex = true;
523 bool ridge = false, inset = false, dotted = false;
524 Color::Shading shading = Color::SHADING_NORMAL;
525
526 if (!style->borderColor.bottom || style->borderWidth.bottom == 0)
527 return;
528
529 switch (style->borderStyle.bottom) {
530 case BORDER_NONE:
531 case BORDER_HIDDEN:
532 break;
533 case BORDER_DOTTED:
534 dotted = true;
535 case BORDER_DASHED:
536 w = style->borderWidth.bottom;
537 view->drawTypedLine(style->borderColor.bottom, shading,
538 dotted ? LINE_DOTTED : LINE_DASHED,
539 w, x1+w/2, y1-w/2, x2-w/2, y2-w/2);
540 break;
541 case BORDER_SOLID:
542 case BORDER_INSET:
543 inset = true;
544 case BORDER_OUTSET:
545 if (style->borderStyle.bottom != BORDER_SOLID)
546 shading = (inset) ? Color::SHADING_LIGHT : Color::SHADING_DARK;
547
548 if (style->borderWidth.bottom == 1) { /* 1 pixel line */
549 view->drawLine(style->borderColor.bottom, shading, x1, y1, x2, y2);
550 } else {
551 points[0][0] = x1 - 1;
552 points[1][0] = x2 + 2;
553 points[0][1] = points[1][1] = y1 + 1;
554 points[2][0] = points[1][0] - style->borderWidth.right;
555 points[3][0] = points[0][0] + style->borderWidth.left;
556 points[2][1] = points[3][1] = points[0][1]-style->borderWidth.bottom;
557 view->drawPolygon (style->borderColor.bottom, shading, filled, convex,
558 points, 4);
559 }
560 break;
561 case BORDER_RIDGE:
562 ridge = true;
563 case BORDER_GROOVE:
564 w = style->borderWidth.bottom;
565 d = w & 1;
566 points[0][0] = x1 - 1;
567 points[1][0] = x2 + 2 - d;
568 points[0][1] = points[1][1] = y1 + 1;
569 points[2][0] = points[1][0] - style->borderWidth.right / 2;
570 points[3][0] = points[0][0] + style->borderWidth.left / 2 + d;
571 points[2][1] = points[3][1] = points[0][1] - w/2 - d;
572 shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
573 view->drawPolygon (style->borderColor.bottom, shading, filled, convex,
574 points, 4);
575 // clockwise
576 points[0][0] = x1 + style->borderWidth.left - 1;
577 points[1][0] = x2 + 1 - style->borderWidth.right + 1;
578 points[0][1] = points[1][1] = y1 - w + 1;
579 points[2][0] = points[1][0] + style->borderWidth.right / 2;
580 points[3][0] = points[0][0] - style->borderWidth.left / 2;
581 points[2][1] = points[3][1] = points[0][1] + w/2;
582 shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK;
583 view->drawPolygon (style->borderColor.bottom, shading, filled, convex,
584 points, 4);
585 break;
586 case BORDER_DOUBLE:
587 w = (int) rint(style->borderWidth.bottom / 3.0);
588 d = w ? style->borderWidth.bottom - 2 * w : 0;
589 int w_l = (int) rint(style->borderWidth.left / 3.0);
590 int w_r = (int) rint(style->borderWidth.right / 3.0);
591 if (style->borderWidth.bottom == 1) {
592 view->drawLine(style->borderColor.bottom, shading, x1, y1, x2, y2);
593 break;
594 }
595 points[0][0] = x2 + 2;
596 points[1][0] = x1 - 1;
597 points[0][1] = points[1][1] = y1 + 1;
598 points[2][0] = points[1][0] + w_l;
599 points[3][0] = points[0][0] - w_r;
600 points[2][1] = points[3][1] = points[0][1] - w;
601 view->drawPolygon (style->borderColor.bottom, shading, filled, convex,
602 points, 4);
603 points[0][0] = x2 + 2 - style->borderWidth.right + w_r;
604 points[1][0] = x1 - 1 + style->borderWidth.left - w_l;
605 points[0][1] = points[1][1] = y1 + 1 - w - d;
606 points[2][0] = x1 - 1 + style->borderWidth.left;
607 points[3][0] = x2 + 2 - style->borderWidth.right;
608 points[2][1] = points[3][1] = y1 + 1 - style->borderWidth.bottom;
609 view->drawPolygon (style->borderColor.bottom, shading, filled, convex,
610 points, 4);
611 break;
612 }
613 }
614
615 static void drawBorderLeft(View *view, Style *style,
616 int x1, int y1, int x2, int y2)
617
618 {
619 int points[4][2], d, w;
620 bool filled = true, convex = true;
621 bool ridge = false, inset = false, dotted = false;
622 Color::Shading shading = Color::SHADING_NORMAL;
623
624 if (!style->borderColor.left || style->borderWidth.left == 0)
625 return;
626
627 switch (style->borderStyle.left) {
628 case BORDER_NONE:
629 case BORDER_HIDDEN:
630 break;
631 case BORDER_DOTTED:
632 dotted = true;
633 case BORDER_DASHED:
634 w = style->borderWidth.left;
635 view->drawTypedLine(style->borderColor.left, shading,
636 dotted ? LINE_DOTTED : LINE_DASHED,
637 w, x1+w/2, y1+w/2, x1+w/2, y2-w/2);
638 break;
639 case BORDER_SOLID:
640 case BORDER_INSET:
641 inset = true;
642 case BORDER_OUTSET:
643 if (style->borderStyle.left != BORDER_SOLID)
644 shading = (inset) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
645 if (style->borderWidth.left == 1) { /* 1 pixel line */
646 view->drawLine(style->borderColor.left, shading, x1, y1, x2, y2);
647 } else {
648 points[0][0] = points[1][0] = x1;
649 points[0][1] = y1 - 1;
650 points[1][1] = y2 + 1;
651 points[2][0] = points[3][0] = points[0][0] + style->borderWidth.left;
652 points[2][1] = points[1][1] - style->borderWidth.bottom;
653 points[3][1] = points[0][1] + style->borderWidth.top;
654 view->drawPolygon (style->borderColor.left, shading, filled, convex,
655 points, 4);
656 }
657 break;
658 case BORDER_RIDGE:
659 ridge = true;
660 case BORDER_GROOVE:
661 w = style->borderWidth.left;
662 d = w & 1;
663 points[0][0] = points[1][0] = x1;
664 points[0][1] = y1;
665 points[1][1] = y2;
666 points[2][0] = points[3][0] = x1 + w / 2 + d;
667 points[2][1] = y2 - style->borderWidth.bottom / 2;
668 points[3][1] = y1 + style->borderWidth.top / 2;
669 shading = (ridge) ? Color::SHADING_LIGHT : Color::SHADING_DARK;
670 view->drawPolygon (style->borderColor.left, shading, filled, convex,
671 points, 4);
672 points[0][0] = points[1][0] = x1 + w / 2 + d;
673 points[0][1] = y1 + style->borderWidth.top / 2;
674 points[1][1] = y2 - style->borderWidth.bottom / 2;
675 points[2][0] = points[3][0] = x1 + w;
676 points[2][1] = y2 - style->borderWidth.bottom;
677 points[3][1] = y1 + style->borderWidth.top;
678 shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
679 view->drawPolygon (style->borderColor.left, shading, filled, convex,
680 points, 4);
681 break;
682 case BORDER_DOUBLE:
683 w = (int) rint(style->borderWidth.left / 3.0);
684 d = w ? style->borderWidth.left - 2 * w : 0;
685 int w_b = (int) rint(style->borderWidth.bottom / 3.0);
686 int w_t = (int) rint(style->borderWidth.top / 3.0);
687 if (style->borderWidth.left == 1) {
688 view->drawLine(style->borderColor.left, shading, x1, y1, x2, y2-1);
689 break;
690 }
691 points[0][0] = points[1][0] = x1;
692 points[0][1] = y1 - 1;
693 points[1][1] = y2 + 1;
694 points[2][0] = points[3][0] = points[0][0] + w;
695 points[2][1] = points[1][1] - w_b;
696 points[3][1] = points[0][1] + w_t;
697 view->drawPolygon (style->borderColor.left, shading, filled, convex,
698 points, 4);
699 points[0][0] = points[1][0] = x1 + w + d;
700 points[0][1] = y1 - 1 + style->borderWidth.top - w_t;
701 points[1][1] = y2 + 1 - style->borderWidth.bottom + w_b;
702 points[2][0] = points[3][0] = points[0][0] + w;
703 points[2][1] = y2 + 1 - style->borderWidth.bottom;
704 points[3][1] = y1 - 1 + style->borderWidth.top;
705 view->drawPolygon (style->borderColor.left, shading, filled, convex,
706 points, 4);
707 break;
708 }
709 }
710
711 static void drawBorderRight(View *view, Style *style,
712 int x1, int y1, int x2, int y2)
713
714 {
715 int points[4][2], d, w;
716 const bool filled = true, convex = true;
717 bool ridge = false, inset = false, dotted = false;
718 Color::Shading shading = Color::SHADING_NORMAL;
719
720 if (!style->borderColor.right || style->borderWidth.right == 0)
721 return;
722
723 switch (style->borderStyle.right) {
724 case BORDER_NONE:
725 case BORDER_HIDDEN:
726 break;
727 case BORDER_DOTTED:
728 dotted = true;
729 case BORDER_DASHED:
730 w = style->borderWidth.right;
731 view->drawTypedLine(style->borderColor.right, shading,
732 dotted ? LINE_DOTTED : LINE_DASHED,
733 w, x1 - w/2, y1 + w/2, x1 - w/2, y2 - w/2);
734 break;
735 case BORDER_SOLID:
736 case BORDER_INSET:
737 inset = true;
738 case BORDER_OUTSET:
739 if (style->borderStyle.right != BORDER_SOLID)
740 shading = (inset) ? Color::SHADING_LIGHT : Color::SHADING_DARK;
741 if (style->borderWidth.right == 1) { /* 1 pixel line */
742 view->drawLine(style->borderColor.right, shading, x1, y1, x2, y2);
743 } else {
744 points[0][0] = points[1][0] = x1 + 1;
745 points[0][1] = y1 - 1;
746 points[1][1] = y2 + 1;
747 points[2][0] = points[3][0] = points[0][0]-style->borderWidth.right;
748 points[2][1] = points[1][1] - style->borderWidth.bottom;
749 points[3][1] = points[0][1] + style->borderWidth.top;
750 view->drawPolygon (style->borderColor.right, shading, filled, convex,
751 points,4);
752 }
753 break;
754 case BORDER_RIDGE:
755 ridge = true;
756 case BORDER_GROOVE:
757 w = style->borderWidth.right;
758 d = w & 1;
759 points[0][0] = points[1][0] = x1 + 1;
760 points[0][1] = y1;
761 points[1][1] = y2;
762 points[2][0] = points[3][0] = points[0][0] - w / 2 - d;
763 points[2][1] = y2 - style->borderWidth.bottom / 2;
764 points[3][1] = points[0][1] + style->borderWidth.top / 2;
765 shading = (ridge) ? Color::SHADING_DARK : Color::SHADING_LIGHT;
766 view->drawPolygon (style->borderColor.right, shading, filled, convex,
767 points, 4);
768 points[0][0] = points[1][0] = x1 + 1 - w / 2 - d;
769 points[0][1] = y1 + style->borderWidth.top / 2;
770 points[1][1] = y2 - style->borderWidth.bottom / 2;
771 points[2][0] = points[3][0] = x1 + 1 - w;
772 points[2][1] = y2 - style->borderWidth.bottom;
773 points[3][1] = y1 + style->borderWidth.top;
774 shading = (ridge) ? Color::SHADING_LIGHT: Color::SHADING_DARK;
775 view->drawPolygon (style->borderColor.right, shading, filled, convex,
776 points, 4);
777 break;
778 case BORDER_DOUBLE:
779 w = (int) rint(style->borderWidth.right / 3.0);
780 d = w ? style->borderWidth.right - 2 * w : 0;
781 int w_b = (int) rint(style->borderWidth.bottom / 3.0);
782 int w_t = (int) rint(style->borderWidth.top / 3.0);
783 if (style->borderWidth.right == 1) {
784 view->drawLine(style->borderColor.right, shading, x1, y1, x2, y2);
785 break;
786 }
787 points[0][0] = points[1][0] = x1 + 1;
788 points[0][1] = y1 - 1;
789 points[1][1] = y2 + 1;
790 points[2][0] = points[3][0] = points[0][0] - w;
791 points[2][1] = points[1][1] - w_b;
792 points[3][1] = points[0][1] + w_t;
793 view->drawPolygon (style->borderColor.right, shading, filled, convex,
794 points, 4);
795 points[0][0] = points[1][0] = x1 + 1 - w - d;
796 points[0][1] = y1 - 1 + style->borderWidth.top - w_t;
797 points[1][1] = y2 + 1 - style->borderWidth.bottom + w_b;
798 points[2][0] = points[3][0] = points[0][0] - w;
799 points[2][1] = y2 + 1 - style->borderWidth.bottom;
800 points[3][1] = y1 - 1 + style->borderWidth.top;
801 view->drawPolygon (style->borderColor.right, shading, filled, convex,
802 points, 4);
803 break;
804 }
805 }
806
807 /**
808 * \brief Draw the border of a region in window, according to style.
809 *
810 * Used by dw::core::Widget::drawBox and dw::core::Widget::drawWidgetBox.
811 */
812 void drawBorder (View *view, Rectangle *area,
813 int x, int y, int width, int height,
814 Style *style, bool inverse)
815 {
816 /** \todo a lot! */
817 int xb1, yb1, xb2, yb2;
818
819 // top left and bottom right point of outer border boundary
820 xb1 = x + style->margin.left;
821 yb1 = y + style->margin.top;
822 xb2 = x + (width > 0 ? width - 1 : 0) - style->margin.right;
823 yb2 = y + (height > 0 ? height - 1 : 0) - style->margin.bottom;
824
825 /*
826 // top left and bottom right point of inner border boundary
827 xp1 = xb1 + style->borderWidth.left;
828 yp1 = yb1 + style->borderWidth.top;
829 xp2 = xb2 - style->borderWidth.right;
830 yp2 = yb2 - style->borderWidth.bottom;
831
832 light = inverse ? Color::SHADING_DARK : Color::SHADING_LIGHT;
833 dark = inverse ? Color::SHADING_LIGHT : Color::SHADING_DARK;
834 normal = inverse ? Color::SHADING_INVERSE : Color::SHADING_NORMAL;
835 */
836
837 drawBorderRight(view, style, xb2, yb1, xb2, yb2);
838 drawBorderLeft(view, style, xb1, yb1, xb1, yb2);
839 drawBorderTop(view, style, xb1, yb1, xb2, yb1);
840 drawBorderBottom(view, style, xb1, yb2, xb2, yb2);
841 }
842
843
844 /**
845 * \brief Draw the background (content plus padding) of a region in window,
846 * according to style.
847 *
848 * Used by dw::core::Widget::drawBox and dw::core::Widget::drawWidgetBox.
849 */
850 void drawBackground (View *view, Rectangle *area,
851 int x, int y, int width, int height,
852 Style *style, bool inverse)
853 {
854 Rectangle bgArea, intersection;
855
856 if (style->backgroundColor) {
857 bgArea.x = x + style->margin.left + style->borderWidth.left;
858 bgArea.y = y + style->margin.top + style->borderWidth.top;
859 bgArea.width =
860 width - style->margin.left - style->borderWidth.left -
861 style->margin.right - style->borderWidth.right;
862 bgArea.height =
863 height - style->margin.top - style->borderWidth.top -
864 style->margin.bottom - style->borderWidth.bottom;
865
866 if (area->intersectsWith (&bgArea, &intersection))
867 view->drawRectangle (style->backgroundColor,
868 inverse ?
869 Color::SHADING_INVERSE : Color::SHADING_NORMAL,
870 true, intersection.x, intersection.y,
871 intersection.width, intersection.height);
872 }
873 }
874
875 // ----------------------------------------------------------------------
876
877 static const char
878 *const roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" },
879 *const roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" },
880 *const roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" },
881 *const roman_I3[] = { "","M","MM","MMM","MMMM" };
882
883 static void strtolower (char *s)
884 {
885 for ( ; *s; s++)
886 *s = tolower (*s);
887 }
888
889 /**
890 * \brief Convert a number into a string, in a given list style.
891 *
892 * Used for ordered lists.
893 */
894 void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType)
895 {
896 int i3, i2, i1, i0;
897 bool low = false;
898 int start_ch = 'A';
899
900 if (buflen <= 0)
901 return;
902
903 switch(listStyleType){
904 case LIST_STYLE_TYPE_LOWER_ALPHA:
905 case LIST_STYLE_TYPE_LOWER_LATIN:
906 start_ch = 'a';
907 case LIST_STYLE_TYPE_UPPER_ALPHA:
908 case LIST_STYLE_TYPE_UPPER_LATIN:
909 i0 = num - 1;
910 i1 = i0/26 - 1; i2 = i1/26 - 1;
911 if (i2 > 25) /* more than 26+26^2+26^3=18278 elements ? */
912 snprintf(buf, buflen, "****.");
913 else
914 snprintf(buf, buflen, "%c%c%c.",
915 i2<0 ? ' ' : start_ch + i2%26,
916 i1<0 ? ' ' : start_ch + i1%26,
917 i0<0 ? ' ' : start_ch + i0%26);
918 break;
919 case LIST_STYLE_TYPE_LOWER_ROMAN:
920 low = true;
921 case LIST_STYLE_TYPE_UPPER_ROMAN:
922 i0 = num;
923 i1 = i0/10; i2 = i1/10; i3 = i2/10;
924 i0 %= 10; i1 %= 10; i2 %= 10;
925 if (num < 0 || i3 > 4) /* more than 4999 elements ? */
926 snprintf(buf, buflen, "****.");
927 else
928 snprintf(buf, buflen, "%s%s%s%s.", roman_I3[i3], roman_I2[i2],
929 roman_I1[i1], roman_I0[i0]);
930 break;
931 case LIST_STYLE_TYPE_DECIMAL:
932 default:
933 snprintf(buf, buflen, "%d.", num);
934 break;
935 }
936
937 // ensure termination
938 buf[buflen - 1] = '\0';
939
940 if (low)
941 strtolower(buf);
942
943 }
944
945 } // namespace style
946 } // namespace dw
947 } // namespace core
0 #ifndef __DW_STYLE_HH__
1 #define __DW_STYLE_HH__
2
3 #include <stdint.h>
4
5 #ifndef __INCLUDED_FROM_DW_CORE_HH__
6 # error Do not include this file directly, use "core.hh" instead.
7 #endif
8
9 namespace dw {
10 namespace core {
11
12 /**
13 * \brief Anything related to Dillo %Widget styles is defined here.
14 *
15 * <h3>Overview</h3>
16 *
17 * dw::core::style::Style provides some resources and attributes for
18 * drawing widgets, as well as for parts of a widget (e.g., dw::Textblock
19 * uses styles for its words). Creating a style is done by filling a
20 * dw::core::style::StyleAttrs with the attributes and calling
21 * dw::core::style::Style::create:
22 *
23 * \code
24 * dw::core::style::Style styleAttrs;
25 * dw::core::style::Style *style;
26 * dw::core::Layout *layout;
27 *
28 * // ...
29 *
30 * styleAttrs.foo = bar;
31 * // etc.
32 * style = dw::core::style::Style::create (&styleAttrs, layout);
33 * // do something with style
34 * \endcode
35 *
36 * After this, the attributes of a dw::core::style::Style should not be
37 * changed anymore, since styles are often shared between different
38 * widgets etc. (see below). Most times, you simply copy the attributes
39 * of another style (possible, since dw::core::style::Style is a sub
40 * class of dw::core::style::StyleAttrs), modify them and create a new
41 * style:
42 *
43 * \code
44 * styleAttrs = *anotherStyle;
45 * styleAttrs.foo = baz;
46 * style = dw::core::style::Style::create (&styleAttrs, layout);
47 * \endcode
48 *
49 * The dw::core::style::Font structure can be created by
50 * dw::core::style::Font::create, in a similar, with
51 * dw::core::style::FontAttrs, and colors by
52 * dw::core::style::Color::create, passing 0xrrggbb as an
53 * argument. Furthermore, there is dw::core::style::Tooltip, created by
54 * dw::core::style::Tooltip::create.
55 *
56 * Notice that fonts, colors and tooltips are only intended to be used in
57 * conjunction with dw::core::style::Style.
58 *
59 *
60 * <h3>Naming</h3>
61 *
62 * dw::core::style::Style will become important for CSS, each CSS
63 * attribute, which is supported by dillo, will refer to an attribute in
64 * dw::core::style::Style. For this reason, the attributes in
65 * dw::core::style::Style get the names from the CSS attributes, with
66 * "camelCase" instead of hyphens (e.g. "background-color" becomes
67 * "backgroundColor").
68 *
69 * However, dw::core::style::Style will be extended by some more
70 * attributes, which are not defined by CSS. To distinguish them, they
71 * get the prefix "x_", e.g. dw::core::style::Style::x_link.
72 *
73 *
74 * <h3>Lengths and Percentages</h3>
75 *
76 * dw::core::style::Length is a simple data type for lengths and
77 * percentages:
78 *
79 * <ul>
80 * <li> A length refers to an absolute measurement. It is used to
81 * represent the HTML type %Pixels; and the CSS type \<length\>.
82 *
83 * For CSS lengths, there are two units: (i) pixels and absolute
84 * units, which have to be converted to pixels (a pixel is, unlike
85 * in the CSS specification, treated as absolute unit), and (ii) the
86 * relative units "em" and "ex" (see below).
87 *
88 * <li> A percentage refers to a value relative to another value. It is
89 * used for the HTML type %Length; (except %Pixels;), and the CSS
90 * type \<percentage\>.
91 *
92 * <li> A relative length can be used in lists of HTML MultiLengths.
93 * </ul>
94 *
95 * Since many values in CSS may be either lengths or percentages, a
96 * single type is very useful.
97 *
98 * <h4>Useful Functions</h4>
99 *
100 * Creating lengths:
101 *
102 * <ul>
103 * <li> dw::core::style::createAbsLength
104 * <li> dw::core::style::createPerLength
105 * <li> dw::core::style::createRelLength
106 * </ul>
107 *
108 * Examine lengths:
109 *
110 * <ul>
111 * <li> dw::core::style::isAbsLength
112 * <li> dw::core::style::isPerLength
113 * <li> dw::core::style::isRelLength
114 * <li> dw::core::style::absLengthVal
115 * <li> dw::core::style::perLengthVal
116 * <li> dw::core::style::relLengthVal
117 * </ul>
118 *
119 *
120 * <h3>Boxes</h3>
121 *
122 * <h4>The CSS %Box Model</h4>
123 *
124 * For borders, margins etc., the box model defined by CSS2 is
125 * used. dw::core::style::Style contains some members defining these
126 * attributes. A dw::core::Widget must use these values for any
127 * calculation of sizes. There are some helper functions (see
128 * dw/style.hh). A dw::core::style::Style box looks quite similar to a
129 * CSS box:
130 *
131 * \image html dw-style-box-model.png
132 *
133 * <h4>Background colors</h4>
134 *
135 * The background color is stored in
136 * dw::core::style::Style::backgroundColor, which may be NULL (the
137 * background color of the parent widget is shining through).
138 *
139 * For toplevel widgets, this color is set as the background color of the
140 * views (dw::core::View::setBgColor), for other widgets, a filled
141 * rectangle is drawn, covering the content and padding. (This is
142 * compliant with CSS2, the background color of the toplevel element
143 * covers the whole canvas.)
144 *
145 * <h4>Drawing</h4>
146 *
147 * The following methods may be useful:
148 *
149 * <ul>
150 * <li> dw::core::Widget::drawWidgetBox for drawing the box of a widget
151 * (typically at the beginning of the implementation of
152 * dw::core::Widget::draw), and
153 *
154 * <li> dw::core::Widget::drawBox, for drawing parts of a widget (e.g.
155 * dw::Textblock::Word, which has its own dw::Textblock::Word::style).
156 * </ul>
157 *
158 *
159 * <h3>Notes on Memory Management</h3>
160 *
161 * Memory management is done by reference counting,
162 * dw::core::style::Style::create returns a pointer to
163 * dw::core::style::Style with an increased reference counter, so you
164 * should care about calling dw::core::style::Style::unref if it is not
165 * used anymore. You do \em not need to care about the reference counters
166 * of fonts and styles.
167 *
168 * In detail:
169 *
170 * <ul>
171 * <li> dw::core::style::Style::ref is called in
172 *
173 * <ul>
174 * <li> dw::core::Widget::setStyle to assign a style to a widget,
175 * <li> dw::Textblock::addText, dw::Textblock::addWidget,
176 * dw::Textblock::addAnchor, dw::Textblock::addSpace,
177 * dw::Textblock::addParbreak and dw::Textblock::addLinebreak,
178 * to assign a style to a dw::Textblock::Word, and
179 * <li> by the HTML parser, when pushing an element on the stack.
180 * </ul>
181 *
182 * <li> dw::core::style::Style::unref is called in
183 *
184 * <ul>
185 * <li> dw::core::Widget::~Widget, dw::Textblock::~Textblock, by the
186 * HTML parser, when popping an element fom the stack, and
187 * <li> dw::core::Widget::setStyle, dw::Textblock::addText etc.,
188 * these methods overwrite an existing style.
189 * </ul>
190 * </ul>
191 */
192 namespace style {
193
194 enum Cursor {
195 CURSOR_CROSSHAIR,
196 CURSOR_DEFAULT,
197 CURSOR_POINTER,
198 CURSOR_MOVE,
199 CURSOR_E_RESIZE,
200 CURSOR_NE_RESIZE,
201 CURSOR_NW_RESIZE,
202 CURSOR_N_RESIZE,
203 CURSOR_SE_RESIZE,
204 CURSOR_SW_RESIZE,
205 CURSOR_S_RESIZE,
206 CURSOR_W_RESIZE,
207 CURSOR_TEXT,
208 CURSOR_WAIT,
209 CURSOR_HELP
210 };
211
212 enum BorderCollapse {
213 BORDER_MODEL_SEPARATE,
214 BORDER_MODEL_COLLAPSE
215 };
216
217 enum BorderStyle {
218 BORDER_NONE,
219 BORDER_HIDDEN,
220 BORDER_DOTTED,
221 BORDER_DASHED,
222 BORDER_SOLID,
223 BORDER_DOUBLE,
224 BORDER_GROOVE,
225 BORDER_RIDGE,
226 BORDER_INSET,
227 BORDER_OUTSET
228 };
229
230 enum TextAlignType {
231 TEXT_ALIGN_LEFT,
232 TEXT_ALIGN_RIGHT,
233 TEXT_ALIGN_CENTER,
234 TEXT_ALIGN_JUSTIFY,
235 TEXT_ALIGN_STRING
236 };
237
238 enum VAlignType {
239 VALIGN_TOP,
240 VALIGN_BOTTOM,
241 VALIGN_MIDDLE,
242 VALIGN_BASELINE,
243 VALIGN_SUB,
244 VALIGN_SUPER,
245 VALIGN_TEXT_TOP,
246 VALIGN_TEXT_BOTTOM,
247 };
248
249 /**
250 * \todo Incomplete. Has to be completed for a CSS implementation.
251 */
252 enum DisplayType {
253 DISPLAY_BLOCK,
254 DISPLAY_INLINE,
255 DISPLAY_LIST_ITEM,
256 DISPLAY_NONE,
257 DISPLAY_TABLE,
258 DISPLAY_TABLE_ROW_GROUP,
259 DISPLAY_TABLE_HEADER_GROUP,
260 DISPLAY_TABLE_FOOTER_GROUP,
261 DISPLAY_TABLE_ROW,
262 DISPLAY_TABLE_CELL
263 };
264
265 enum LineType {
266 LINE_NORMAL,
267 LINE_DOTTED,
268 LINE_DASHED
269 };
270
271 enum ListStylePosition {
272 LIST_STYLE_POSITION_INSIDE,
273 LIST_STYLE_POSITION_OUTSIDE
274 };
275
276 enum ListStyleType {
277 LIST_STYLE_TYPE_DISC,
278 LIST_STYLE_TYPE_CIRCLE,
279 LIST_STYLE_TYPE_SQUARE,
280 LIST_STYLE_TYPE_DECIMAL,
281 LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO,
282 LIST_STYLE_TYPE_LOWER_ROMAN,
283 LIST_STYLE_TYPE_UPPER_ROMAN,
284 LIST_STYLE_TYPE_LOWER_GREEK,
285 LIST_STYLE_TYPE_LOWER_ALPHA,
286 LIST_STYLE_TYPE_LOWER_LATIN,
287 LIST_STYLE_TYPE_UPPER_ALPHA,
288 LIST_STYLE_TYPE_UPPER_LATIN,
289 LIST_STYLE_TYPE_HEBREW,
290 LIST_STYLE_TYPE_ARMENIAN,
291 LIST_STYLE_TYPE_GEORGIAN,
292 LIST_STYLE_TYPE_CJK_IDEOGRAPHIC,
293 LIST_STYLE_TYPE_HIRAGANA,
294 LIST_STYLE_TYPE_KATAKANA,
295 LIST_STYLE_TYPE_HIRAGANA_IROHA,
296 LIST_STYLE_TYPE_KATAKANA_IROHA,
297 LIST_STYLE_TYPE_NONE
298 };
299
300 enum FontStyle {
301 FONT_STYLE_NORMAL,
302 FONT_STYLE_ITALIC,
303 FONT_STYLE_OBLIQUE
304 };
305
306 enum FontVariant {
307 FONT_VARIANT_NORMAL,
308 FONT_VARIANT_SMALL_CAPS
309 };
310
311 enum TextDecoration {
312 TEXT_DECORATION_NONE = 0,
313 TEXT_DECORATION_UNDERLINE = 1 << 0,
314 TEXT_DECORATION_OVERLINE = 1 << 1,
315 TEXT_DECORATION_LINE_THROUGH = 1 << 2,
316 TEXT_DECORATION_BLINK = 1 << 3
317 };
318
319 enum WhiteSpace {
320 WHITE_SPACE_NORMAL,
321 WHITE_SPACE_PRE,
322 WHITE_SPACE_NOWRAP,
323 WHITE_SPACE_PRE_WRAP,
324 WHITE_SPACE_PRE_LINE,
325 };
326
327 /**
328 * \brief Type for representing all lengths within dw::core::style.
329 *
330 * Lengths are int's. Absolute lengths are represented in the following way:
331 *
332 * \image html dw-style-length-absolute.png
333 *
334 * Percentages:
335 *
336 * \image html dw-style-length-percentage.png
337 *
338 * Relative lengths (only used in HTML):
339 *
340 * \image html dw-style-length-relative.png
341 *
342 * This is an implementation detail, use one of the following functions:
343 *
344 * Creating lengths:
345 *
346 * <ul>
347 * <li> dw::core::style::createAbsLength
348 * <li> dw::core::style::createPerLength
349 * <li> dw::core::style::createRelLength
350 * </ul>
351 *
352 * Examine lengths:
353 *
354 * <ul>
355 * <li> dw::core::style::isAbsLength
356 * <li> dw::core::style::isPerLength
357 * <li> dw::core::style::isRelLength
358 * <li> dw::core::style::absLengthVal
359 * <li> dw::core::style::perLengthVal
360 * <li> dw::core::style::relLengthVal
361 * </ul>
362 *
363 * "auto" lengths are represented as dw::core::style::LENGTH_AUTO.
364 */
365 typedef int Length;
366
367 /** \brief Returns a length of \em n pixels. */
368 inline Length createAbsLength(int n) { return (n << 2) | 1; }
369
370 /** \brief Returns a percentage, \em v is relative to 1, not to 100. */
371 inline Length createPerLength(double v) {
372 return ((int)(v * (1 << 18)) & ~3) | 2; }
373
374 /** \brief Returns a relative length. */
375 inline Length createRelLength(double v) {
376 return ((int)(v * (1 << 18)) & ~3) | 3; }
377
378 /** \brief Returns true if \em l is an absolute length. */
379 inline bool isAbsLength(Length l) { return (l & 3) == 1; }
380
381 /** \brief Returns true if \em l is a percentage. */
382 inline bool isPerLength(Length l) { return (l & 3) == 2; }
383
384 /** \brief Returns true if \em l is a relative length. */
385 inline bool isRelLength(Length l) { return (l & 3) == 3; }
386
387 /** \brief Returns the value of a length in pixels, as an integer. */
388 inline int absLengthVal(Length l) { return l >> 2; }
389
390 /** \brief Returns the value of a percentage, relative to 1, as a double. */
391 inline double perLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); }
392
393 /** \brief Returns the value of a relative length, as a float. */
394 inline double relLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); }
395
396 enum {
397 /** \brief Represents "auto" lengths. */
398 LENGTH_AUTO = 0
399 };
400
401 /**
402 * \brief Represents a dimension box according to the CSS box model.
403 *
404 * Used for dw::core::style::Style::margin,
405 * dw::core::style::Style::borderWidth, and dw::core::style::Style::padding.
406 */
407 class Box
408 {
409 public:
410 /* in future also percentages */
411 int top, right, bottom, left;
412
413 inline void setVal(int val) { top = right = bottom = left = val; }
414 inline bool equals (Box *other) {
415 return top == other->top &&
416 right == other->right &&
417 bottom == other->bottom &&
418 left == other->left;
419 }
420 inline int hashValue () {
421 return top + right + bottom + left;
422 }
423 };
424
425 class Font;
426 class Color;
427 class Tooltip;
428
429 /**
430 * \sa dw::core::style
431 */
432 class StyleAttrs : public lout::object::Object
433 {
434 public:
435 Font *font;
436 int textDecoration; /* No TextDecoration because of problems converting
437 * TextDecoration <-> int */
438 Color *color, *backgroundColor;
439
440 TextAlignType textAlign;
441 VAlignType valign;
442 char textAlignChar; /* In future, strings will be supported. */
443
444 int hBorderSpacing, vBorderSpacing, wordSpacing;
445 Length width, height, lineHeight, textIndent;
446
447 Box margin, borderWidth, padding;
448 BorderCollapse borderCollapse;
449 struct { Color *top, *right, *bottom, *left; } borderColor;
450 struct { BorderStyle top, right, bottom, left; } borderStyle;
451
452 DisplayType display;
453 WhiteSpace whiteSpace;
454 ListStylePosition listStylePosition;
455 ListStyleType listStyleType;
456 Cursor cursor;
457
458 int x_link;
459 int x_img;
460 Tooltip *x_tooltip;
461
462 void initValues ();
463 void resetValues ();
464
465 bool sizeDiffs (StyleAttrs *otherStyleAttrs);
466
467 inline void setBorderColor(Color *val) {
468 borderColor.top = borderColor.right = borderColor.bottom
469 = borderColor.left = val; }
470 inline void setBorderStyle(BorderStyle val) {
471 borderStyle.top = borderStyle.right = borderStyle.bottom
472 = borderStyle.left = val; }
473
474 inline int boxOffsetX ()
475 {
476 return margin.left + borderWidth.left + padding.left;
477 }
478 inline int boxRestWidth ()
479 {
480 return margin.right + borderWidth.right + padding.right;
481 }
482 inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); }
483 inline int boxOffsetY ()
484 {
485 return margin.top + borderWidth.top + padding.top;
486 }
487 inline int boxRestHeight ()
488 {
489 return margin.bottom + borderWidth.bottom + padding.bottom;
490 }
491 inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); }
492
493 inline bool hasBackground () { return backgroundColor != NULL; }
494
495 bool equals (lout::object::Object *other);
496 int hashValue ();
497 };
498
499
500 /**
501 * \sa dw::core::style
502 */
503 class Style: public StyleAttrs
504 {
505 private:
506 static int totalRef;
507 int refCount;
508 static lout::container::typed::HashTable <StyleAttrs, Style> *styleTable;
509
510 Style (StyleAttrs *attrs);
511
512 protected:
513 ~Style();
514
515 void copyAttrs (StyleAttrs *attrs);
516
517 public:
518 inline static Style *create (Layout *layout, StyleAttrs *attrs)
519 {
520 Style *style = styleTable->get (attrs);
521 if (style) {
522 style->ref ();
523 } else {
524 style = new Style (attrs);
525 styleTable->put(style, style);
526 }
527 return style;
528 }
529
530 inline void ref () { refCount++; }
531 inline void unref () { if (--refCount == 0) delete this; }
532 };
533
534
535 /**
536 * \sa dw::core::style
537 */
538 class TooltipAttrs: public lout::object::String
539 {
540 public:
541 TooltipAttrs(const char *text): lout::object::String(text) { }
542 };
543
544 /**
545 * \sa dw::core::style
546 */
547 class Tooltip: public TooltipAttrs
548 {
549 private:
550 int refCount;
551
552 protected:
553 Tooltip (const char *text): TooltipAttrs(text) { refCount = 0; }
554
555 public:
556 static Tooltip *create (dw::core::Layout *layout, const char *text);
557 inline void ref () { refCount++; }
558 inline void unref ()
559 { if (--refCount == 0) delete this; }
560
561 inline virtual void onEnter () { }
562 inline virtual void onLeave () { }
563 inline virtual void onMotion () { }
564 };
565
566
567 /**
568 * \sa dw::core::style
569 */
570 class FontAttrs: public lout::object::Object
571 {
572 public:
573 const char *name;
574 int size;
575 int weight;
576 int letterSpacing;
577 FontVariant fontVariant;
578 FontStyle style;
579
580 bool equals(lout::object::Object *other);
581 int hashValue();
582 };
583
584
585 /**
586 * \sa dw::core::style
587 */
588 class Font: public FontAttrs
589 {
590 private:
591 int refCount;
592
593 static Font *create0 (Layout *layout, FontAttrs *attrs, bool tryEverything);
594
595 protected:
596 inline Font () { refCount = 0; }
597 virtual ~Font ();
598
599 void copyAttrs (FontAttrs *attrs);
600
601 public:
602 int ascent, descent;
603 int spaceWidth;
604 int xHeight;
605
606 static Font *create (Layout *layout, FontAttrs *attrs);
607 static bool exists (Layout *layout, const char *name);
608
609 inline void ref () { refCount++; }
610 inline void unref () { if (--refCount == 0) delete this; }
611 };
612
613
614 /**
615 * \sa dw::core::style
616 */
617 class ColorAttrs: public lout::object::Object
618 {
619 protected:
620 int color;
621
622 public:
623 inline ColorAttrs(int color)
624 {
625 this->color = color;
626 }
627
628 inline int getColor () { return color; }
629
630 bool equals(lout::object::Object *other);
631 int hashValue();
632 };
633
634
635 /**
636 * \sa dw::core::style
637 */
638 class Color: public ColorAttrs
639 {
640 private:
641 int refCount;
642
643 void remove(dw::core::Layout *layout);
644 int shadeColor (int color, int d);
645
646 protected:
647 inline Color (int color): ColorAttrs (color) {
648 refCount = 0; }
649 virtual ~Color ();
650
651 public:
652 enum Shading { SHADING_NORMAL, SHADING_INVERSE, SHADING_DARK, SHADING_LIGHT,
653 SHADING_NUM };
654
655 protected:
656 int shadeColor (int color, Shading shading);
657
658 public:
659 static Color *create (Layout *layout, int color);
660
661 inline void ref () { refCount++; }
662 inline void unref ()
663 { if (--refCount == 0) delete this; }
664 };
665
666 void drawBorder (View *view, Rectangle *area,
667 int x, int y, int width, int height,
668 Style *style, bool inverse);
669 void drawBackground (View *view, Rectangle *area,
670 int x, int y, int width, int height,
671 Style *style, bool inverse);
672 void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType);
673
674 } // namespace style
675 } // namespace dw
676 } // namespace core
677
678 #endif // __DW_STYLE_HH__
679
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 //#define DBG
20
21 #include "table.hh"
22 #include "../lout/msg.h"
23 #include "../lout/misc.hh"
24
25 #define MAX misc::max
26
27 using namespace lout;
28
29 namespace dw {
30
31 int Table::CLASS_ID = -1;
32
33 Table::Table(bool limitTextWidth)
34 {
35 registerName ("dw::Table", &CLASS_ID);
36 setFlags (BLOCK_LEVEL);
37 setFlags (USES_HINTS);
38 setButtonSensitive(false);
39
40 this->limitTextWidth = limitTextWidth;
41
42 rowClosed = false;
43
44 // random values
45 availWidth = 100;
46 availAscent = 100;
47 availDescent = 0;
48
49 numRows = 0;
50 numCols = 0;
51 curRow = -1;
52 curCol = 0;
53
54 children = new misc::SimpleVector <Child*> (16);
55 colExtremes = new misc::SimpleVector<core::Extremes> (8);
56 colWidths = new misc::SimpleVector <int> (8);
57 cumHeight = new misc::SimpleVector <int> (8);
58 rowSpanCells = new misc::SimpleVector <int> (8);
59 colSpanCells = new misc::SimpleVector <int> (8);
60 baseline = new misc::SimpleVector <int> (8);
61 rowStyle = new misc::SimpleVector <core::style::Style*> (8);
62
63 hasColPercent = 0;
64 colPercents = new misc::SimpleVector <float> (8);
65
66 redrawX = 0;
67 redrawY = 0;
68 }
69
70
71 Table::~Table()
72 {
73 for (int i = 0; i < children->size (); i++) {
74 if (children->get(i)) {
75 switch (children->get(i)->type) {
76 case Child::CELL:
77 delete children->get(i)->cell.widget;
78 break;
79 case Child::SPAN_SPACE:
80 break;
81 }
82
83 delete children->get(i);
84 }
85 }
86
87 for (int i = 0; i < rowStyle->size (); i++)
88 if (rowStyle->get (i))
89 rowStyle->get(i)->unref ();
90
91 delete children;
92 delete colExtremes;
93 delete colWidths;
94 delete cumHeight;
95 delete rowSpanCells;
96 delete colSpanCells;
97 delete baseline;
98 delete rowStyle;
99 delete colPercents;
100 }
101
102 void Table::sizeRequestImpl (core::Requisition *requisition)
103 {
104 forceCalcCellSizes ();
105
106 /**
107 * \bug Baselines are not regarded here.
108 */
109 requisition->width = getStyle()->boxDiffWidth ()
110 + (numCols + 1) * getStyle()->hBorderSpacing;
111 for (int col = 0; col < numCols; col++)
112 requisition->width += colWidths->get (col);
113
114 requisition->ascent =
115 getStyle()->boxDiffHeight () + cumHeight->get (numRows)
116 + getStyle()->vBorderSpacing;
117 requisition->descent = 0;
118
119 }
120
121 void Table::getExtremesImpl (core::Extremes *extremes)
122 {
123 if (numCols == 0) {
124 extremes->minWidth = extremes->maxWidth = 0;
125 return;
126 }
127
128 forceCalcColumnExtremes ();
129
130 extremes->minWidth = extremes->maxWidth =
131 (numCols + 1) * getStyle()->hBorderSpacing
132 + getStyle()->boxDiffWidth ();
133 for (int col = 0; col < numCols; col++) {
134 extremes->minWidth += colExtremes->getRef(col)->minWidth;
135 extremes->maxWidth += colExtremes->getRef(col)->maxWidth;
136 }
137 if (core::style::isAbsLength (getStyle()->width)) {
138 extremes->minWidth =
139 MAX (extremes->minWidth,
140 core::style::absLengthVal(getStyle()->width));
141 extremes->maxWidth =
142 MAX (extremes->maxWidth,
143 core::style::absLengthVal(getStyle()->width));
144 }
145
146 _MSG(" Table::getExtremesImpl, {%d, %d} numCols=%d\n",
147 extremes->minWidth, extremes->maxWidth, numCols);
148 }
149
150 void Table::sizeAllocateImpl (core::Allocation *allocation)
151 {
152 calcCellSizes ();
153
154 /**
155 * \bug Baselines are not regarded here.
156 */
157
158 int offy =
159 allocation->y + getStyle()->boxOffsetY () + getStyle()->vBorderSpacing;
160 int x =
161 allocation->x + getStyle()->boxOffsetX () + getStyle()->hBorderSpacing;
162
163 for (int col = 0; col < numCols; col++) {
164 for (int row = 0; row < numRows; row++) {
165 int n = row * numCols + col;
166 if (childDefined (n)) {
167 int width =
168 (children->get(n)->cell.colspanEff - 1)
169 * getStyle()->hBorderSpacing;
170 for (int i = 0; i < children->get(n)->cell.colspanEff; i++)
171 width += colWidths->get (col + i);
172
173 core::Allocation childAllocation;
174 core::Requisition childRequisition;
175
176 children->get(n)->cell.widget->sizeRequest (&childRequisition);
177
178 childAllocation.x = x;
179 childAllocation.y = cumHeight->get (row) + offy;
180 childAllocation.width = width;
181 childAllocation.ascent = childRequisition.ascent;
182 childAllocation.descent =
183 cumHeight->get (row + children->get(n)->cell.rowspan)
184 - cumHeight->get (row) - getStyle()->vBorderSpacing
185 - childRequisition.ascent;
186 children->get(n)->cell.widget->sizeAllocate (&childAllocation);
187 }
188 }
189
190 x += colWidths->get (col) + getStyle()->hBorderSpacing;
191 }
192 }
193
194 void Table::resizeDrawImpl ()
195 {
196 queueDrawArea (redrawX, 0, allocation.width - redrawX, getHeight ());
197 queueDrawArea (0, redrawY, allocation.width, getHeight () - redrawY);
198 redrawX = allocation.width;
199 redrawY = getHeight ();
200 }
201
202 void Table::setWidth (int width)
203 {
204 // If limitTextWidth is set, a queueResize may also be necessary.
205 if (availWidth != width || limitTextWidth) {
206 _MSG(" Table::setWidth %d\n", width);
207 availWidth = width;
208 queueResize (0, false);
209 }
210 }
211
212 void Table::setAscent (int ascent)
213 {
214 if (availAscent != ascent) {
215 availAscent = ascent;
216 queueResize (0, false);
217 }
218 }
219
220 void Table::setDescent (int descent)
221 {
222 if (availDescent != descent) {
223 availDescent = descent;
224 queueResize (0, false);
225 }
226 }
227
228 void Table::draw (core::View *view, core::Rectangle *area)
229 {
230 // Can be optimized, by iterating on the lines in area.
231 drawWidgetBox (view, area, false);
232
233 #if 0
234 int offx = getStyle()->boxOffsetX () + getStyle()->hBorderSpacing;
235 int offy = getStyle()->boxOffsetY () + getStyle()->vBorderSpacing;
236 int width = getContentWidth ();
237
238 // This part seems unnecessary. It also segfaulted sometimes when
239 // cumHeight size was less than numRows. --jcid
240 for (int row = 0; row < numRows; row++) {
241 if (rowStyle->get (row))
242 drawBox (view, rowStyle->get (row), area,
243 offx, offy + cumHeight->get (row),
244 width - 2*getStyle()->hBorderSpacing,
245 cumHeight->get (row + 1) - cumHeight->get (row)
246 - getStyle()->vBorderSpacing, false);
247 }
248 #endif
249
250 for (int i = 0; i < children->size (); i++) {
251 if (childDefined (i)) {
252 Widget *child = children->get(i)->cell.widget;
253 core::Rectangle childArea;
254 if (child->intersects (area, &childArea))
255 child->draw (view, &childArea);
256 }
257 }
258 }
259
260 void Table::removeChild (Widget *child)
261 {
262 /** \bug Not implemented. */
263 }
264
265 core::Iterator *Table::iterator (core::Content::Type mask, bool atEnd)
266 {
267 return new TableIterator (this, mask, atEnd);
268 }
269
270 void Table::addCell (Widget *widget, int colspan, int rowspan)
271 {
272 Child *child;
273 int colspanEff;
274
275 // We limit the values for colspan and rowspan to 50, to avoid
276 // attacks by malicious web pages.
277 if (colspan > 50 || colspan < 0) {
278 MSG_WARN("colspan = %d is set to 50.\n", colspan);
279 colspan = 50;
280 }
281 if (rowspan > 50 || rowspan <= 0) {
282 MSG_WARN("rowspan = %d is set to 50.\n", rowspan);
283 rowspan = 50;
284 }
285
286 if (numRows == 0) {
287 // to prevent a crash
288 MSG("addCell: cell without row.\n");
289 addRow (NULL);
290 }
291
292 if (rowClosed) {
293 MSG_WARN("Last cell had colspan=0.\n");
294 addRow (NULL);
295 }
296
297 if (colspan == 0) {
298 colspanEff = MAX (numCols - curCol, 1);
299 rowClosed = true;
300 } else
301 colspanEff = colspan;
302
303 // Find next free cell-
304 while (curCol < numCols &&
305 (child = children->get(curRow * numCols + curCol)) != NULL &&
306 child->type == Child::SPAN_SPACE)
307 curCol++;
308
309 _MSG("Table::addCell numCols=%d,curCol=%d,colspan=%d,colspanEff=%d\n",
310 numCols, curCol, colspan, colspanEff);
311
312 // Increase children array, when necessary.
313 if (curRow + rowspan > numRows)
314 reallocChildren (numCols, curRow + rowspan);
315 if (curCol + colspanEff > numCols)
316 reallocChildren (curCol + colspanEff, numRows);
317
318 // Fill span space.
319 for (int col = 0; col < colspanEff; col++)
320 for (int row = 0; row < rowspan; row++)
321 if (!(col == 0 && row == 0)) {
322 int i = (curRow + row) * numCols + curCol + col;
323
324 child = children->get(i);
325 if (child) {
326 MSG("Overlapping spans in table.\n");
327 assert(child->type == Child::SPAN_SPACE);
328 delete child;
329 }
330 child = new Child ();
331 child->type = Child::SPAN_SPACE;
332 child->spanSpace.startCol = curCol;
333 child->spanSpace.startRow = curRow;
334 children->set (i, child);
335 }
336
337 // Set the "root" cell.
338 child = new Child ();
339 child->type = Child::CELL;
340 child->cell.widget = widget;
341 child->cell.colspanOrig = colspan;
342 child->cell.colspanEff = colspanEff;
343 child->cell.rowspan = rowspan;
344 children->set (curRow * numCols + curCol, child);
345
346 curCol += colspanEff;
347
348 widget->setParent (this);
349 if (rowStyle->get (curRow))
350 widget->setBgColor (rowStyle->get(curRow)->backgroundColor);
351 queueResize (0, true);
352
353 #if 0
354 // show table structure in stdout
355 for (int row = 0; row < numRows; row++) {
356 for (int col = 0; col < numCols; col++) {
357 int n = row * numCols + col;
358 if (!(child = children->get (n))) {
359 MSG("[null ] ");
360 } else if (children->get(n)->type == Child::CELL) {
361 MSG("[CELL rs=%d] ", child->cell.rowspan);
362 } else if (children->get(n)->type == Child::SPAN_SPACE) {
363 MSG("[SPAN rs=%d] ", child->cell.rowspan);
364 } else {
365 MSG("[Unk. ] ");
366 }
367 }
368 MSG("\n");
369 }
370 MSG("\n");
371 #endif
372 }
373
374 void Table::addRow (core::style::Style *style)
375 {
376 curRow++;
377
378 if (curRow >= numRows)
379 reallocChildren (numCols, curRow + 1);
380
381 if (rowStyle->get (curRow))
382 rowStyle->get(curRow)->unref ();
383
384 rowStyle->set (curRow, style);
385 if (style)
386 style->ref ();
387
388 curCol = 0;
389 rowClosed = false;
390 }
391
392 TableCell *Table::getCellRef ()
393 {
394 core::Widget *child;
395
396 for (int row = 0; row <= numRows; row++) {
397 int n = curCol + row * numCols;
398 if (childDefined (n)) {
399 child = children->get(n)->cell.widget;
400 if (child->instanceOf (TableCell::CLASS_ID))
401 return (TableCell*)child;
402 }
403 }
404
405 return NULL;
406 }
407
408 void Table::reallocChildren (int newNumCols, int newNumRows)
409 {
410 assert (newNumCols >= numCols);
411 assert (newNumRows >= numRows);
412
413 children->setSize (newNumCols * newNumRows);
414
415 if (newNumCols > numCols) {
416 // Complicated case, array got also wider.
417 for (int row = newNumRows - 1; row >= 0; row--) {
418 int colspan0Col = -1, colspan0Row = -1;
419
420 // Copy old part.
421 for (int col = numCols - 1; col >= 0; col--) {
422 int n = row * newNumCols + col;
423 children->set (n, children->get (row * numCols + col));
424 if (children->get (n)) {
425 switch (children->get(n)->type) {
426 case Child::CELL:
427 if (children->get(n)->cell.colspanOrig == 0) {
428 colspan0Col = col;
429 colspan0Row = row;
430 children->get(n)->cell.colspanEff = newNumCols - col;
431 }
432 break;
433 case Child::SPAN_SPACE:
434 if (children->get(children->get(n)->spanSpace.startRow
435 * numCols +
436 children->get(n)->spanSpace.startCol)
437 ->cell.colspanOrig == 0) {
438 colspan0Col = children->get(n)->spanSpace.startCol;
439 colspan0Row = children->get(n)->spanSpace.startRow;
440 }
441 break;
442 }
443 }
444 }
445
446 // Fill rest of the column.
447 if (colspan0Col == -1) {
448 for (int col = numCols; col < newNumCols; col++)
449 children->set (row * newNumCols + col, NULL);
450 } else {
451 for (int col = numCols; col < newNumCols; col++) {
452 Child *child = new Child ();
453 child->type = Child::SPAN_SPACE;
454 child->spanSpace.startCol = colspan0Col;
455 child->spanSpace.startRow = colspan0Row;
456 children->set (row * newNumCols + col, child);
457 }
458 }
459 }
460 }
461
462 // Bottom part of the children array.
463 for (int row = numRows; row < newNumRows; row++)
464 for (int col = 0; col < newNumCols; col++)
465 children->set (row * newNumCols + col, NULL);
466
467 // Simple arrays.
468 rowStyle->setSize (newNumRows);
469 for (int row = numRows; row < newNumRows; row++)
470 rowStyle->set (row, NULL);
471 // Rest is increased, when needed.
472
473 numCols = newNumCols;
474 numRows = newNumRows;
475 }
476
477 // ----------------------------------------------------------------------
478
479 void Table::calcCellSizes ()
480 {
481 if (needsResize ())
482 forceCalcCellSizes ();
483 }
484
485
486 void Table::forceCalcCellSizes ()
487 {
488 int totalWidth = 0, childHeight, forceTotalWidth = 1;
489 core::Extremes extremes;
490
491 // Will also call calcColumnExtremes(), when needed.
492 getExtremes (&extremes);
493
494 if (core::style::isAbsLength (getStyle()->width)) {
495 totalWidth = core::style::absLengthVal (getStyle()->width);
496 } else if (core::style::isPerLength (getStyle()->width)) {
497 /*
498 * If the width is > 100%, we use 100%, this prevents ugly
499 * results. (May be changed in future, when a more powerful
500 * rendering is implemented, to handle fixed positions etc.,
501 * as defined by CSS2.)
502 */
503 totalWidth =
504 (int)(availWidth
505 * misc::min (core::style::perLengthVal (getStyle()->width),
506 1.0));
507 } else if (getStyle()->width == core::style::LENGTH_AUTO) {
508 totalWidth = availWidth;
509 forceTotalWidth = 0;
510 }
511
512 _MSG(" availWidth = %d\n", availWidth);
513 _MSG(" totalWidth1 = %d\n", totalWidth);
514
515 if (totalWidth < extremes.minWidth)
516 totalWidth = extremes.minWidth;
517 totalWidth = totalWidth
518 - (numCols + 1) * getStyle()->hBorderSpacing
519 - getStyle()->boxDiffWidth ();
520
521 _MSG(" totalWidth2 = %d curCol=%d\n", totalWidth,curCol);
522
523
524 colWidths->setSize (numCols, 0);
525 cumHeight->setSize (numRows + 1, 0);
526 rowSpanCells->setSize (0);
527 baseline->setSize (numRows);
528
529 _MSG(" extremes = %d,%d\n", extremes.minWidth, extremes.maxWidth);
530 _MSG(" getStyle()->boxDiffWidth() = %d\n", getStyle()->boxDiffWidth());
531 _MSG(" getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing);
532
533
534 apportion_percentages2 (totalWidth, forceTotalWidth);
535 if (!hasColPercent)
536 apportion2 (totalWidth, forceTotalWidth);
537
538 setCumHeight (0, 0);
539 for (int row = 0; row < numRows; row++) {
540 /**
541 * \bug dw::Table::baseline is not filled.
542 */
543 int rowHeight = 0;
544
545 for (int col = 0; col < numCols; col++) {
546 int n = row * numCols + col;
547 if (childDefined (n)) {
548 int width = (children->get(n)->cell.colspanEff - 1)
549 * getStyle()->hBorderSpacing;
550 for (int i = 0; i < children->get(n)->cell.colspanEff; i++)
551 width += colWidths->get (col + i);
552
553 core::Requisition childRequisition;
554 children->get(n)->cell.widget->setWidth (width);
555 children->get(n)->cell.widget->sizeRequest (&childRequisition);
556 childHeight = childRequisition.ascent + childRequisition.descent;
557 if (children->get(n)->cell.rowspan == 1) {
558 rowHeight = MAX (rowHeight, childHeight);
559 } else {
560 rowSpanCells->increase();
561 rowSpanCells->set(rowSpanCells->size()-1, n);
562 }
563 }
564 }/*for col*/
565
566 setCumHeight (row + 1,
567 cumHeight->get (row) + rowHeight + getStyle()->vBorderSpacing);
568
569 }/*for row*/
570
571 apportionRowSpan ();
572 }
573
574 void Table::apportionRowSpan ()
575 {
576 int *rowHeight = NULL;
577
578 for (int c = 0; c < rowSpanCells->size(); ++c) {
579 int n = rowSpanCells->get(c);
580 int row = n / numCols;
581 int rs = children->get(n)->cell.rowspan;
582 int sumRows = cumHeight->get(row+rs) - cumHeight->get(row);
583 core::Requisition childRequisition;
584 children->get(n)->cell.widget->sizeRequest (&childRequisition);
585 int spanHeight = childRequisition.ascent + childRequisition.descent
586 + getStyle()->vBorderSpacing;
587 if (sumRows >= spanHeight)
588 continue;
589
590 // Cell size is too small.
591 _MSG("Short cell %d, sumRows=%d spanHeight=%d\n",
592 n,sumRows,spanHeight);
593
594 // Fill height array
595 if (!rowHeight) {
596 rowHeight = new int[numRows];
597 for (int i = 0; i < numRows; i++)
598 rowHeight[i] = cumHeight->get(i+1) - cumHeight->get(i);
599 }
600 #ifdef DBG
601 MSG(" rowHeight { ");
602 for (int i = 0; i < numRows; i++)
603 MSG("%d ", rowHeight[i]);
604 MSG("}\n");
605 #endif
606
607 // Calc new row sizes for this span.
608 int cumHnew_i = 0, cumh_i = 0, hnew_i;
609 for (int i = row; i < row + rs; ++i) {
610 hnew_i =
611 sumRows == 0 ? (int)((float)(spanHeight-cumHnew_i)/(row+rs-i)) :
612 (sumRows-cumh_i) <= 0 ? 0 :
613 (int)((float)(spanHeight-cumHnew_i)*rowHeight[i]/(sumRows-cumh_i));
614
615 _MSG(" i=%-3d h=%d hnew_i=%d =%d*%d/%d cumh_i=%d cumHnew_i=%d\n",
616 i,rowHeight[i],hnew_i,
617 spanHeight-cumHnew_i,rowHeight[i],sumRows-cumh_i,
618 cumh_i, cumHnew_i);
619
620 cumHnew_i += hnew_i;
621 cumh_i += rowHeight[i];
622 rowHeight[i] = hnew_i;
623 }
624 // Update cumHeight
625 for (int i = 0; i < numRows; ++i)
626 setCumHeight (i+1, cumHeight->get(i) + rowHeight[i]);
627 }
628 delete[] rowHeight;
629 }
630
631
632 /**
633 * \brief Fills dw::Table::colExtremes, only if recalculation is necessary.
634 *
635 * \bug Some parts are missing.
636 */
637 void Table::calcColumnExtremes ()
638 {
639 if (extremesChanged ())
640 forceCalcColumnExtremes ();
641 }
642
643
644 /**
645 * \brief Fills dw::Table::colExtremes in all cases.
646 */
647 void Table::forceCalcColumnExtremes ()
648 {
649 _MSG(" Table::forceCalcColumnExtremes numCols=%d\n", numCols);
650
651 if (numCols == 0)
652 return;
653
654 colExtremes->setSize (numCols);
655 colPercents->setSize (numCols);
656 colSpanCells->setSize (0);
657 /* 1. cells with colspan = 1 */
658 for (int col = 0; col < numCols; col++) {
659 colExtremes->getRef(col)->minWidth = 0;
660 colExtremes->getRef(col)->maxWidth = 0;
661 colPercents->set(col, LEN_AUTO);
662
663 for (int row = 0; row < numRows; row++) {
664 int n = row * numCols + col;
665 if (!childDefined (n))
666 continue;
667 if (children->get(n)->cell.colspanEff == 1) {
668 core::Extremes cellExtremes;
669 int cellMinW, cellMaxW, pbm;
670 core::style::Length width =
671 children->get(n)->cell.widget->getStyle()->width;
672 pbm = (numCols + 1) * getStyle()->hBorderSpacing
673 + children->get(n)->cell.widget->getStyle()->boxDiffWidth ();
674 children->get(n)->cell.widget->getExtremes (&cellExtremes);
675 if (core::style::isAbsLength (width)) {
676 // Fixed lengths include table padding, border and margin.
677 cellMinW = cellExtremes.minWidth;
678 cellMaxW = MAX (cellMinW,
679 core::style::absLengthVal(width) - pbm);
680 } else {
681 cellMinW = cellExtremes.minWidth;
682 cellMaxW = cellExtremes.maxWidth;
683 }
684
685 _MSG("FCCE, col%d colMin,colMax,cellMin,cellMax = %d,%d,%d,%d\n",
686 col,
687 colExtremes->getRef(col)->minWidth,
688 colExtremes->getRef(col)->maxWidth,
689 cellMinW, cellMaxW);
690
691 colExtremes->getRef(col)->minWidth =
692 MAX (colExtremes->getRef(col)->minWidth, cellMinW);
693 colExtremes->getRef(col)->maxWidth =
694 MAX (colExtremes->getRef(col)->minWidth, MAX (
695 colExtremes->getRef(col)->maxWidth,
696 cellMaxW));
697
698 // Also fill the colPercents array in this pass
699 if (core::style::isPerLength (width)) {
700 hasColPercent = 1;
701 if (colPercents->get(col) == LEN_AUTO)
702 colPercents->set(col, core::style::perLengthVal(width));
703 } else if (core::style::isAbsLength (width)) {
704 // We treat LEN_ABS as a special case of LEN_AUTO.
705 /*
706 * if (colPercents->get(col) == LEN_AUTO)
707 * colPercents->set(col, LEN_ABS);
708 */
709 }
710 } else {
711 colSpanCells->increase();
712 colSpanCells->set(colSpanCells->size()-1, n);
713 }
714 }
715 }
716
717 /* 2. cells with colspan > 1 */
718 /* If needed, here we set proportionally apportioned col maximums */
719 for (int c = 0; c < colSpanCells->size(); ++c) {
720 core::Extremes cellExtremes;
721 int cellMinW, cellMaxW, pbm;
722 int n = colSpanCells->get(c);
723 int col = n % numCols;
724 int cs = children->get(n)->cell.colspanEff;
725 core::style::Length width =
726 children->get(n)->cell.widget->getStyle()->width;
727 pbm = (numCols + 1) * getStyle()->hBorderSpacing
728 + children->get(n)->cell.widget->getStyle()->boxDiffWidth ();
729 children->get(n)->cell.widget->getExtremes (&cellExtremes);
730 if (core::style::isAbsLength (width)) {
731 // Fixed lengths include table padding, border and margin.
732 cellMinW = cellExtremes.minWidth;
733 cellMaxW = MAX (cellMinW, core::style::absLengthVal(width) - pbm);
734 } else {
735 cellMinW = cellExtremes.minWidth;
736 cellMaxW = cellExtremes.maxWidth;
737 }
738 int minSumCols = 0, maxSumCols = 0;
739 for (int i = 0; i < cs; ++i) {
740 minSumCols += colExtremes->getRef(col+i)->minWidth;
741 maxSumCols += colExtremes->getRef(col+i)->maxWidth;
742 }
743
744 _MSG("cs=%d spanWidth=%d,%d sumCols=%d,%d\n",
745 cs,cellMinW,cellMaxW,minSumCols,maxSumCols);
746
747 if (minSumCols >= cellMinW && maxSumCols >= cellMaxW)
748 continue;
749
750 // Cell size is too small; apportion {min,max} for this colspan.
751 int spanMinW = MAX (MAX(cs, minSumCols),
752 cellMinW - (cs-1) * getStyle()->hBorderSpacing),
753 spanMaxW = MAX (MAX(cs, maxSumCols),
754 cellMaxW - (cs-1) * getStyle()->hBorderSpacing);
755
756 if (minSumCols == 0) {
757 // No single cells defined for this span => pre-apportion equally
758 minSumCols = spanMinW; maxSumCols = spanMaxW;
759 int minW = spanMinW, maxW = spanMaxW;
760 for (int i = 0; i < cs; ++i) {
761 colExtremes->getRef(col+i)->minWidth = minW / (cs - i);
762 colExtremes->getRef(col+i)->maxWidth = maxW / (cs - i);
763 minW -= colExtremes->getRef(col+i)->minWidth;
764 maxW -= colExtremes->getRef(col+i)->maxWidth;
765 }
766 }
767
768 // This numbers will help if the span has percents.
769 int spanHasColPercent = 0;
770 int availSpanMinW = spanMinW;
771 float cumSpanPercent = 0.0f;
772 for (int i = col; i < col + cs; ++i) {
773 if (colPercents->get(i) > 0.0f) {
774 cumSpanPercent += colPercents->get(i);
775 ++spanHasColPercent;
776 } else
777 availSpanMinW -= colExtremes->getRef(i)->minWidth;
778 }
779
780 // Calculate weighted-apportion columns for this span.
781 int wMin = 0, wMax;
782 int cumMaxWnew = 0, cumMaxWold = 0, goalMaxW = spanMaxW;
783 int curAppW = maxSumCols;
784 int curExtraW = spanMinW - minSumCols;
785 for (int i = col; i < col + cs; ++i) {
786
787 if (!spanHasColPercent) {
788 int d_a = colExtremes->getRef(i)->maxWidth;
789 int d_w = curAppW > 0 ? (int)((float)curExtraW * d_a/curAppW) : 0;
790 if (d_a < 0||d_w < 0) {
791 MSG("d_a=%d d_w=%d\n",d_a,d_w);
792 exit(1);
793 }
794 wMin = colExtremes->getRef(i)->minWidth + d_w;
795 colExtremes->getRef(i)->minWidth = wMin;
796 curExtraW -= d_w;
797 curAppW -= d_a;
798 } else {
799 if (colPercents->get(i) > 0.0f) {
800 wMin = MAX (colExtremes->getRef(i)->minWidth,
801 (int)(availSpanMinW
802 * colPercents->get(i)/cumSpanPercent));
803 colExtremes->getRef(i)->minWidth = wMin;
804 }
805 }
806
807 wMax = (goalMaxW-cumMaxWnew <= 0) ? 0 :
808 (int)((float)(goalMaxW-cumMaxWnew)
809 * colExtremes->getRef(i)->maxWidth
810 / (maxSumCols-cumMaxWold));
811 wMax = MAX (wMin, wMax);
812 cumMaxWnew += wMax;
813 cumMaxWold += colExtremes->getRef(i)->maxWidth;
814 colExtremes->getRef(i)->maxWidth = wMax;
815
816 _MSG("i=%d, wMin=%d wMax=%d cumMaxWold=%d\n",
817 i,wMin,wMax,cumMaxWold);
818
819 }
820 #ifdef DBG
821 MSG("col min,max: [");
822 for (int i = 0; i < numCols; i++)
823 MSG("%d,%d ",
824 colExtremes->getRef(i)->minWidth,
825 colExtremes->getRef(i)->maxWidth);
826 MSG("]\n");
827 MSG("getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing);
828 #endif
829 }
830 }
831
832 /**
833 * \brief Apportionment function for AUTO-length columns.
834 * 'extremes' comes filled, 'result' comes defined for percentage columns.
835 */
836 void Table::apportion2 (int totalWidth, int forceTotalWidth)
837 {
838 if (colExtremes->size() == 0)
839 return;
840 #ifdef DBG
841 MSG("app2, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n",
842 availWidth, totalWidth, forceTotalWidth);
843 MSG("app2, extremes: ( ");
844 for (int i = 0; i < colExtremes->size (); i++)
845 MSG("%d,%d ",
846 colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth);
847 MSG(")\n");
848 #endif
849 int minAutoWidth = 0, maxAutoWidth = 0, availAutoWidth = totalWidth;
850 for (int col = 0; col < numCols; col++) {
851 if (colPercents->get(col) == LEN_ABS) { // set absolute lengths
852 setColWidth (col, colExtremes->get(col).minWidth);
853 }
854 if (colPercents->get(col) == LEN_AUTO) {
855 maxAutoWidth += colExtremes->get(col).maxWidth;
856 minAutoWidth += colExtremes->get(col).minWidth;
857 } else
858 availAutoWidth -= colWidths->get(col);
859 }
860
861 if (!maxAutoWidth) // no LEN_AUTO cols!
862 return;
863
864 colWidths->setSize (colExtremes->size (), 0);
865
866 if (!forceTotalWidth && maxAutoWidth < availAutoWidth) {
867 // Enough space for the maximum table, don't widen past max.
868 availAutoWidth = maxAutoWidth;
869 }
870
871 // General case.
872 int curTargetWidth = MAX (availAutoWidth, minAutoWidth);
873 int curExtraWidth = curTargetWidth - minAutoWidth;
874 int curMaxWidth = maxAutoWidth;
875 int curNewWidth = minAutoWidth;
876 for (int col = 0; col < numCols; col++) {
877 _MSG("app2, col %d, minWidth=%d maxWidth=%d\n",
878 col,extremes->get(col).minWidth, colExtremes->get(col).maxWidth);
879
880 if (colPercents->get(col) != LEN_AUTO)
881 continue;
882
883 int colMinWidth = colExtremes->getRef(col)->minWidth;
884 int colMaxWidth = colExtremes->getRef(col)->maxWidth;
885 int w = (curMaxWidth <= 0) ? 0 :
886 (int)((float)curTargetWidth * colMaxWidth/curMaxWidth);
887
888 _MSG("app2, curTargetWidth=%d colMaxWidth=%d curMaxWidth=%d "
889 "curNewWidth=%d ",
890 curTargetWidth, colMaxWidth,curMaxWidth,curNewWidth);
891 _MSG("w = %d, ", w);
892
893 if (w <= colMinWidth)
894 w = colMinWidth;
895 else if (curNewWidth - colMinWidth + w > curTargetWidth)
896 w = colMinWidth + curExtraWidth;
897
898 _MSG("w = %d\n", w);
899
900 curNewWidth -= colMinWidth;
901 curMaxWidth -= colMaxWidth;
902 curExtraWidth -= (w - colMinWidth);
903 curTargetWidth -= w;
904 setColWidth (col, w);
905 }
906 #ifdef DBG
907 MSG("app2, result: ( ");
908 for (int i = 0; i < colWidths->size (); i++)
909 MSG("%d ", colWidths->get (i));
910 MSG(")\n");
911 #endif
912 }
913
914 void Table::apportion_percentages2(int totalWidth, int forceTotalWidth)
915 {
916 int hasTablePercent = core::style::isPerLength (getStyle()->width) ? 1 : 0;
917
918 if (colExtremes->size() == 0 || (!hasTablePercent && !hasColPercent))
919 return;
920
921 // If there's a table-wide percentage, totalWidth comes already scaled.
922 _MSG("APP_P, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n",
923 availWidth, totalWidth, forceTotalWidth);
924
925 if (!hasColPercent) {
926 #ifdef DBG
927 MSG("APP_P, only a table-wide percentage\n");
928 MSG("APP_P, extremes = { ");
929 for (int col = 0; col < numCols; col++)
930 MSG("%d,%d ", colExtremes->getRef(col)->minWidth,
931 colExtremes->getRef(col)->maxWidth);
932 MSG("}\n");
933 #endif
934 // It has only a table-wide percentage. Apportion non-absolute widths.
935 int sumMaxWidth = 0, perAvailWidth = totalWidth;
936 for (int col = 0; col < numCols; col++) {
937 if (colPercents->get(col) == LEN_ABS)
938 perAvailWidth -= colExtremes->getRef(col)->maxWidth;
939 else
940 sumMaxWidth += colExtremes->getRef(col)->maxWidth;
941 }
942
943 _MSG("APP_P, perAvailWidth=%d, sumMaxWidth=%d\n",
944 perAvailWidth, sumMaxWidth);
945
946 for (int col = 0; col < numCols; col++) {
947 int max_wi = colExtremes->getRef(col)->maxWidth, new_wi;
948 if (colPercents->get(col) != LEN_ABS) {
949 new_wi = MAX (colExtremes->getRef(col)->minWidth,
950 (int)((float)max_wi * perAvailWidth/sumMaxWidth));
951 setColWidth (col, new_wi);
952 perAvailWidth -= new_wi;
953 sumMaxWidth -= max_wi;
954 }
955 }
956 #ifdef DBG
957 MSG("APP_P, result = { ");
958 for (int col = 0; col < numCols; col++)
959 MSG("%d ", colWidths->get(col));
960 MSG("}\n");
961 #endif
962
963 } else {
964 // we'll have to apportion...
965 _MSG("APP_P, we'll have to apportion...\n");
966
967 // Calculate cumPercent and available space
968 float cumPercent = 0.0f;
969 int hasAutoCol = 0;
970 int sumMinWidth = 0, sumMaxWidth = 0, sumMinNonPer = 0, sumMaxNonPer = 0;
971 for (int col = 0; col < numCols; col++) {
972 if (colPercents->get(col) > 0.0f) {
973 cumPercent += colPercents->get(col);
974 } else {
975 sumMinNonPer += colExtremes->getRef(col)->minWidth;
976 sumMaxNonPer += colExtremes->getRef(col)->maxWidth;
977 hasAutoCol += (colPercents->get(col) == LEN_AUTO);
978 }
979 sumMinWidth += colExtremes->getRef(col)->minWidth;
980 sumMaxWidth += colExtremes->getRef(col)->maxWidth;
981
982 _MSG("APP_P, col %d minWidth=%d maxWidth=%d\n", col,
983 colExtremes->getRef(col)->minWidth,
984 colExtremes->getRef(col)->maxWidth);
985 }
986 int oldTotalWidth = totalWidth;
987 if (!forceTotalWidth) {
988 if (sumMaxNonPer == 0 || cumPercent < 0.99f) {
989 // only percentage columns, or cumPercent < 100% => restrict width
990 int totW = (int)(sumMaxNonPer/(1.0f-cumPercent));
991 for (int col = 0; col < numCols; col++) {
992 totW = MAX (totW, (int)(colExtremes->getRef(col)->maxWidth
993 / colPercents->get(col)));
994 }
995 totalWidth = misc::min (totW, totalWidth);
996 }
997 }
998
999 // make sure there's enough space
1000 totalWidth = MAX (totalWidth, sumMinWidth);
1001 // extraWidth is always >= 0
1002 int extraWidth = totalWidth - sumMinWidth;
1003 int sumMinWidthPer = sumMinWidth - sumMinNonPer;
1004 int curPerWidth = sumMinWidthPer;
1005 // percentages refer to workingWidth
1006 int workingWidth = totalWidth - sumMinNonPer;
1007 if (cumPercent < 0.99f) {
1008 // In this case, use the whole table width
1009 workingWidth = totalWidth;
1010 curPerWidth = sumMinWidth;
1011 }
1012
1013 _MSG("APP_P, oldTotalWidth=%d totalWidth=%d"
1014 " workingWidth=%d extraWidth=%d sumMinNonPer=%d\n",
1015 oldTotalWidth,totalWidth,workingWidth,extraWidth,sumMinNonPer);
1016
1017 for (int col = 0; col < numCols; col++) {
1018 int colMinWidth = colExtremes->getRef(col)->minWidth;
1019 if (colPercents->get(col) >= 0.0f) {
1020 int w = (int)(workingWidth * colPercents->get(col));
1021 if (w < colMinWidth)
1022 w = colMinWidth;
1023 else if (curPerWidth - colMinWidth + w > workingWidth)
1024 w = colMinWidth + extraWidth;
1025 extraWidth -= (w - colMinWidth);
1026 curPerWidth += (w - colMinWidth);
1027 setColWidth (col, w);
1028 } else {
1029 setColWidth (col, colMinWidth);
1030 }
1031 }
1032
1033 if (cumPercent < 0.99f) {
1034 // Will have to apportion the other columns
1035 #ifdef DBG
1036 MSG("APP_P, extremes: ( ");
1037 for (int i = 0; i < colExtremes->size (); i++)
1038 MSG("%d,%d ",
1039 colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth);
1040 MSG(")\n");
1041 #endif
1042 curPerWidth -= sumMinNonPer;
1043 int perWidth = (int)(curPerWidth/cumPercent);
1044 totalWidth = MAX (totalWidth, perWidth);
1045 totalWidth = misc::min (totalWidth, oldTotalWidth);
1046
1047 _MSG("APP_P, curPerWidth=%d perWidth=%d, totalWidth=%d\n",
1048 curPerWidth, perWidth, totalWidth);
1049
1050 if (hasAutoCol == 0) {
1051 // Special case, cumPercent < 100% and no other columns to expand.
1052 // We'll honor totalWidth by expanding the percentage cols.
1053 int extraWidth = totalWidth - curPerWidth - sumMinNonPer;
1054 for (int col = 0; col < numCols; col++) {
1055 if (colPercents->get(col) >= 0.0f) {
1056 int d = (int)(extraWidth * colPercents->get(col)/cumPercent);
1057 setColWidth (col, colWidths->get(col) + d);
1058 }
1059 }
1060 }
1061 }
1062 #ifdef DBG
1063 MSG("APP_P, result ={ ");
1064 for (int col = 0; col < numCols; col++)
1065 MSG("%d ", colWidths->get(col));
1066 MSG("}\n");
1067 #endif
1068 apportion2 (totalWidth, 2);
1069
1070 #ifdef DBG
1071 MSG("APP_P, percent={");
1072 for (int col = 0; col < numCols; col++)
1073 MSG("%f ", colPercents->get(col));
1074 MSG("}\n");
1075 MSG("APP_P, result ={ ");
1076 for (int col = 0; col < numCols; col++)
1077 MSG("%d ", colWidths->get(col));
1078 MSG("}\n");
1079 #endif
1080 }
1081 }
1082
1083 // ----------------------------------------------------------------------
1084
1085 Table::TableIterator::TableIterator (Table *table,
1086 core::Content::Type mask, bool atEnd):
1087 core::Iterator (table, mask, atEnd)
1088 {
1089 index = atEnd ? table->children->size () : -1;
1090 content.type = atEnd ? core::Content::END : core::Content::START;
1091 }
1092
1093 Table::TableIterator::TableIterator (Table *table,
1094 core::Content::Type mask, int index):
1095 core::Iterator (table, mask, false)
1096 {
1097 this->index = index;
1098
1099 if (index < 0)
1100 content.type = core::Content::START;
1101 else if (index >= table->children->size ())
1102 content.type = core::Content::END;
1103 else {
1104 content.type = core::Content::WIDGET;
1105 content.widget = table->children->get(index)->cell.widget;
1106 }
1107 }
1108
1109 object::Object *Table::TableIterator::clone()
1110 {
1111 return new TableIterator ((Table*)getWidget(), getMask(), index);
1112 }
1113
1114 int Table::TableIterator::compareTo(misc::Comparable *other)
1115 {
1116 return index - ((TableIterator*)other)->index;
1117 }
1118
1119 bool Table::TableIterator::next ()
1120 {
1121 Table *table = (Table*)getWidget();
1122
1123 if (content.type == core::Content::END)
1124 return false;
1125
1126 // tables only contain widgets:
1127 if ((getMask() & core::Content::WIDGET) == 0) {
1128 content.type = core::Content::END;
1129 return false;
1130 }
1131
1132 do {
1133 index++;
1134 if (index >= table->children->size ()) {
1135 content.type = core::Content::END;
1136 return false;
1137 }
1138 } while (table->children->get(index) == NULL ||
1139 table->children->get(index)->type != Child::CELL);
1140
1141 content.type = core::Content::WIDGET;
1142 content.widget = table->children->get(index)->cell.widget;
1143 return true;
1144 }
1145
1146 bool Table::TableIterator::prev ()
1147 {
1148 Table *table = (Table*)getWidget();
1149
1150 if (content.type == core::Content::START)
1151 return false;
1152
1153 // tables only contain widgets:
1154 if ((getMask() & core::Content::WIDGET) == 0) {
1155 content.type = core::Content::START;
1156 return false;
1157 }
1158
1159 do {
1160 index--;
1161 if (index < 0) {
1162 content.type = core::Content::START;
1163 return false;
1164 }
1165 } while (table->children->get(index) == NULL ||
1166 table->children->get(index)->type != Child::CELL);
1167
1168 content.type = core::Content::WIDGET;
1169 content.widget = table->children->get(index)->cell.widget;
1170 return true;
1171 }
1172
1173 void Table::TableIterator::highlight (int start, int end,
1174 core::HighlightLayer layer)
1175 {
1176 /** todo Needs this an implementation? */
1177 }
1178
1179 void Table::TableIterator::unhighlight (int direction,
1180 core::HighlightLayer layer)
1181 {
1182 }
1183
1184 void Table::TableIterator::getAllocation (int start, int end,
1185 core::Allocation *allocation)
1186 {
1187 /** \bug Not implemented. */
1188 }
1189
1190 } // namespace dw
0 #ifndef __DW_TABLE_HH__
1 #define __DW_TABLE_HH__
2
3 #include "core.hh"
4 #include "tablecell.hh"
5 #include "../lout/misc.hh"
6
7 namespace dw {
8
9 /**
10 * \brief A Widget for rendering tables.
11 *
12 * <h3>Introduction</h3>
13 *
14 * The dw::Table widget is used to render HTML tables.
15 *
16 * Each cell is itself a separate widget. Any widget may be used, however, in
17 * dillo, only instances of dw::Textblock and dw::TableCell are used as
18 * children of dw::Table.
19 *
20 *
21 * <h3>Sizes</h3>
22 *
23 * <h4>General</h4>
24 *
25 * The following diagram shows the dependencies between the different
26 * functions, which are related to size calculation. Click on the boxes
27 * for more information.
28 *
29 * \dot
30 * digraph G {
31 * node [shape=record, fontname=Helvetica, fontsize=10, color="#c0c0c0"];
32 * edge [arrowhead="open", arrowtail="none", labelfontname=Helvetica,
33 * labelfontsize=10, color="#404040", labelfontcolor="#000080",
34 * fontname=Helvetica, fontsize=10];
35 * fontname=Helvetica; fontsize=10;
36 *
37 * sizeRequestImpl [color="#0000ff", URL="\ref dw::Table::sizeRequestImpl"];
38 * sizeAllocateImpl [color="#0000ff",
39 * URL="\ref dw::Table::sizeAllocateImpl"];
40 * getExtremesImpl [color="#0000ff", URL="\ref dw::Table::getExtremesImpl"];
41 *
42 * subgraph cluster_sizes {
43 * style="dashed"; color="#8080c0";
44 * calcCellSizes [URL="\ref dw::Table::calcCellSizes"];
45 * forceCalcCellSizes [URL="\ref dw::Table::forceCalcCellSizes"];
46 * }
47 *
48 * subgraph cluster_extremes {
49 * style="dashed"; color="#8080c0";
50 * calcColumnExtremes [URL="\ref dw::Table::calcColumnExtremes"];
51 * forceCalcColumnExtremes[URL="\ref dw::Table::forceCalcColumnExtremes"];
52 * }
53 *
54 * sizeRequestImpl -> forceCalcCellSizes [label="[B]"];
55 * sizeAllocateImpl -> calcCellSizes [label="[A]"];
56 * getExtremesImpl -> forceCalcColumnExtremes [label="[B]"];
57 *
58 * forceCalcCellSizes -> calcColumnExtremes;
59 *
60 * calcCellSizes -> forceCalcCellSizes [style="dashed", label="[C]"];
61 * calcColumnExtremes -> forceCalcColumnExtremes [style="dashed",
62 * label="[C]"];
63 * }
64 * \enddot
65 *
66 * [A] In this case, the new calculation is \em not forced, but only
67 * done, when necessary.
68 *
69 * [B] In this case, the new calculation is allways necessary, since [C]
70 * is the case.
71 *
72 * [C] Whether this function is called, depends on NEEDS_RESIZE /
73 * EXTREMES_CHANGED.
74 *
75 *
76 * <h4>Apportionment</h4>
77 *
78 * \sa\ref rounding-errors
79 *
80 * Given two array \f$e_{i,\min}\f$ and \f$e_{i,\max}\f$, which
81 * represent the column minima and maxima, and a total width \f$W\f$, \em
82 * apportionment means to calculate column widths \f$w_{i}\f$, with
83 *
84 * \f[e_{i,\min} \le w_{i} \le e_{i,\max}\f]
85 *
86 * and
87 *
88 * \f[\sum w_{i} = W\f]
89 *
90 * There are different algorithms for apportionment, a simple one is
91 * recommended in the HTML 4.0.1 specification
92 * (http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.5.2.2):
93 *
94 * \f[w_{i} = e_{i,\min} +
95 * {e_{i,\max} - e_{i,\min}\over\sum e_{i,\max} - \sum e_{i,\min}}
96 * (W - \sum e_{i,\min})\f]
97 *
98 * This one is used currently, but another one will be used soon, which is
99 * described below. The rest of this chapter is independent of the exact
100 * apportionment algorithm.
101 *
102 * When referring to the apportionment function, we will call it
103 * \f$a_i (W, (e_{i,\min}), (e_{i,\min}))\f$ and write
104 * something like this:
105 *
106 * \f[w_{i} = a_i (W, (e_{i,\min}), (e_{i,\max})) \f]
107 *
108 * It is implemented by dw::Table::apportion.
109 *
110 * <h4>Column Extremes</h4>
111 *
112 * \sa\ref rounding-errors
113 *
114 * The sizes, which all other sizes depend on, are column extremes, which
115 * define, how wide a column may be at min and at max. They are
116 * calculated in the following way:
117 *
118 * <ol>
119 * <li> First, only cells with colspan = 1 are regarded:
120 *
121 * \f[ e_{\hbox{base},i,\min} = \max \{ e_{\hbox{cell},i,j,\min} \} \f]
122 * \f[ e_{\hbox{base},i,\max} = \max \{ e_{\hbox{cell},i,j,\max} \} \f]
123 *
124 * only for cells \f$(i, j)\f$ with colspan = 1.
125 *
126 * <li> Then,
127 * \f$e_{\hbox{span},i,\min}\f$ (but not \f$e_{\hbox{span},i,\max}\f$)
128 * are calculated from cells with colspan > 1. (In the following formulas,
129 * the cell at \f$(i_1, j)\f$ always span from \f$i_1\f$ to \f$i_2\f$.)
130 * If the minimal width of the column exceeds the sum of the column minima
131 * calculated in the last step:
132 *
133 * \f[e_{\hbox{cell},i_1,j,\min} >
134 * \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\min}\f]
135 *
136 * then the minimal width of this cell is apportioned to the columns:
137 *
138 * <ul>
139 * <li> If the minimal width of this cell also exceeds the sum of the
140 * column maxima:
141 *
142 * \f[e_{\hbox{cell},i_1,j,\min} >
143 * \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}\f]
144 *
145 * then \f$e_{\hbox{cell},i_1,j,\min}\f$ is apportioned in a simple
146 * way:
147 *
148 * \f[e_{\hbox{span},i,j,\min} =
149 * e_{\hbox{base},i,\max}
150 * {e_{\hbox{span},i,j,\min} \over
151 * \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}}\f]
152 *
153 * <li> Otherwise, the apportionment function is used:
154 *
155 * \f[e_{\hbox{span},i,j,\min} =
156 * a_i (e_{\hbox{cell},i_1,j,\min},
157 * (e_{\hbox{cell},i_1,j,\min} \ldots
158 * e_{\hbox{cell},i_2,j,\min}),
159 * (e_{\hbox{cell},i_1,j,\max} \ldots
160 * e_{\hbox{cell},i_2,j,\max}))\f]
161 * </ul>
162 *
163 * After this, \f$e_{\hbox{span},i,\min}\f$ is then the maximum of all
164 * \f$e_{\hbox{span},i,j,\min}\f$.
165 *
166 * <li> Finally, the maximum of both is used.
167 * \f[ e_{i,\min} =
168 * \max \{ e_{\hbox{base},i,\min}, e_{\hbox{span},i,\min} \} \f]
169 * \f[ e_{i,\max} =
170 * \max \{ e_{\hbox{base},i,\max}, e_{i,\min} \} \f]
171 * For the maxima, there is no \f$e_{\hbox{span},i,\max}\f$, but it has to
172 * be assured, that the maximum is always greater than or equal to the
173 * minimum.
174 * </ol>
175 *
176 * Generally, if absolute widths are specified, they are, instead of the
177 * results of dw::core::Widget::getExtremes, taken for the minimal and
178 * maximal width of a cell (minus the box difference, i.e. the difference
179 * between content size and widget size). If the content width
180 * specification is smaller than the minimal content width of the widget
181 * (determined by dw::core::Widget::getExtremes), the latter is used
182 * instead.
183 *
184 * If percentage widths are specified, they are also collected, as column
185 * maxima. A similar method as for the extremes is used, for cells with
186 * colspan > 1:
187 *
188 * \f[w_{\hbox{span},i,j,\%} =
189 * a_i (w_{\hbox{cell},i_1,j,\%},
190 * (e_{\hbox{cell},i_1,j,\min} \ldots e_{\hbox{cell},i_2,j,\min}),
191 * (e_{\hbox{cell},i_1,j,\max} \ldots e_{\hbox{cell},i_2,j,\max}))\f]
192 *
193 * <h4>Cell Sizes</h4>
194 *
195 * <h5>Determining the Width of the Table</h5>
196 *
197 * The total width is
198 *
199 * <ul>
200 * <li> the specified absolute width of the table, when given, or
201 * <li> the available width (set by dw::Table::setWidth) times the specified
202 * percentage width of t(at max 100%), if the latter is given, or
203 * <li> otherwise the available width.
204 * </ul>
205 *
206 * In any case, it is corrected, if it is less than the minimal width
207 * (but not if it is greater than the maximal width).
208 *
209 * \bug The parentheses is not fully clear, look at the old code.
210 *
211 * Details on differences because of styles are omitted. Below, this
212 * total width is called \f$W\f$.
213 *
214 * <h5>Evaluating percentages</h5>
215 *
216 * The following algorithms are used to solve collisions between
217 * different size specifications (absolute and percentage). Generally,
218 * inherent sizes and specified absolute sizes are preferred.
219 *
220 * <ol>
221 * <li> First, calculate the sum of the minimal widths, for columns, where
222 * no percentage width has been specified. The difference to the total
223 * width is at max available to the columns with percentage width
224 * specifications:
225 *
226 * \f[W_{\hbox{columns}_\%,\hbox{available}} = W - \sum e_{i,\min}\f]
227 *
228 * with only those columns \f$i\f$ with no percentage width specification.
229 *
230 * <li> Then, calculate the sum of the widths, which the columns with
231 * percentage width specification would allocate, when fully adhering to
232 * them:
233 *
234 * \f[W_{\hbox{columns}_\%,\hbox{best}} = W \sum w_{i,\%}\f]
235 *
236 * with only those columns \f$i\f$ with a percentage width specification.
237 *
238 * <li> Two cases are distinguished:
239 *
240 * <ul>
241 * <li> \f$W_{\hbox{columns}_\%,\hbox{available}} \ge
242 * W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the
243 * percentage widths can be used without any modification, by
244 * setting the extremes:
245 *
246 * \f[e_{i,\min} = e_{i,\max} = W w_{i,\%}\f]
247 *
248 * for only those columns \f$i\f$ with a percentage width
249 * specification.
250 *
251 * <li> \f$W_{\hbox{columns}_\%,\hbox{available}} <
252 * W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the widths
253 * for these columns must be cut down:
254 *
255 * \f[e_{i,\min} = e_{i,\max} =
256 * w_{i,\%}
257 * {W_{\hbox{columns}_\%,\hbox{available}} \over
258 * w_{\hbox{total},\%}}\f]
259 *
260 * with
261 *
262 * \f[w_{\hbox{total},\%} = \sum w_{i,\%}\f]
263 *
264 * in both cases for only those columns \f$i\f$ with a percentage
265 * width specification.
266 * </ul>
267 * </ol>
268 *
269 * (\f$e_{i,\min}\f$ and \f$e_{i,\max}\f$ are set \em temporarily here,
270 * the notation should be a bit clearer.)
271 *
272 *
273 * <h5>Column Widths</h5>
274 *
275 * The column widths are now simply calculated by applying the
276 * apportionment function.
277 *
278 *
279 * <h5>Row Heights</h5>
280 *
281 * ...
282 *
283 * <h3>Alternative Apportionment Algorithm</h3>
284 *
285 * The algorithm described here tends to result in more homogeneous column
286 * widths.
287 *
288 * The following rule leads to well-defined \f$w_{i}\f$: All columns
289 * \f$i\f$ have have the same width \f$w\f$, except:
290 * <ul>
291 * <li> \f$w < e_{i,\min}\f$, or
292 * <li> \f$w > e_{i,\max}\f$.
293 * </ul>
294 *
295 * Furthermore, \f$w\f$ is
296 * <ul>
297 * <li> less than all \f$e_{i,\min}\f$ of columns not having \f$w\f$ as
298 * width, and
299 * <li> greater than all \f$e_{i,\min}\f$ of columns not having \f$w\f$ as
300 * width.
301 * </ul>
302 *
303 * Of course, \f$\sum w_{i} = W\f$ must be the case.
304 *
305 * Based on an initial value \f$w = {W\over n}\f$, \f$w\f$ can iteratively
306 * adjusted, based on these rules.
307 *
308 *
309 * <h3>Borders, Paddings, Spacing</h3>
310 *
311 * Currently, DwTable supports only the separated borders model (see CSS
312 * specification). Borders, paddings, spacing is done by creating
313 * dw::core::style::Style structures with values equivalent to following CSS:
314 *
315 * <pre>
316 * TABLE {
317 * border: outset \em table-border;
318 * border-collapse: separate;
319 * border-spacing: \em table-cellspacing;
320 * background-color: \em table-bgcolor;
321 * }
322 *
323 * TD TH {
324 * border: inset \em table-border;
325 * padding: \em table-cellspacing;
326 * background-color: \em td/th-bgcolor;
327 * }
328 * </pre>
329 *
330 * Here, \em foo-bar refers to the attribute \em bar of the tag \em foo foo.
331 * Look at the HTML parser for more details.
332 */
333 class Table: public core::Widget
334 {
335 private:
336
337 struct Child
338 {
339 enum {
340 CELL, // cell starts here
341 SPAN_SPACE // part of a spanning cell
342 } type;
343 union {
344 struct {
345 core::Widget *widget;
346 int colspanOrig, colspanEff, rowspan;
347 } cell;
348 struct {
349 int startCol, startRow; // where the cell starts
350 } spanSpace;
351 };
352 };
353
354 class TableIterator: public core::Iterator
355 {
356 private:
357 int index;
358
359 public:
360 TableIterator (Table *table, core::Content::Type mask, bool atEnd);
361 TableIterator (Table *table, core::Content::Type mask, int index);
362
363 lout::object::Object *clone();
364 int compareTo(lout::misc::Comparable *other);
365
366 bool next ();
367 bool prev ();
368 void highlight (int start, int end, core::HighlightLayer layer);
369 void unhighlight (int direction, core::HighlightLayer layer);
370 void getAllocation (int start, int end, core::Allocation *allocation);
371 };
372
373 friend class TableIterator;
374
375 bool limitTextWidth, rowClosed;
376 int availWidth, availAscent, availDescent; // set by set...
377
378 int numRows, numCols, curRow, curCol;
379 lout::misc::SimpleVector<Child*> *children;
380
381 int redrawX, redrawY;
382
383 /**
384 * \brief The extremes of all columns.
385 */
386 lout::misc::SimpleVector<core::Extremes> *colExtremes;
387
388 /**
389 * \brief The widths of all columns.
390 */
391 lout::misc::SimpleVector<int> *colWidths;
392
393 /**
394 * Row cumulative height array: cumHeight->size() is numRows + 1,
395 * cumHeight->get(0) is 0, cumHeight->get(numRows) is the total table
396 * height.
397 */
398 lout::misc::SimpleVector<int> *cumHeight;
399 /**
400 * If a Cell has rowspan > 1, it goes into this array
401 */
402 lout::misc::SimpleVector<int> *rowSpanCells;
403 /**
404 * If a Cell has colspan > 1, it goes into this array
405 */
406 lout::misc::SimpleVector<int> *colSpanCells;
407 lout::misc::SimpleVector<int> *baseline;
408
409 lout::misc::SimpleVector<core::style::Style*> *rowStyle;
410
411 /**
412 * hasColPercent becomes true when any cell specifies a percentage width.
413 * A negative value in colPercents means LEN_AUTO or LEN_ABS.
414 */
415 enum { LEN_AUTO = -1, LEN_ABS = -2};
416 int hasColPercent;
417 lout::misc::SimpleVector<float> *colPercents;
418
419 inline bool childDefined(int n)
420 {
421 return n < children->size() && children->get(n) != NULL &&
422 children->get(n)->type != Child::SPAN_SPACE;
423 }
424
425 void reallocChildren (int newNumCols, int newNumRows);
426
427 void calcCellSizes ();
428 void forceCalcCellSizes ();
429 void apportionRowSpan ();
430
431 void calcColumnExtremes ();
432 void forceCalcColumnExtremes ();
433
434 void apportion2 (int totalWidth, int forceTotalWidth);
435 void apportion_percentages2 (int totalWidth, int forceTotalWidth);
436
437 void setCumHeight (int row, int value)
438 {
439 if (value != cumHeight->get (row)) {
440 redrawY = lout::misc::min ( redrawY, value );
441 cumHeight->set (row, value);
442 }
443 }
444
445 inline void setColWidth (int col, int value)
446 {
447 if (value != colWidths->get (col)) {
448 redrawX = lout::misc::min (redrawX, value);
449 colWidths->set (col, value);
450 }
451 }
452
453 protected:
454 void sizeRequestImpl (core::Requisition *requisition);
455 void getExtremesImpl (core::Extremes *extremes);
456 void sizeAllocateImpl (core::Allocation *allocation);
457 void resizeDrawImpl ();
458
459 void setWidth (int width);
460 void setAscent (int ascent);
461 void setDescent (int descent);
462 void draw (core::View *view, core::Rectangle *area);
463
464 //bool buttonPressImpl (core::EventButton *event);
465 //bool buttonReleaseImpl (core::EventButton *event);
466 //bool motionNotifyImpl (core::EventMotion *event);
467
468 void removeChild (Widget *child);
469
470 public:
471 static int CLASS_ID;
472
473 Table(bool limitTextWidth);
474 ~Table();
475
476 core::Iterator *iterator (core::Content::Type mask, bool atEnd);
477
478 void addCell (Widget *widget, int colspan, int rowspan);
479 void addRow (core::style::Style *style);
480 TableCell *getCellRef ();
481 };
482
483 } // namespace dw
484
485 #endif // __DW_TABLE_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "tablecell.hh"
22 #include <stdio.h>
23
24 namespace dw {
25
26 int TableCell::CLASS_ID = -1;
27
28 TableCell::TableCell (TableCell *ref, bool limitTextWidth):
29 AlignedTextblock (limitTextWidth)
30 {
31 registerName ("dw::TableCell", &CLASS_ID);
32
33 /** \bug ignoreLine1OffsetSometimes does not work? */
34 //ignoreLine1OffsetSometimes = true;
35 charWordIndex = -1;
36 setRefTextblock (ref);
37 setButtonSensitive(true);
38 }
39
40 TableCell::~TableCell()
41 {
42 }
43
44 void TableCell::wordWrap(int wordIndex)
45 {
46 Textblock::Word *word;
47 const char *p;
48
49 Textblock::wordWrap (wordIndex);
50
51 if (charWordIndex == -1) {
52 word = words->getRef (wordIndex);
53 if (word->content.type == core::Content::TEXT) {
54 if ((p = strchr (word->content.text,
55 word->style->textAlignChar))) {
56 charWordIndex = wordIndex;
57 charWordPos = p - word->content.text + 1;
58 } else if (word->style->textAlignChar == ' ' &&
59 word->content.space) {
60 charWordIndex = wordIndex + 1;
61 charWordPos = 0;
62 }
63 }
64 }
65
66 if (wordIndex == charWordIndex)
67 updateValue ();
68 }
69
70 int TableCell::getValue ()
71 {
72 Textblock::Word *word;
73 int i, wordIndex;
74 int w;
75
76 if (charWordIndex == -1)
77 wordIndex = words->size () -1;
78 else
79 wordIndex = charWordIndex;
80
81 w = 0;
82 for (i = 0; i < wordIndex; i++) {
83 word = words->getRef (i);
84 w += word->size.width + word->origSpace;
85 }
86
87 if (charWordIndex == -1) {
88 if (words->size () > 0) {
89 word = words->getRef (words->size () - 1);
90 w += word->size.width;
91 }
92 } else {
93 word = words->getRef (charWordIndex);
94 w += layout->textWidth (word->style->font, word->content.text,
95 charWordPos);
96 }
97
98 return w;
99 }
100
101 void TableCell::setMaxValue (int maxValue, int value)
102 {
103 line1Offset = maxValue - value;
104 queueResize (0, true);
105 }
106
107 } // namespace dw
0 #ifndef __DW_TABLECELL_HH__
1 #define __DW_TABLECELL_HH__
2
3 #include "core.hh"
4 #include "alignedtextblock.hh"
5
6 namespace dw {
7
8 class TableCell: public AlignedTextblock
9 {
10 private:
11 int charWordIndex, charWordPos;
12
13 protected:
14 void wordWrap(int wordIndex);
15
16 int getValue ();
17 void setMaxValue (int maxValue, int value);
18
19 public:
20 static int CLASS_ID;
21
22 TableCell(TableCell *ref, bool limitTextWidth);
23 ~TableCell();
24 };
25
26 } // namespace dw
27
28 #endif // __DW_TABLECELL_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "textblock.hh"
22 #include "../lout/msg.h"
23 #include "../lout/misc.hh"
24
25 #include <stdio.h>
26 #include <limits.h>
27
28 using namespace lout;
29
30 namespace dw {
31
32 int Textblock::CLASS_ID = -1;
33
34 Textblock::Textblock (bool limitTextWidth)
35 {
36 registerName ("dw::Textblock", &CLASS_ID);
37 setFlags (BLOCK_LEVEL);
38 setFlags (USES_HINTS);
39 setButtonSensitive(true);
40
41 hasListitemValue = false;
42 innerPadding = 0;
43 line1Offset = 0;
44 line1OffsetEff = 0;
45 ignoreLine1OffsetSometimes = false;
46 mustQueueResize = false;
47 redrawY = 0;
48 lastWordDrawn = -1;
49
50 /*
51 * The initial sizes of lines and words should not be
52 * too high, since this will waste much memory with tables
53 * containing many small cells. The few more calls to realloc
54 * should not decrease the speed considerably.
55 * (Current setting is for minimal memory usage. An interesting fact
56 * is that high values decrease speed due to memory handling overhead!)
57 * TODO: Some tests would be useful.
58 */
59 lines = new misc::SimpleVector <Line> (1);
60 words = new misc::SimpleVector <Word> (1);
61 anchors = new misc::SimpleVector <Anchor> (1);
62
63 //DBG_OBJ_SET_NUM(page, "num_lines", num_lines);
64
65 lastLineWidth = 0;
66 lastLineParMin = 0;
67 lastLineParMax = 0;
68 wrapRef = -1;
69
70 //DBG_OBJ_SET_NUM(page, "last_line_width", last_line_width);
71 //DBG_OBJ_SET_NUM(page, "last_line_par_min", last_line_par_min);
72 //DBG_OBJ_SET_NUM(page, "last_line_par_max", last_line_par_max);
73 //DBG_OBJ_SET_NUM(page, "wrap_ref", wrap_ref);
74
75 hoverLink = -1;
76
77 // random values
78 availWidth = 100;
79 availAscent = 100;
80 availDescent = 0;
81
82 hoverTooltip = NULL;
83
84 this->limitTextWidth = limitTextWidth;
85
86 for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
87 /* hlStart[layer].index > hlEnd[layer].index means no highlighting */
88 hlStart[layer].index = 1;
89 hlStart[layer].nChar = 0;
90 hlEnd[layer].index = 0;
91 hlEnd[layer].nChar = 0;
92 }
93 }
94
95 Textblock::~Textblock ()
96 {
97 //_MSG ("Textblock::~Textblock\n");
98
99 for (int i = 0; i < words->size(); i++) {
100 Word *word = words->getRef (i);
101 if (word->content.type == core::Content::WIDGET)
102 delete word->content.widget;
103 word->style->unref ();
104 word->spaceStyle->unref ();
105 }
106
107 for (int i = 0; i < anchors->size(); i++) {
108 Anchor *anchor = anchors->getRef (i);
109 /* This also frees the names (see removeAnchor() and related). */
110 removeAnchor(anchor->name);
111 }
112
113 delete lines;
114 delete words;
115 delete anchors;
116
117 /* Make sure we don't own widgets anymore. Necessary before call of
118 parent class destructor. (???) */
119 words = NULL;
120
121 //DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
122 }
123
124 /**
125 * The ascent of a textblock is the ascent of the first line, plus
126 * padding/border/margin. This can be used to align the first lines
127 * of several textblocks in a horizontal line.
128 */
129 void Textblock::sizeRequestImpl (core::Requisition *requisition)
130 {
131 rewrap ();
132
133 if (lines->size () > 0) {
134 Line *lastLine = lines->getRef (lines->size () - 1);
135 requisition->width =
136 misc::max (lastLine->maxLineWidth, lastLineWidth);
137 /* Note: the breakSpace of the last line is ignored, so breaks
138 at the end of a textblock are not visible. */
139 requisition->ascent = lines->getRef(0)->boxAscent;
140 requisition->descent = lastLine->top
141 + lastLine->boxAscent + lastLine->boxDescent -
142 lines->getRef(0)->boxAscent;
143 } else {
144 requisition->width = lastLineWidth;
145 requisition->ascent = 0;
146 requisition->descent = 0;
147 }
148
149 requisition->width += innerPadding + getStyle()->boxDiffWidth ();
150 requisition->ascent += getStyle()->boxOffsetY ();
151 requisition->descent += getStyle()->boxRestHeight ();
152
153 if (requisition->width < availWidth)
154 requisition->width = availWidth;
155 }
156
157 /**
158 * Get the extremes of a word within a textblock.
159 */
160 void Textblock::getWordExtremes (Word *word, core::Extremes *extremes)
161 {
162 if (word->content.type == core::Content::WIDGET) {
163 if (word->content.widget->usesHints ())
164 word->content.widget->getExtremes (extremes);
165 else {
166 if (core::style::isPerLength
167 (word->content.widget->getStyle()->width)) {
168 extremes->minWidth = 0;
169 if (word->content.widget->hasContents ())
170 extremes->maxWidth = 1000000;
171 else
172 extremes->maxWidth = 0;
173 } else if (core::style::isAbsLength
174 (word->content.widget->getStyle()->width)) {
175 /* Fixed lengths are only applied to the content, so we have to
176 * add padding, border and margin. */
177 extremes->minWidth = extremes->maxWidth =
178 core::style::absLengthVal (word->content.widget->getStyle()
179 ->width)
180 + word->style->boxDiffWidth ();
181 } else
182 word->content.widget->getExtremes (extremes);
183 }
184 } else {
185 extremes->minWidth = word->size.width;
186 extremes->maxWidth = word->size.width;
187 }
188 }
189
190 void Textblock::getExtremesImpl (core::Extremes *extremes)
191 {
192 core::Extremes wordExtremes;
193 Line *line;
194 Word *word;
195 int wordIndex, lineIndex;
196 int parMin, parMax;
197 bool nowrap;
198
199 //DBG_MSG (widget, "extremes", 0, "Dw_page_get_extremes");
200 //DBG_MSG_START (widget);
201
202 if (lines->size () == 0) {
203 /* empty page */
204 extremes->minWidth = 0;
205 extremes->maxWidth = 0;
206 } else if (wrapRef == -1) {
207 /* no rewrap necessary -> values in lines are up to date */
208 line = lines->getRef (lines->size () - 1);
209 /* Historical note: The former distinction between lines with and without
210 * words[first_word]->nowrap set is no longer necessary, since
211 * Dw_page_real_word_wrap sets max_word_min to the correct value in any
212 * case. */
213 extremes->minWidth = line->maxWordMin;
214 extremes->maxWidth = misc::max (line->maxParMax, lastLineParMax);
215 //DBG_MSG (widget, "extremes", 0, "simple case");
216 } else {
217 /* Calculate the extremes, based on the values in the line from
218 where a rewrap is necessary. */
219 //DBG_MSG (widget, "extremes", 0, "complex case");
220
221 if (wrapRef == 0) {
222 extremes->minWidth = 0;
223 extremes->maxWidth = 0;
224 parMin = 0;
225 parMax = 0;
226 } else {
227 line = lines->getRef (wrapRef);
228 extremes->minWidth = line->maxWordMin;
229 extremes->maxWidth = line->maxParMax;
230 parMin = line->parMin;
231 parMax = line->parMax;
232
233 //DBG_MSGF (widget, "extremes", 0, "parMin = %d", parMin);
234 }
235
236 //_MSG ("*** parMin = %d\n", parMin);
237
238 int prevWordSpace = 0;
239 for (lineIndex = wrapRef; lineIndex < lines->size (); lineIndex++) {
240 //DBG_MSGF (widget, "extremes", 0, "line %d", lineIndex);
241 //DBG_MSG_START (widget);
242 core::style::WhiteSpace ws;
243
244 line = lines->getRef (lineIndex);
245 ws = words->getRef(line->firstWord)->style->whiteSpace;
246 nowrap = ws == core::style::WHITE_SPACE_PRE ||
247 ws == core::style::WHITE_SPACE_NOWRAP;
248
249 //DEBUG_MSG (DEBUG_SIZE_LEVEL, " line %d (of %d), nowrap = %d\n",
250 // lineIndex, page->num_lines, nowrap);
251
252 for (wordIndex = line->firstWord; wordIndex <= line->lastWord;
253 wordIndex++) {
254 word = words->getRef (wordIndex);
255 getWordExtremes (word, &wordExtremes);
256
257 if (wordIndex == 0) {
258 wordExtremes.minWidth += line1OffsetEff;
259 wordExtremes.maxWidth += line1OffsetEff;
260 //DEBUG_MSG (DEBUG_SIZE_LEVEL + 1,
261 // " (next plus %d)\n", page->line1_offset);
262 }
263
264 if (nowrap) {
265 parMin += prevWordSpace + wordExtremes.minWidth;
266 //DBG_MSGF (widget, "extremes", 0, "parMin = %d", parMin);
267 } else {
268 if (extremes->minWidth < wordExtremes.minWidth)
269 extremes->minWidth = wordExtremes.minWidth;
270 }
271
272 _MSG("parMax = %d, wordMaxWidth=%d, prevWordSpace=%d\n",
273 parMax, wordExtremes.maxWidth, prevWordSpace);
274 if (word->content.type != core::Content::BREAK)
275 parMax += prevWordSpace;
276 parMax += wordExtremes.maxWidth;
277 prevWordSpace = word->origSpace;
278
279 //DEBUG_MSG (DEBUG_SIZE_LEVEL + 1,
280 // " word %s: maxWidth = %d\n",
281 // a_Dw_content_text (&word->content),
282 // word_extremes.maxWidth);
283 }
284
285 if ((words->getRef(line->lastWord)->content.type
286 == core::Content::BREAK ) ||
287 lineIndex == lines->size () - 1 ) {
288
289 //DEBUG_MSG (DEBUG_SIZE_LEVEL + 2,
290 // " parMax = %d, after word %d (%s)\n",
291 // parMax, line->last_word - 1,
292 // a_Dw_content_text (&word->content));
293
294 if (extremes->maxWidth < parMax)
295 extremes->maxWidth = parMax;
296
297 if (nowrap) {
298 //DBG_MSGF (widget, "extremes", 0, "parMin = %d", parMin);
299 if (extremes->minWidth < parMin)
300 extremes->minWidth = parMin;
301
302 //DEBUG_MSG (DEBUG_SIZE_LEVEL + 2,
303 // " parMin = %d, after word %d (%s)\n",
304 // parMin, line->last_word - 1,
305 // a_Dw_content_text (&word->content));
306 }
307
308 prevWordSpace = 0;
309 parMin = 0;
310 parMax = 0;
311 }
312
313 //DBG_MSG_END (widget);
314 }
315
316 //DEBUG_MSG (DEBUG_SIZE_LEVEL + 3, " Result: %d, %d\n",
317 // extremes->minWidth, extremes->maxWidth);
318 }
319
320 //DBG_MSGF (widget, "extremes", 0, "width difference: %d + %d",
321 // page->inner_padding, p_Dw_style_box_diff_width (widget->style));
322
323 int diff = innerPadding + getStyle()->boxDiffWidth ();
324 extremes->minWidth += diff;
325 extremes->maxWidth += diff;
326
327 //DBG_MSG_END (widget);
328 }
329
330
331 void Textblock::sizeAllocateImpl (core::Allocation *allocation)
332 {
333 int lineIndex, wordIndex;
334 Line *line;
335 Word *word;
336 int xCursor;
337 core::Allocation childAllocation;
338 core::Allocation *oldChildAllocation;
339
340 if (allocation->width != this->allocation.width) {
341 redrawY = 0;
342 }
343
344 for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) {
345 line = lines->getRef (lineIndex);
346 xCursor = lineXOffsetWidget (line);
347
348 for (wordIndex = line->firstWord; wordIndex <= line->lastWord;
349 wordIndex++) {
350 word = words->getRef (wordIndex);
351
352 if (wordIndex == lastWordDrawn + 1) {
353 redrawY = misc::min (redrawY, lineYOffsetWidget (line));
354 }
355
356 if (word->content.type == core::Content::WIDGET) {
357 /** \todo Justification within the line is done here. */
358 childAllocation.x = xCursor + allocation->x;
359 /* align=top:
360 childAllocation.y = line->top + allocation->y;
361 */
362
363 /* align=bottom (base line) */
364 /* Commented lines break the n2 and n3 test cases at
365 * http://www.dillo.org/test/img/ */
366 childAllocation.y =
367 lineYOffsetCanvasAllocation (line, allocation)
368 + (line->boxAscent - word->size.ascent);
369 // - word->content.widget->getStyle()->margin.top;
370 childAllocation.width = word->size.width;
371 childAllocation.ascent = word->size.ascent;
372 // + word->content.widget->getStyle()->margin.top;
373 childAllocation.descent = word->size.descent;
374 // + word->content.widget->getStyle()->margin.bottom;
375
376 oldChildAllocation = word->content.widget->getAllocation();
377
378 if (childAllocation.x != oldChildAllocation->x ||
379 childAllocation.y != oldChildAllocation->y ||
380 childAllocation.width != oldChildAllocation->width) {
381 /* The child widget has changed its position or its width
382 * so we need to redraw from this line onwards.
383 */
384 redrawY = misc::min (redrawY, lineYOffsetWidget (line));
385 if (word->content.widget->wasAllocated ()) {
386 redrawY = misc::min (redrawY,
387 oldChildAllocation->y - this->allocation.y);
388 }
389
390 } else if (childAllocation.ascent + childAllocation.descent !=
391 oldChildAllocation->ascent + oldChildAllocation->descent) {
392 /* The child widget has changed its height. We need to redraw
393 * from where it changed.
394 * It's important not to draw from the line base, because the
395 * child might be a table covering the whole page so we would
396 * end up redrawing the whole screen over and over.
397 * The drawing of the child content is left to the child itself.
398 * However this optimization is only possible if the widget is
399 * the only word in the line apart from an optional BREAK.
400 * Otherwise the height change of the widget could change the
401 * position of other words in the line, requiring a
402 * redraw of the complete line.
403 */
404 if (line->lastWord == line->firstWord ||
405 (line->lastWord == line->firstWord + 1 &&
406 words->getRef (line->lastWord)->content.type ==
407 core::Content::BREAK)) {
408
409 int childChangedY =
410 misc::min(childAllocation.y - allocation->y +
411 childAllocation.ascent + childAllocation.descent,
412 oldChildAllocation->y - this->allocation.y +
413 oldChildAllocation->ascent +
414 oldChildAllocation->descent);
415
416 redrawY = misc::min (redrawY, childChangedY);
417 } else {
418 redrawY = misc::min (redrawY, lineYOffsetWidget (line));
419 }
420 }
421 word->content.widget->sizeAllocate (&childAllocation);
422 }
423
424 xCursor += (word->size.width + word->effSpace);
425 }
426 }
427
428 for (int i = 0; i < anchors->size(); i++) {
429 Anchor *anchor = anchors->getRef(i);
430 int y;
431
432 if (anchor->wordIndex >= words->size()) {
433 y = allocation->y + allocation->ascent + allocation->descent;
434 } else {
435 Line *line = lines->getRef(findLineOfWord (anchor->wordIndex));
436 y = lineYOffsetCanvasAllocation (line, allocation);
437 }
438 changeAnchor (anchor->name, y);
439 }
440 }
441
442 void Textblock::resizeDrawImpl ()
443 {
444 queueDrawArea (0, redrawY, allocation.width, getHeight () - redrawY);
445 if (lines->size () > 0) {
446 Line *lastLine = lines->getRef (lines->size () - 1);
447 /* Remember the last word that has been drawn so we can ensure to
448 * draw any new added words (see sizeAllocateImpl()).
449 */
450 lastWordDrawn = lastLine->lastWord;
451 }
452
453 redrawY = getHeight ();
454 }
455
456 void Textblock::markSizeChange (int ref)
457 {
458 markChange (ref);
459 }
460
461 void Textblock::markExtremesChange (int ref)
462 {
463 markChange (ref);
464 }
465
466 /*
467 * Implementation for both mark_size_change and mark_extremes_change.
468 */
469 void Textblock::markChange (int ref)
470 {
471 if (ref != -1) {
472 //DBG_MSGF (page, "wrap", 0, "Dw_page_mark_size_change (ref = %d)", ref);
473
474 if (wrapRef == -1)
475 wrapRef = ref;
476 else
477 wrapRef = misc::min (wrapRef, ref);
478
479 //DBG_OBJ_SET_NUM (page, "wrap_ref", page->wrap_ref);
480 }
481 }
482
483 void Textblock::setWidth (int width)
484 {
485 /* If limitTextWidth is set to YES, a queue_resize may also be
486 * necessary. */
487 if (availWidth != width || limitTextWidth) {
488 //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
489 // "Dw_page_set_width: Calling p_Dw_widget_queue_resize, "
490 // "in page with %d word(s)\n",
491 // page->num_words);
492
493 availWidth = width;
494 queueResize (0, false);
495 mustQueueResize = false;
496 redrawY = 0;
497 }
498 }
499
500 void Textblock::setAscent (int ascent)
501 {
502 if (availAscent != ascent) {
503 //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
504 // "Dw_page_set_ascent: Calling p_Dw_widget_queue_resize, "
505 // "in page with %d word(s)\n",
506 // page->num_words);
507
508 availAscent = ascent;
509 queueResize (0, false);
510 mustQueueResize = false;
511 }
512 }
513
514 void Textblock::setDescent (int descent)
515 {
516 if (availDescent != descent) {
517 //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
518 // "Dw_page_set_descent: Calling p_Dw_widget_queue_resize, "
519 // "in page with %d word(s)\n",
520 // page->num_words);
521
522 availDescent = descent;
523 queueResize (0, false);
524 mustQueueResize = false;
525 }
526 }
527
528 bool Textblock::buttonPressImpl (core::EventButton *event)
529 {
530 return sendSelectionEvent (core::SelectionState::BUTTON_PRESS, event);
531 }
532
533 bool Textblock::buttonReleaseImpl (core::EventButton *event)
534 {
535 return sendSelectionEvent (core::SelectionState::BUTTON_RELEASE, event);
536 }
537
538 bool Textblock::motionNotifyImpl (core::EventMotion *event)
539 {
540 if (event->state & core::BUTTON1_MASK)
541 return sendSelectionEvent (core::SelectionState::BUTTON_MOTION, event);
542 else {
543 bool inSpace;
544 int linkOld = hoverLink;
545 core::style::Tooltip *tooltipOld = hoverTooltip;
546 const Word *word = findWord (event->xWidget, event->yWidget, &inSpace);
547
548 // cursor from word or widget style
549 if (word == NULL) {
550 setCursor (getStyle()->cursor);
551 hoverLink = -1;
552 hoverTooltip = NULL;
553 } else {
554 core::style::Style *style = inSpace ? word->spaceStyle : word->style;
555 setCursor (style->cursor);
556 hoverLink = style->x_link;
557 hoverTooltip = style->x_tooltip;
558 }
559 // Show/hide tooltip
560 if (tooltipOld != hoverTooltip) {
561 if (tooltipOld)
562 tooltipOld->onLeave ();
563 if (hoverTooltip)
564 hoverTooltip->onEnter ();
565 } else if (hoverTooltip)
566 hoverTooltip->onMotion ();
567
568 if (hoverLink != linkOld)
569 return layout->emitLinkEnter (this, hoverLink, -1, -1, -1);
570 else
571 return hoverLink != -1;
572 }
573 }
574
575 void Textblock::enterNotifyImpl (core::EventCrossing *event)
576 {
577 }
578
579 void Textblock::leaveNotifyImpl (core::EventCrossing *event)
580 {
581 hoverLink = -1;
582 (void) layout->emitLinkEnter (this, hoverLink, -1, -1, -1);
583 if (hoverTooltip) {
584 hoverTooltip->onLeave();
585 hoverTooltip = NULL;
586 }
587 }
588
589 /**
590 * \brief Send event to selection.
591 */
592 bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
593 core::MousePositionEvent *event)
594 {
595 core::Iterator *it;
596 Line *line, *lastLine;
597 int nextWordStartX, wordStartX, wordX, nextWordX, yFirst, yLast;
598 int charPos = 0, link = -1, prevPos, wordIndex, lineIndex;
599 Word *word;
600 bool found, r;
601
602 if (words->size () == 0) {
603 wordIndex = -1;
604 } else {
605 lastLine = lines->getRef (lines->size () - 1);
606 yFirst = lineYOffsetCanvasI (0);
607 yLast = lineYOffsetCanvas (lastLine) + lastLine->boxAscent +
608 lastLine->boxDescent;
609 if (event->yCanvas < yFirst) {
610 // Above the first line: take the first word.
611 wordIndex = 0;
612 charPos = 0;
613 } else if (event->yCanvas >= yLast) {
614 // Below the last line: take the last word.
615 wordIndex = words->size () - 1;
616 word = words->getRef (wordIndex);
617 charPos = word->content.type == core::Content::TEXT ?
618 strlen (word->content.text) : 0;
619 } else {
620 lineIndex = findLineIndex (event->yWidget);
621 line = lines->getRef (lineIndex);
622
623 // Pointer within the break space?
624 if (event->yWidget >
625 (lineYOffsetWidget (line) + line->boxAscent + line->boxDescent)) {
626 // Choose this break.
627 wordIndex = line->lastWord;
628 charPos = 0;
629 } else if (event->xWidget < lineXOffsetWidget (line)) {
630 // Left of the first word in the line.
631 wordIndex = line->firstWord;
632 charPos = 0;
633 } else {
634 nextWordStartX = lineXOffsetWidget (line);
635 found = false;
636 for (wordIndex = line->firstWord;
637 !found && wordIndex <= line->lastWord;
638 wordIndex++) {
639 word = words->getRef (wordIndex);
640 wordStartX = nextWordStartX;
641 nextWordStartX += word->size.width + word->effSpace;
642
643 if (event->xWidget >= wordStartX &&
644 event->xWidget < nextWordStartX) {
645 // We have found the word.
646 if (word->content.type == core::Content::TEXT) {
647 // Search the character the mouse pointer is in.
648 // nextWordX is the right side of this character.
649 charPos = 0;
650 while ((nextWordX = wordStartX +
651 layout->textWidth (word->style->font,
652 word->content.text, charPos))
653 <= event->xWidget)
654 charPos = layout->nextGlyph (word->content.text,
655 charPos);
656 // The left side of this character.
657 prevPos = layout->prevGlyph (word->content.text, charPos);
658 wordX = wordStartX + layout->textWidth (word->style->font,
659 word->content.text,
660 prevPos);
661
662 // If the mouse pointer is left from the middle, use the
663 // left position, otherwise, use the right one.
664 if (event->xWidget <= (wordX + nextWordX) / 2)
665 charPos = prevPos;
666 } else {
667 // Depends on whether the pointer is within the left or
668 // right half of the (non-text) word.
669 if (event->xWidget >=
670 (wordStartX + nextWordStartX) / 2)
671 charPos = core::SelectionState::END_OF_WORD;
672 else
673 charPos = 0;
674 }
675
676 found = true;
677 link = word->style ? word->style->x_link : -1;
678 break;
679 }
680 }
681
682 if (!found) {
683 // No word found in this line (i.e. we are on the right side),
684 // take the last of this line.
685 wordIndex = line->lastWord;
686 if (wordIndex >= words->size ())
687 wordIndex--;
688 word = words->getRef (wordIndex);
689 charPos = word->content.type == core::Content::TEXT ?
690 strlen (word->content.text) :
691 (int)core::SelectionState::END_OF_WORD;
692 }
693 }
694 }
695 }
696 it = new TextblockIterator (this, core::Content::SELECTION_CONTENT,
697 wordIndex);
698 r = selectionHandleEvent (eventType, it, charPos, link, event);
699 it->unref ();
700 return r;
701 }
702
703 void Textblock::removeChild (Widget *child)
704 {
705 /** \bug Not implemented. */
706 }
707
708 core::Iterator *Textblock::iterator (core::Content::Type mask, bool atEnd)
709 {
710 return new TextblockIterator (this, mask, atEnd);
711 }
712
713 /*
714 * ...
715 *
716 * availWidth is passed from wordWrap, to avoid calculating it twice.
717 */
718 void Textblock::justifyLine (Line *line, int availWidth)
719 {
720 /* To avoid rounding errors, the calculation is based on accumulated
721 * values (*_cum). */
722 int i;
723 int origSpaceSum, origSpaceCum;
724 int effSpaceDiffCum, lastEffSpaceDiffCum;
725 int diff;
726
727 diff = availWidth - lastLineWidth;
728 if (diff > 0) {
729 origSpaceSum = 0;
730 for (i = line->firstWord; i < line->lastWord; i++)
731 origSpaceSum += words->getRef(i)->origSpace;
732
733 origSpaceCum = 0;
734 lastEffSpaceDiffCum = 0;
735 for (i = line->firstWord; i < line->lastWord; i++) {
736 origSpaceCum += words->getRef(i)->origSpace;
737
738 if (origSpaceCum == 0)
739 effSpaceDiffCum = lastEffSpaceDiffCum;
740 else
741 effSpaceDiffCum = diff * origSpaceCum / origSpaceSum;
742
743 words->getRef(i)->effSpace = words->getRef(i)->origSpace +
744 (effSpaceDiffCum - lastEffSpaceDiffCum);
745 //DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", i,
746 // page->words[i].eff_space);
747
748 lastEffSpaceDiffCum = effSpaceDiffCum;
749 }
750 }
751 }
752
753
754 Textblock::Line *Textblock::addLine (int wordIndex, bool newPar)
755 {
756 Line *lastLine;
757
758 //DBG_MSG (page, "wrap", 0, "Dw_page_add_line");
759 //DBG_MSG_START (page);
760
761 lines->increase ();
762 //DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
763
764 //DEBUG_MSG (DEBUG_REWRAP_LEVEL, "--- new line %d in %p, with word %d of %d"
765 // "\n", page->num_lines - 1, page, word_ind, page->num_words);
766
767 lastLine = lines->getRef (lines->size () - 1);
768
769 if (lines->size () == 1) {
770 lastLine->top = 0;
771 lastLine->maxLineWidth = line1OffsetEff;
772 lastLine->maxWordMin = 0;
773 lastLine->maxParMax = 0;
774 } else {
775 Line *prevLine = lines->getRef (lines->size () - 2);
776
777 lastLine->top = prevLine->top + prevLine->boxAscent +
778 prevLine->boxDescent + prevLine->breakSpace;
779 lastLine->maxLineWidth = prevLine->maxLineWidth;
780 lastLine->maxWordMin = prevLine->maxWordMin;
781 lastLine->maxParMax = prevLine->maxParMax;
782 }
783
784 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.top", page->num_lines - 1,
785 // lastLine->top);
786 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxLineWidth", page->num_lines - 1,
787 // lastLine->maxLineWidth);
788 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxWordMin", page->num_lines - 1,
789 // lastLine->maxWordMin);
790 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxParMax", page->num_lines - 1,
791 // lastLine->maxParMax);
792 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMin", page->num_lines - 1,
793 // lastLine->parMin);
794 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMax", page->num_lines - 1,
795 // lastLine->parMax);
796
797 lastLine->firstWord = wordIndex;
798 lastLine->boxAscent = lastLine->contentAscent = 0;
799 lastLine->boxDescent = lastLine->contentDescent = 0;
800 lastLine->marginDescent = 0;
801 lastLine->breakSpace = 0;
802 lastLine->leftOffset = 0;
803
804 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
805 // lastLine->boxAscent);
806 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
807 // lastLine->boxDescent);
808
809 /* update values in line */
810 lastLine->maxLineWidth = misc::max (lastLine->maxLineWidth, lastLineWidth);
811
812 if (lines->size () > 1)
813 lastLineWidth = 0;
814 else
815 lastLineWidth = line1OffsetEff;
816
817 if (newPar) {
818 lastLine->maxParMax = misc::max (lastLine->maxParMax, lastLineParMax);
819 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxParMax", page->num_lines - 1,
820 // lastLine->maxParMax);
821
822 /* The following code looks questionable (especially since the values
823 * will be overwritten). In any case, line1OffsetEff is probably
824 * supposed to go into lastLinePar*, not lastLine->par*.
825 */
826 if (lines->size () > 1) {
827 lastLine->parMin = 0;
828 lastLine->parMax = 0;
829 } else {
830 lastLine->parMin = line1OffsetEff;
831 lastLine->parMax = line1OffsetEff;
832 }
833 lastLineParMin = 0;
834 lastLineParMax = 0;
835
836 //DBG_OBJ_SET_NUM(page, "lastLineParMin", page->lastLineParMin);
837 //DBG_OBJ_SET_NUM(page, "lastLineParMax", page->lastLineParMax);
838 }
839
840 lastLine->parMin = lastLineParMin;
841 lastLine->parMax = lastLineParMax;
842
843 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMin", page->num_lines - 1,
844 // lastLine->parMin);
845 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMax", page->num_lines - 1,
846 // lastLine->parMax);
847
848 //DBG_MSG_END (page);
849 return lastLine;
850 }
851
852 /*
853 * This method is called in two cases: (i) when a word is added
854 * (ii) when a page has to be (partially) rewrapped. It does word wrap,
855 * and adds new lines if necessary.
856 */
857 void Textblock::wordWrap(int wordIndex)
858 {
859 Line *lastLine;
860 Word *word;
861 int availWidth, lastSpace, leftOffset, len;
862 bool newLine = false, newPar = false;
863 core::Extremes wordExtremes;
864
865 //DBG_MSGF (page, "wrap", 0, "Dw_page_real_word_wrap (%d): %s, width = %d",
866 // word_ind, a_Dw_content_html (&page->words[word_ind].content),
867 // page->words[word_ind].size.width);
868 //DBG_MSG_START (page);
869
870 availWidth = this->availWidth - getStyle()->boxDiffWidth() - innerPadding;
871 if (limitTextWidth &&
872 layout->getUsesViewport () &&
873 availWidth > layout->getWidthViewport () - 10)
874 availWidth = layout->getWidthViewport () - 10;
875
876 word = words->getRef (wordIndex);
877 word->effSpace = word->origSpace;
878
879 /* Test whether line1Offset can be used. */
880 if (wordIndex == 0) {
881 if (ignoreLine1OffsetSometimes &&
882 line1Offset + word->size.width > availWidth) {
883 line1OffsetEff = 0;
884 } else {
885 int indent = 0;
886
887 if (word->content.type == core::Content::WIDGET &&
888 word->content.widget->blockLevel() == true) {
889 /* don't use text-indent when nesting blocks */
890 } else {
891 if (core::style::isPerLength(getStyle()->textIndent)) {
892 indent = misc::roundInt(this->availWidth *
893 core::style::perLengthVal (getStyle()->textIndent));
894 } else {
895 indent = core::style::absLengthVal (getStyle()->textIndent);
896 }
897 }
898 line1OffsetEff = line1Offset + indent;
899 }
900 }
901
902 if (lines->size () == 0) {
903 //DBG_MSG (page, "wrap", 0, "first line");
904 newLine = true;
905 newPar = true;
906 lastLine = NULL;
907 } else {
908 Word *prevWord = words->getRef (wordIndex - 1);
909
910 lastLine = lines->getRef (lines->size () - 1);
911
912 if (prevWord->content.type == core::Content::BREAK) {
913 //DBG_MSG (page, "wrap", 0, "after a break");
914 /* previous word is a break */
915 newLine = true;
916 newPar = true;
917 } else if (word->style->whiteSpace == core::style::WHITE_SPACE_NOWRAP ||
918 word->style->whiteSpace == core::style::WHITE_SPACE_PRE) {
919 //DBG_MSGF (page, "wrap", 0, "no wrap (white_space = %d)",
920 // word->style->white_space);
921 newLine = false;
922 newPar = false;
923 } else if (lastLine->firstWord != wordIndex) {
924 /* Does new word fit into the last line? */
925 //DBG_MSGF (page, "wrap", 0,
926 // "word %d (%s) fits? (%d + %d + %d &lt;= %d)...",
927 // word_ind, a_Dw_content_html (&word->content),
928 // page->lastLine_width, prevWord->orig_space,
929 // word->size.width, availWidth);
930 newLine = lastLineWidth + prevWord->origSpace + word->size.width >
931 availWidth;
932 //DBG_MSGF (page, "wrap", 0, "... %s.",
933 // newLine ? "No" : "Yes");
934 }
935 }
936
937 if (newLine) {
938 if (word->style->textAlign == core::style::TEXT_ALIGN_JUSTIFY &&
939 lastLine != NULL && !newPar) {
940 justifyLine (lastLine, availWidth);
941 }
942 lastLine = addLine (wordIndex, newPar);
943 }
944
945 lastLine->lastWord = wordIndex;
946 lastLine->boxAscent = misc::max (lastLine->boxAscent, word->size.ascent);
947 lastLine->boxDescent = misc::max (lastLine->boxDescent, word->size.descent);
948
949 len = word->style->font->ascent;
950 if (word->style->valign == core::style::VALIGN_SUPER)
951 len += len / 2;
952 lastLine->contentAscent = misc::max (lastLine->contentAscent, len);
953
954 len = word->style->font->descent;
955 if (word->style->valign == core::style::VALIGN_SUB)
956 len += word->style->font->ascent / 3;
957 lastLine->contentDescent = misc::max (lastLine->contentDescent, len);
958
959 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
960 // lastLine->boxAscent);
961 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
962 // lastLine->boxDescent);
963
964 if (word->content.type == core::Content::WIDGET) {
965 lastLine->marginDescent =
966 misc::max (lastLine->marginDescent,
967 word->size.descent +
968 word->content.widget->getStyle()->margin.bottom);
969
970 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
971 // lastLine->descent);
972
973 /* If the widget is not in the first line of the paragraph, its top
974 * margin may make the line higher.
975 */
976 if (lines->size () > 1) {
977 /* Here, we know already what the break and the bottom margin
978 * contributed to the space before this line.
979 */
980 lastLine->boxAscent =
981 misc::max (lastLine->boxAscent,
982 word->size.ascent
983 + word->content.widget->getStyle()->margin.top);
984
985 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
986 // lastLine->boxAscent);
987 }
988 } else {
989 lastLine->marginDescent =
990 misc::max (lastLine->marginDescent, lastLine->boxDescent);
991
992 if (word->content.type == core::Content::BREAK)
993 lastLine->breakSpace =
994 misc::max (word->content.breakSpace,
995 lastLine->marginDescent - lastLine->boxDescent,
996 lastLine->breakSpace);
997 }
998
999 lastSpace = (wordIndex > 0) ? words->getRef(wordIndex - 1)->origSpace : 0;
1000
1001 if (!newLine)
1002 lastLineWidth += lastSpace;
1003 if (!newPar) {
1004 lastLineParMin += lastSpace;
1005 lastLineParMax += lastSpace;
1006 }
1007
1008 lastLineWidth += word->size.width;
1009
1010 getWordExtremes (word, &wordExtremes);
1011 lastLineParMin += wordExtremes.maxWidth; /* Why maxWidth? */
1012 lastLineParMax += wordExtremes.maxWidth;
1013
1014 if (word->style->whiteSpace == core::style::WHITE_SPACE_NOWRAP ||
1015 word->style->whiteSpace == core::style::WHITE_SPACE_PRE) {
1016 lastLine->parMin += wordExtremes.minWidth + lastSpace;
1017 /* This may also increase the accumulated minimum word width. */
1018 lastLine->maxWordMin =
1019 misc::max (lastLine->maxWordMin, lastLine->parMin);
1020 /* NOTE: Most code relies on that all values of nowrap are equal for all
1021 * words within one line. */
1022 } else {
1023 lastLine->maxWordMin =
1024 misc::max (lastLine->maxWordMin, wordExtremes.minWidth);
1025 }
1026
1027 //DBG_OBJ_SET_NUM(page, "lastLine_par_min", page->lastLine_par_min);
1028 //DBG_OBJ_SET_NUM(page, "lastLine_par_max", page->lastLine_par_max);
1029 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_min", page->num_lines - 1,
1030 // lastLine->par_min);
1031 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_max", page->num_lines - 1,
1032 // lastLine->par_max);
1033 //DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_word_min", page->num_lines - 1,
1034 // lastLine->max_word_min);
1035
1036 /* Align the line.
1037 * \todo Use block's style instead once paragraphs become proper blocks.
1038 */
1039 if (word->content.type != core::Content::BREAK) {
1040 switch (word->style->textAlign) {
1041 case core::style::TEXT_ALIGN_LEFT:
1042 case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */
1043 case core::style::TEXT_ALIGN_STRING: /* handled elsewhere (in the
1044 * future) */
1045 leftOffset = 0;
1046 break;
1047 case core::style::TEXT_ALIGN_RIGHT:
1048 leftOffset = availWidth - lastLineWidth;
1049 break;
1050 case core::style::TEXT_ALIGN_CENTER:
1051 leftOffset = (availWidth - lastLineWidth) / 2;
1052 break;
1053 default:
1054 /* compiler happiness */
1055 leftOffset = 0;
1056 }
1057
1058 /* For large lines (images etc), which do not fit into the viewport: */
1059 if (leftOffset < 0)
1060 leftOffset = 0;
1061
1062 if (hasListitemValue && lastLine == lines->getRef (0)) {
1063 /* List item markers are always on the left. */
1064 lastLine->leftOffset = 0;
1065 words->getRef(0)->effSpace = words->getRef(0)->origSpace + leftOffset;
1066 //DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", 0,
1067 // page->words[0].eff_space);
1068 } else {
1069 lastLine->leftOffset = leftOffset;
1070 }
1071 }
1072 mustQueueResize = true;
1073
1074 //DBG_MSG_END (page);
1075 }
1076
1077
1078 /**
1079 * Calculate the size of a widget within the page.
1080 * (Subject of change in the near future!)
1081 */
1082 void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size)
1083 {
1084 core::Requisition requisition;
1085 int availWidth, availAscent, availDescent;
1086 core::style::Style *wstyle = widget->getStyle();
1087
1088 /* We ignore line1_offset[_eff]. */
1089 availWidth = this->availWidth - getStyle()->boxDiffWidth () - innerPadding;
1090 availAscent = this->availAscent - getStyle()->boxDiffHeight ();
1091 availDescent = this->availDescent;
1092
1093 if (widget->usesHints ()) {
1094 widget->setWidth (availWidth);
1095 widget->setAscent (availAscent);
1096 widget->setDescent (availDescent);
1097 widget->sizeRequest (size);
1098 // size->ascent -= wstyle->margin.top;
1099 // size->descent -= wstyle->margin.bottom;
1100 } else {
1101 /* TODO: Use margin.{top|bottom} here, like above.
1102 * (No harm for the next future.) */
1103 if (wstyle->width == core::style::LENGTH_AUTO ||
1104 wstyle->height == core::style::LENGTH_AUTO)
1105 widget->sizeRequest (&requisition);
1106
1107 if (wstyle->width == core::style::LENGTH_AUTO)
1108 size->width = requisition.width;
1109 else if (core::style::isAbsLength (wstyle->width))
1110 /* Fixed lengths are only applied to the content, so we have to
1111 * add padding, border and margin. */
1112 size->width = core::style::absLengthVal (wstyle->width)
1113 + wstyle->boxDiffWidth ();
1114 else
1115 size->width = (int) (core::style::perLengthVal (wstyle->width)
1116 * availWidth);
1117
1118 if (wstyle->height == core::style::LENGTH_AUTO) {
1119 size->ascent = requisition.ascent;
1120 size->descent = requisition.descent;
1121 } else if (core::style::isAbsLength (wstyle->height)) {
1122 /* Fixed lengths are only applied to the content, so we have to
1123 * add padding, border and margin. */
1124 size->ascent = core::style::absLengthVal (wstyle->height)
1125 + wstyle->boxDiffHeight ();
1126 size->descent = 0;
1127 } else {
1128 double len = core::style::perLengthVal (wstyle->height);
1129 size->ascent = (int) (len * availAscent);
1130 size->descent = (int) (len * availDescent);
1131 }
1132 }
1133 }
1134
1135 /**
1136 * Rewrap the page from the line from which this is necessary.
1137 * There are basically two times we'll want to do this:
1138 * either when the viewport is resized, or when the size changes on one
1139 * of the child widgets.
1140 */
1141 void Textblock::rewrap ()
1142 {
1143 int i, wordIndex;
1144 Word *word;
1145 Line *lastLine;
1146
1147 if (wrapRef == -1)
1148 /* page does not have to be rewrapped */
1149 return;
1150
1151 //DBG_MSGF (page, "wrap", 0,
1152 // "Dw_page_rewrap: page->wrap_ref = %d, in page with %d word(s)",
1153 // page->wrap_ref, page->num_words);
1154 //DBG_MSG_START (page);
1155
1156 /* All lines up from page->wrap_ref will be rebuild from the word list,
1157 * the line list up from this position is rebuild. */
1158 lines->setSize (wrapRef);
1159 lastLineWidth = 0;
1160 //DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
1161 //DBG_OBJ_SET_NUM(page, "lastLine_width", page->lastLine_width);
1162
1163 /* In the word list, start at the last word plus one in the line before. */
1164 if (wrapRef > 0) {
1165 /* Note: In this case, Dw_page_real_word_wrap will immediately find
1166 * the need to rewrap the line, since we start with the last one (plus
1167 * one). This is also the reason, why page->lastLine_width is set
1168 * to the length of the line. */
1169 lastLine = lines->getRef (lines->size () - 1);
1170
1171 lastLineParMin = lastLine->parMin;
1172 lastLineParMax = lastLine->parMax;
1173
1174 wordIndex = lastLine->lastWord + 1;
1175 for (i = lastLine->firstWord; i < lastLine->lastWord; i++)
1176 lastLineWidth += (words->getRef(i)->size.width +
1177 words->getRef(i)->origSpace);
1178 lastLineWidth += words->getRef(lastLine->lastWord)->size.width;
1179 } else {
1180 lastLineParMin = 0;
1181 lastLineParMax = 0;
1182
1183 wordIndex = 0;
1184 }
1185
1186 for (; wordIndex < words->size (); wordIndex++) {
1187 word = words->getRef (wordIndex);
1188
1189 if (word->content.type == core::Content::WIDGET)
1190 calcWidgetSize (word->content.widget, &word->size);
1191 wordWrap (wordIndex);
1192
1193 if (word->content.type == core::Content::WIDGET) {
1194 word->content.widget->parentRef = lines->size () - 1;
1195 //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
1196 // word->content.widget->parent_ref);
1197 }
1198
1199 //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
1200 // "Assigning parent_ref = %d to rewrapped word %d, "
1201 // "in page with %d word(s)\n",
1202 // page->num_lines - 1, wordIndex, page->num_words);
1203
1204 /* todo_refactoring:
1205 if (word->content.type == DW_CONTENT_ANCHOR)
1206 p_Dw_gtk_viewport_change_anchor
1207 (widget, word->content.anchor,
1208 Dw_page_line_total_y_offset (page,
1209 &page->lines[page->num_lines - 1]));
1210 */
1211 }
1212
1213 /* Next time, the page will not have to be rewrapped. */
1214 wrapRef = -1;
1215
1216 //DBG_MSG_END (page);
1217 }
1218
1219 /*
1220 * Draw the decorations on a word.
1221 */
1222 void Textblock::decorateText(core::View *view, core::style::Style *style,
1223 core::style::Color::Shading shading,
1224 int x, int yBase, int width)
1225 {
1226 int y, height;
1227
1228 height = 1 + style->font->xHeight / 12;
1229 if (style->textDecoration & core::style::TEXT_DECORATION_UNDERLINE) {
1230 y = yBase + style->font->descent / 3;
1231 view->drawRectangle (style->color, shading, true, x, y, width, height);
1232 }
1233 if (style->textDecoration & core::style::TEXT_DECORATION_OVERLINE) {
1234 y = yBase - style->font->ascent;
1235 view->drawRectangle (style->color, shading, true, x, y, width, height);
1236 }
1237 if (style->textDecoration & core::style::TEXT_DECORATION_LINE_THROUGH) {
1238 y = yBase + (style->font->descent - style->font->ascent) / 2 +
1239 style->font->descent / 4;
1240 view->drawRectangle (style->color, shading, true, x, y, width, height);
1241 }
1242 }
1243
1244 /*
1245 * Draw a word of text.
1246 */
1247 void Textblock::drawText(int wordIndex, core::View *view,core::Rectangle *area,
1248 int xWidget, int yWidgetBase)
1249 {
1250 Word *word = words->getRef(wordIndex);
1251 int xWorld = allocation.x + xWidget;
1252 core::style::Style *style = word->style;
1253 int yWorldBase;
1254
1255 /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */
1256 if (style->valign == core::style::VALIGN_SUB)
1257 yWidgetBase += style->font->ascent / 3;
1258 else if (style->valign == core::style::VALIGN_SUPER) {
1259 yWidgetBase -= style->font->ascent / 2;
1260 }
1261 yWorldBase = yWidgetBase + allocation.y;
1262
1263 view->drawText (style->font, style->color,
1264 core::style::Color::SHADING_NORMAL, xWorld, yWorldBase,
1265 word->content.text, strlen (word->content.text));
1266
1267 if (style->textDecoration)
1268 decorateText(view, style, core::style::Color::SHADING_NORMAL, xWorld,
1269 yWorldBase, word->size.width);
1270
1271 for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
1272 if (hlStart[layer].index <= wordIndex &&
1273 hlEnd[layer].index >= wordIndex) {
1274 const int wordLen = strlen (word->content.text);
1275 int xStart, width;
1276 int firstCharIdx = 0;
1277 int lastCharIdx = wordLen;
1278
1279 if (wordIndex == hlStart[layer].index)
1280 firstCharIdx = misc::min (hlStart[layer].nChar, wordLen);
1281
1282 if (wordIndex == hlEnd[layer].index)
1283 lastCharIdx = misc::min (hlEnd[layer].nChar, wordLen);
1284
1285 xStart = xWorld;
1286 if (firstCharIdx)
1287 xStart += layout->textWidth (style->font, word->content.text,
1288 firstCharIdx);
1289 if (firstCharIdx == 0 && lastCharIdx == wordLen)
1290 width = word->size.width;
1291 else
1292 width = layout->textWidth (style->font,
1293 word->content.text + firstCharIdx,
1294 lastCharIdx - firstCharIdx);
1295 if (width > 0) {
1296 /* Highlight text */
1297 core::style::Color *wordBgColor;
1298
1299 if (!(wordBgColor = style->backgroundColor))
1300 wordBgColor = getBgColor();
1301
1302 /* Draw background for highlighted text. */
1303 view->drawRectangle (
1304 wordBgColor, core::style::Color::SHADING_INVERSE, true, xStart,
1305 yWorldBase - style->font->ascent, width,
1306 style->font->ascent + style->font->descent);
1307
1308 /* Highlight the text. */
1309 view->drawText (style->font, style->color,
1310 core::style::Color::SHADING_INVERSE, xStart,
1311 yWorldBase, word->content.text + firstCharIdx,
1312 lastCharIdx - firstCharIdx);
1313
1314 if (style->textDecoration)
1315 decorateText(view, style, core::style::Color::SHADING_INVERSE,
1316 xStart, yWorldBase, width);
1317 }
1318 }
1319 }
1320 }
1321
1322 /*
1323 * Draw a space.
1324 */
1325 void Textblock::drawSpace(int wordIndex, core::View *view,
1326 core::Rectangle *area, int xWidget, int yWidgetBase)
1327 {
1328 Word *word = words->getRef(wordIndex);
1329 int xWorld = allocation.x + xWidget;
1330 int yWorldBase;
1331 core::style::Style *style = word->spaceStyle;
1332 bool highlight = false;
1333
1334 /* Adjust the space baseline if it is <SUP>-ed or <SUB>-ed */
1335 if (style->valign == core::style::VALIGN_SUB)
1336 yWidgetBase += style->font->ascent / 3;
1337 else if (style->valign == core::style::VALIGN_SUPER) {
1338 yWidgetBase -= style->font->ascent / 2;
1339 }
1340 yWorldBase = allocation.y + yWidgetBase;
1341
1342 for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
1343 if (hlStart[layer].index <= wordIndex &&
1344 hlEnd[layer].index > wordIndex) {
1345 highlight = true;
1346 break;
1347 }
1348 }
1349 if (highlight) {
1350 core::style::Color *spaceBgColor;
1351
1352 if (!(spaceBgColor = style->backgroundColor))
1353 spaceBgColor = getBgColor();
1354
1355 view->drawRectangle (
1356 spaceBgColor, core::style::Color::SHADING_INVERSE, true, xWorld,
1357 yWorldBase - style->font->ascent, word->effSpace,
1358 style->font->ascent + style->font->descent);
1359 }
1360 if (style->textDecoration) {
1361 core::style::Color::Shading shading = highlight ?
1362 core::style::Color::SHADING_INVERSE :
1363 core::style::Color::SHADING_NORMAL;
1364
1365 decorateText(view, style, shading, xWorld, yWorldBase, word->effSpace);
1366 }
1367 }
1368
1369 /*
1370 * Paint a line
1371 * - x and y are toplevel dw coordinates (Question: what Dw? Changed. Test!)
1372 * - area is used always (ev. set it to event->area)
1373 * - event is only used when is_expose
1374 */
1375 void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
1376 {
1377 int xWidget = lineXOffsetWidget(line);
1378 int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
1379
1380 /* Here's an idea on how to optimize this routine to minimize the number
1381 * of drawing calls:
1382 *
1383 * Copy the text from the words into a buffer, adding a new word
1384 * only if: the attributes match, and the spacing is either zero or
1385 * equal to the width of ' '. In the latter case, copy a " " into
1386 * the buffer. Then draw the buffer. */
1387
1388 for (int wordIndex = line->firstWord;
1389 wordIndex <= line->lastWord && xWidget < area->x + area->width;
1390 wordIndex++) {
1391 Word *word = words->getRef(wordIndex);
1392
1393 if (xWidget + word->size.width + word->effSpace >= area->x) {
1394 if (word->content.type == core::Content::TEXT ||
1395 word->content.type == core::Content::WIDGET) {
1396
1397 if (word->size.width > 0) {
1398 if (word->content.type == core::Content::WIDGET) {
1399 core::Widget *child = word->content.widget;
1400 core::Rectangle childArea;
1401
1402 if (child->intersects (area, &childArea))
1403 child->draw (view, &childArea);
1404 } else {
1405 if (word->style->hasBackground ()) {
1406 drawBox (view, word->style, area, xWidget,
1407 yWidgetBase - line->boxAscent, word->size.width,
1408 line->boxAscent + line->boxDescent, false);
1409 }
1410 drawText(wordIndex, view, area, xWidget, yWidgetBase);
1411 }
1412 }
1413 if (word->effSpace > 0 && wordIndex < line->lastWord &&
1414 words->getRef(wordIndex + 1)->content.type !=
1415 core::Content::BREAK) {
1416 if (word->spaceStyle->hasBackground ())
1417 drawBox (view, word->spaceStyle, area,
1418 xWidget + word->size.width,
1419 yWidgetBase - line->boxAscent, word->effSpace,
1420 line->boxAscent + line->boxDescent, false);
1421 drawSpace(wordIndex, view, area, xWidget + word->size.width,
1422 yWidgetBase);
1423 }
1424
1425 }
1426 }
1427 xWidget += word->size.width + word->effSpace;
1428 }
1429 }
1430
1431 /**
1432 * Find the first line index that includes y, relative to top of widget.
1433 */
1434 int Textblock::findLineIndex (int y)
1435 {
1436 int maxIndex = lines->size () - 1;
1437 int step, index, low = 0;
1438
1439 step = (lines->size() + 1) >> 1;
1440 while ( step > 1 ) {
1441 index = low + step;
1442 if (index <= maxIndex &&
1443 lineYOffsetWidgetI (index) <= y)
1444 low = index;
1445 step = (step + 1) >> 1;
1446 }
1447
1448 if (low < maxIndex && lineYOffsetWidgetI (low + 1) <= y)
1449 low++;
1450
1451 /*
1452 * This new routine returns the line number between (top) and
1453 * (top + size.ascent + size.descent + breakSpace): the space
1454 * _below_ the line is considered part of the line. Old routine
1455 * returned line number between (top - previous_line->breakSpace)
1456 * and (top + size.ascent + size.descent): the space _above_ the
1457 * line was considered part of the line. This is important for
1458 * Dw_page_find_link() --EG
1459 * That function has now been inlined into Dw_page_motion_notify() --JV
1460 */
1461 return low;
1462 }
1463
1464 /**
1465 * \brief Find the line of word \em wordIndex.
1466 */
1467 int Textblock::findLineOfWord (int wordIndex)
1468 {
1469 int high = lines->size () - 1, index, low = 0;
1470
1471 if (wordIndex < 0 || wordIndex >= words->size ())
1472 return -1;
1473
1474 while (true) {
1475 index = (low + high) / 2;
1476 if (wordIndex >= lines->getRef(index)->firstWord) {
1477 if (wordIndex <= lines->getRef(index)->lastWord)
1478 return index;
1479 else
1480 low = index + 1;
1481 } else
1482 high = index - 1;
1483 }
1484 }
1485
1486 /**
1487 * \brief Find the index of the word, or -1.
1488 */
1489 Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace)
1490 {
1491 int lineIndex, wordIndex;
1492 int xCursor, lastXCursor, yWidgetBase;
1493 Line *line;
1494 Word *word;
1495
1496 *inSpace = false;
1497
1498 if ((lineIndex = findLineIndex (y)) >= lines->size ())
1499 return NULL;
1500 line = lines->getRef (lineIndex);
1501 yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
1502 if (yWidgetBase + line->boxDescent <= y)
1503 return NULL;
1504
1505 xCursor = lineXOffsetWidget (line);
1506 for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
1507 word = words->getRef (wordIndex);
1508 lastXCursor = xCursor;
1509 xCursor += word->size.width + word->effSpace;
1510 if (lastXCursor <= x && xCursor > x &&
1511 y > yWidgetBase - word->size.ascent &&
1512 y <= yWidgetBase + word->size.descent) {
1513 *inSpace = x >= xCursor - word->effSpace;
1514 return word;
1515 }
1516 }
1517
1518 return NULL;
1519 }
1520
1521 void Textblock::draw (core::View *view, core::Rectangle *area)
1522 {
1523 int lineIndex;
1524 Line *line;
1525
1526 drawWidgetBox (view, area, false);
1527
1528 lineIndex = findLineIndex (area->y);
1529
1530 for (; lineIndex < lines->size (); lineIndex++) {
1531 line = lines->getRef (lineIndex);
1532 if (lineYOffsetWidget (line) >= area->y + area->height)
1533 break;
1534
1535 drawLine (line, view, area);
1536 }
1537 }
1538
1539 /**
1540 * Add a new word (text, widget etc.) to a page.
1541 */
1542 Textblock::Word *Textblock::addWord (int width, int ascent, int descent,
1543 core::style::Style *style)
1544 {
1545 Word *word;
1546
1547 words->increase ();
1548
1549 word = words->getRef (words->size() - 1);
1550 word->size.width = width;
1551 word->size.ascent = ascent;
1552 word->size.descent = descent;
1553 word->origSpace = 0;
1554 word->effSpace = 0;
1555 word->content.space = false;
1556
1557 //DBG_OBJ_ARRSET_NUM (page, "words.%d.size.width", page->num_words - 1,
1558 // word->size.width);
1559 //DBG_OBJ_ARRSET_NUM (page, "words.%d.size.descent", page->num_words - 1,
1560 // word->size.descent);
1561 //DBG_OBJ_ARRSET_NUM (page, "words.%d.size.ascent", page->num_words - 1,
1562 // word->size.ascent);
1563 //DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", page->num_words - 1,
1564 // word->orig_space);
1565 //DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", page->num_words - 1,
1566 // word->eff_space);
1567 //DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", page->num_words - 1,
1568 // word->content.space);
1569
1570 word->style = style;
1571 word->spaceStyle = style;
1572 style->ref ();
1573 style->ref ();
1574
1575 return word;
1576 }
1577
1578 /**
1579 * Calculate the size of a text word.
1580 */
1581 void Textblock::calcTextSize (const char *text, size_t len,
1582 core::style::Style *style,
1583 core::Requisition *size)
1584 {
1585 size->width = layout->textWidth (style->font, text, len);
1586 size->ascent = style->font->ascent;
1587 size->descent = style->font->descent;
1588
1589 /*
1590 * For 'normal' line height, just use ascent and descent from font.
1591 * For absolute/percentage, line height is relative to font size, which
1592 * is (irritatingly) smaller than ascent+descent.
1593 */
1594 if (style->lineHeight != core::style::LENGTH_AUTO) {
1595 int height, leading;
1596 float factor = style->font->size;
1597
1598 factor /= (style->font->ascent + style->font->descent);
1599
1600 size->ascent = lout::misc::roundInt(size->ascent * factor);
1601 size->descent = lout::misc::roundInt(size->descent * factor);
1602
1603 /* TODO: The containing block's line-height property gives a minimum
1604 * height for the line boxes. (Even when it's set to 'normal', i.e.,
1605 * AUTO? Apparently.) Once all block elements make Textblocks or
1606 * something, this can be handled.
1607 */
1608 if (core::style::isAbsLength (style->lineHeight))
1609 height = core::style::absLengthVal(style->lineHeight);
1610 else
1611 height = lout::misc::roundInt (
1612 core::style::perLengthVal(style->lineHeight) *
1613 style->font->size);
1614 leading = height - style->font->size;
1615
1616 size->ascent += leading / 2;
1617 size->descent += leading - (leading / 2);
1618 }
1619
1620 /* In case of a sub or super script we increase the word's height and
1621 * potentially the line's height.
1622 */
1623 if (style->valign == core::style::VALIGN_SUB)
1624 size->descent += (style->font->ascent / 3);
1625 else if (style->valign == core::style::VALIGN_SUPER)
1626 size->ascent += (style->font->ascent / 2);
1627 }
1628
1629
1630 /**
1631 * Add a word to the page structure.
1632 */
1633 void Textblock::addText (const char *text, size_t len,
1634 core::style::Style *style)
1635 {
1636 Word *word;
1637 core::Requisition size;
1638
1639 calcTextSize (text, len, style, &size);
1640 word = addWord (size.width, size.ascent, size.descent, style);
1641 word->content.type = core::Content::TEXT;
1642 word->content.text = layout->textZone->strndup(text, len);
1643
1644 //DBG_OBJ_ARRSET_STR (page, "words.%d.content.text", page->num_words - 1,
1645 // word->content.text);
1646
1647 wordWrap (words->size () - 1);
1648 }
1649
1650 /**
1651 * Add a widget (word type) to the page.
1652 */
1653 void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
1654 {
1655 Word *word;
1656 core::Requisition size;
1657
1658 /* We first assign -1 as parent_ref, since the call of widget->size_request
1659 * will otherwise let this Textblock be rewrapped from the beginning.
1660 * (parent_ref is actually undefined, but likely has the value 0.) At the,
1661 * end of this function, the correct value is assigned. */
1662 widget->parentRef = -1;
1663
1664 widget->setParent (this);
1665 widget->setStyle (style);
1666
1667 calcWidgetSize (widget, &size);
1668 word = addWord (size.width, size.ascent, size.descent, style);
1669
1670 word->content.type = core::Content::WIDGET;
1671 word->content.widget = widget;
1672
1673 //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", page->num_words - 1,
1674 // word->content.widget);
1675
1676 wordWrap (words->size () - 1);
1677 word->content.widget->parentRef = lines->size () - 1;
1678 //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
1679 // word->content.widget->parent_ref);
1680
1681 //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
1682 // "Assigning parent_ref = %d to added word %d, "
1683 // "in page with %d word(s)\n",
1684 // page->num_lines - 1, page->num_words - 1, page->num_words);
1685 }
1686
1687
1688 /**
1689 * Add an anchor to the page. "name" is copied, so no strdup is necessary for
1690 * the caller.
1691 *
1692 * Return true on success, and false, when this anchor had already been
1693 * added to the widget tree.
1694 */
1695 bool Textblock::addAnchor (const char *name, core::style::Style *style)
1696 {
1697 char *copy;
1698 int y;
1699
1700 // Since an anchor does not take any space, it is safe to call
1701 // addAnchor already here.
1702 if (wasAllocated ()) {
1703 if (lines->size () == 0)
1704 y = allocation.y;
1705 else
1706 y = allocation.y + lineYOffsetWidgetI (lines->size () - 1);
1707 copy = Widget::addAnchor (name, y);
1708 } else
1709 copy = Widget::addAnchor (name);
1710
1711 if (copy == NULL)
1712 /**
1713 * \todo It may be necessary for future uses to save the anchor in
1714 * some way, e.g. when parts of the widget tree change.
1715 */
1716 return false;
1717 else {
1718 Anchor *anchor;
1719
1720 anchors->increase();
1721 anchor = anchors->getRef(anchors->size() - 1);
1722 anchor->name = copy;
1723 anchor->wordIndex = words->size();
1724 return true;
1725 }
1726 }
1727
1728
1729 /**
1730 * ?
1731 */
1732 void Textblock::addSpace (core::style::Style *style)
1733 {
1734 int wordIndex = words->size () - 1;
1735
1736 if (wordIndex >= 0) {
1737 Word *word = words->getRef(wordIndex);
1738
1739 if (!word->content.space) {
1740 word->content.space = true;
1741 word->effSpace = word->origSpace = style->font->spaceWidth +
1742 style->wordSpacing;
1743
1744 //DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", nw,
1745 // page->words[nw].orig_space);
1746 //DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", nw,
1747 // page->words[nw].eff_space);
1748 //DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", nw,
1749 // page->words[nw].content.space);
1750 word->spaceStyle->unref ();
1751 word->spaceStyle = style;
1752 style->ref ();
1753 }
1754 }
1755 }
1756
1757
1758 /**
1759 * Cause a paragraph break
1760 */
1761 void Textblock::addParbreak (int space, core::style::Style *style)
1762 {
1763 Word *word;
1764
1765 /* A break may not be the first word of a page, or directly after
1766 the bullet/number (which is the first word) in a list item. (See
1767 also comment in Dw_page_size_request.) */
1768 if (words->size () == 0 ||
1769 (hasListitemValue && words->size () == 1)) {
1770 /* This is a bit hackish: If a break is added as the
1771 first/second word of a page, and the parent widget is also a
1772 Textblock, and there is a break before -- this is the case when
1773 a widget is used as a text box (lists, blockquotes, list
1774 items etc) -- then we simply adjust the break before, in a
1775 way that the space is in any case visible. */
1776 Widget *widget;
1777
1778 /* Find the widget where to adjust the breakSpace. */
1779 for (widget = this;
1780 widget->getParent() &&
1781 widget->getParent()->instanceOf (Textblock::CLASS_ID);
1782 widget = widget->getParent ()) {
1783 Textblock *textblock2 = (Textblock*)widget->getParent ();
1784 int index = textblock2->hasListitemValue ? 1 : 0;
1785 bool isfirst = (textblock2->words->getRef(index)->content.type
1786 == core::Content::WIDGET
1787 && textblock2->words->getRef(index)->content.widget
1788 == widget);
1789 if (!isfirst) {
1790 /* The page we searched for has been found. */
1791 Word *word2;
1792 int lineno = widget->parentRef;
1793
1794 if (lineno > 0 &&
1795 (word2 =
1796 textblock2->words->getRef(textblock2->lines
1797 ->getRef(lineno - 1)->firstWord)) &&
1798 word2->content.type == core::Content::BREAK) {
1799 if (word2->content.breakSpace < space) {
1800 word2->content.breakSpace = space;
1801 textblock2->queueResize (lineno, false);
1802 textblock2->mustQueueResize = false;
1803 }
1804 }
1805 return;
1806 }
1807 /* Otherwise continue to examine parents. */
1808 }
1809 /* Return in any case. */
1810 return;
1811 }
1812
1813 /* Another break before? */
1814 if ((word = words->getRef(words->size () - 1)) &&
1815 word->content.type == core::Content::BREAK) {
1816 Line *lastLine = lines->getRef (lines->size () - 1);
1817
1818 word->content.breakSpace =
1819 misc::max (word->content.breakSpace, space);
1820 lastLine->breakSpace =
1821 misc::max (word->content.breakSpace,
1822 lastLine->marginDescent - lastLine->boxDescent,
1823 lastLine->breakSpace);
1824 return;
1825 }
1826
1827 word = addWord (0, 0, 0, style);
1828 word->content.type = core::Content::BREAK;
1829 word->content.breakSpace = space;
1830 wordWrap (words->size () - 1);
1831 }
1832
1833 /*
1834 * Cause a line break.
1835 */
1836 void Textblock::addLinebreak (core::style::Style *style)
1837 {
1838 Word *word;
1839
1840 if (words->size () == 0 ||
1841 words->getRef(words->size () - 1)->content.type == core::Content::BREAK)
1842 // An <BR> in an empty line gets the height of the current font
1843 // (why would someone else place it here?), ...
1844 word = addWord (0, style->font->ascent, style->font->descent, style);
1845 else
1846 // ... otherwise, it has no size (and does not enlarge the line).
1847 word = addWord (0, 0, 0, style);
1848
1849 word->content.type = core::Content::BREAK;
1850 word->content.breakSpace = 0;
1851 wordWrap (words->size () - 1);
1852 }
1853
1854
1855 /**
1856 * \brief Search recursively through widget.
1857 *
1858 * This is an optimized version of the general
1859 * dw::core::Widget::getWidgetAtPoint method.
1860 */
1861 core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
1862 {
1863 int lineIndex, wordIndex;
1864 Line *line;
1865
1866 if (x < allocation.x ||
1867 y < allocation.y ||
1868 x > allocation.x + allocation.width ||
1869 y > allocation.y + getHeight ()) {
1870 return NULL;
1871 }
1872
1873 lineIndex = findLineIndex (y - allocation.y);
1874
1875 if (lineIndex < 0 || lineIndex >= lines->size ()) {
1876 return this;
1877 }
1878
1879 line = lines->getRef (lineIndex);
1880
1881 for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
1882 Word *word = words->getRef (wordIndex);
1883
1884 if (word->content.type == core::Content::WIDGET) {
1885 core::Widget * childAtPoint;
1886 childAtPoint = word->content.widget->getWidgetAtPoint (x, y,
1887 level + 1);
1888 if (childAtPoint) {
1889 return childAtPoint;
1890 }
1891 }
1892 }
1893
1894 return this;
1895 }
1896
1897
1898 /**
1899 * This function "hands" the last break of a page "over" to a parent
1900 * page. This is used for "collapsing spaces".
1901 */
1902 void Textblock::handOverBreak (core::style::Style *style)
1903 {
1904 if (lines->size() > 0) {
1905 Widget *parent;
1906 Line *lastLine = lines->getRef (lines->size () - 1);
1907
1908 if (lastLine->breakSpace != 0 && (parent = getParent()) &&
1909 parent->instanceOf (Textblock::CLASS_ID)) {
1910 Textblock *textblock2 = (Textblock*) parent;
1911 textblock2->addParbreak(lastLine->breakSpace, style);
1912 }
1913 }
1914 }
1915
1916 /*
1917 * Any words added by a_Dw_page_add_... are not immediately (queued to
1918 * be) drawn, instead, this function must be called. This saves some
1919 * calls to p_Dw_widget_queue_resize.
1920 *
1921 */
1922 void Textblock::flush ()
1923 {
1924 if (mustQueueResize) {
1925 queueResize (-1, true);
1926 mustQueueResize = false;
1927 }
1928 }
1929
1930
1931 // next: Dw_page_find_word
1932
1933 void Textblock::changeLinkColor (int link, int newColor)
1934 {
1935 for (int lineIndex = 0; lineIndex < lines->size(); lineIndex++) {
1936 bool changed = false;
1937 Line *line = lines->getRef (lineIndex);
1938 int wordIdx;
1939
1940 for (wordIdx = line->firstWord; wordIdx <= line->lastWord; wordIdx++){
1941 Word *word = words->getRef(wordIdx);
1942
1943 if (word->style->x_link == link) {
1944 core::style::StyleAttrs styleAttrs;
1945
1946 switch (word->content.type) {
1947 case core::Content::TEXT:
1948 { core::style::Style *old_style = word->style;
1949 styleAttrs = *old_style;
1950 styleAttrs.color = core::style::Color::create (layout,
1951 newColor);
1952 word->style = core::style::Style::create (layout, &styleAttrs);
1953 old_style->unref();
1954 old_style = word->spaceStyle;
1955 styleAttrs = *old_style;
1956 styleAttrs.color = core::style::Color::create (layout,
1957 newColor);
1958 word->spaceStyle =
1959 core::style::Style::create(layout, &styleAttrs);
1960 old_style->unref();
1961 break;
1962 }
1963 case core::Content::WIDGET:
1964 { core::Widget *widget = word->content.widget;
1965 styleAttrs = *widget->getStyle();
1966 styleAttrs.color = core::style::Color::create (layout,
1967 newColor);
1968 styleAttrs.setBorderColor(
1969 core::style::Color::create (layout, newColor));
1970 widget->setStyle(
1971 core::style::Style::create (layout, &styleAttrs));
1972 break;
1973 }
1974 default:
1975 break;
1976 }
1977 changed = true;
1978 }
1979 }
1980 if (changed)
1981 queueDrawArea (0, lineYOffsetWidget(line), allocation.width,
1982 line->boxAscent + line->boxDescent);
1983 }
1984 }
1985
1986 void Textblock::changeWordStyle (int from, int to, core::style::Style *style,
1987 bool includeFirstSpace, bool includeLastSpace)
1988 {
1989 }
1990
1991 // ----------------------------------------------------------------------
1992
1993 Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
1994 core::Content::Type mask,
1995 bool atEnd):
1996 core::Iterator (textblock, mask, atEnd)
1997 {
1998 index = atEnd ? textblock->words->size () : -1;
1999 content.type = atEnd ? core::Content::END : core::Content::START;
2000 }
2001
2002 Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
2003 core::Content::Type mask,
2004 int index):
2005 core::Iterator (textblock, mask, false)
2006 {
2007 this->index = index;
2008
2009 if (index < 0)
2010 content.type = core::Content::START;
2011 else if (index >= textblock->words->size ())
2012 content.type = core::Content::END;
2013 else
2014 content = textblock->words->getRef(index)->content;
2015 }
2016
2017 object::Object *Textblock::TextblockIterator::clone()
2018 {
2019 return new TextblockIterator ((Textblock*)getWidget(), getMask(), index);
2020 }
2021
2022 int Textblock::TextblockIterator::compareTo(misc::Comparable *other)
2023 {
2024 return index - ((TextblockIterator*)other)->index;
2025 }
2026
2027 bool Textblock::TextblockIterator::next ()
2028 {
2029 Textblock *textblock = (Textblock*)getWidget();
2030
2031 if (content.type == core::Content::END)
2032 return false;
2033
2034 do {
2035 index++;
2036 if (index >= textblock->words->size ()) {
2037 content.type = core::Content::END;
2038 return false;
2039 }
2040 } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
2041
2042 content = textblock->words->getRef(index)->content;
2043 return true;
2044 }
2045
2046 bool Textblock::TextblockIterator::prev ()
2047 {
2048 Textblock *textblock = (Textblock*)getWidget();
2049
2050 if (content.type == core::Content::START)
2051 return false;
2052
2053 do {
2054 index--;
2055 if (index < 0) {
2056 content.type = core::Content::START;
2057 return false;
2058 }
2059 } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
2060
2061 content = textblock->words->getRef(index)->content;
2062 return true;
2063 }
2064
2065 void Textblock::TextblockIterator::highlight (int start, int end,
2066 core::HighlightLayer layer)
2067 {
2068 Textblock *textblock = (Textblock*)getWidget();
2069 int index1 = index, index2 = index;
2070
2071 if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
2072 /* nothing is highlighted */
2073 textblock->hlStart[layer].index = index;
2074 textblock->hlEnd[layer].index = index;
2075 }
2076
2077 if (textblock->hlStart[layer].index >= index) {
2078 index2 = textblock->hlStart[layer].index;
2079 textblock->hlStart[layer].index = index;
2080 textblock->hlStart[layer].nChar = start;
2081 }
2082
2083 if (textblock->hlEnd[layer].index <= index) {
2084 index2 = textblock->hlEnd[layer].index;
2085 textblock->hlEnd[layer].index = index;
2086 textblock->hlEnd[layer].nChar = end;
2087 }
2088
2089 textblock->queueDrawRange (index1, index2);
2090 }
2091
2092 void Textblock::TextblockIterator::unhighlight (int direction,
2093 core::HighlightLayer layer)
2094 {
2095 Textblock *textblock = (Textblock*)getWidget();
2096 int index1 = index, index2 = index;
2097
2098 if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
2099 return;
2100
2101 if (direction == 0) {
2102 index1 = textblock->hlStart[layer].index;
2103 index2 = textblock->hlEnd[layer].index;
2104 textblock->hlStart[layer].index = 1;
2105 textblock->hlEnd[layer].index = 0;
2106 } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
2107 index1 = textblock->hlStart[layer].index;
2108 textblock->hlStart[layer].index = index + 1;
2109 textblock->hlStart[layer].nChar = 0;
2110 } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
2111 index1 = textblock->hlEnd[layer].index;
2112 textblock->hlEnd[layer].index = index - 1;
2113 textblock->hlEnd[layer].nChar = INT_MAX;
2114 }
2115
2116 textblock->queueDrawRange (index1, index2);
2117 }
2118
2119 void Textblock::queueDrawRange (int index1, int index2)
2120 {
2121 int from = misc::min (index1, index2);
2122 int to = misc::max (index1, index2);
2123
2124 from = misc::min (from, words->size () - 1);
2125 from = misc::max (from, 0);
2126 to = misc::min (to, words->size () - 1);
2127 to = misc::max (to, 0);
2128
2129 int line1idx = findLineOfWord (from);
2130 int line2idx = findLineOfWord (to);
2131
2132 if (line1idx >= 0 && line2idx >= 0) {
2133 Line *line1 = lines->getRef (line1idx),
2134 *line2 = lines->getRef (line2idx);
2135 int y = lineYOffsetWidget (line1) + line1->boxAscent -
2136 line1->contentAscent;
2137 int h = lineYOffsetWidget (line2) + line2->boxAscent +
2138 line2->contentDescent - y;
2139
2140 queueDrawArea (0, y, allocation.width, h);
2141 }
2142 }
2143
2144 void Textblock::TextblockIterator::getAllocation (int start, int end,
2145 core::Allocation *allocation)
2146 {
2147 Textblock *textblock = (Textblock*)getWidget();
2148 int lineIndex = textblock->findLineOfWord (index);
2149 Line *line = textblock->lines->getRef (lineIndex);
2150 Word *word = textblock->words->getRef (index);
2151
2152 allocation->x =
2153 textblock->allocation.x + textblock->lineXOffsetWidget (line);
2154
2155 for (int i = line->firstWord; i < index; i++) {
2156 Word *w = textblock->words->getRef(i);
2157 allocation->x += w->size.width + w->effSpace;
2158 }
2159 if (start > 0 && word->content.type == core::Content::TEXT) {
2160 allocation->x += textblock->layout->textWidth (word->style->font,
2161 word->content.text,
2162 start);
2163 }
2164 allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
2165 word->size.ascent;
2166
2167 allocation->width = word->size.width;
2168 if (word->content.type == core::Content::TEXT) {
2169 int wordEnd = strlen(word->content.text);
2170
2171 if (start > 0 || end < wordEnd) {
2172 end = misc::min(end, wordEnd); /* end could be INT_MAX */
2173 allocation->width =
2174 textblock->layout->textWidth (word->style->font,
2175 word->content.text + start,
2176 end - start);
2177 }
2178 }
2179 allocation->ascent = word->size.ascent;
2180 allocation->descent = word->size.descent;
2181 }
2182
2183 } // namespace dw
0 #ifndef __DW_TEXTBLOCK_HH__
1 #define __DW_TEXTBLOCK_HH__
2
3 #include "core.hh"
4 #include "../lout/misc.hh"
5
6 namespace dw {
7
8 /**
9 * \brief A Widget for rendering text blocks, i.e. paragraphs or sequences
10 * of paragraphs.
11 *
12 * <h3>Signals</h3>
13 *
14 * dw::Textblock uses the signals defined in
15 * dw::core::Layout::LinkReceiver, related to links. The coordinates are
16 * always -1.
17 *
18 *
19 * <h3>Collapsing Spaces</h3>
20 *
21 * The idea behind this is that every paragraph has a specific vertical
22 * space around and that they are combined to one space, according to
23 * rules stated below. A paragraph consists either of the lines between
24 * two paragraph breaks within a dw::Textblock, or of a dw::Textblock
25 * within a dw::Textblock, in a single line; the latter is used for
26 * indented boxes and list items.
27 *
28 * The rules:
29 *
30 * <ol>
31 * <li> If a paragraph is following by another, the space between them is the
32 * maximum of both box spaces:
33 *
34 * \image html dw-textblock-collapsing-spaces-1-1.png
35 *
36 * are combined like this:
37 *
38 * \image html dw-textblock-collapsing-spaces-1-2.png
39 *
40 * <li> a) If one paragraph is the first paragraph within another, the upper
41 * space of these paragraphs collapse. b) The analogue is the case for the
42 * last box:
43 *
44 * \image html dw-textblock-collapsing-spaces-2-1.png
45 *
46 * If B and C are put into A, the result is:
47 *
48 * \image html dw-textblock-collapsing-spaces-2-2.png
49 * </ol>
50 *
51 * For achieving this, there are some features of dw::Textblock:
52 *
53 * <ul>
54 * <li> Consequent breaks are automatically combined, according to
55 * rule 1. See the code of dw::Textblock::addParBreak for details.
56 *
57 * <li> If a break is added as the first word of the dw::Textblock within
58 * another dw::Textblock, collapsing according to rule 2a is done
59 * automatically. See the code of dw::Textblock::addParBreak.
60 *
61 * <li> To collapse spaces according to rule 2b,
62 * dw::Textblock::addParBreak::handOverBreak must be called for
63 * the \em inner widget. The HTML parser does this in
64 * Html_eventually_pop_dw.
65 * </ul>
66 *
67 *
68 * <h3>Collapsing Margins</h3>
69 *
70 * Collapsing margins, as defined in the CSS2 specification, are,
71 * supported in addition to collapsing spaces. Also, spaces and margins
72 * collapse themselves. I.e., the space between two paragraphs is the
73 * maximum of the space calculated as described in "Collapsing Spaces"
74 * and the space calculated according to the rules for collapsing margins.
75 *
76 * (This is an intermediate hybrid state, collapsing spaces are used in
77 * the current version of dillo, while I implemented collapsing margins
78 * for the CSS prototype and integrated it already into the main trunk. For
79 * a pure CSS-based dillo, collapsing spaces will not be needed anymore, and
80 * may be removed for simplicity.)
81 *
82 *
83 * <h3>Some Internals</h3>
84 *
85 * There are 3 lists, dw::Textblock::words, dw::Textblock::lines, and
86 * dw::Textblock::anchors. The word list is quite static; only new words
87 * may be added. A word is either text, a widget, or a break.
88 *
89 * Lines refer to the word list (first and last). They are completely
90 * redundant, i.e., they can be rebuilt from the words. Lines can be
91 * rewrapped either completely or partially (see "Incremental Resizing"
92 * below). For the latter purpose, several values are accumulated in the
93 * lines. See dw::Textblock::Line for details.
94 *
95 * Anchors associate the anchor name with the index of the next word at
96 * the point of the anchor.
97 *
98 * <h4>Incremental Resizing</h4>
99 *
100 * dw::Textblock makes use of incremental resizing as described in \ref
101 * dw-widget-sizes. The parentRef is, for children of a dw::Textblock, simply
102 * the number of the line.
103 *
104 * Generally, there are three cases which may change the size of the
105 * widget:
106 *
107 * <ul>
108 * <li> The available size of the widget has changed, e.g., because the
109 * user has changed the size of the browser window. In this case,
110 * it is necessary to rewrap all the lines.
111 *
112 * <li> A child widget has changed its size. In this case, only a rewrap
113 * down from the line where this widget is located is necessary.
114 *
115 * (This case is very important for tables. Tables are quite at the
116 * bottom, so that a partial rewrap is relevant. Otherwise, tables
117 * change their size quite often, so that this is necessary for a
118 * fast, non-blocking rendering)
119 *
120 * <li> A word (or widget, break etc.) is added to the text block. This
121 * makes it possible to reuse the old size by simply adjusting the
122 * current width and height, so no rewrapping is necessary.
123 * </ul>
124 *
125 * The state of the size calculation is stored in wrapRef within
126 * dw::Textblock, which has the value -1 if no rewrapping of lines
127 * necessary, or otherwise the line from which a rewrap is necessary.
128 *
129 */
130 class Textblock: public core::Widget
131 {
132 protected:
133 struct Line
134 {
135 int firstWord; /* first word's index in word vector */
136 int lastWord; /* last word's index in word vector */
137
138 /* "top" is always relative to the top of the first line, i.e.
139 * page->lines[0].top is always 0. */
140 int top, boxAscent, boxDescent, contentAscent, contentDescent,
141 breakSpace, leftOffset;
142
143 /* This is similar to descent, but includes the bottom margins of the
144 * widgets within this line. */
145 int marginDescent;
146
147 /* The following members contain accumulated values, from the top
148 * down to the line before. */
149 int maxLineWidth; /* maximum of all line widths */
150 int maxWordMin; /* maximum of all word minima */
151 int maxParMax; /* maximum of all paragraph maxima */
152 int parMin; /* the minimal total width down from the last
153 * paragraph start, to the *beginning* of the
154 * line */
155 int parMax; /* the maximal total width down from the last
156 * paragraph start, to the *beginning* of the
157 * line */
158 };
159
160 struct Word
161 {
162 /* TODO: perhaps add a xLeft? */
163 core::Requisition size;
164 /* Space after the word, only if it's not a break: */
165 short origSpace; /* from font, set by addSpace */
166 short effSpace; /* effective space, set by wordWrap,
167 * used for drawing etc. */
168 core::Content content;
169
170 core::style::Style *style;
171 core::style::Style *spaceStyle; /* initially the same as of the word,
172 later set by a_Dw_page_add_space */
173 };
174
175 struct Anchor
176 {
177 char *name;
178 int wordIndex;
179 };
180
181 class TextblockIterator: public core::Iterator
182 {
183 private:
184 int index;
185
186 public:
187 TextblockIterator (Textblock *textblock, core::Content::Type mask,
188 bool atEnd);
189 TextblockIterator (Textblock *textblock, core::Content::Type mask,
190 int index);
191
192 lout::object::Object *clone();
193 int compareTo(lout::misc::Comparable *other);
194
195 bool next ();
196 bool prev ();
197 void highlight (int start, int end, core::HighlightLayer layer);
198 void unhighlight (int direction, core::HighlightLayer layer);
199 void getAllocation (int start, int end, core::Allocation *allocation);
200 };
201
202 friend class TextblockIterator;
203
204 /* These fields provide some ad-hoc-functionality, used by sub-classes. */
205 bool hasListitemValue; /* If true, the first word of the page is treated
206 specially (search in source). */
207 int innerPadding; /* This is an additional padding on the left side
208 (used by ListItem). */
209 int line1Offset; /* This is an additional offset of the first line.
210 May be negative (shift to left) or positive
211 (shift to right). */
212 int line1OffsetEff; /* The "effective" value of line1_offset, may
213 differ from line1_offset when
214 ignoreLine1OffsetSometimes is set to true. */
215
216 /* The following is really hackish: It is used for DwTableCell (see
217 * comment in dw_table_cell.c), to avoid too wide table columns. If
218 * set to true, it has following effects:
219 *
220 * (i) line1_offset is ignored in calculating the minimal width
221 * (which is used by DwTable!), and
222 * (ii) line1_offset is ignored (line1_offset_eff is set to 0),
223 * when line1_offset plus the width of the first word is
224 * greater than the the available witdh.
225 *
226 * \todo Eliminate all these ad-hoc features by a new, simpler and
227 * more elegant design. ;-)
228 */
229 bool ignoreLine1OffsetSometimes;
230
231 bool mustQueueResize;
232
233 bool limitTextWidth; /* from preferences */
234
235 int redrawY;
236 int lastWordDrawn;
237
238 /* These values are set by set_... */
239 int availWidth, availAscent, availDescent;
240
241 int lastLineWidth;
242 int lastLineParMin;
243 int lastLineParMax;
244 int wrapRef; /* [0 based] */
245
246 lout::misc::SimpleVector <Line> *lines;
247 lout::misc::SimpleVector <Word> *words;
248 lout::misc::SimpleVector <Anchor> *anchors;
249
250 struct {int index, nChar;}
251 hlStart[core::HIGHLIGHT_NUM_LAYERS], hlEnd[core::HIGHLIGHT_NUM_LAYERS];
252
253 int hoverLink; /* The link under the button. */
254 core::style::Tooltip *hoverTooltip; /* The tooltip under the button. No ref
255 * hold. */
256
257
258 void queueDrawRange (int index1, int index2);
259 void getWordExtremes (Word *word, core::Extremes *extremes);
260 void markChange (int ref);
261 void justifyLine (Line *line, int availWidth);
262 Line *addLine (int wordInd, bool newPar);
263 void calcWidgetSize (core::Widget *widget, core::Requisition *size);
264 void rewrap ();
265 void decorateText(core::View *view, core::style::Style *style,
266 core::style::Color::Shading shading,
267 int x, int yBase, int width);
268 void drawText(int wordIndex, core::View *view, core::Rectangle *area,
269 int xWidget, int yWidgetBase);
270 void drawSpace(int wordIndex, core::View *view, core::Rectangle *area,
271 int xWidget, int yWidgetBase);
272 void drawLine (Line *line, core::View *view, core::Rectangle *area);
273 int findLineIndex (int y);
274 int findLineOfWord (int wordIndex);
275 Word *findWord (int x, int y, bool *inSpace);
276
277 Word *addWord (int width, int ascent, int descent,
278 core::style::Style *style);
279 void calcTextSize (const char *text, size_t len, core::style::Style *style,
280 core::Requisition *size);
281
282
283 /**
284 * \brief Returns the x offset (the indentation plus any offset needed for
285 * centering or right justification) for the line.
286 *
287 * The offset returned is relative to the page *content* (i.e. without
288 * border etc.).
289 */
290 inline int lineXOffsetContents (Line *line)
291 {
292 return innerPadding + line->leftOffset +
293 (line == lines->getRef (0) ? line1OffsetEff : 0);
294 }
295
296 /**
297 * \brief Like lineXOffset, but relative to the allocation (i.e.
298 * including border etc.).
299 */
300 inline int lineXOffsetWidget (Line *line)
301 {
302 return lineXOffsetContents (line) + getStyle()->boxOffsetX ();
303 }
304
305 inline int lineYOffsetWidgetAllocation (Line *line,
306 core::Allocation *allocation)
307 {
308 return line->top + (allocation->ascent - lines->getRef(0)->boxAscent);
309 }
310
311 inline int lineYOffsetWidget (Line *line)
312 {
313 return lineYOffsetWidgetAllocation (line, &allocation);
314 }
315
316 /**
317 * Like lineYOffsetCanvas, but with the allocation as parameter.
318 */
319 inline int lineYOffsetCanvasAllocation (Line *line,
320 core::Allocation *allocation)
321 {
322 return allocation->y + lineYOffsetWidgetAllocation(line, allocation);
323 }
324
325 /**
326 * Returns the y offset (within the canvas) of a line.
327 */
328 inline int lineYOffsetCanvas (Line *line)
329 {
330 return lineYOffsetCanvasAllocation(line, &allocation);
331 }
332
333 inline int lineYOffsetWidgetI (int lineIndex)
334 {
335 return lineYOffsetWidget (lines->getRef (lineIndex));
336 }
337
338 inline int lineYOffsetCanvasI (int lineIndex)
339 {
340 return lineYOffsetCanvas (lines->getRef (lineIndex));
341 }
342
343 bool sendSelectionEvent (core::SelectionState::EventType eventType,
344 core::MousePositionEvent *event);
345
346 virtual void wordWrap(int wordIndex);
347
348 void sizeRequestImpl (core::Requisition *requisition);
349 void getExtremesImpl (core::Extremes *extremes);
350 void sizeAllocateImpl (core::Allocation *allocation);
351 void resizeDrawImpl ();
352
353 void markSizeChange (int ref);
354 void markExtremesChange (int ref);
355 void setWidth (int width);
356 void setAscent (int ascent);
357 void setDescent (int descent);
358 void draw (core::View *view, core::Rectangle *area);
359
360 bool buttonPressImpl (core::EventButton *event);
361 bool buttonReleaseImpl (core::EventButton *event);
362 bool motionNotifyImpl (core::EventMotion *event);
363 void enterNotifyImpl (core::EventCrossing *event);
364 void leaveNotifyImpl (core::EventCrossing *event);
365
366 void removeChild (Widget *child);
367
368 public:
369 static int CLASS_ID;
370
371 Textblock(bool limitTextWidth);
372 ~Textblock();
373
374 core::Iterator *iterator (core::Content::Type mask, bool atEnd);
375
376 void flush ();
377
378 void addText (const char *text, size_t len, core::style::Style *style);
379 inline void addText (const char *text, core::style::Style *style)
380 {
381 addText (text, strlen(text), style);
382 }
383 void addWidget (core::Widget *widget, core::style::Style *style);
384 bool addAnchor (const char *name, core::style::Style *style);
385 void addSpace(core::style::Style *style);
386 void addParbreak (int space, core::style::Style *style);
387 void addLinebreak (core::style::Style *style);
388
389 core::Widget *getWidgetAtPoint (int x, int y, int level);
390 void handOverBreak (core::style::Style *style);
391 void changeLinkColor (int link, int newColor);
392 void changeWordStyle (int from, int to, core::style::Style *style,
393 bool includeFirstSpace, bool includeLastSpace);
394 };
395
396 } // namespace dw
397
398 #endif // __DW_TEXTBLOCK_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "core.hh"
22 #include "../lout/msg.h"
23
24 using namespace lout;
25
26 namespace dw {
27 namespace core {
28
29 Rectangle::Rectangle (int x, int y, int width, int height)
30 {
31 this->x = x;
32 this->y = y;
33 this->width = width;
34 this->height = height;
35 }
36
37 /*
38 * Draw rectangle in view relative to point (x,y).
39 */
40 void Rectangle::draw (core::View *view, core::style::Style *style, int x,int y)
41 {
42 const bool filled = false;
43
44 view->drawRectangle(style->color, core::style::Color::SHADING_NORMAL,filled,
45 x + this->x, y + this->y, this->width, this->height);
46 }
47
48 /**
49 * Return whether this rectangle and otherRect intersect. If yes,
50 * return the intersection rectangle in dest.
51 */
52 bool Rectangle::intersectsWith (Rectangle *otherRect, Rectangle *dest)
53 {
54 bool doIntersect =
55 this->x < otherRect->x + otherRect->width &&
56 this->y < otherRect->y + otherRect->height &&
57 otherRect->x < this->x + this->width &&
58 otherRect->y < this->y + this->height;
59
60 if (doIntersect) {
61 dest->x = misc::max(this->x, otherRect->x);
62 dest->y = misc::max(this->y, otherRect->y);
63 dest->width = misc::min(this->x + this->width,
64 otherRect->x + otherRect->width) - dest->x;
65 dest->height = misc::min(this->y + this->height,
66 otherRect->y + otherRect->height) - dest->y;
67 } else {
68 dest->x = dest->y = dest->width = dest->height = 0;
69 }
70
71 return doIntersect;
72 }
73
74 /*
75 * Return whether this is a subset of otherRect.
76 */
77 bool Rectangle::isSubsetOf (Rectangle *otherRect)
78 {
79 return
80 x >= otherRect->x &&
81 y >= otherRect->y &&
82 x + width <= otherRect->x + otherRect->width &&
83 y + height <= otherRect->y + otherRect->height;
84 }
85
86 bool Rectangle::isPointWithin (int x, int y)
87 {
88 return
89 x >= this->x && y >= this->y &&
90 x < this->x + width && y < this->y + height;
91 }
92
93 // ----------------------------------------------------------------------
94
95 Circle::Circle (int x, int y, int radius)
96 {
97 this->x = x;
98 this->y = y;
99 this->radius = radius;
100 }
101
102 /*
103 * Draw circle in view relative to point (x,y).
104 */
105 void Circle::draw (core::View *view, core::style::Style *style, int x, int y)
106 {
107 const bool filled = false;
108
109 view->drawArc(style->color, core::style::Color::SHADING_NORMAL, filled,
110 x + this->x, y + this->y, 2 * this->radius, 2 * this->radius,
111 0, 360);
112 }
113
114 bool Circle::isPointWithin (int x, int y)
115 {
116 return
117 (x - this->x) * (x - this->x) + (y - this->y) * (y - this->y)
118 <= radius * radius;
119 }
120
121 // ----------------------------------------------------------------------
122
123 Polygon::Polygon ()
124 {
125 points = new misc::SimpleVector<Point> (8);
126 minx = miny = 0xffffff;
127 maxx = maxy = -0xffffff;
128 }
129
130 Polygon::~Polygon ()
131 {
132 delete points;
133 }
134
135 /*
136 * Draw polygon in view relative to point (x,y).
137 */
138 void Polygon::draw (core::View *view, core::style::Style *style, int x, int y)
139 {
140 if (points->size()) {
141 int i;
142 const bool filled = false, convex = false;
143 int (*pointArray)[2] =
144 (int (*)[2]) malloc(points->size() * sizeof(*pointArray));
145
146 for (i = 0; i < points->size(); i++) {
147 pointArray[i][0] = x + points->getRef(i)->x;
148 pointArray[i][1] = y + points->getRef(i)->y;
149 }
150 view->drawPolygon(style->color, core::style::Color::SHADING_NORMAL,
151 filled, convex, pointArray, i);
152 free(pointArray);
153 }
154 }
155
156 void Polygon::addPoint (int x, int y)
157 {
158 points->increase ();
159 points->getRef(points->size () - 1)->x = x;
160 points->getRef(points->size () - 1)->y = y;
161
162 minx = misc::min(minx, x);
163 miny = misc::min(miny, y);
164 maxx = misc::max(maxx, x);
165 maxy = misc::max(maxy, y);
166 }
167
168 /**
169 * \brief Return, whether the line, limited by (ax1, ay1) and (ax2, ay2),
170 * crosses the unlimited line, determined by two points (bx1, by1) and
171 * (bx2, by2).
172 */
173 bool Polygon::linesCross0(int ax1, int ay1, int ax2, int ay2,
174 int bx1, int by1, int bx2, int by2)
175 {
176 /** TODO Some more description */
177 // If the scalar product is 0, it means that one point is on the second
178 // line, so we check for <= 0, not < 0.
179 int z1 = zOfVectorProduct (ax1 - bx1, ay1 - by1, bx2 - bx1, by2 - by1);
180 int z2 = zOfVectorProduct (ax2 - bx1, ay2 - by1, bx2 - bx1, by2 - by1);
181
182 return (z1 <= 0 && z2 >= 0) || (z1 >= 0 && z2 <= 0);
183 }
184
185 /**
186 * \brief Return, whether the line, limited by (ax1, ay1) and (ax2, ay2),
187 * crosses the line, limited by (bx1, by1) and (bx2, by2).
188 */
189 bool Polygon::linesCross(int ax1, int ay1, int ax2, int ay2,
190 int bx1, int by1, int bx2, int by2)
191 {
192 bool cross =
193 linesCross0 (ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) &&
194 linesCross0 (bx1, by1, bx2, by2, ax1, ay1, ax2, ay2);
195 _MSG("(%d, %d) - (%d, %d) and (%d, %d) - (%d, %d) cross? %s.\n",
196 ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, cross ? "Yes" : "No");
197 return cross;
198 }
199
200 bool Polygon::isPointWithin (int x, int y)
201 {
202 if (points->size () < 3 ||
203 (x < minx || x > maxx || y < miny || y >= maxy))
204 return false;
205 else {
206 int numCrosses = 0;
207 for (int i = 0; i < points->size () - 1; i++) {
208 if (linesCross (minx - 1, miny - 1, x, y,
209 points->getRef(i)->x, points->getRef(i)->y,
210 points->getRef(i + 1)->x, points->getRef(i + 1)->y))
211 numCrosses++;
212 }
213 if (linesCross (minx - 1, miny - 1, x, y,
214 points->getRef(points->size () - 1)->x,
215 points->getRef(points->size () - 1)->y,
216 points->getRef(0)->x, points->getRef(0)->y))
217 numCrosses++;
218
219 return numCrosses % 2 == 1;
220 }
221 }
222
223 Region::Region()
224 {
225 rectangleList = new container::typed::List <Rectangle> (true);
226 }
227
228 Region::~Region()
229 {
230 delete rectangleList;
231 }
232
233 /**
234 * \brief Add a rectangle to the region and combine it with
235 * existing rectangles if possible.
236 * The number of rectangles is forced to be less than 16
237 * by combining excessive rectangles.
238 */
239 void Region::addRectangle (Rectangle *rPointer)
240 {
241 container::typed::Iterator <Rectangle> it;
242 Rectangle *r = new Rectangle (rPointer->x, rPointer->y,
243 rPointer->width, rPointer->height);
244
245 for (it = rectangleList->iterator (); it.hasNext (); ) {
246 Rectangle *ownRect = it.getNext ();
247
248 int combinedHeight =
249 misc::max(r->y + r->height, ownRect->y + ownRect->height) -
250 misc::min(r->y, ownRect->y);
251 int combinedWidth =
252 misc::max(r->x + r->width, ownRect->x + ownRect->width) -
253 misc::min(r->x, ownRect->x);
254
255 if (rectangleList->size() >= 16 ||
256 combinedWidth * combinedHeight <=
257 ownRect->width * ownRect->height + r->width * r->height) {
258
259 r->x = misc::min(r->x, ownRect->x);
260 r->y = misc::min(r->y, ownRect->y);
261 r->width = combinedWidth;
262 r->height = combinedHeight;
263
264 rectangleList->removeRef (ownRect);
265 }
266 }
267
268 rectangleList->append (r);
269 }
270
271 } // namespace dw
272 } // namespace core
0 #ifndef __DW_TYPES_HH__
1 #define __DW_TYPES_HH__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 namespace dw {
8 namespace core {
9
10 namespace style {
11 class Style;
12 }
13
14 enum HPosition
15 {
16 HPOS_LEFT,
17 HPOS_CENTER,
18 HPOS_RIGHT,
19 HPOS_INTO_VIEW, /* scroll only, until the content in question comes
20 * into view */
21 HPOS_NO_CHANGE
22 };
23
24 enum VPosition
25 {
26 VPOS_TOP,
27 VPOS_CENTER,
28 VPOS_BOTTOM,
29 VPOS_INTO_VIEW, /* scroll only, until the content in question comes
30 * into view */
31 VPOS_NO_CHANGE
32 };
33
34 enum ScrollCommand {SCREEN_UP_CMD, SCREEN_DOWN_CMD, LINE_UP_CMD, LINE_DOWN_CMD,
35 LEFT_CMD, RIGHT_CMD, TOP_CMD, BOTTOM_CMD};
36
37 /*
38 * Different "layers" may be highlighted in a widget.
39 */
40 enum HighlightLayer
41 {
42 HIGHLIGHT_SELECTION,
43 HIGHLIGHT_FINDTEXT,
44 HIGHLIGHT_NUM_LAYERS
45 };
46
47 struct Point
48 {
49 int x;
50 int y;
51 };
52
53 /**
54 * \brief Abstract interface for different shapes.
55 */
56 class Shape: public lout::object::Object
57 {
58 public:
59 virtual bool isPointWithin (int x, int y) = 0;
60 virtual void draw (core::View *view, core::style::Style *style, int x,
61 int y) = 0;
62 };
63
64 /**
65 * \brief dw::core::Shape implemtation for simple rectangles.
66 */
67 class Rectangle: public Shape
68 {
69 public:
70 int x;
71 int y;
72 int width;
73 int height;
74
75 inline Rectangle () { }
76 Rectangle (int x, int y, int width, int height);
77
78 void draw (core::View *view, core::style::Style *style, int x, int y);
79 bool intersectsWith (Rectangle *otherRect, Rectangle *dest);
80 bool isSubsetOf (Rectangle *otherRect);
81 bool isPointWithin (int x, int y);
82 bool isEmpty () { return width <= 0 || height <= 0; };
83 };
84
85 /**
86 * \brief dw::core::Shape implemtation for simple circles.
87 */
88 class Circle: public Shape
89 {
90 public:
91 int x, y, radius;
92
93 Circle (int x, int y, int radius);
94
95 void draw (core::View *view, core::style::Style *style, int x, int y);
96 bool isPointWithin (int x, int y);
97 };
98
99 /**
100 * \brief dw::core::Shape implemtation for polygons.
101 */
102 class Polygon: public Shape
103 {
104 private:
105 lout::misc::SimpleVector<Point> *points;
106 int minx, miny, maxx, maxy;
107
108 /**
109 * \brief Return the z-coordinate of the vector product of two
110 * vectors, whose z-coordinate is 0 (so that x and y of
111 * the vector product is 0, too).
112 */
113 inline int zOfVectorProduct(int x1, int y1, int x2, int y2) {
114 return x1 * y2 - x2 * y1;
115 }
116
117 bool linesCross0(int ax1, int ay1, int ax2, int ay2,
118 int bx1, int by1, int bx2, int by2);
119 bool linesCross(int ax1, int ay1, int ax2, int ay2,
120 int bx1, int by1, int bx2, int by2);
121
122 public:
123 Polygon ();
124 ~Polygon ();
125
126 void draw (core::View *view, core::style::Style *style, int x, int y);
127 void addPoint (int x, int y);
128 bool isPointWithin (int x, int y);
129 };
130
131 /**
132 * Implementation for a point set.
133 * Currently represented as a set of rectangles not containing
134 * each other.
135 * It is guaranteed that the rectangles returned by rectangles ()
136 * cover all rectangles that were added with addRectangle ().
137 */
138 class Region
139 {
140 private:
141 lout::container::typed::List <Rectangle> *rectangleList;
142
143 public:
144 Region ();
145 ~Region ();
146
147 void clear () { rectangleList->clear (); };
148
149 void addRectangle (Rectangle *r);
150
151 lout::container::typed::Iterator <Rectangle> rectangles ()
152 {
153 return rectangleList->iterator ();
154 };
155 };
156
157 /**
158 * \brief Represents the allocation, i.e. actual position and size of a
159 * dw::core::Widget.
160 */
161 struct Allocation
162 {
163 int x;
164 int y;
165 int width;
166 int ascent;
167 int descent;
168 };
169
170 struct Requisition
171 {
172 int width;
173 int ascent;
174 int descent;
175 };
176
177 struct Extremes
178 {
179 int minWidth;
180 int maxWidth;
181 };
182
183 struct Content
184 {
185 enum Type {
186 START = 1 << 0,
187 END = 1 << 1,
188 TEXT = 1 << 2,
189 WIDGET = 1 << 3,
190 BREAK = 1 << 4,
191 ALL = 0xff,
192 REAL_CONTENT = 0xff ^ (START | END),
193 SELECTION_CONTENT = TEXT | WIDGET | BREAK
194 };
195 /* Content is embedded in struct Word therefore we
196 * try to be space efficient.
197 */
198 short type;
199 bool space;
200 union {
201 const char *text;
202 Widget *widget;
203 int breakSpace;
204 };
205 };
206
207 } // namespace dw
208 } // namespace core
209
210 #endif // __DW_TYPES_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "core.hh"
22
23 #include <stdio.h>
24
25 namespace dw {
26 namespace core {
27 namespace ui {
28
29 using namespace lout;
30 using namespace lout::object;
31
32 int Embed::CLASS_ID = -1;
33
34 Embed::Embed(Resource *resource)
35 {
36 registerName ("dw::core::ui::Embed", &CLASS_ID);
37 this->resource = resource;
38 resource->setEmbed (this);
39 }
40
41 Embed::~Embed()
42 {
43 delete resource;
44 }
45
46 void Embed::sizeRequestImpl (Requisition *requisition)
47 {
48 resource->sizeRequest (requisition);
49 }
50
51 void Embed::getExtremesImpl (Extremes *extremes)
52 {
53 resource->getExtremes (extremes);
54 }
55
56 void Embed::sizeAllocateImpl (Allocation *allocation)
57 {
58 resource->sizeAllocate (allocation);
59 }
60
61 void Embed::enterNotifyImpl (core::EventCrossing *event)
62 {
63 resource->emitEnter();
64 Widget::enterNotifyImpl(event);
65 }
66
67 void Embed::leaveNotifyImpl (core::EventCrossing *event)
68 {
69 resource->emitLeave();
70 Widget::leaveNotifyImpl(event);
71 }
72
73 bool Embed::buttonPressImpl (core::EventButton *event)
74 {
75 bool handled;
76
77 if (event->button == 3) {
78 resource->emitClicked(event);
79 handled = true;
80 } else {
81 handled = false;
82 }
83 return handled;
84 }
85
86 void Embed::setWidth (int width)
87 {
88 resource->setWidth (width);
89 }
90
91 void Embed::setAscent (int ascent)
92 {
93 resource->setAscent (ascent);
94 }
95
96 void Embed::setDescent (int descent)
97 {
98 resource->setDescent (descent);
99 }
100
101 void Embed::setDisplayed (bool displayed)
102 {
103 resource->setDisplayed (displayed);
104 }
105
106 void Embed::setEnabled (bool enabled)
107 {
108 resource->setEnabled (enabled);
109 }
110
111 void Embed::draw (View *view, Rectangle *area)
112 {
113 drawWidgetBox (view, area, false);
114 resource->draw (view, area);
115 }
116
117 Iterator *Embed::iterator (Content::Type mask, bool atEnd)
118 {
119 return resource->iterator (mask, atEnd);
120 }
121
122 void Embed::setStyle (style::Style *style)
123 {
124 resource->setStyle (style);
125 Widget::setStyle (style);
126 }
127
128 // ----------------------------------------------------------------------
129
130 bool Resource::ActivateEmitter::emitToReceiver (lout::signal::Receiver
131 *receiver,
132 int signalNo,
133 int argc, Object **argv)
134 {
135 ActivateReceiver *ar = (ActivateReceiver*)receiver;
136 Resource *res = (Resource*)((Pointer*)argv[0])->getValue ();
137
138 switch (signalNo) {
139 case 0:
140 ar->activate (res);
141 break;
142 case 1:
143 ar->enter (res);
144 break;
145 case 2:
146 ar->leave (res);
147 break;
148 default:
149 misc::assertNotReached ();
150 }
151 return false;
152 }
153
154 void Resource::ActivateEmitter::emitActivate (Resource *resource)
155 {
156 Pointer p (resource);
157 Object *argv[1] = { &p };
158 emitVoid (0, 1, argv);
159 }
160
161 void Resource::ActivateEmitter::emitEnter (Resource *resource)
162 {
163 Pointer p (resource);
164 Object *argv[1] = { &p };
165 emitVoid (1, 1, argv);
166 }
167
168 void Resource::ActivateEmitter::emitLeave (Resource *resource)
169 {
170 Pointer p (resource);
171 Object *argv[1] = { &p };
172 emitVoid (2, 1, argv);
173 }
174
175 // ----------------------------------------------------------------------
176
177 Resource::~Resource ()
178 {
179 }
180
181 void Resource::setEmbed (Embed *embed)
182 {
183 this->embed = embed;
184 }
185
186 void Resource::getExtremes (Extremes *extremes)
187 {
188 /* Simply return the requisition width */
189 Requisition requisition;
190 sizeRequest (&requisition);
191 extremes->minWidth = extremes->maxWidth = requisition.width;
192 }
193
194 void Resource::sizeAllocate (Allocation *allocation)
195 {
196 }
197
198 void Resource::setWidth (int width)
199 {
200 }
201
202 void Resource::setAscent (int ascent)
203 {
204 }
205
206 void Resource::setDescent (int descent)
207 {
208 }
209
210 void Resource::setDisplayed (bool displayed)
211 {
212 }
213
214 void Resource::draw (View *view, Rectangle *area)
215 {
216 }
217
218 void Resource::setStyle (style::Style *style)
219 {
220 }
221
222 void Resource::emitEnter ()
223 {
224 activateEmitter.emitEnter(this);
225 }
226
227 void Resource::emitLeave ()
228 {
229 activateEmitter.emitLeave(this);
230 }
231
232 bool Resource::ClickedEmitter::emitToReceiver(lout::signal::Receiver *receiver,
233 int signalNo, int argc,
234 Object **argv)
235 {
236 ((ClickedReceiver*)receiver)
237 ->clicked ((Resource*)((Pointer*)argv[0])->getValue (),
238 (EventButton*)((Pointer*)argv[1])->getValue());
239 return false;
240 }
241
242 void Resource::ClickedEmitter::emitClicked (Resource *resource,
243 EventButton *event)
244 {
245 Pointer p1 (resource);
246 Pointer p2 (event);
247 Object *argv[2] = { &p1, &p2 };
248 emitVoid (0, 2, argv);
249 }
250
251 // ----------------------------------------------------------------------
252
253 Iterator *LabelButtonResource::iterator (Content::Type mask, bool atEnd)
254 {
255 /** \todo Perhaps in brackets? */
256 // return new TextIterator (getEmbed (), mask, atEnd, getLabel ());
257 /** \bug Not implemented. */
258 return new EmptyIterator (getEmbed (), mask, atEnd);
259 }
260
261 // ----------------------------------------------------------------------
262
263 void ComplexButtonResource::LayoutReceiver::canvasSizeChanged (int width,
264 int ascent,
265 int descent)
266 {
267 /**
268 * \todo The argument to queueResize is not always true. (But this works.)
269 */
270 resource->queueResize (true);
271 }
272
273 ComplexButtonResource::ComplexButtonResource ()
274 {
275 layout = NULL;
276 layoutReceiver.resource = this;
277 click_x = click_y = -1;
278 }
279
280 void ComplexButtonResource::init (Widget *widget)
281 {
282 this->childWidget = widget;
283
284 layout = new Layout (createPlatform ());
285 setLayout (layout);
286 layout->setWidget (widget);
287 layout->connect (&layoutReceiver);
288 }
289
290 void ComplexButtonResource::setEmbed (Embed *embed)
291 {
292 ButtonResource::setEmbed (embed);
293
294 if (childWidget->usesHints ())
295 embed->setUsesHints ();
296 }
297
298 ComplexButtonResource::~ComplexButtonResource ()
299 {
300 delete layout;
301 }
302
303 void ComplexButtonResource::sizeRequest (Requisition *requisition)
304 {
305 Requisition widgetRequisition;
306 childWidget->sizeRequest (&widgetRequisition);
307 requisition->width = widgetRequisition.width + 2 * reliefXThickness ();
308 requisition->ascent = widgetRequisition.ascent + reliefYThickness ();
309 requisition->descent = widgetRequisition.descent + reliefYThickness ();
310 }
311
312 void ComplexButtonResource::getExtremes (Extremes *extremes)
313 {
314 Extremes widgetExtremes;
315 childWidget->getExtremes (&widgetExtremes);
316 extremes->minWidth = widgetExtremes.minWidth + 2 * reliefXThickness ();
317 extremes->maxWidth = widgetExtremes.maxWidth + 2 * reliefXThickness ();
318 }
319
320 void ComplexButtonResource::sizeAllocate (Allocation *allocation)
321 {
322 }
323
324 void ComplexButtonResource::setWidth (int width)
325 {
326 childWidget->setWidth (width - 2 * reliefXThickness ());
327 }
328
329 void ComplexButtonResource::setAscent (int ascent)
330 {
331 childWidget->setAscent (ascent - reliefYThickness ());
332 }
333
334 void ComplexButtonResource::setDescent (int descent)
335 {
336 childWidget->setDescent (descent - reliefYThickness ());
337 }
338
339 Iterator *ComplexButtonResource::iterator (Content::Type mask, bool atEnd)
340 {
341 /**
342 * \bug Implementation.
343 * This is a bit more complicated: We have two layouts here.
344 */
345 return new EmptyIterator (getEmbed (), mask, atEnd);
346 }
347
348 // ----------------------------------------------------------------------
349
350 Iterator *TextResource::iterator (Content::Type mask, bool atEnd)
351 {
352 // return new TextIterator (getEmbed (), mask, atEnd, getText ());
353 /** \bug Not implemented. */
354 return new EmptyIterator (getEmbed (), mask, atEnd);
355 }
356
357 // ----------------------------------------------------------------------
358
359 Iterator *CheckButtonResource::iterator (Content::Type mask, bool atEnd)
360 {
361 //return new TextIterator (getEmbed (), mask, atEnd,
362 // isActivated () ? "[X]" : "[ ]");
363 /** \bug Not implemented. */
364 return new EmptyIterator (getEmbed (), mask, atEnd);
365 }
366
367 // ----------------------------------------------------------------------
368
369 RadioButtonResource::GroupIterator::~GroupIterator ()
370 {
371 }
372
373 Iterator *RadioButtonResource::iterator (Content::Type mask, bool atEnd)
374 {
375 //return new TextIterator (getEmbed (), mask, atEnd,
376 // isActivated () ? "(*)" : "( )");
377 /** \bug Not implemented. */
378 return new EmptyIterator (getEmbed (), mask, atEnd);
379 }
380
381 } // namespace ui
382 } // namespace core
383 } // namespace core
384
0 #ifndef __DW_UI_HH__
1 #define __DW_UI_HH__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 namespace dw {
8 namespace core {
9
10 /**
11 * \brief Anything related to embedded UI widgets is defined here.
12 *
13 * UI resources are another abstraction for Dw widgets, which are not
14 * fully implemented in a platform-independent way. Typically, they
15 * involve creating widgets, which the underlying UI toolkit provides.
16 *
17 * As you see in this diagram:
18 *
19 * \dot
20 * digraph G {
21 * node [shape=record, fontname=Helvetica, fontsize=10];
22 * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
23 * labelfontsize=10, color="#404040", labelfontcolor="#000080"];
24 * fontname=Helvetica; fontsize=10;
25 *
26 * subgraph cluster_core {
27 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
28 * label="dw::core";
29 *
30 * subgraph cluster_ui {
31 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
32 * label="dw::core::ui";
33 *
34 * Embed [URL="\ref dw::core::ui::Embed"];
35 * Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"];
36 * LabelButtonResource [color="#a0a0a0",
37 * URL="\ref dw::core::ui::LabelButtonResource"];
38 * EntryResource [color="#a0a0a0",
39 * URL="\ref dw::core::ui::EntryResource"];
40 * etc [color="#a0a0a0", label="..."];
41 * }
42 *
43 * Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
44 * }
45 *
46 * subgraph cluster_fltk {
47 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
48 * label="dw::fltk::ui";
49 *
50 * FltkLabelButtonResource
51 * [URL="\ref dw::fltk::ui::FltkLabelButtonResource"];
52 * FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"];
53 * }
54 *
55 * Widget -> Embed;
56 * Embed -> Resource [arrowhead="open", arrowtail="none",
57 * headlabel="1", taillabel="1"];
58 * Resource -> LabelButtonResource;
59 * Resource -> EntryResource;
60 * Resource -> etc;
61 * LabelButtonResource -> FltkLabelButtonResource;
62 * EntryResource -> FltkEntryResource;
63 * }
64 * \enddot
65 *
66 * <center>[\ref uml-legend "legend"]</center>
67 *
68 * there are several levels:
69 *
70 * <ol>
71 * <li> The Dw widget is dw::core::ui::Embed. It delegates most to
72 * dw::core::ui::Resource, which has similar methods like
73 * dw::core::Widget.
74 *
75 * <li> There are several sub interfaces of dw::core::ui::Resource, which
76 * may provide methods, as e.g. dw::core::ui::ListResource::addItem. In a
77 * platform independent context, you can cast the result of
78 * dw::core::ui::Embed::getResource to a specific sub class, if you
79 * know, which one is used. E.g., if you know, that a given instance
80 * dw::core::ui::Embed refers to a dw::core::ui::ListResource, you can
81 * write something like:
82 *
83 * \code
84 * dw::core::ui::Embed *embed;
85 * //...
86 * ((dw::core::ui::ListResource*)embed->getResource ())->addItem ("Hello!");
87 * \endcode
88 *
89 * <li> These sub classes are then fully implemented in a platform specific
90 * way. For an example, look at dw::fltk::ui.
91 * </ol>
92 *
93 * There is a factory interface, dw::core::ui::ResourceFactory, which
94 * provides methods for creating common resources. By calling
95 * dw::core::Layout::getResourceFactory, which calls
96 * dw::core::Platform::getResourceFactory, you get the factory for the used
97 * platform.
98 *
99 * It is possible to define additional sub classes of
100 * dw::core::ui::Resource, but since they are not provided by
101 * dw::core::ui::ResourceFactory, you have to define some other
102 * abstractions, if you want to remain platform independent.
103 *
104 *
105 * <h3>...</h3>
106 *
107 *
108 * <h3>Resouces needed for HTML</h3>
109 *
110 * This chapter describes, how the form controls defined by HTML are
111 * implemented in Dw. Some of them do not refer to UI resources, but to
112 * other widgets, links to the respective documentations are provided
113 * here.
114 *
115 * <h4>Resouces created with \<INPUT\></h4>
116 *
117 * The HTML \<INPUT\> is always implemented by using UI
118 * resources. \<INPUT\> element has the following attributes:
119 *
120 * <table>
121 * <tr><th>Attribute <th>Implementation
122 * <tr><td>type <td>This defines the resource you have to instantiate.
123 * <tr><td>name <td>Not needed within Dw.
124 * <tr><td>value <td>The initial value is treated differently by different
125 * resources.
126 * <tr><td>checked <td>Parameter to
127 * dw::core::ui::ResourceFactory::createCheckButtonResource
128 * and dw::core::ui::ResourceFactory::createRadioButtonResource.
129 * <tr><td>disabled <td>This is provided for all resources by
130 * dw::core::ui::Resource::setEnabled.
131 * <tr><td>readonly <td>This is provided by
132 * dw::core::ui::TextResource::setEditable.
133 * <tr><td>size <td>This is handled by styles.
134 * <tr><td>maxlength <td>Parameter of
135 * dw::core::ui::ResourceFactory::createEntryResource.
136 * <tr><td>src <td>Handled by the caller (HTML parser).
137 * <tr><td>alt <td>Handled by the caller (HTML parser).
138 * <tr><td>usemap <td>Handled by the caller (HTML parser).
139 * <tr><td>ismap <td>Handled by the caller (HTML parser).
140 * <tr><td>tabindex <td>Not supported currently.
141 * <tr><td>accesskey <td>Not supported currently.
142 * <tr><td>onfocus <td>Not supported currently.
143 * <tr><td>onblur <td>Not supported currently.
144 * <tr><td>onselect <td>Not supported currently.
145 * <tr><td>onchange <td>Not supported currently.
146 * <tr><td>accept <td>Not supported currently.
147 * </table>
148 *
149 * For the different values of \em type, the following resources can be
150 * used:
151 *
152 * <table>
153 * <tr><th>Type <th>Resource
154 * <th>Factory Method
155 * <tr><td>text <td>dw::core::ui::EntryResource
156 * <td>dw::core::ui::ResourceFactory::createEntryResource
157 * <tr><td>password <td>dw::core::ui::EntryResource
158 * <td>dw::core::ui::ResourceFactory::createEntryResource
159 * <tr><td>checkbox <td>dw::core::ui::CheckButtonResource
160 * <td>dw::core::ui::ResourceFactory::createCheckButtonResource
161 * <tr><td>radio <td>dw::core::ui::RadioButtonResource
162 * <td>dw::core::ui::ResourceFactory::createRadioButtonResource
163 * <tr><td>submit <td>dw::core::ui::LabelButtonResource
164 * <td>dw::core::ui::ResourceFactory::createLabelButtonResource
165 * <tr><td>image <td>dw::core::ui::ComplexButtonResource
166 * <td>dw::core::ui::ResourceFactory::createComplexButtonResource,
167 * width a dw::Image inside and relief = false.
168 * <tr><td>reset <td>dw::core::ui::LabelButtonResource
169 * <td>dw::core::ui::ResourceFactory::createLabelButtonResource
170 * <tr><td>button <td>dw::core::ui::LabelButtonResource
171 * <td>dw::core::ui::ResourceFactory::createLabelButtonResource
172 * <tr><td>hidden <td>No rendering necessary.
173 * <td>-
174 * <tr><td>file <td>Not supported currently.
175 * <td>-
176 * </table>
177 *
178 * <h4>\<SELECT\>, \<OPTGROUP\>, and \<OPTION\></h4>
179 *
180 * \<SELECT\> is implemented either by dw::core::ui::OptionMenuResource
181 * (better suitable for \em size = 1 and single selection) or
182 * dw::core::ui::ListResource, which have a common base,
183 * dw::core::ui::SelectionResource. In the latter case, \em size must be
184 * specified via dw::core::style::Style.
185 *
186 * Factory methods are dw::core::ui::ResourceFactory::createListResource and
187 * dw::core::ui::ResourceFactory::createOptionMenuResource.
188 *
189 * \<OPTION\>'s are added via dw::core::ui::SelectionResource::addItem.
190 *
191 * \<OPTGROUP\> are created by using dw::core::ui::SelectionResource::pushGroup
192 * and dw::core::ui::SelectionResource::popGroup.
193 *
194 * For lists, the selection mode must be set in
195 * dw::core::ui::ResourceFactory::createListResource.
196 *
197 * <h4>\<TEXTAREA\></h4>
198 *
199 * \<TEXTAREA\> is implemented by dw::core::ui::MultiLineTextResource,
200 * the factory method is
201 * dw::core::ui::ResourceFactory::createMultiLineTextResource.
202 * dw::core::ui::TextResource::setEditable can be used, as for entries.
203 *
204 * <h4>\<BUTTON\></h4>
205 *
206 * For handling \<BUTTON\>, dw::core::ui::ComplexButtonResource should be used,
207 * with a dw::Textblock inside, and relief = true. The contents of \<BUTTON\>
208 * is then added to the dw::Textblock.
209 *
210 * \todo describe activation signal
211 */
212 namespace ui {
213
214 class Resource;
215
216 /**
217 * \brief A widget for embedding UI widgets.
218 *
219 * \sa dw::core::ui
220 */
221 class Embed: public Widget
222 {
223 friend class Resource;
224
225 private:
226 Resource *resource;
227
228 protected:
229 void sizeRequestImpl (Requisition *requisition);
230 void getExtremesImpl (Extremes *extremes);
231 void sizeAllocateImpl (Allocation *allocation);
232 void enterNotifyImpl (core::EventCrossing *event);
233 void leaveNotifyImpl (core::EventCrossing *event);
234 bool buttonPressImpl (core::EventButton *event);
235
236 public:
237 static int CLASS_ID;
238
239 Embed(Resource *resource);
240 ~Embed();
241
242 void setWidth (int width);
243 void setAscent (int ascent);
244 void setDescent (int descent);
245 void setDisplayed (bool displayed);
246 void setEnabled (bool enabled);
247 void draw (View *view, Rectangle *area);
248 Iterator *iterator (Content::Type mask, bool atEnd);
249 void setStyle (style::Style *style);
250
251 inline void setUsesHints () { setFlags (USES_HINTS); }
252
253 inline Resource *getResource () { return resource; }
254 };
255
256 /**
257 * \brief Basic interface for all resources.
258 *
259 * \sa dw::core::ui
260 */
261 class Resource
262 {
263 friend class Embed;
264
265 public:
266 /**
267 * \brief Receiver interface for the "activate" signal.
268 */
269 class ActivateReceiver: public lout::signal::Receiver
270 {
271 public:
272 virtual void activate (Resource *resource) = 0;
273 virtual void enter (Resource *resource) = 0;
274 virtual void leave (Resource *resource) = 0;
275 };
276 /**
277 * \brief Receiver interface for the "clicked" signal.
278 */
279 class ClickedReceiver: public lout::signal::Receiver
280 {
281 public:
282 virtual void clicked (Resource *resource, EventButton *event) = 0;
283 };
284
285 private:
286 class ActivateEmitter: public lout::signal::Emitter
287 {
288 protected:
289 bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
290 int argc, Object **argv);
291 public:
292 inline void connectActivate (ActivateReceiver *receiver) {
293 connect (receiver); }
294 void emitActivate (Resource *resource);
295 void emitEnter (Resource *resource);
296 void emitLeave (Resource *resource);
297 };
298
299 class ClickedEmitter: public lout::signal::Emitter
300 {
301 protected:
302 bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
303 int argc, Object **argv);
304 public:
305 inline void connectClicked (ClickedReceiver *receiver) {
306 connect (receiver); }
307 void emitClicked (Resource *resource, EventButton *event);
308 };
309
310 Embed *embed;
311 ActivateEmitter activateEmitter;
312 ClickedEmitter clickedEmitter;
313
314 void emitEnter ();
315 void emitLeave ();
316 protected:
317 inline void queueResize (bool extremesChanged) {
318 if (embed) embed->queueResize (0, extremesChanged);
319 }
320
321 virtual Embed *getEmbed () { return embed; }
322 virtual void setEmbed (Embed *embed);
323
324 inline void emitActivate () {
325 return activateEmitter.emitActivate (this); }
326 inline void emitClicked (EventButton *event) {
327 clickedEmitter.emitClicked (this, event); }
328
329 public:
330 inline Resource () { embed = NULL; }
331
332 virtual ~Resource ();
333
334 virtual void sizeRequest (Requisition *requisition) = 0;
335 virtual void getExtremes (Extremes *extremes);
336 virtual void sizeAllocate (Allocation *allocation);
337 virtual void setWidth (int width);
338 virtual void setAscent (int ascent);
339 virtual void setDescent (int descent);
340 virtual void setDisplayed (bool displayed);
341 virtual void draw (View *view, Rectangle *area);
342 virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0;
343 virtual void setStyle (style::Style *style);
344
345 virtual bool isEnabled () = 0;
346 virtual void setEnabled (bool enabled) = 0;
347
348 inline void connectActivate (ActivateReceiver *receiver) {
349 activateEmitter.connectActivate (receiver); }
350 inline void connectClicked (ClickedReceiver *receiver) {
351 clickedEmitter.connectClicked (receiver); }
352 };
353
354
355 class ButtonResource: public Resource
356 {};
357
358 /**
359 * \brief Interface for labelled buttons resources.
360 */
361 class LabelButtonResource: public ButtonResource
362 {
363 public:
364 Iterator *iterator (Content::Type mask, bool atEnd);
365
366 virtual const char *getLabel () = 0;
367 virtual void setLabel (const char *label) = 0;
368 };
369
370 class ComplexButtonResource: public ButtonResource
371 {
372 private:
373 class LayoutReceiver: public Layout::Receiver
374 {
375 public:
376 ComplexButtonResource *resource;
377
378 void canvasSizeChanged (int width, int ascent, int descent);
379 };
380
381 friend class LayoutReceiver;
382 LayoutReceiver layoutReceiver;
383
384 Widget *childWidget;
385
386 protected:
387 Layout *layout;
388 int click_x, click_y;
389
390 void setEmbed (Embed *embed);
391
392 virtual Platform *createPlatform () = 0;
393 virtual void setLayout (Layout *layout) = 0;
394
395 virtual int reliefXThickness () = 0;
396 virtual int reliefYThickness () = 0;
397
398 void init (Widget *widget);
399
400 public:
401 ComplexButtonResource ();
402 ~ComplexButtonResource ();
403
404 void sizeRequest (Requisition *requisition);
405 void getExtremes (Extremes *extremes);
406 void sizeAllocate (Allocation *allocation);
407 void setWidth (int width);
408 void setAscent (int ascent);
409 void setDescent (int descent);
410 Iterator *iterator (Content::Type mask, bool atEnd);
411 int getClickX () {return click_x;};
412 int getClickY () {return click_y;};
413 };
414
415 /**
416 * \brief Base interface for dw::core::ui::ListResource and
417 * dw::core::ui::OptionMenuResource.
418 */
419 class SelectionResource: public Resource
420 {
421 public:
422 virtual void addItem (const char *str, bool enabled, bool selected) = 0;
423 virtual void pushGroup (const char *name, bool enabled) = 0;
424 virtual void popGroup () = 0;
425
426 virtual int getNumberOfItems () = 0;
427 virtual bool isSelected (int index) = 0;
428 };
429
430 class ListResource: public SelectionResource
431 {
432 public:
433 enum SelectionMode {
434 /**
435 * \brief Exactly one item is selected.
436 *
437 * If no item is selected initially, the first one is selected.
438 */
439 SELECTION_EXACTLY_ONE,
440
441 /**
442 * \brief Exactly one item is selected, except possibly at the beginning.
443 *
444 * If no item is selected initially, no one is selected automatically.
445 * The user may not unselect the only selected item.
446 */
447 SELECTION_EXACTLY_ONE_BY_USER,
448
449 /**
450 * \brief At most one item is selected.
451 *
452 * If no item is selected initially, no one is selected automatically.
453 * The user may unselect the only selected item.
454 */
455 SELECTION_AT_MOST_ONE,
456
457 /**
458 * \brief An arbitrary number of items may be selected.
459 */
460 SELECTION_MULTIPLE
461 };
462 };
463
464 class OptionMenuResource: public SelectionResource
465 {
466 };
467
468 class TextResource: public Resource
469 {
470 public:
471 Iterator *iterator (Content::Type mask, bool atEnd);
472
473 virtual const char *getText () = 0;
474 virtual void setText (const char *text) = 0;
475 virtual bool isEditable () = 0;
476 virtual void setEditable (bool editable) = 0;
477 };
478
479 class EntryResource: public TextResource
480 {
481 public:
482 enum { UNLIMITED_MAX_LENGTH = -1 };
483 };
484
485 class MultiLineTextResource: public TextResource
486 {
487 };
488
489
490 class ToggleButtonResource: public Resource
491 {
492 public:
493 virtual bool isActivated () = 0;
494 virtual void setActivated (bool activated) = 0;
495 };
496
497 class CheckButtonResource: public ToggleButtonResource
498 {
499 public:
500 Iterator *iterator (Content::Type mask, bool atEnd);
501 };
502
503 class RadioButtonResource: public ToggleButtonResource
504 {
505 public:
506 class GroupIterator
507 {
508 protected:
509 GroupIterator () { }
510 virtual ~GroupIterator ();
511
512 public:
513 virtual bool hasNext () = 0;
514 virtual RadioButtonResource *getNext () = 0;
515 virtual void unref () = 0;
516 };
517
518 /**
519 * \brief Return an iterator, to access all radio button resources
520 * within the group.
521 */
522 virtual GroupIterator *groupIterator () = 0;
523
524 Iterator *iterator (Content::Type mask, bool atEnd);
525 };
526
527
528 /**
529 * \brief A factory for the common resource.
530 */
531 class ResourceFactory: public lout::object::Object
532 {
533 public:
534 virtual LabelButtonResource *createLabelButtonResource (const char *label)
535 = 0;
536 virtual ComplexButtonResource *createComplexButtonResource (Widget *widget,
537 bool relief)
538 = 0;
539 virtual ListResource *createListResource (ListResource::SelectionMode
540 selectionMode, int rows) = 0;
541 virtual OptionMenuResource *createOptionMenuResource () = 0;
542 virtual EntryResource *createEntryResource (int maxLength, bool password,
543 const char *label) = 0;
544 virtual MultiLineTextResource *createMultiLineTextResource (int cols,
545 int rows) = 0;
546 virtual CheckButtonResource *createCheckButtonResource (bool activated) = 0;
547 virtual RadioButtonResource *createRadioButtonResource (RadioButtonResource
548 *groupedWith,
549 bool activated) = 0;
550 };
551
552 } // namespace ui
553 } // namespace core
554 } // namespace dw
555
556 #endif // __DW_UI_HH__
0 #ifndef __DW_VIEW_HH__
1 #define __DW_VIEW_HH__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 namespace dw {
8 namespace core {
9
10 /**
11 * \brief An interface to encapsulate platform dependent drawing.
12 *
13 * \sa\ref dw-overview, \ref dw-layout-views
14 */
15 class View: public lout::object::Object
16 {
17 public:
18 /*
19 * ----------------------------
20 * Operations on the view
21 * ----------------------------
22 */
23
24 /**
25 * \brief This methods notifies the view, that it has been attached to a
26 * layout.
27 */
28 virtual void setLayout (Layout *layout) = 0;
29
30 /**
31 * \brief Set the canvas size.
32 */
33 virtual void setCanvasSize (int width, int ascent, int descent) = 0;
34
35 /**
36 * \brief Set the cursor appearance.
37 */
38 virtual void setCursor (style::Cursor cursor) = 0;
39
40 /**
41 * \brief Set the background of the view.
42 */
43 virtual void setBgColor (style::Color *color) = 0;
44
45 /*
46 * ---------------------------------------------------------
47 * Scrolling and Related. Only usesViewport must be
48 * implemented, if it returns false, the other methods
49 * are never called.
50 * ---------------­-----------­-----------------------------
51 */
52
53 /**
54 * \brief Return, whether this view uses a viewport.
55 */
56 virtual bool usesViewport () = 0;
57
58 /**
59 * \brief Get the thickness of the horizontal scrollbar, when it is
60 * visible.
61 *
62 * Does not have to be implemented, when usesViewport returns false.
63 */
64 virtual int getHScrollbarThickness () = 0;
65
66 /**
67 * \brief Get the thickness of the vertical scrollbar, when it is
68 * visible.
69 *
70 * Does not have to be implemented, when usesViewport returns false.
71 */
72 virtual int getVScrollbarThickness () = 0;
73
74 /**
75 * \brief Scroll the vieport to the given position.
76 *
77 * Does not have to be implemented, when usesViewport returns false.
78 */
79 virtual void scrollTo (int x, int y) = 0;
80
81 /**
82 * \brief Scroll the viewport as commanded.
83 */
84 virtual void scroll (ScrollCommand) { };
85
86 /**
87 * \brief Set the viewport size.
88 *
89 * Does not have to be implemented, when usesViewport returns false.
90 *
91 * This will normally imply a resize of the UI widget. Width and height are
92 * the dimensions of the new size, \em including the scrollbar thicknesses.
93 *
94 */
95 virtual void setViewportSize (int width, int height,
96 int hScrollbarThickness,
97 int vScrollbarThickness) = 0;
98
99 /*
100 * -----------------------
101 * Drawing functions
102 * -----------------------
103 */
104
105 /**
106 * \brief Called before drawing.
107 *
108 * All actual drawing operations will be enclosed into calls of
109 * dw::core:View::startDrawing and dw::core:View::finishDrawing. They
110 * may be implemented, e.g. when a backing
111 * pixmap is used, to prevent flickering. StartDrawing() will then
112 * initialize the backing pixmap, all other drawing operations will draw
113 * into it, and finishDrawing() will merge it into the window.
114 */
115 virtual void startDrawing (Rectangle *area) = 0;
116
117 /**
118 * \brief Called after drawing.
119 *
120 * \sa dw::core:View::startDrawing
121 */
122 virtual void finishDrawing (Rectangle *area) = 0;
123
124 /**
125 * \brief Queue a region, which is given in \em canvas coordinates, for
126 * drawing.
127 *
128 * The view implementation is responsible, that this region is drawn, either
129 * immediately, or (which is more typical, since more efficient) the areas
130 * are collected, combined (as far as possible), and the drawing is later
131 * done in an idle function.
132 */
133 virtual void queueDraw (Rectangle *area) = 0;
134
135 /**
136 * \brief Queue the total viewport for drawing.
137 *
138 * \sa dw::core::View::queueDraw
139 */
140 virtual void queueDrawTotal () = 0;
141
142 /**
143 * \brief Cancel a draw queue request.
144 *
145 * If dw::core::View::queueDraw or dw::core::View::queueDrawTotal have been
146 * called before, and the actual drawing was not processed yet, the actual
147 * drawing should be cancelled. Otherwise, the cancellation should be
148 * ignored.
149 */
150 virtual void cancelQueueDraw () = 0;
151
152 /*
153 * The following methods should be self-explaining.
154 */
155
156 virtual void drawPoint (style::Color *color,
157 style::Color::Shading shading,
158 int x, int y) = 0;
159 virtual void drawLine (style::Color *color,
160 style::Color::Shading shading,
161 int x1, int y1, int x2, int y2) = 0;
162 virtual void drawTypedLine (style::Color *color,
163 style::Color::Shading shading,
164 style::LineType type, int width,
165 int x1, int y1, int x2, int y2) = 0;
166 virtual void drawRectangle (style::Color *color,
167 style::Color::Shading shading, bool filled,
168 int x, int y, int width, int height) = 0;
169 virtual void drawArc (style::Color *color,
170 style::Color::Shading shading, bool filled,
171 int centerX, int centerY, int width, int height,
172 int angle1, int angle2) = 0;
173 virtual void drawPolygon (style::Color *color,
174 style::Color::Shading shading,
175 bool filled, bool convex, int points[][2],
176 int npoints) = 0;
177 virtual void drawText (style::Font *font,
178 style::Color *color,
179 style::Color::Shading shading,
180 int x, int y, const char *text, int len) = 0;
181
182 virtual void drawImage (Imgbuf *imgbuf, int xRoot, int yRoot,
183 int x, int y, int width, int height) = 0;
184
185 /*
186 * --------------
187 * Clipping
188 * --------------
189 */
190
191 /*
192 * To prevent drawing outside of a given area, a clipping view may be
193 * requested, which also implements this interface. The clipping view is
194 * related to the parent view (clipping views may be nested!), anything
195 * which is drawn into this clipping view, is later merged again into the
196 * parent view. An implementation will typically use additional pixmaps,
197 * which are later merged into the parent view pixmap/window.
198 */
199
200 virtual View *getClippingView (int x, int y, int width, int height) = 0;
201 virtual void mergeClippingView (View *clippingView) = 0;
202 };
203
204 } // namespace dw
205 } // namespace core
206
207 #endif // __DW_VIEW_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "core.hh"
22
23 #include "../lout/msg.h"
24 #include "../lout/debug.hh"
25
26 using namespace lout;
27 using namespace lout::object;
28
29 namespace dw {
30 namespace core {
31
32 // ----------------------------------------------------------------------
33
34 int Widget::CLASS_ID = -1;
35
36 Widget::Widget ()
37 {
38 registerName ("dw::core::Widget", &CLASS_ID);
39
40 flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS);
41 parent = NULL;
42 layout = NULL;
43
44 allocation.x = -1;
45 allocation.y = -1;
46 allocation.width = 1;
47 allocation.ascent = 1;
48 allocation.descent = 0;
49
50 style = NULL;
51 bgColor = NULL;
52 buttonSensitive = true;
53 buttonSensitiveSet = false;
54
55 deleteCallbackData = NULL;
56 deleteCallbackFunc = NULL;
57 }
58
59 Widget::~Widget ()
60 {
61 if (deleteCallbackFunc)
62 deleteCallbackFunc (deleteCallbackData);
63
64 if (style)
65 style->unref ();
66
67 if (parent)
68 parent->removeChild (this);
69 else
70 layout->removeWidget ();
71 }
72
73
74 /**
75 * \brief Calculates the intersection of widget->allocation and area, returned
76 * in intersection (in widget coordinates!).
77 *
78 * Typically used by containers when
79 * drawing their children. Returns whether intersection is not empty.
80 */
81 bool Widget::intersects (Rectangle *area, Rectangle *intersection)
82 {
83 Rectangle parentArea, childArea;
84
85 parentArea = *area;
86 parentArea.x += parent->allocation.x;
87 parentArea.y += parent->allocation.y;
88
89 childArea.x = allocation.x;
90 childArea.y = allocation.y;
91 childArea.width = allocation.width;
92 childArea.height = getHeight ();
93
94 if (parentArea.intersectsWith (&childArea, intersection)) {
95 intersection->x -= allocation.x;
96 intersection->y -= allocation.y;
97 return true;
98 } else
99 return false;
100 }
101
102 void Widget::setParent (Widget *parent)
103 {
104 this->parent = parent;
105 layout = parent->layout;
106
107 if (!buttonSensitiveSet)
108 buttonSensitive = parent->buttonSensitive;
109
110 //DBG_OBJ_ASSOC (widget, parent);
111 }
112
113 void Widget::queueDrawArea (int x, int y, int width, int height)
114 {
115 /** \todo Maybe only the intersection? */
116 layout->queueDraw (x + allocation.x, y + allocation.y, width, height);
117 _MSG("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height);
118 }
119
120 /**
121 * \brief This method should be called, when a widget changes its size.
122 */
123 void Widget::queueResize (int ref, bool extremesChanged)
124 {
125 Widget *widget2, *child;
126
127 //DEBUG_MSG (DEBUG_SIZE,
128 // "a %stop-level %s with parent_ref = %d has changed its size\n",
129 // widget->parent ? "non-" : "",
130 // gtk_type_name (GTK_OBJECT_TYPE (widget)), widget->parent_ref);
131
132 setFlags (NEEDS_RESIZE);
133 setFlags (NEEDS_ALLOCATE);
134 markSizeChange (ref);
135
136 if (extremesChanged) {
137 setFlags (EXTREMES_CHANGED);
138 markExtremesChange (ref);
139 }
140
141 for (widget2 = parent, child = this;
142 widget2;
143 child = widget2, widget2 = widget2->parent) {
144 widget2->setFlags (NEEDS_RESIZE);
145 widget2->markSizeChange (child->parentRef);
146 widget2->setFlags (NEEDS_ALLOCATE);
147
148 //DEBUG_MSG (DEBUG_ALLOC,
149 // "setting DW_NEEDS_ALLOCATE for a %stop-level %s "
150 // "with parent_ref = %d\n",
151 // widget2->parent ? "non-" : "",
152 // gtk_type_name (GTK_OBJECT_TYPE (widget2)),
153 // widget2->parent_ref);
154
155 if (extremesChanged) {
156 widget2->setFlags (EXTREMES_CHANGED);
157 widget2->markExtremesChange (child->parentRef);
158 }
159 }
160
161 if (layout)
162 layout->queueResize ();
163 }
164
165
166 /**
167 * \brief This method is a wrapper for Widget::sizeRequestImpl(); it calls
168 * the latter only when needed.
169 */
170 void Widget::sizeRequest (Requisition *requisition)
171 {
172 if (needsResize ()) {
173 /** \todo Check requisition == &(this->requisition) and do what? */
174 sizeRequestImpl (requisition);
175 this->requisition = *requisition;
176 unsetFlags (NEEDS_RESIZE);
177
178 DBG_OBJ_SET_NUM (this, "requisition->width", requisition->width);
179 DBG_OBJ_SET_NUM (this, "requisition->ascent", requisition->ascent);
180 DBG_OBJ_SET_NUM (this, "requisition->descent", requisition->descent);
181 } else
182 *requisition = this->requisition;
183 }
184
185 /**
186 * \brief Wrapper for Widget::getExtremesImpl().
187 */
188 void Widget::getExtremes (Extremes *extremes)
189 {
190 if (extremesChanged ()) {
191 getExtremesImpl (extremes);
192 this->extremes = *extremes;
193 unsetFlags (EXTREMES_CHANGED);
194
195 DBG_OBJ_SET_NUM (this, "extremes->minWidth", extremes->minWidth);
196 DBG_OBJ_SET_NUM (this, "extremes->maxWidth", extremes->maxWidth);
197 } else
198 *extremes = this->extremes;
199 }
200
201 /**
202 * \brief Wrapper for Widget::sizeAllocateImpl, calls the latter only when
203 * needed.
204 */
205 void Widget::sizeAllocate (Allocation *allocation)
206 {
207 if (needsAllocate () ||
208 allocation->x != this->allocation.x ||
209 allocation->y != this->allocation.y ||
210 allocation->width != this->allocation.width ||
211 allocation->ascent != this->allocation.ascent ||
212 allocation->descent != this->allocation.descent) {
213
214 //DEBUG_MSG (DEBUG_ALLOC,
215 // "a %stop-level %s with parent_ref = %d is newly allocated "
216 // "from %d, %d, %d x %d x %d ...\n",
217 // widget->parent ? "non-" : "",
218 // (GTK_OBJECT_TYPE_NAME (widget), widget->parent_ref,
219 // widget->allocation.x, widget->allocation.y,
220 // widget->allocation.width, widget->allocation.ascent,
221 // widget->allocation.descent);
222
223 if (wasAllocated ()) {
224 layout->queueDrawExcept (
225 this->allocation.x,
226 this->allocation.y,
227 this->allocation.width,
228 this->allocation.ascent + this->allocation.descent,
229 allocation->x,
230 allocation->y,
231 allocation->width,
232 allocation->ascent + allocation->descent);
233 }
234
235 sizeAllocateImpl (allocation);
236
237 //DEBUG_MSG (DEBUG_ALLOC, "... to %d, %d, %d x %d x %d\n",
238 // widget->allocation.x, widget->allocation.y,
239 // widget->allocation.width, widget->allocation.ascent,
240 // widget->allocation.descent);
241
242 this->allocation = *allocation;
243 unsetFlags (NEEDS_ALLOCATE);
244 setFlags (WAS_ALLOCATED);
245
246 resizeDrawImpl ();
247
248 DBG_OBJ_SET_NUM (this, "allocation.x", this->allocation.x);
249 DBG_OBJ_SET_NUM (this, "allocation.y", this->allocation.y);
250 DBG_OBJ_SET_NUM (this, "allocation.width", this->allocation.width);
251 DBG_OBJ_SET_NUM (this, "allocation.ascent", this->allocation.ascent);
252 DBG_OBJ_SET_NUM (this, "allocation.descent", this->allocation.descent);
253 }
254
255 /*unsetFlags (NEEDS_RESIZE);*/
256 }
257
258 bool Widget::buttonPress (EventButton *event)
259 {
260 return buttonPressImpl (event);
261 }
262
263 bool Widget::buttonRelease (EventButton *event)
264 {
265 return buttonReleaseImpl (event);
266 }
267
268 bool Widget::motionNotify (EventMotion *event)
269 {
270 return motionNotifyImpl (event);
271 }
272
273 void Widget::enterNotify (EventCrossing *event)
274 {
275 enterNotifyImpl (event);
276 }
277
278 void Widget::leaveNotify (EventCrossing *event)
279 {
280 leaveNotifyImpl (event);
281 }
282
283 /**
284 * \brief Change the style of a widget.
285 *
286 * The old style is automatically unreferred, the new is referred. If this
287 * call causes the widget to change its size, dw::core::Widget::queueResize
288 * is called.
289 */
290 void Widget::setStyle (style::Style *style)
291 {
292 bool sizeChanged;
293
294 style->ref ();
295
296 if (this->style) {
297 sizeChanged = this->style->sizeDiffs (style);
298 this->style->unref ();
299 } else
300 sizeChanged = true;
301
302 this->style = style;
303
304 if (layout != NULL) {
305 layout->updateCursor ();
306 }
307
308 if (sizeChanged)
309 queueResize (0, true);
310 else
311 queueDraw ();
312 }
313
314 /**
315 * \brief Set the background "behind" the widget, if it is not the
316 * background of the parent widget, e.g. the background of a table
317 * row.
318 */
319 void Widget::setBgColor (style::Color *bgColor)
320 {
321 this->bgColor = bgColor;
322 }
323
324 /**
325 * \brief Get the actual background of a widget.
326 */
327 style::Color *Widget::getBgColor ()
328 {
329 Widget *widget = this;
330
331 while (widget != NULL) {
332 if (widget->style->backgroundColor)
333 return widget->style->backgroundColor;
334 if (widget->bgColor)
335 return widget->bgColor;
336
337 widget = widget->parent;
338 }
339
340 return layout->getBgColor ();
341 }
342
343
344 /**
345 * \brief Draw borders and background of a widget part, which allocation is
346 * given by (x, y, width, height) (widget coordinates).
347 *
348 * area is given in widget coordinates.
349 */
350 void Widget::drawBox (View *view, style::Style *style, Rectangle *area,
351 int x, int y, int width, int height, bool inverse)
352 {
353 Rectangle viewArea;
354 viewArea.x = area->x + allocation.x;
355 viewArea.y = area->y + allocation.y;
356 viewArea.width = area->width;
357 viewArea.height = area->height;
358
359 style::drawBorder (view, &viewArea, allocation.x + x, allocation.y + y,
360 width, height, style, inverse);
361
362 /** \todo Background images? */
363 if (style->backgroundColor)
364 style::drawBackground (view, &viewArea,
365 allocation.x + x, allocation.y + y, width, height,
366 style, inverse);
367 }
368
369 /**
370 * \brief Draw borders and background of a widget.
371 *
372 * area is given in widget coordinates.
373 *
374 */
375 void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse)
376 {
377 Rectangle viewArea;
378 viewArea.x = area->x + allocation.x;
379 viewArea.y = area->y + allocation.y;
380 viewArea.width = area->width;
381 viewArea.height = area->height;
382
383 style::drawBorder (view, &viewArea, allocation.x, allocation.y,
384 allocation.width, getHeight (), style, inverse);
385
386 /** \todo Adjust following comment from the old dw sources. */
387 /*
388 * - Toplevel widget background colors are set as viewport
389 * background color. This is not crucial for the rendering, but
390 * looks a bit nicer when scrolling. Furthermore, the viewport
391 * does anything else in this case.
392 *
393 * - Since widgets are always drawn from top to bottom, it is
394 * *not* necessary to draw the background if
395 * widget->style->background_color is NULL (shining through).
396 */
397 /** \todo Background images? */
398
399 if (style->backgroundColor &&
400 (parent || layout->getBgColor () != style->backgroundColor))
401 style::drawBackground (view, &viewArea, allocation.x, allocation.y,
402 allocation.width, getHeight (), style, inverse);
403 }
404
405 /*
406 * This function is used by some widgets, when they are selected (as a whole).
407 *
408 * \todo This could be accelerated by using clipping bitmaps. Two important
409 * issues:
410 *
411 * (i) There should always been a pixel in the upper-left corner of the
412 * *widget*, so probably two different clipping bitmaps have to be
413 * used (10/01 and 01/10).
414 *
415 * (ii) Should a new GC always be created?
416 *
417 * \bug Not implemented.
418 */
419 void Widget::drawSelected (View *view, Rectangle *area)
420 {
421 }
422
423
424 void Widget::setButtonSensitive (bool buttonSensitive)
425 {
426 this->buttonSensitive = buttonSensitive;
427 buttonSensitiveSet = true;
428 }
429
430
431 /**
432 * \brief Get the widget at the root of the tree, this widget is part from.
433 */
434 Widget *Widget::getTopLevel ()
435 {
436 Widget *widget = this;
437
438 while (widget->parent)
439 widget = widget->parent;
440
441 return widget;
442 }
443
444 /**
445 * \brief Get the level of the widget within the tree.
446 *
447 * The root widget has the level 0.
448 */
449 int Widget::getLevel ()
450 {
451 Widget *widget = this;
452 int level = 0;
453
454 while (widget->parent) {
455 level++;
456 widget = widget->parent;
457 }
458
459 return level;
460 }
461
462 /**
463 * \brief Get the widget with the highest level, which is a direct ancestor of
464 * widget1 and widget2.
465 */
466 Widget *Widget::getNearestCommonAncestor (Widget *otherWidget)
467 {
468 Widget *widget1 = this, *widget2 = otherWidget;
469 int level1 = widget1->getLevel (), level2 = widget2->getLevel();
470
471 /* Get both widgets onto the same level.*/
472 while (level1 > level2) {
473 widget1 = widget1->parent;
474 level1--;
475 }
476
477 while (level2 > level1) {
478 widget2 = widget2->parent;
479 level2--;
480 }
481
482 /* Search upwards. */
483 while (widget1 != widget2) {
484 if (widget1->parent == NULL) {
485 MSG_WARN("widgets in different trees\n");
486 return NULL;
487 }
488
489 widget1 = widget1->parent;
490 widget2 = widget2->parent;
491 }
492
493 return widget1;
494 }
495
496
497 /**
498 * \brief Search recursively through widget.
499 *
500 * Used by dw::core::Layout:getWidgetAtPoint.
501 */
502 Widget *Widget::getWidgetAtPoint (int x, int y, int level)
503 {
504 Iterator *it;
505 Widget *childAtPoint;
506
507 //_MSG ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n",
508 // 3 * level, "", gtk_type_name (GTK_OBJECT_TYPE (widget)), widget,
509 // allocation.x, allocation.y,
510 // allocation.width, allocation.ascent,
511 // allocation.descent);
512
513 if (x >= allocation.x &&
514 y >= allocation.y &&
515 x <= allocation.x + allocation.width &&
516 y <= allocation.y + getHeight ()) {
517 //_MSG ("%*s -> inside\n", 3 * level, "");
518 /*
519 * Iterate over the children of this widget. Test recursively, whether
520 * the point is within the child (or one of its children...). If there
521 * is such a child, it is returned. Otherwise, this widget is returned.
522 */
523 childAtPoint = NULL;
524 it = iterator (Content::WIDGET, false);
525
526 while (childAtPoint == NULL && it->next ())
527 childAtPoint = it->getContent()->widget->getWidgetAtPoint (x, y,
528 level + 1);
529
530 it->unref ();
531
532 if (childAtPoint)
533 return childAtPoint;
534 else
535 return this;
536 } else
537 return NULL;
538 }
539
540
541 void Widget::scrollTo (HPosition hpos, VPosition vpos,
542 int x, int y, int width, int height)
543 {
544 layout->scrollTo (hpos, vpos,
545 x + allocation.x, y + allocation.y, width, height);
546 }
547
548 void Widget::getExtremesImpl (Extremes *extremes)
549 {
550 /* Simply return the requisition width */
551 Requisition requisition;
552 sizeRequest (&requisition);
553 extremes->minWidth = extremes->maxWidth = requisition.width;
554 }
555
556 void Widget::sizeAllocateImpl (Allocation *allocation)
557 {
558 }
559
560 void Widget::markSizeChange (int ref)
561 {
562 }
563
564 void Widget::markExtremesChange (int ref)
565 {
566 }
567
568 void Widget::setWidth (int width)
569 {
570 }
571
572 void Widget::setAscent (int ascent)
573 {
574 }
575
576 void Widget::setDescent (int descent)
577 {
578 }
579
580 bool Widget::buttonPressImpl (EventButton *event)
581 {
582 return false;
583 }
584
585 bool Widget::buttonReleaseImpl (EventButton *event)
586 {
587 return false;
588 }
589
590 bool Widget::motionNotifyImpl (EventMotion *event)
591 {
592 return false;
593 }
594
595 void Widget::enterNotifyImpl (EventCrossing *)
596 {
597 core::style::Tooltip *tooltip = getStyle()->x_tooltip;
598
599 if (tooltip)
600 tooltip->onEnter();
601 }
602
603 void Widget::leaveNotifyImpl (EventCrossing *)
604 {
605 core::style::Tooltip *tooltip = getStyle()->x_tooltip;
606
607 if (tooltip)
608 tooltip->onLeave();
609 }
610
611 void Widget::removeChild (Widget *child)
612 {
613 // Should be implemented.
614 misc::assertNotReached ();
615 }
616
617
618
619 } // namespace dw
620 } // namespace core
0 #ifndef __DW_WIDGET_HH__
1 #define __DW_WIDGET_HH__
2
3 #ifndef __INCLUDED_FROM_DW_CORE_HH__
4 # error Do not include this file directly, use "core.hh" instead.
5 #endif
6
7 #include "../lout/identity.hh"
8
9 /**
10 * \brief The type for callback functions.
11 */
12 typedef void (*DW_Callback_t)(void *data);
13
14 namespace dw {
15 namespace core {
16
17 /**
18 * \brief The base class of all dillo widgets.
19 *
20 * \sa\ref dw-overview, \ref dw-layout-widgets
21 */
22 class Widget: public lout::identity::IdentifiableObject
23 {
24 friend class Layout;
25
26 protected:
27 enum Flags {
28 /**
29 * \brief Set, when dw::core::Widget::requisition is not up to date
30 * anymore.
31 */
32 NEEDS_RESIZE = 1 << 0,
33
34 /**
35 * \brief Only used internally, set to enforce size allocation.
36 *
37 * (I've forgotten the case, for which this is necessary.)
38 */
39 NEEDS_ALLOCATE = 1 << 1,
40
41 /**
42 * \brief Set, when dw::core::Widget::extremes is not up to date
43 * anymore.
44 */
45 EXTREMES_CHANGED = 1 << 2,
46
47 /**
48 * \brief Set by the widget itself (in the constructor), when set...
49 * methods are implemented.
50 *
51 * Will hopefully be removed, after redesigning the size model.
52 */
53 USES_HINTS = 1 << 3,
54
55 /**
56 * \brief Set by the widget itself (in the constructor), when it contains
57 * some contents, e.g. an image, as opposed to a horizontal ruler.
58 *
59 * Will hopefully be removed, after redesigning the size model.
60 */
61 HAS_CONTENTS = 1 << 4,
62
63 /**
64 * \brief Set, when a widget was already once allocated,
65 *
66 * The dw::Image widget uses this flag, see dw::Image::setBuffer.
67 */
68 WAS_ALLOCATED = 1 << 5,
69
70 /**
71 * \brief Set for block-level widgets (as opposed to inline widgets)
72 */
73 BLOCK_LEVEL = 1 << 6,
74 };
75
76 private:
77 /**
78 * \brief The parent widget, NULL for top-level widgets.
79 */
80 Widget *parent;
81 style::Style *style;
82
83 Flags flags;
84
85 /**
86 * \brief Size_request() stores the result of the last call of
87 * size_request_impl().
88 *
89 * Do not read this directly, but call size_request().
90 */
91 Requisition requisition;
92
93 /**
94 * \brief Analogue to dw::core::Widget::requisition.
95 */
96 Extremes extremes;
97
98 /**
99 * \brief See dw::core::Widget::setBgColor().
100 */
101 style::Color *bgColor;
102
103 /**
104 * \brief See dw::core::Widget::setButtonSensitive().
105 */
106 bool buttonSensitive;
107
108 /**
109 * \brief See dw::core::Widget::setButtonSensitive().
110 */
111 bool buttonSensitiveSet;
112
113 public:
114 /**
115 * \brief This value is defined by the parent widget, and used for
116 * incremential resizing.
117 *
118 * See documentation for an explanation.
119 */
120 int parentRef;
121
122 protected:
123
124 /**
125 * \brief The current allocation: size and position, always relative to the
126 * canvas.
127 */
128 Allocation allocation;
129
130 inline int getHeight () { return allocation.ascent + allocation.descent; }
131 inline int getContentWidth() { return allocation.width
132 - style->boxDiffWidth (); }
133 inline int getContentHeight() { return getHeight ()
134 - style->boxDiffHeight (); }
135
136 Layout *layout;
137
138 inline void setFlags (Flags f) { flags = (Flags)(flags | f); }
139 inline void unsetFlags (Flags f) { flags = (Flags)(flags & ~f); }
140
141
142 inline void queueDraw ()
143 {
144 queueDrawArea (0, 0, allocation.width, getHeight());
145 }
146 void queueDrawArea (int x, int y, int width, int height);
147 void queueResize (int ref, bool extremesChanged);
148
149 /**
150 * \brief See \ref dw-widget-sizes.
151 */
152 virtual void sizeRequestImpl (Requisition *requisition) = 0;
153
154 /**
155 * \brief See \ref dw-widget-sizes.
156 */
157 virtual void getExtremesImpl (Extremes *extremes);
158
159 /**
160 * \brief See \ref dw-widget-sizes.
161 */
162 virtual void sizeAllocateImpl (Allocation *allocation);
163
164 /**
165 * \brief Called after sizeAllocateImpl() to redraw necessary areas.
166 * By default the whole widget is redrawn.
167 */
168 virtual void resizeDrawImpl () { queueDraw (); };
169
170 /**
171 * \brief See \ref dw-widget-sizes.
172 */
173 virtual void markSizeChange (int ref);
174
175 /**
176 * \brief See \ref dw-widget-sizes.
177 */
178 virtual void markExtremesChange (int ref);
179
180 virtual bool buttonPressImpl (EventButton *event);
181 virtual bool buttonReleaseImpl (EventButton *event);
182 virtual bool motionNotifyImpl (EventMotion *event);
183 virtual void enterNotifyImpl (EventCrossing *event);
184 virtual void leaveNotifyImpl (EventCrossing *event);
185
186 inline char *addAnchor (const char* name)
187 { return layout->addAnchor (this, name); }
188
189 inline char *addAnchor (const char* name, int y)
190 { return layout->addAnchor (this, name, y); }
191
192 inline void changeAnchor (char* name, int y)
193 { layout->changeAnchor (this, name, y); }
194
195 inline void removeAnchor (char* name)
196 { layout->removeAnchor (this, name); }
197
198 //inline void updateBgColor () { layout->updateBgColor (); }
199
200 inline void setCursor (style::Cursor cursor)
201 { layout->setCursor (cursor); }
202
203 inline bool selectionButtonPress (Iterator *it, int charPos, int linkNo,
204 EventButton *event, bool withinContent)
205 { return layout->selectionState.buttonPress (it, charPos, linkNo, event); }
206
207 inline bool selectionButtonRelease (Iterator *it, int charPos, int linkNo,
208 EventButton *event, bool withinContent)
209 { return layout->selectionState.buttonRelease (it, charPos, linkNo, event);}
210
211 inline bool selectionButtonMotion (Iterator *it, int charPos, int linkNo,
212 EventMotion *event, bool withinContent)
213 { return layout->selectionState.buttonMotion (it, charPos, linkNo, event); }
214
215 inline bool selectionHandleEvent (SelectionState::EventType eventType,
216 Iterator *it, int charPos, int linkNo,
217 MousePositionEvent *event)
218 { return layout->selectionState.handleEvent (eventType, it, charPos, linkNo,
219 event); }
220
221 private:
222 void *deleteCallbackData;
223 DW_Callback_t deleteCallbackFunc;
224
225 public:
226 inline void setDeleteCallback(DW_Callback_t func, void *data)
227 { deleteCallbackFunc = func; deleteCallbackData = data; }
228
229 public:
230 static int CLASS_ID;
231
232 Widget ();
233 ~Widget ();
234
235 inline bool needsResize () { return flags & NEEDS_RESIZE; }
236 inline bool needsAllocate () { return flags & NEEDS_ALLOCATE; }
237 inline bool extremesChanged () { return flags & EXTREMES_CHANGED; }
238 inline bool wasAllocated () { return flags & WAS_ALLOCATED; }
239 inline bool usesHints () { return flags & USES_HINTS; }
240 inline bool hasContents () { return flags & HAS_CONTENTS; }
241 inline bool blockLevel () { return flags & BLOCK_LEVEL; }
242
243 void setParent (Widget *parent);
244
245 inline style::Style *getStyle () { return style; }
246 /** \todo I do not like this. */
247 inline Allocation *getAllocation () { return &allocation; }
248
249 void sizeRequest (Requisition *requisition);
250 void getExtremes (Extremes *extremes);
251 void sizeAllocate (Allocation *allocation);
252 virtual void setWidth (int width);
253 virtual void setAscent (int ascent);
254 virtual void setDescent (int descent);
255
256 bool intersects (Rectangle *area, Rectangle *intersection);
257
258 /** Area is given in widget coordinates. */
259 virtual void draw (View *view, Rectangle *area) = 0;
260
261 bool buttonPress (EventButton *event);
262 bool buttonRelease (EventButton *event);
263 bool motionNotify (EventMotion *event);
264 void enterNotify (EventCrossing *event);
265 void leaveNotify (EventCrossing *event);
266
267 virtual void setStyle (style::Style *style);
268 void setBgColor (style::Color *bgColor);
269 style::Color *getBgColor ();
270
271 void drawBox (View *view, style::Style *style, Rectangle *area,
272 int x, int y, int width, int height, bool inverse);
273 void drawWidgetBox (View *view, Rectangle *area, bool inverse);
274 void drawSelected (View *view, Rectangle *area);
275
276 void setButtonSensitive (bool buttonSensitive);
277 inline bool isButtonSensitive () { return buttonSensitive; }
278
279 inline Widget *getParent () { return parent; }
280 Widget *getTopLevel ();
281 int getLevel ();
282 Widget *getNearestCommonAncestor (Widget *otherWidget);
283
284 inline Layout *getLayout () { return layout; }
285
286 virtual Widget *getWidgetAtPoint (int x, int y, int level);
287
288 void scrollTo (HPosition hpos, VPosition vpos,
289 int x, int y, int width, int height);
290
291 /**
292 * \brief Return an iterator for this widget.
293 *
294 * \em mask can narrow the types returned by the iterator, this can
295 * enhance performance quite much, e.g. when only searching for child
296 * widgets.
297 *
298 * With \em atEnd == false, the iterator starts \em before the beginning,
299 * i.e. the first call of dw::core::Iterator::next will let the iterator
300 * point on the first piece of contents. Likewise, With \em atEnd == true,
301 * the iterator starts \em after the last piece of contents, call
302 * dw::core::Iterator::prev in this case.
303 */
304 virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0;
305 virtual void removeChild (Widget *child);
306 };
307
308 } // namespace dw
309 } // namespace core
310
311 #endif // __DW_WIDGET_HH__
1111 fi
1212
1313 if [ ! -x dpid/dpid ] ; then
14 echo "You MUST run this script after make."
14 echo "This script may only be run AFTER make."
1515 exit 1
1616 fi
17
18 # Try to communicate with any currently-running dpid to tell it
19 # to stop itself and the dpi programs.
20 dpidc stop
1721
1822 if [ ! -d $BASE ] ; then
1923 mkdir $BASE
2226 mkdir $BASE2
2327 fi
2428
25 cp dpid/dpid dpid/dpidc $BASE
26 strip $BASE/dpid
29 cp dpid/dpid dpid/dpidc dpid/dpidrc $BASE
30 strip $BASE/dpid $BASE/dpidc
2731
2832 cd dpi
2933 for F in *.dpi ; do
+0
-323
install-sh less more
0 #!/bin/sh
1 # install - install a program, script, or datafile
2
3 scriptversion=2005-02-02.21
4
5 # This originates from X11R5 (mit/util/scripts/install.sh), which was
6 # later released in X11R6 (xc/config/util/install.sh) with the
7 # following copyright and license.
8 #
9 # Copyright (C) 1994 X Consortium
10 #
11 # Permission is hereby granted, free of charge, to any person obtaining a copy
12 # of this software and associated documentation files (the "Software"), to
13 # deal in the Software without restriction, including without limitation the
14 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
15 # sell copies of the Software, and to permit persons to whom the Software is
16 # furnished to do so, subject to the following conditions:
17 #
18 # The above copyright notice and this permission notice shall be included in
19 # all copies or substantial portions of the Software.
20 #
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
25 # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
26 # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #
28 # Except as contained in this notice, the name of the X Consortium shall not
29 # be used in advertising or otherwise to promote the sale, use or other deal-
30 # ings in this Software without prior written authorization from the X Consor-
31 # tium.
32 #
33 #
34 # FSF changes to this file are in the public domain.
35 #
36 # Calling this script install-sh is preferred over install.sh, to prevent
37 # `make' implicit rules from creating a file called install from it
38 # when there is no Makefile.
39 #
40 # This script is compatible with the BSD install script, but was written
41 # from scratch. It can only install one file at a time, a restriction
42 # shared with many OS's install programs.
43
44 # set DOITPROG to echo to test this script
45
46 # Don't use :- since 4.3BSD and earlier shells don't like it.
47 doit="${DOITPROG-}"
48
49 # put in absolute paths if you don't have them in your path; or use env. vars.
50
51 mvprog="${MVPROG-mv}"
52 cpprog="${CPPROG-cp}"
53 chmodprog="${CHMODPROG-chmod}"
54 chownprog="${CHOWNPROG-chown}"
55 chgrpprog="${CHGRPPROG-chgrp}"
56 stripprog="${STRIPPROG-strip}"
57 rmprog="${RMPROG-rm}"
58 mkdirprog="${MKDIRPROG-mkdir}"
59
60 chmodcmd="$chmodprog 0755"
61 chowncmd=
62 chgrpcmd=
63 stripcmd=
64 rmcmd="$rmprog -f"
65 mvcmd="$mvprog"
66 src=
67 dst=
68 dir_arg=
69 dstarg=
70 no_target_directory=
71
72 usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
73 or: $0 [OPTION]... SRCFILES... DIRECTORY
74 or: $0 [OPTION]... -t DIRECTORY SRCFILES...
75 or: $0 [OPTION]... -d DIRECTORIES...
76
77 In the 1st form, copy SRCFILE to DSTFILE.
78 In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
79 In the 4th, create DIRECTORIES.
80
81 Options:
82 -c (ignored)
83 -d create directories instead of installing files.
84 -g GROUP $chgrpprog installed files to GROUP.
85 -m MODE $chmodprog installed files to MODE.
86 -o USER $chownprog installed files to USER.
87 -s $stripprog installed files.
88 -t DIRECTORY install into DIRECTORY.
89 -T report an error if DSTFILE is a directory.
90 --help display this help and exit.
91 --version display version info and exit.
92
93 Environment variables override the default commands:
94 CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
95 "
96
97 while test -n "$1"; do
98 case $1 in
99 -c) shift
100 continue;;
101
102 -d) dir_arg=true
103 shift
104 continue;;
105
106 -g) chgrpcmd="$chgrpprog $2"
107 shift
108 shift
109 continue;;
110
111 --help) echo "$usage"; exit $?;;
112
113 -m) chmodcmd="$chmodprog $2"
114 shift
115 shift
116 continue;;
117
118 -o) chowncmd="$chownprog $2"
119 shift
120 shift
121 continue;;
122
123 -s) stripcmd=$stripprog
124 shift
125 continue;;
126
127 -t) dstarg=$2
128 shift
129 shift
130 continue;;
131
132 -T) no_target_directory=true
133 shift
134 continue;;
135
136 --version) echo "$0 $scriptversion"; exit $?;;
137
138 *) # When -d is used, all remaining arguments are directories to create.
139 # When -t is used, the destination is already specified.
140 test -n "$dir_arg$dstarg" && break
141 # Otherwise, the last argument is the destination. Remove it from $@.
142 for arg
143 do
144 if test -n "$dstarg"; then
145 # $@ is not empty: it contains at least $arg.
146 set fnord "$@" "$dstarg"
147 shift # fnord
148 fi
149 shift # arg
150 dstarg=$arg
151 done
152 break;;
153 esac
154 done
155
156 if test -z "$1"; then
157 if test -z "$dir_arg"; then
158 echo "$0: no input file specified." >&2
159 exit 1
160 fi
161 # It's OK to call `install-sh -d' without argument.
162 # This can happen when creating conditional directories.
163 exit 0
164 fi
165
166 for src
167 do
168 # Protect names starting with `-'.
169 case $src in
170 -*) src=./$src ;;
171 esac
172
173 if test -n "$dir_arg"; then
174 dst=$src
175 src=
176
177 if test -d "$dst"; then
178 mkdircmd=:
179 chmodcmd=
180 else
181 mkdircmd=$mkdirprog
182 fi
183 else
184 # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
185 # might cause directories to be created, which would be especially bad
186 # if $src (and thus $dsttmp) contains '*'.
187 if test ! -f "$src" && test ! -d "$src"; then
188 echo "$0: $src does not exist." >&2
189 exit 1
190 fi
191
192 if test -z "$dstarg"; then
193 echo "$0: no destination specified." >&2
194 exit 1
195 fi
196
197 dst=$dstarg
198 # Protect names starting with `-'.
199 case $dst in
200 -*) dst=./$dst ;;
201 esac
202
203 # If destination is a directory, append the input filename; won't work
204 # if double slashes aren't ignored.
205 if test -d "$dst"; then
206 if test -n "$no_target_directory"; then
207 echo "$0: $dstarg: Is a directory" >&2
208 exit 1
209 fi
210 dst=$dst/`basename "$src"`
211 fi
212 fi
213
214 # This sed command emulates the dirname command.
215 dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
216
217 # Make sure that the destination directory exists.
218
219 # Skip lots of stat calls in the usual case.
220 if test ! -d "$dstdir"; then
221 defaultIFS='
222 '
223 IFS="${IFS-$defaultIFS}"
224
225 oIFS=$IFS
226 # Some sh's can't handle IFS=/ for some reason.
227 IFS='%'
228 set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
229 shift
230 IFS=$oIFS
231
232 pathcomp=
233
234 while test $# -ne 0 ; do
235 pathcomp=$pathcomp$1
236 shift
237 if test ! -d "$pathcomp"; then
238 $mkdirprog "$pathcomp"
239 # mkdir can fail with a `File exist' error in case several
240 # install-sh are creating the directory concurrently. This
241 # is OK.
242 test -d "$pathcomp" || exit
243 fi
244 pathcomp=$pathcomp/
245 done
246 fi
247
248 if test -n "$dir_arg"; then
249 $doit $mkdircmd "$dst" \
250 && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
251 && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
252 && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
253 && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
254
255 else
256 dstfile=`basename "$dst"`
257
258 # Make a couple of temp file names in the proper directory.
259 dsttmp=$dstdir/_inst.$$_
260 rmtmp=$dstdir/_rm.$$_
261
262 # Trap to clean up those temp files at exit.
263 trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
264 trap '(exit $?); exit' 1 2 13 15
265
266 # Copy the file name to the temp name.
267 $doit $cpprog "$src" "$dsttmp" &&
268
269 # and set any options; do chmod last to preserve setuid bits.
270 #
271 # If any of these fail, we abort the whole thing. If we want to
272 # ignore errors from any of these, just make sure not to ignore
273 # errors from the above "$doit $cpprog $src $dsttmp" command.
274 #
275 { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
276 && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
277 && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
278 && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&
279
280 # Now rename the file to the real destination.
281 { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \
282 || {
283 # The rename failed, perhaps because mv can't rename something else
284 # to itself, or perhaps because mv is so ancient that it does not
285 # support -f.
286
287 # Now remove or move aside any old file at destination location.
288 # We try this two ways since rm can't unlink itself on some
289 # systems and the destination file might be busy for other
290 # reasons. In this case, the final cleanup might fail but the new
291 # file should still install successfully.
292 {
293 if test -f "$dstdir/$dstfile"; then
294 $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
295 || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
296 || {
297 echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
298 (exit 1); exit 1
299 }
300 else
301 :
302 fi
303 } &&
304
305 # Now rename the file to the real destination.
306 $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
307 }
308 }
309 fi || { (exit 1); exit 1; }
310 done
311
312 # The final little trick to "correctly" pass the exit status to the exit trap.
313 {
314 (exit 0); exit 0
315 }
316
317 # Local variables:
318 # eval: (add-hook 'write-file-hooks 'time-stamp)
319 # time-stamp-start: "scriptversion="
320 # time-stamp-format: "%:y-%02m-%02d.%02H"
321 # time-stamp-end: "$"
322 # End:
0 AM_CPPFLAGS = \
1 -I$(top_srcdir)
2
3 noinst_LIBRARIES = liblout.a
4
5 liblout_a_SOURCES = \
6 container.cc \
7 container.hh \
8 debug.hh \
9 identity.cc \
10 identity.hh \
11 misc.cc \
12 misc.hh \
13 object.cc \
14 object.hh \
15 signal.cc \
16 signal.hh \
17 msg.h
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "container.hh"
22 #include "misc.hh"
23
24 namespace lout {
25
26 using namespace object;
27
28 namespace container {
29
30 namespace untyped {
31
32 // -------------
33 // Iterator
34 // -------------
35
36 Iterator::Iterator()
37 {
38 impl = NULL;
39 }
40
41 Iterator::Iterator(const Iterator &it2)
42 {
43 impl = it2.impl;
44 if (impl)
45 impl->ref();
46 }
47
48 Iterator::Iterator(Iterator &it2)
49 {
50 impl = it2.impl;
51 if (impl)
52 impl->ref();
53 }
54
55 Iterator &Iterator::operator=(const Iterator &it2)
56 {
57 if (impl)
58 impl->unref();
59 impl = it2.impl;
60 if (impl)
61 impl->ref();
62 return *this;
63 }
64
65 Iterator &Iterator::operator=(Iterator &it2)
66 {
67 if (impl)
68 impl->unref();
69 impl = it2.impl;
70 if (impl)
71 impl->ref();
72 return *this;
73 }
74
75 Iterator::~Iterator()
76 {
77 if (impl)
78 impl->unref();
79 }
80
81 // ----------------
82 // Collection
83 // ----------------
84
85 void Collection::intoStringBuffer(misc::StringBuffer *sb)
86 {
87 sb->append("{ ");
88 bool first = true;
89 for (Iterator it = iterator(); it.hasNext(); ) {
90 if (!first)
91 sb->append(", ");
92 it.getNext()->intoStringBuffer(sb);
93 first = false;
94 }
95 sb->append(" }");
96 }
97
98 // ------------
99 // Vector
100 // ------------
101
102 Vector::Vector(int initSize, bool ownerOfObjects)
103 {
104 numAlloc = initSize == 0 ? 1 : initSize;
105 this->ownerOfObjects = ownerOfObjects;
106 numElements = 0;
107 array = (Object**)malloc(numAlloc * sizeof(Object*));
108 }
109
110 Vector::~Vector()
111 {
112 clear();
113 free(array);
114 }
115
116 void Vector::put(Object *newElement, int newPos)
117 {
118 if (newPos == -1)
119 newPos = numElements;
120
121 // Old entry is overwritten.
122 if (newPos < numElements) {
123 if (ownerOfObjects && array[newPos])
124 delete array[newPos];
125 }
126
127 // Allocated memory has to be increased.
128 if (newPos >= numAlloc) {
129 while (newPos >= numAlloc)
130 numAlloc *= 2;
131 array = (Object**)realloc(array, numAlloc * sizeof(Object*));
132 }
133
134 // Insert NULL's into possible gap before new position.
135 for (int i = numElements; i < newPos; i++)
136 array[i] = NULL;
137
138 if (newPos >= numElements)
139 numElements = newPos + 1;
140
141 array[newPos] = newElement;
142 }
143
144 void Vector::clear()
145 {
146 if (ownerOfObjects) {
147 for (int i = 0; i < numElements; i++)
148 if (array[i])
149 delete array[i];
150 }
151
152 numElements = 0;
153 }
154
155 void Vector::insert(Object *newElement, int pos)
156 {
157 if (pos >= numElements)
158 put(newElement, pos);
159 else {
160 numElements++;
161
162 // Allocated memory has to be increased.
163 if (numElements >= numAlloc) {
164 numAlloc *= 2;
165 array = (Object**)realloc(array, numAlloc * sizeof(Object*));
166 }
167
168 for (int i = numElements - 1; i > pos; i--)
169 array[i] = array[i - 1];
170
171 array[pos] = newElement;
172 }
173 }
174
175 void Vector::remove(int pos)
176 {
177 if (ownerOfObjects && array[pos])
178 delete array[pos];
179
180 for (int i = pos + 1; i < numElements; i++)
181 array[i - 1] = array[i];
182
183 numElements--;
184 }
185
186 /**
187 * Sort the elements in the vector. Assumes that all elements are Comparable's.
188 */
189 void Vector::sort()
190 {
191 qsort(array, numElements, sizeof(Object*), misc::Comparable::compareFun);
192 }
193
194
195 /**
196 * \bug Not implemented.
197 */
198 Collection0::AbstractIterator* Vector::createIterator()
199 {
200 return NULL;
201 }
202
203 // ------------
204 // List
205 // ------------
206
207 List::List(bool ownerOfObjects)
208 {
209 this->ownerOfObjects = ownerOfObjects;
210 first = last = NULL;
211 numElements = 0;
212 }
213
214 List::~List()
215 {
216 clear();
217 }
218
219 void List::clear()
220 {
221 while (first) {
222 if (ownerOfObjects && first->object)
223 delete first->object;
224 Node *next = first->next;
225 delete first;
226 first = next;
227 }
228
229 last = NULL;
230 numElements = 0;
231 }
232
233 void List::append(Object *element)
234 {
235 Node *newLast = new Node;
236 newLast->next = NULL;
237 newLast->object = element;
238
239 if (last) {
240 last->next = newLast;
241 last = newLast;
242 } else
243 first = last = newLast;
244
245 numElements++;
246 }
247
248
249 bool List::remove0(Object *element, bool compare, bool doNotDeleteAtAll)
250 {
251 Node *beforeCur, *cur;
252
253 for (beforeCur = NULL, cur = first; cur; beforeCur = cur, cur = cur->next) {
254 if (compare ?
255 (cur->object && element->equals(cur->object)) :
256 element == cur->object) {
257 if (beforeCur) {
258 beforeCur->next = cur->next;
259 if (cur->next == NULL)
260 last = beforeCur;
261 } else {
262 first = cur->next;
263 if (first == NULL)
264 last = NULL;
265 }
266
267 if (ownerOfObjects && cur->object && !doNotDeleteAtAll)
268 delete cur->object;
269 delete cur;
270
271 numElements--;
272 return true;
273 }
274 }
275
276 return false;
277 }
278
279 Object *List::ListIterator::getNext()
280 {
281 Object *object;
282
283 if (current) {
284 object = current->object;
285 current = current->next;
286 } else
287 object = NULL;
288
289 return object;
290 }
291
292 bool List::ListIterator::hasNext()
293 {
294 return current != NULL;
295 }
296
297 Collection0::AbstractIterator* List::createIterator()
298 {
299 return new ListIterator(first);
300 }
301
302
303 // ---------------
304 // HashTable
305 // ---------------
306
307 HashTable::HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize)
308 {
309 this->ownerOfKeys = ownerOfKeys;
310 this->ownerOfValues = ownerOfValues;
311 this->tableSize = tableSize;
312
313 table = new Node*[tableSize];
314 for (int i = 0; i < tableSize; i++)
315 table[i] = NULL;
316 }
317
318 HashTable::~HashTable()
319 {
320 for (int i = 0; i < tableSize; i++) {
321 Node *n1 = table[i];
322 while (n1) {
323 Node *n2 = n1->next;
324
325 if (ownerOfValues && n1->value)
326 delete n1->value;
327 if (ownerOfKeys)
328 delete n1->key;
329 delete n1;
330
331 n1 = n2;
332 }
333 }
334
335 delete[] table;
336 }
337
338 void HashTable::intoStringBuffer(misc::StringBuffer *sb)
339 {
340 sb->append("{ ");
341
342 bool first = true;
343 for (int i = 0; i < tableSize; i++) {
344 for (Node *node = table[i]; node; node = node->next) {
345 if (!first)
346 sb->append(", ");
347 node->key->intoStringBuffer(sb);
348 sb->append(" => ");
349 node->value->intoStringBuffer(sb);
350 first = false;
351 }
352 }
353
354 sb->append(" }");
355 }
356
357 void HashTable::put(Object *key, Object *value)
358 {
359 int h = calcHashValue(key);
360 Node *n = new Node;
361 n->key = key;
362 n->value = value;
363 n->next = table[h];
364 table[h] = n;
365 }
366
367 bool HashTable::contains(Object *key)
368 {
369 int h = calcHashValue(key);
370 for (Node *n = table[h]; n; n = n->next) {
371 if (key->equals(n->key))
372 return true;
373 }
374
375 return false;
376 }
377
378 Object *HashTable::get(Object *key)
379 {
380 int h = calcHashValue(key);
381 for (Node *n = table[h]; n; n = n->next) {
382 if (key->equals(n->key))
383 return n->value;
384 }
385
386 return NULL;
387 }
388
389 bool HashTable::remove(Object *key)
390 {
391 int h = calcHashValue(key);
392 Node *last, *cur;
393
394 for (last = NULL, cur = table[h]; cur; last = cur, cur = cur->next) {
395 if (key->equals(cur->key)) {
396 if (last)
397 last->next = cur->next;
398 else
399 table[h] = cur->next;
400
401 if (ownerOfValues && cur->value)
402 delete cur->value;
403 if (ownerOfKeys)
404 delete cur->key;
405 delete cur;
406
407 return true;
408 }
409 }
410
411 return false;
412 }
413
414 Object *HashTable::getKey (Object *key)
415 {
416 int h = calcHashValue(key);
417 for (Node *n = table[h]; n; n = n->next) {
418 if (key->equals(n->key))
419 return n->key;
420 }
421
422 return NULL;
423 }
424
425 HashTable::HashTableIterator::HashTableIterator(HashTable *table)
426 {
427 this->table = table;
428 node = NULL;
429 pos = -1;
430 gotoNext();
431 }
432
433 void HashTable::HashTableIterator::gotoNext()
434 {
435 if (node)
436 node = node->next;
437
438 while (node == NULL) {
439 if (pos >= table->tableSize - 1)
440 return;
441 pos++;
442 node = table->table[pos];
443 }
444 }
445
446
447 Object *HashTable::HashTableIterator::getNext()
448 {
449 Object *result;
450 if (node)
451 result = node->key;
452 else
453 result = NULL;
454
455 gotoNext();
456 return result;
457 }
458
459 bool HashTable::HashTableIterator::hasNext()
460 {
461 return node != NULL;
462 }
463
464 Collection0::AbstractIterator* HashTable::createIterator()
465 {
466 return new HashTableIterator(this);
467 }
468
469 // -----------
470 // Stack
471 // -----------
472
473 Stack::Stack (bool ownerOfObjects)
474 {
475 this->ownerOfObjects = ownerOfObjects;
476 bottom = top = NULL;
477 numElements = 0;
478 }
479
480 Stack::~Stack()
481 {
482 while (top)
483 pop ();
484 }
485
486 void Stack::push (object::Object *object)
487 {
488 Node *newTop = new Node ();
489 newTop->object = object;
490 newTop->prev = top;
491
492 top = newTop;
493 if (bottom == NULL)
494 bottom = top;
495
496 numElements++;
497 }
498
499 void Stack::pushUnder (object::Object *object)
500 {
501 Node *newBottom = new Node ();
502 newBottom->object = object;
503 newBottom->prev = NULL;
504 if (bottom != NULL)
505 bottom->prev = newBottom;
506
507 bottom = newBottom;
508 if (top == NULL)
509 top = bottom;
510
511 numElements++;
512 }
513
514 void Stack::pop ()
515 {
516 Node *newTop = top->prev;
517
518 if (ownerOfObjects)
519 delete top->object;
520 delete top;
521
522 top = newTop;
523 if (top == NULL)
524 bottom = NULL;
525
526 numElements--;
527 }
528
529 Object *Stack::StackIterator::getNext()
530 {
531 Object *object;
532
533 if (current) {
534 object = current->object;
535 current = current->prev;
536 } else
537 object = NULL;
538
539 return object;
540 }
541
542 bool Stack::StackIterator::hasNext()
543 {
544 return current != NULL;
545 }
546
547 Collection0::AbstractIterator* Stack::createIterator()
548 {
549 return new StackIterator(top);
550 }
551
552 } // namespace untyped
553
554 } // namespace container
555
556 } // namespace lout
0 #ifndef __LOUT_CONTAINER_HH_
1 #define __LOUT_CONTAINER_HH_
2
3 #include "object.hh"
4
5 /**
6 * \brief This namespace contains a framework for container classes, which
7 * members are instances of object::Object.
8 *
9 * A common problem in languanges without garbage collection is, where the
10 * children belong to, and so, who is responsible to delete them (instantiation
11 * is always done by the caller). This information is here told to the
12 * collections, each container has a constructor with the parameter
13 * "ownerOfObjects" (HashTable has two such parameters, for keys and values).
14 *
15 * \sa container::untyped, container::typed
16 */
17 namespace lout {
18
19 namespace container {
20
21 /**
22 * \brief The container classes defined here contain instances of
23 * object::Object.
24 *
25 * Different sub-classes may be mixed, and you have to care about casting,
26 * there is no type-safety.
27 */
28 namespace untyped {
29
30 /**
31 * \brief ...
32 */
33 class Collection0: public object::Object
34 {
35 friend class Iterator;
36
37 protected:
38 /**
39 * \brief The base class for all iterators, as created by
40 * container::untyped::Collection::createIterator.
41 */
42 class AbstractIterator: public object::Object
43 {
44 private:
45 int refcount;
46
47 public:
48 AbstractIterator() { refcount = 1; }
49
50 void ref () { refcount++; }
51 void unref () { refcount--; if (refcount == 0) delete this; }
52
53 virtual bool hasNext () = 0;
54 virtual Object *getNext () = 0;
55 };
56
57 protected:
58 virtual AbstractIterator* createIterator() = 0;
59 };
60
61 /**
62 * \brief This is a small wrapper for AbstractIterator, which may be used
63 * directly, not as a pointer, to makes memory management simpler.
64 */
65 class Iterator
66 {
67 friend class Collection;
68
69 private:
70 Collection0::AbstractIterator *impl;
71
72 // Should not instantiated directly.
73 inline Iterator(Collection0::AbstractIterator *impl) { this->impl = impl; }
74
75 public:
76 Iterator();
77 Iterator(const Iterator &it2);
78 Iterator(Iterator &it2);
79 ~Iterator();
80 Iterator &operator=(const Iterator &it2);
81 Iterator &operator=(Iterator &it2);
82
83 inline bool hasNext() { return impl->hasNext(); }
84 inline object::Object *getNext() { return impl->getNext(); }
85 };
86
87 /**
88 * \brief Abstract base class for all container objects in container::untyped.
89 */
90 class Collection: public Collection0
91 {
92 public:
93 void intoStringBuffer(misc::StringBuffer *sb);
94 inline Iterator iterator() { Iterator it(createIterator()); return it; }
95 };
96
97
98 /**
99 * \brief Container, which is implemented by an array, which is
100 * dynamically resized.
101 */
102 class Vector: public Collection
103 {
104 private:
105 object::Object **array;
106 int numAlloc, numElements;
107 bool ownerOfObjects;
108
109 protected:
110 AbstractIterator* createIterator();
111
112 public:
113 Vector(int initSize, bool ownerOfObjects);
114 ~Vector();
115
116 void put(object::Object *newElement, int newPos = -1);
117 void insert(object::Object *newElement, int pos);
118 void remove(int pos);
119 inline object::Object *get(int pos)
120 { return (pos >= 0 && pos < numElements) ? array[pos] : NULL; }
121 inline int size() { return numElements; }
122 void clear();
123 void sort();
124 };
125
126
127 /**
128 * \brief A single-linked list.
129 */
130 class List: public Collection
131 {
132 friend class ListIterator;
133
134 private:
135 struct Node
136 {
137 public:
138 object::Object *object;
139 Node *next;
140 };
141
142 class ListIterator: public AbstractIterator
143 {
144 private:
145 List::Node *current;
146 public:
147 ListIterator(List::Node *node) { current = node; }
148 bool hasNext();
149 Object *getNext();
150 };
151
152 Node *first, *last;
153 bool ownerOfObjects;
154 int numElements;
155
156 bool remove0(object::Object *element, bool compare, bool doNotDeleteAtAll);
157
158 protected:
159 AbstractIterator* createIterator();
160
161 public:
162 List(bool ownerOfObjects);
163 ~List();
164
165 void clear();
166 void append(object::Object *element);
167 inline bool removeRef(object::Object *element)
168 { return remove0(element, false, false); }
169 inline bool remove(object::Object *element)
170 { return remove0(element, true, false); }
171 inline bool detachRef(object::Object *element)
172 { return remove0(element, false, true); }
173 inline int size() { return numElements; }
174 inline bool isEmpty() { return numElements == 0; }
175 inline object::Object *getFirst() { return first->object; }
176 inline object::Object *getLast() { return last->object; }
177 };
178
179
180 /**
181 * \brief A hash table.
182 */
183 class HashTable: public Collection
184 {
185 friend class HashTableIterator;
186
187 private:
188 struct Node
189 {
190 object::Object *key, *value;
191 Node *next;
192 };
193
194 class HashTableIterator: public Collection0::AbstractIterator
195 {
196 private:
197 HashTable *table;
198 HashTable::Node *node;
199 int pos;
200
201 void gotoNext();
202
203 public:
204 HashTableIterator(HashTable *table);
205 bool hasNext();
206 Object *getNext();
207 };
208
209 Node **table;
210 int tableSize;
211 bool ownerOfKeys, ownerOfValues;
212
213 private:
214 inline int calcHashValue(object::Object *key)
215 {
216 return abs(key->hashValue()) % tableSize;
217 }
218
219 protected:
220 AbstractIterator* createIterator();
221
222 public:
223 HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251);
224 ~HashTable();
225
226 void intoStringBuffer(misc::StringBuffer *sb);
227
228 void put (object::Object *key, object::Object *value);
229 bool contains (object::Object *key);
230 Object *get (object::Object *key);
231 bool remove (object::Object *key);
232 Object *getKey (Object *key);
233 };
234
235 /**
236 * \brief A stack (LIFO).
237 *
238 * Note that the iterator returns all elements in the reversed order they have
239 * been put on the stack.
240 */
241 class Stack: public Collection
242 {
243 friend class StackIterator;
244
245 private:
246 class Node
247 {
248 public:
249 object::Object *object;
250 Node *prev;
251 };
252
253 class StackIterator: public AbstractIterator
254 {
255 private:
256 Stack::Node *current;
257 public:
258 StackIterator(Stack::Node *node) { current = node; }
259 bool hasNext();
260 Object *getNext();
261 };
262
263 Node *bottom, *top;
264 bool ownerOfObjects;
265 int numElements;
266
267 protected:
268 AbstractIterator* createIterator();
269
270 public:
271 Stack (bool ownerOfObjects);
272 ~Stack();
273
274 void push (object::Object *object);
275 void pushUnder (object::Object *object);
276 inline object::Object *getTop () { return top ? top->object : NULL; }
277 void pop ();
278 inline int size() { return numElements; }
279 };
280
281 } // namespace untyped
282
283 /**
284 * \brief This namespace provides thin wrappers, implemented as C++ templates,
285 * to gain type-safety.
286 *
287 * Notice, that nevertheless, all objects still have to be instances of
288 * object::Object.
289 */
290 namespace typed {
291
292 template <class T> class Collection;
293
294 /**
295 * \brief Typed version of container::untyped::Iterator.
296 */
297 template <class T> class Iterator
298 {
299 friend class Collection<T>;
300
301 private:
302 untyped::Iterator base;
303
304 public:
305 inline Iterator() { }
306 inline Iterator(const Iterator<T> &it2) { this->base = it2.base; }
307 inline Iterator(Iterator<T> &it2) { this->base = it2.base; }
308 ~Iterator() { }
309 inline Iterator &operator=(const Iterator<T> &it2)
310 { this->base = it2.base; return *this; }
311 inline Iterator &operator=(Iterator<T> &it2)
312 { this->base = it2.base; return *this; }
313
314 inline bool hasNext() { return this->base.hasNext(); }
315 inline T *getNext() { return (T*)this->base.getNext(); }
316 };
317
318 /**
319 * \brief Abstract base class template for all container objects in
320 * container::typed.
321 *
322 * Actually, a wrapper for container::untyped::Collection.
323 */
324 template <class T> class Collection: public object::Object
325 {
326 protected:
327 untyped::Collection *base;
328
329 public:
330 void intoStringBuffer(misc::StringBuffer *sb)
331 { this->base->intoStringBuffer(sb); }
332
333 inline Iterator<T> iterator() {
334 Iterator<T> it; it.base = this->base->iterator(); return it; }
335 };
336
337
338 /**
339 * \brief Typed version of container::untyped::Vector.
340 */
341 template <class T> class Vector: public Collection <T>
342 {
343 public:
344 inline Vector(int initSize, bool ownerOfObjects) {
345 this->base = new untyped::Vector(initSize, ownerOfObjects); }
346 ~Vector() { delete this->base; }
347
348 inline void put(T *newElement, int newPos = -1)
349 { ((untyped::Vector*)this->base)->put(newElement, newPos); }
350 inline void insert(T *newElement, int pos)
351 { ((untyped::Vector*)this->base)->insert(newElement, pos); }
352 inline void remove(int pos) { ((untyped::Vector*)this->base)->remove(pos); }
353 inline T *get(int pos)
354 { return (T*)((untyped::Vector*)this->base)->get(pos); }
355 inline int size() { return ((untyped::Vector*)this->base)->size(); }
356 inline void clear() { ((untyped::Vector*)this->base)->clear(); }
357 inline void sort() { ((untyped::Vector*)this->base)->sort(); }
358 };
359
360
361 /**
362 * \brief Typed version of container::untyped::List.
363 */
364 template <class T> class List: public Collection <T>
365 {
366 public:
367 inline List(bool ownerOfObjects)
368 { this->base = new untyped::List(ownerOfObjects); }
369 ~List() { delete this->base; }
370
371 inline void clear() { ((untyped::List*)this->base)->clear(); }
372 inline void append(T *element)
373 { ((untyped::List*)this->base)->append(element); }
374 inline bool removeRef(T *element) {
375 return ((untyped::List*)this->base)->removeRef(element); }
376 inline bool remove(T *element) {
377 return ((untyped::List*)this->base)->remove(element); }
378 inline bool detachRef(T *element) {
379 return ((untyped::List*)this->base)->detachRef(element); }
380
381 inline int size() { return ((untyped::List*)this->base)->size(); }
382 inline bool isEmpty()
383 { return ((untyped::List*)this->base)->isEmpty(); }
384 inline T *getFirst()
385 { return (T*)((untyped::List*)this->base)->getFirst(); }
386 inline T *getLast()
387 { return (T*)((untyped::List*)this->base)->getLast(); }
388 };
389
390
391 /**
392 * \brief Typed version of container::untyped::HashTable.
393 */
394 template <class K, class V> class HashTable: public Collection <K>
395 {
396 public:
397 inline HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251)
398 { this->base = new untyped::HashTable(ownerOfKeys, ownerOfValues,
399 tableSize); }
400 ~HashTable() { delete this->base; }
401
402 inline void put(K *key, V *value)
403 { return ((untyped::HashTable*)this->base)->put(key, value); }
404 inline bool contains(K *key)
405 { return ((untyped::HashTable*)this->base)->contains(key); }
406 inline V *get(K *key)
407 { return (V*)((untyped::HashTable*)this->base)->get(key); }
408 inline bool remove(K *key)
409 { return ((untyped::HashTable*)this->base)->remove(key); }
410 inline K *getKey(K *key)
411 { return (K*)((untyped::HashTable*)this->base)->getKey(key); }
412 };
413
414 /**
415 * \brief Typed version of container::untyped::Stack.
416 */
417 template <class T> class Stack: public Collection <T>
418 {
419 public:
420 inline Stack (bool ownerOfObjects)
421 { this->base = new untyped::Stack (ownerOfObjects); }
422 ~Stack() { delete this->base; }
423
424 inline void push (T *object) {
425 ((untyped::Stack*)this->base)->push (object); }
426 inline void pushUnder (T *object)
427 { ((untyped::Stack*)this->base)->pushUnder (object); }
428 inline T *getTop ()
429 { return (T*)((untyped::Stack*)this->base)->getTop (); }
430 inline void pop () { ((untyped::Stack*)this->base)->pop (); }
431 inline int size() { return ((untyped::Stack*)this->base)->size(); }
432 };
433
434 } // namespace untyped
435
436 } // namespace container
437
438 } // namespace lout
439
440 #endif // __LOUT_CONTAINER_HH_
0 #ifndef __LOUT_DEBUG_H__
1 #define __LOUT_DEBUG_H__
2
3 /*
4 * Simple debug messages. Add:
5 *
6 * #define DEBUG_LEVEL <n>
7 * #include "debug.h"
8 *
9 * to the file you are working on, or let DEBUG_LEVEL undefined to
10 * disable all messages. A higher level denotes a greater importance
11 * of the message.
12 */
13
14 #include <stdio.h>
15
16 #define D_STMT_START do
17 #define D_STMT_END while (0)
18
19 # ifdef DEBUG_LEVEL
20 # define DEBUG_MSG(level, ...) \
21 D_STMT_START { \
22 if (DEBUG_LEVEL && (level) >= DEBUG_LEVEL) \
23 printf(__VA_ARGS__); \
24 } D_STMT_END
25 # else
26 # define DEBUG_MSG(level, ...)
27 # endif /* DEBUG_LEVEL */
28
29
30
31 /*
32 * Following is experimental, and will be explained soon.
33 */
34
35 #ifdef DBG_RTFL
36
37 #include <unistd.h>
38 #include <stdio.h>
39
40 #define DBG_MSG(obj, aspect, prio, msg) \
41 D_STMT_START { \
42 printf ("[rtfl]%s:%d:%d:msg:%p:%s:%d:%s\n", \
43 __FILE__, __LINE__, getpid(), obj, aspect, prio, msg); \
44 fflush (stdout); \
45 } D_STMT_END
46
47 #define DBG_MSGF(obj, aspect, prio, fmt, ...) \
48 D_STMT_START { \
49 printf ("[rtfl]%s:%d:%d:msg:%p:%s:%d:" fmt "\n", \
50 __FILE__, __LINE__, getpid(), obj, aspect, prio, __VA_ARGS__); \
51 fflush (stdout); \
52 } D_STMT_END
53
54 #define DBG_MSG_START(obj) \
55 D_STMT_START { \
56 printf ("[rtfl]%s:%d:%d:msg-start:%p\n", \
57 __FILE__, __LINE__, getpid(), obj); \
58 fflush (stdout); \
59 } D_STMT_END
60
61 #define DBG_MSG_END(obj) \
62 D_STMT_START { \
63 printf ("[rtfl]%s:%d:%d:msg-end:%p\n", \
64 __FILE__, __LINE__, getpid(), obj); \
65 fflush (stdout); \
66 } D_STMT_END
67
68 #define DBG_OBJ_CREATE(obj, klass) \
69 D_STMT_START { \
70 printf ("[rtfl]%s:%d:%d:obj-create:%p:%s\n", \
71 __FILE__, __LINE__, getpid(), obj, klass); \
72 fflush (stdout); \
73 } D_STMT_END
74
75 #define DBG_OBJ_ASSOC(child, parent) \
76 D_STMT_START { \
77 printf ("[rtfl]%s:%d:%d:obj-assoc:%p:%p\n", \
78 __FILE__, __LINE__, getpid(), child, parent); \
79 fflush (stdout); \
80 } D_STMT_END
81
82 #define DBG_OBJ_SET_NUM(obj, var, val) \
83 D_STMT_START { \
84 printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%d\n", \
85 __FILE__, __LINE__, getpid(), obj, var, val); \
86 fflush (stdout); \
87 } D_STMT_END
88
89 #define DBG_OBJ_SET_STR(obj, var, val) \
90 D_STMT_START { \
91 printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%s\n", \
92 __FILE__, __LINE__, getpid(), obj, var, val); \
93 fflush (stdout); \
94 } D_STMT_END
95
96 #define DBG_OBJ_SET_PTR(obj, var, val) \
97 D_STMT_START { \
98 printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%p\n", \
99 __FILE__, __LINE__, getpid(), obj, var, val); \
100 fflush (stdout); \
101 } D_STMT_END
102
103 #define DBG_OBJ_ARRSET_NUM(obj, var, ind, val) \
104 D_STMT_START { \
105 printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%d\n", \
106 __FILE__, __LINE__, getpid(), obj, ind, val); \
107 fflush (stdout); \
108 } D_STMT_END
109
110 #define DBG_OBJ_ARRSET_STR(obj, var, ind, val) \
111 D_STMT_START { \
112 printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%s\n", \
113 __FILE__, __LINE__, getpid(), obj, ind, val); \
114 fflush (stdout); \
115 } D_STMT_END
116
117 #define DBG_OBJ_ARRSET_PTR(obj, var, ind, val) \
118 D_STMT_START { \
119 printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%p\n", \
120 __FILE__, __LINE__, getpid(), obj, ind, val); \
121 fflush (stdout); \
122 } D_STMT_END
123
124 #define DBG_OBJ_COLOR(klass, color) \
125 D_STMT_START { \
126 printf ("[rtfl]%s:%d:%d:obj-color:%s:%s\n", \
127 __FILE__, __LINE__, getpid(), klass, color); \
128 fflush (stdout); \
129 } D_STMT_END
130
131 #else /* DBG_RTFL */
132
133 #define DBG_MSG(obj, aspect, prio, msg)
134 #define DBG_MSGF(obj, aspect, prio, fmt, ...)
135 #define DBG_MSG_START(obj)
136 #define DBG_MSG_END(obj)
137 #define DBG_OBJ_CREATE(obj, klass)
138 #define DBG_OBJ_ASSOC(child, parent)
139 #define DBG_OBJ_SET_NUM(obj, var, val)
140 #define DBG_OBJ_SET_STR(obj, var, val)
141 #define DBG_OBJ_SET_PTR(obj, var, val)
142 #define DBG_OBJ_ARRSET_NUM(obj, var, ind, val)
143 #define DBG_OBJ_ARRSET_STR(obj, var, ind, val)
144 #define DBG_OBJ_ARRSET_PTR(obj, var, ind, val)
145 #define DBG_OBJ_COLOR(klass, color)
146
147 #endif /* DBG_RTFL */
148
149 #endif /* __LOUT_DEBUG_H__ */
150
151
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "identity.hh"
22
23 #include <stdio.h>
24
25 namespace lout {
26 namespace identity {
27
28 // ------------------------
29 // IdentifiableObject
30 // ------------------------
31
32 using namespace object;
33 using namespace container::typed;
34
35 IdentifiableObject::Class::Class (IdentifiableObject::Class *parent, int id,
36 const char *className)
37 {
38 this->parent = parent;
39 this->id = id;
40 this->className = className;
41 }
42
43 HashTable <ConstString, IdentifiableObject::Class>
44 *IdentifiableObject::classesByName =
45 new HashTable<ConstString, IdentifiableObject::Class> (true, true);
46 Vector <IdentifiableObject::Class> *IdentifiableObject::classesById =
47 new Vector <IdentifiableObject::Class> (16, false);
48 IdentifiableObject::Class *IdentifiableObject::currentlyConstructedClass;
49
50 IdentifiableObject::IdentifiableObject ()
51 {
52 currentlyConstructedClass = NULL;
53 }
54
55 void IdentifiableObject::intoStringBuffer(misc::StringBuffer *sb)
56 {
57 sb->append("<instance of ");
58 sb->append(getClassName());
59 sb->append(">");
60 }
61
62 /**
63 * \brief This method must be called in the constructor for the sub class.
64 * See class comment for details.
65 */
66 void IdentifiableObject::registerName (const char *className, int *classId)
67 {
68 ConstString str (className);
69 Class *klass = classesByName->get (&str);
70 if (klass == NULL) {
71 klass = new Class (currentlyConstructedClass, classesById->size (),
72 className);
73 ConstString *key = new ConstString (className);
74 classesByName->put (key, klass);
75 classesById->put (klass);
76 *classId = klass->id;
77 }
78
79 this->classId = klass->id;
80 currentlyConstructedClass = klass;
81 }
82
83 /**
84 * \brief Returns, whether this class is an instance of the class, given by
85 * \em otherClassId, or of a sub class of this class.
86 */
87 bool IdentifiableObject::instanceOf (int otherClassId)
88 {
89 if (otherClassId == -1)
90 // Other class has not been registered yet, while it should have been,
91 // if this class is an instance of it or of a sub-class.
92 return false;
93
94 Class *otherClass = classesById->get (otherClassId);
95
96 if (otherClass == NULL) {
97 fprintf (stderr,
98 "WARNING: Something got wrong here, it seems that a "
99 "CLASS_ID was not initialized properly.\n");
100 return false;
101 }
102
103 for (Class *klass = classesById->get (classId); klass != NULL;
104 klass = klass->parent) {
105 if (klass == otherClass)
106 return true;
107 }
108
109 return false;
110 }
111
112 } // namespace identity
113 } // namespace lout
0 #ifndef __LOUT_OBJECTX_HH__
1 #define __LOUT_OBJECTX_HH__
2
3 #include "object.hh"
4 #include "container.hh"
5 #include "signal.hh"
6
7 /**
8 * \brief Some stuff to identify classes of objects at run-time.
9 */
10
11 namespace lout {
12
13 namespace identity {
14
15 /**
16 * \brief Instances of classes, which are sub classes of this class, may
17 * be identified at run-time.
18 *
19 * <h3>Testing the class</h3>
20 *
21 * Since e.g. dw::Textblock is a sub class of IdentifiableObject, and
22 * implemented in the correct way (as described below), for any given
23 * IdentifiableObject the following test can be done:
24 *
25 * \code
26 * identity::IdentifiableObject *o;
27 * // ...
28 * bool isATextblock = o->instanceOf(dw::Textblock::CLASS_ID);
29 * \endcode
30 *
31 * \em isATextblock is true, when \em o is an instance of dw::Textblock,
32 * or of a sub class of dw::Textblock. Otherwise, \em isATextblock is false.
33 *
34 * It is also possible to get the class identifier of an
35 * identity::IdentifiableObject, e.g.
36 *
37 * \code
38 * bool isOnlyATextblock = o->getClassId() == dw::Textblock::CLASS_ID;
39 * \endcode
40 *
41 * would result in true, if o is an instance of dw::Textblock, but not an
42 * instance of a sub class of dw::Textblock.
43 *
44 * <h3>Defining Sub Classes</h3>
45 *
46 * Each direct or indirect sub class of IdentifiableObject must
47 *
48 * <ul>
49 * <li> add a static int CLASS_ID with -1 as initial value, and
50 * <li> call registerName (\em name, &CLASS_ID) in the constructor, where
51 * \em name should be unique, e.g. the fully qualified class name.
52 * </ul>
53 *
54 * After this, <i>class</i>::CLASS_ID refers to a number, which denotes the
55 * class. (If this is still -1, since the class has not yet been instantiated,
56 * any test will fail, which is correct.)
57 *
58 * <h3>Notes on implementation</h3>
59 *
60 * If there are some classes like this:
61 *
62 * \dot
63 * digraph G {
64 * node [shape=record, fontname=Helvetica, fontsize=10];
65 * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
66 * labelfontsize=10, color="#404040", labelfontcolor="#000080"];
67 * fontname=Helvetica; fontsize=10;
68 * IdentifiableObject [color="#a0a0a0"];
69 * A;
70 * B [color="#a0a0a0"];
71 * C;
72 * IdentifiableObject -> A;
73 * IdentifiableObject -> B;
74 * B -> C;
75 * }
76 * \enddot
77 *
78 * <center>[\ref uml-legend "legend"]</center>
79 *
80 * and first, an instance of A, and then an instance of C is created, there
81 * will be the following calls of functions and constructors:
82 *
83 * <ol>
84 * <li> %IdentifiableObject ();
85 * <li> %registerName ("A", &A::CLASS_ID);
86 * <li> %IdentifiableObject ();
87 * <li> %registerName ("B", &B::CLASS_ID);
88 * <li> %registerName ("C", &C::CLASS_ID);
89 * </ol>
90 *
91 * From this, the class hierarchy above can easily constructed, and stored
92 * in identity::IdentifiableObject::classesByName and
93 * in identity::IdentifiableObject::classesById. See the code for details.
94 *
95 * N.b. Multiple inheritance is not supported, the construction of the
96 * tree would become confused.
97 */
98 class IdentifiableObject: public object::Object
99 {
100 private:
101 class Class: public object::Object
102 {
103 public:
104 Class *parent;
105 int id;
106 const char *className;
107
108 Class (Class *parent, int id, const char *className);
109 };
110
111 static container::typed::HashTable <object::ConstString,
112 Class> *classesByName;
113 static container::typed::Vector <Class> *classesById;
114 static Class *currentlyConstructedClass;
115
116 int classId;
117
118 protected:
119 void registerName (const char *className, int *classId);
120
121 public:
122 IdentifiableObject ();
123
124 virtual void intoStringBuffer(misc::StringBuffer *sb);
125
126 /**
127 * \brief Returns the class identifier.
128 *
129 * This is rarely used, rather, tests with
130 * identity::IdentifiableObject::instanceOf are done.
131 */
132 int getClassId () { return classId; }
133
134 /**
135 * \brief Return the name, under which the class of this object was
136 * registered.
137 */
138 const char *getClassName() { return classesById->get(classId)->className; }
139
140 bool instanceOf (int otherClassId);
141 };
142
143 } // namespace identity
144
145 } // namespace lout
146
147 #endif // __LOUT_OBJECTX_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "misc.hh"
22
23 #include <ctype.h>
24 #include <config.h>
25
26 #define PRGNAME PACKAGE "/" VERSION
27
28 namespace lout {
29
30 namespace misc {
31
32 const char *prgName = PRGNAME;
33
34 void init (int argc, char *argv[])
35 {
36 prgName = strdup (argv[0]);
37 }
38
39 // ----------------
40 // Comparable
41 // ----------------
42
43 Comparable::~Comparable()
44 {
45 }
46
47 /**
48 * \brief This static method may be used as compare function for qsort(3), for
49 * an array of Object* (Object*[] or Object**).
50 */
51 int Comparable::compareFun(const void *p1, const void *p2)
52 {
53 Comparable **c1 = (Comparable**)p1;
54 Comparable **c2 = (Comparable**)p2;
55 if (c1 && c2)
56 return ((*c1)->compareTo(*c2));
57 else if (c1)
58 return 1;
59 else if (c2)
60 return -1;
61 else
62 return 0;
63 }
64
65
66 // ------------------
67 // StringBuffer
68 // ------------------
69
70
71 StringBuffer::StringBuffer()
72 {
73 firstNode = lastNode = NULL;
74 numChars = 0;
75 str = NULL;
76 strValid = false;
77 }
78
79 StringBuffer::~StringBuffer()
80 {
81 clear ();
82 if (str)
83 delete[] str;
84 }
85
86 /**
87 * \brief Append a NUL-terminated string to the buffer, without copying.
88 *
89 * No copy is made, so this method should only be used in cases, where
90 * the string would otherwise be freed again. (This method may then
91 * save some CPU cycles.)
92 */
93 void StringBuffer::appendNoCopy(char *str)
94 {
95 Node *node = new Node();
96 node->data = str;
97 node->next = NULL;
98
99 if (firstNode == NULL) {
100 firstNode = node;
101 lastNode = node;
102 } else {
103 lastNode->next = node;
104 lastNode = node;
105 }
106
107 numChars += strlen(str);
108 strValid = false;
109 }
110
111 /**
112 * \brief Return a NUL-terminated strings containing all appended strings.
113 *
114 * The caller does not have to free the string, this is done in
115 * misc::StringBuffer::~StringBuffer.
116 */
117 const char *StringBuffer::getChars()
118 {
119 if (strValid)
120 return str;
121
122 if (str)
123 delete[] str;
124 str = new char[numChars + 1];
125 char *p = str;
126
127 for (Node *node = firstNode; node; node = node->next) {
128 int l = strlen(node->data);
129 memcpy(p, node->data, l * sizeof(char));
130 p += l;
131 }
132
133 *p = 0;
134 strValid = true;
135 return str;
136 }
137
138 /**
139 * \brief Remove all strings appended to the string buffer.
140 */
141 void StringBuffer::clear ()
142 {
143 Node *node, *nextNode;
144 for (node = firstNode; node; node = nextNode) {
145 nextNode = node->next;
146 free(node->data);
147 delete node;
148 }
149 firstNode = lastNode = NULL;
150 numChars = 0;
151 strValid = false;
152 }
153
154
155 // ------------
156 // BitSet
157 // ------------
158
159 BitSet::BitSet(int initBits)
160 {
161 numBytes = bytesForBits(initBits);
162 bits = (unsigned char*)malloc(numBytes * sizeof(unsigned char));
163 clear();
164 }
165
166 BitSet::~BitSet()
167 {
168 free(bits);
169 }
170
171 void BitSet::intoStringBuffer(misc::StringBuffer *sb)
172 {
173 sb->append("[");
174 for (int i = 0; i < numBytes; i++)
175 sb->append(get(i) ? "1" : "0");
176 sb->append("]");
177 }
178
179 bool BitSet::get(int i)
180 {
181 if (8 * i >= numBytes)
182 return false;
183 else
184 return bits[i / 8] & (1 << (i % 8));
185 }
186
187 void BitSet::set(int i, bool val)
188 {
189 if (8 * i >= numBytes) {
190 int newNumBytes = numBytes;
191 while (8 * i >= newNumBytes)
192 newNumBytes *= 2;
193 bits =
194 (unsigned char*)realloc(bits, newNumBytes * sizeof(unsigned char));
195 memset(bits + numBytes, 0, newNumBytes - numBytes);
196 numBytes = newNumBytes;
197 }
198
199 if (val)
200 bits[i / 8] |= (1 << (i % 8));
201 else
202 bits[i / 8] &= ~(1 << (i % 8));
203 }
204
205 void BitSet::clear()
206 {
207 memset(bits, 0, numBytes);
208 }
209
210 } // namespace misc
211
212 } // namespace lout
0 #ifndef __LOUT_MISC_HH__
1 #define __LOUT_MISC_HH__
2
3 #include <stdio.h>
4 #include <stdarg.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <assert.h>
8
9 /**
10 * \brief Miscellaneous stuff, which does not fit anywhere else.
11 *
12 * Actually, the other parts, beginning with ::object, depend on this.
13 */
14 namespace lout {
15
16 namespace misc {
17
18 template <class T> inline T min (T a, T b) { return a < b ? a : b; }
19 template <class T> inline T max (T a, T b) { return a > b ? a : b; }
20
21 template <class T> inline T min (T a, T b, T c)
22 {
23 return (min (a, min (b, c)));
24 }
25 template <class T> inline T max (T a, T b, T c)
26 {
27 return (max (a, max (b, c)));
28 }
29
30 extern const char *prgName;
31
32 void init (int argc, char *argv[]);
33
34 inline void assertNotReached ()
35 {
36 fprintf (stderr, "*** [%s] This should not happen! ***\n", prgName);
37 abort ();
38 }
39
40 inline int roundInt(double d)
41 {
42 return (int) ((d > 0) ? (d + 0.5) : (d - 0.5));
43 }
44
45 /**
46 * \brief Instances of a sub class of this interface may be compared (less,
47 * greater).
48 *
49 * Used for sorting etc.
50 */
51 class Comparable
52 {
53 public:
54 virtual ~Comparable();
55
56 /**
57 * \brief Compare two objects c1 and c2.
58 *
59 * return a value < 0, when c1 is less than c2, a value > 0, when c1
60 * is greater than c2, or 0, when c1 and c2 are equal.
61 *
62 * If also object::Object is implemented, and if c1.equals(c2),
63 * c1.compareTo(c2) must be 0, but, unlike you may expect,
64 * the reversed is not necessarily true. This method returns 0, if,
65 * according to the rules for sorting, there is no difference, but there
66 * may still be differences (not relevant for sorting), which "equals" will
67 * care about.
68 */
69 virtual int compareTo(Comparable *other) = 0;
70
71 static int compareFun(const void *p1, const void *p2);
72 };
73
74 /**
75 * \brief Simple (simpler than container::untyped::Vector and
76 * container::typed::Vector) template based vector.
77 */
78 template <class T> class SimpleVector
79 {
80 private:
81 T *array;
82 int num, numAlloc;
83
84 inline void resize ()
85 {
86 /* This algorithm was tuned for memory&speed with this huge page:
87 * http://downloads.mysql.com/docs/refman-6.0-en.html.tar.gz
88 */
89 if (array == NULL) {
90 this->numAlloc = 1;
91 this->array = (T*) malloc (sizeof (T));
92 }
93 if (this->numAlloc < this->num) {
94 this->numAlloc = (this->num < 100) ?
95 this->num : this->num + this->num/10;
96 this->array =
97 (T*) realloc(this->array, (this->numAlloc * sizeof (T)));
98 }
99 }
100
101 public:
102 inline SimpleVector (int initAlloc)
103 {
104 this->num = 0;
105 this->numAlloc = initAlloc;
106 this->array = NULL;
107 }
108
109 inline SimpleVector (const SimpleVector &o) {
110 this->array = NULL;
111 this->num = o.num;
112 this->numAlloc = o.numAlloc;
113 resize ();
114 memcpy (this->array, o.array, sizeof (T) * num);
115 }
116
117 inline ~SimpleVector ()
118 {
119 if (this->array)
120 free (this->array);
121 }
122
123 /**
124 * \brief Return the number of elements put into this vector.
125 */
126 inline int size() { return this->num; }
127
128 inline T* getArray() { return array; }
129
130 /**
131 * \brief Increase the vector size by one.
132 *
133 * May be necessary before calling misc::SimpleVector::set.
134 */
135 inline void increase() { setSize(this->num + 1); }
136
137 /**
138 * \brief Set the size explicitly.
139 *
140 * May be necessary before calling misc::SimpleVector::set.
141 */
142 inline void setSize(int newSize) {
143 assert (newSize >= 0);
144 this->num = newSize;
145 this->resize ();
146 }
147
148 /**
149 * \brief Set the size explicitly and initialize new values.
150 *
151 * May be necessary before calling misc::SimpleVector::set.
152 */
153 inline void setSize (int newSize, T t) {
154 int oldSize = this->num;
155 setSize (newSize);
156 for (int i = oldSize; i < newSize; i++)
157 set (i, t);
158 }
159
160 /**
161 * \brief Return the reference of one element.
162 *
163 * \sa misc::SimpleVector::get
164 */
165 inline T* getRef (int i) {
166 assert (i >= 0 && this->num - i > 0);
167 return array + i;
168 }
169
170 /**
171 * \brief Return the one element, explicitly.
172 *
173 * The element is copied, so for complex elements, you should rather used
174 * misc::SimpleVector::getRef.
175 */
176 inline T get (int i) {
177 assert (i >= 0 && this->num - i > 0);
178 return this->array[i];
179 }
180
181 /**
182 * \brief Store an object in the vector.
183 *
184 * Unlike in container::untyped::Vector and container::typed::Vector,
185 * you have to care about the size, so a call to
186 * misc::SimpleVector::increase or misc::SimpleVector::setSize may
187 * be necessary before.
188 */
189 inline void set (int i, T t) {
190 assert (i >= 0 && this->num - i > 0);
191 this->array[i] = t;
192 }
193 };
194
195
196 /**
197 * \brief A class for fast concatenation of a large number of strings.
198 */
199 class StringBuffer
200 {
201 private:
202 struct Node
203 {
204 char *data;
205 Node *next;
206 };
207
208 Node *firstNode, *lastNode;
209 int numChars;
210 char *str;
211 bool strValid;
212
213 public:
214 StringBuffer();
215 ~StringBuffer();
216
217 /**
218 * \brief Append a NUL-terminated string to the buffer, with copying.
219 *
220 * A copy is kept in the buffer, so the caller does not have to care
221 * about memory management.
222 */
223 inline void append(const char *str) { appendNoCopy(strdup(str)); }
224 void appendNoCopy(char *str);
225 const char *getChars();
226 void clear ();
227 };
228
229
230 /**
231 * \brief A bit set, which automatically reallocates when needed.
232 */
233 class BitSet
234 {
235 private:
236 unsigned char *bits;
237 int numBytes;
238
239 inline int bytesForBits(int bits) { return bits == 0 ? 1 : (bits + 7) / 8; }
240
241 public:
242 BitSet(int initBits);
243 ~BitSet();
244 void intoStringBuffer(misc::StringBuffer *sb);
245 bool get(int i);
246 void set(int i, bool val);
247 void clear();
248 };
249
250 /**
251 * \brief A simple allocator optimized to handle many small chunks of memory.
252 * The chunks can not be free'd individually. Instead the whole zone must be
253 * free'd with zoneFree().
254 */
255 class ZoneAllocator
256 {
257 private:
258 size_t poolSize, poolLimit, freeIdx;
259 SimpleVector <char*> *pools;
260 SimpleVector <char*> *bulk;
261
262 public:
263 ZoneAllocator (size_t poolSize) {
264 this->poolSize = poolSize;
265 this->poolLimit = poolSize / 4;
266 this->freeIdx = poolSize;
267 this->pools = new SimpleVector <char*> (1);
268 this->bulk = new SimpleVector <char*> (1);
269 };
270
271 ~ZoneAllocator () {
272 zoneFree ();
273 delete pools;
274 delete bulk;
275 }
276
277 inline void * zoneAlloc (size_t t) {
278 void *ret;
279
280 if (t > poolLimit) {
281 bulk->increase ();
282 bulk->set (bulk->size () - 1, (char*) malloc (t));
283 return bulk->get (bulk->size () - 1);
284 }
285
286 if (t > poolSize - freeIdx) {
287 pools->increase ();
288 pools->set (pools->size () - 1, (char*) malloc (poolSize));
289 freeIdx = 0;
290 }
291
292 ret = pools->get (pools->size () - 1) + freeIdx;
293 freeIdx += t;
294 return ret;
295 }
296
297 inline void zoneFree () {
298 for (int i = 0; i < pools->size (); i++)
299 free (pools->get (i));
300 pools->setSize (0);
301 for (int i = 0; i < bulk->size (); i++)
302 free (bulk->get (i));
303 bulk->setSize (0);
304 freeIdx = poolSize;
305 }
306
307 inline const char *strndup (const char *str, size_t t) {
308 char *new_str = (char *) zoneAlloc (t + 1);
309 memcpy (new_str, str, t);
310 new_str[t] = '\0';
311 return new_str;
312 }
313
314 inline const char *strdup (const char *str) {
315 return strndup (str, strlen (str));
316 }
317 };
318
319 } // namespace misc
320
321 } // namespace lout
322
323 #endif // __LOUT_MISC_HH__
0 #ifndef __MSG_H__
1 #define __MSG_H__
2
3 #include <stdio.h>
4
5 #define prefs_show_msg 1
6
7 #define D_STMT_START do
8 #define D_STMT_END while (0)
9
10 /*
11 * You can disable any MSG* macro by adding the '_' prefix.
12 */
13 #define _MSG(...)
14 #define _MSG_WARN(...)
15 #define _MSG_ERR(...)
16
17
18 #define MSG(...) \
19 D_STMT_START { \
20 if (prefs_show_msg){ \
21 printf(__VA_ARGS__); \
22 fflush (stdout); \
23 } \
24 } D_STMT_END
25
26 #define MSG_WARN(...) \
27 D_STMT_START { \
28 if (prefs_show_msg) \
29 printf("** WARNING **: " __VA_ARGS__); \
30 } D_STMT_END
31
32 #define MSG_ERR(...) \
33 D_STMT_START { \
34 if (prefs_show_msg) \
35 printf("** ERROR **: " __VA_ARGS__); \
36 } D_STMT_END
37
38 #endif /* __MSG_H__ */
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "object.hh"
22 #include <stdio.h>
23 #include <config.h>
24
25 namespace lout {
26
27 namespace object {
28
29 // ------------
30 // Object
31 // ------------
32
33 /**
34 * \brief The destructor is defined as virtual (but not abstract), so that
35 * destruction of Object's works properly.
36 */
37 Object::~Object()
38 {
39 }
40
41 /**
42 * \brief Returns, whether two objects are equal.
43 *
44 * The caller should ensure, that this and the object have the same class;
45 * this makes casting of "other" safe. Typically, an implementation should
46 * check this == other first, the caller can assume a fast implementation.
47 */
48 bool Object::equals(Object *other)
49 {
50 misc::assertNotReached ();
51 return false;
52 }
53
54 /**
55 * \brief Return a hash value for the object.
56 */
57 int Object::hashValue()
58 {
59 fprintf (stderr, "Object::hashValue() should be implemented.\n");
60 return 0;
61 }
62
63 /**
64 * \brief Return an exact copy of the object.
65 */
66 Object *Object::clone()
67 {
68 misc::assertNotReached ();
69 return NULL;
70 }
71
72 /**
73 * \brief Use object::Object::intoStringBuffer to return a textual
74 * representation of the object.
75 *
76 * The caller does not have to free the memory, object::Object is responsible
77 * for this.
78 */
79 const char *Object::toString()
80 {
81 /** \todo garbage! */
82 misc::StringBuffer sb;
83 intoStringBuffer(&sb);
84 char *s = strdup(sb.getChars());
85 return s;
86 }
87
88 /**
89 * \brief Store a textual representation of the object in a misc::StringBuffer.
90 *
91 * This is used by object::Object::toString.
92 */
93 void Object::intoStringBuffer(misc::StringBuffer *sb)
94 {
95 sb->append("<not further specified object>");
96 }
97
98 /**
99 * \brief Return the number of bytes, this object totally uses.
100 */
101 size_t Object::sizeOf()
102 {
103 fprintf (stderr, "Object::sizeOf() should be implemented.\n");
104 return sizeof(Object*);
105 }
106
107 // -------------
108 // Pointer
109 // -------------
110
111 bool Pointer::equals(Object *other)
112 {
113 return value == ((Pointer*)other)->value;
114 }
115
116 int Pointer::hashValue()
117 {
118 /* For some unknown reason, this doesn't compile on some 64bit platforms:
119 *
120 * if (sizeof (int) == sizeof (void*))
121 * return (int)value;
122 * else
123 * return ((int*)value)[0] ^ ((int*)value)[1];
124 */
125 #if SIZEOF_VOID_P == 4
126 return (int)value;
127 #else
128 return ((int*)value)[0] ^ ((int*)value)[1];
129 #endif
130 }
131
132 void Pointer::intoStringBuffer(misc::StringBuffer *sb)
133 {
134 char buf[64];
135 snprintf(buf, sizeof(buf), "0x%p", value);
136 sb->append(buf);
137 }
138
139 // -------------
140 // Integer
141 // -------------
142
143 bool Integer::equals(Object *other)
144 {
145 return value == ((Integer*)other)->value;
146 }
147
148 int Integer::hashValue()
149 {
150 return (int)value;
151 }
152
153 void Integer::intoStringBuffer(misc::StringBuffer *sb)
154 {
155 char buf[64];
156 sprintf(buf, "%d", value);
157 sb->append(buf);
158 }
159
160 int Integer::compareTo(Comparable *other)
161 {
162 return value - ((Integer*)other)->value;
163 }
164
165 // -----------------
166 // ConstString
167 // -----------------
168
169 bool ConstString::equals(Object *other)
170 {
171 ConstString *otherString = (ConstString*)other;
172 return
173 this == other ||
174 (str == NULL && otherString->str == NULL) ||
175 (str != NULL && otherString->str != NULL &&
176 strcmp(str, otherString->str) == 0);
177 }
178
179 int ConstString::hashValue()
180 {
181 return hashValue(str);
182 }
183
184
185 int ConstString::compareTo(Comparable *other)
186 {
187 String *otherString = (String*)other;
188 if (str && otherString->str)
189 return strcmp(str, otherString->str);
190 else if (str)
191 return 1;
192 else if (otherString->str)
193 return -1;
194 else
195 return 0;
196 }
197
198
199 int ConstString::hashValue(const char *str)
200 {
201 if (str) {
202 int h = 0;
203 for (int i = 0; str[i]; i++)
204 h = (h * 256 + str[i]);
205 return h;
206 } else
207 return 0;
208 }
209
210 void ConstString::intoStringBuffer(misc::StringBuffer *sb)
211 {
212 sb->append(str);
213 }
214
215 // ------------
216 // String
217 // ------------
218
219 String::String (const char *str): ConstString (str ? strdup(str) : NULL)
220 {
221 }
222
223 String::~String ()
224 {
225 if (str)
226 free((char *)str);
227 }
228
229 // ------------
230 // Pair
231 // ------------
232
233 PairBase::PairBase(Object *first, Object *second)
234 {
235 this->first = first;
236 this->second = second;
237 }
238
239 PairBase::~PairBase()
240 {
241 if (first)
242 delete first;
243 if (second)
244 delete second;
245 }
246
247 bool PairBase::equals(Object *other)
248 {
249 PairBase *otherPair = (PairBase*)other;
250
251 return
252 // Identical?
253 this == other || (
254 (// Both first parts are NULL, ...
255 (first == NULL && otherPair->first == NULL) ||
256 // ... or both first parts are not NULL and equal
257 (first != NULL && otherPair->first != NULL
258 && first->equals (otherPair->first))) &&
259 // Same with second part.
260 ((second == NULL && otherPair->second == NULL) ||
261 (second != NULL && otherPair->second != NULL
262 && second->equals (otherPair->second))));
263 }
264
265 int PairBase::hashValue()
266 {
267 int value = 0;
268
269 if (first)
270 value ^= first->hashValue();
271 if (second)
272 value ^= second->hashValue();
273
274 return value;
275 }
276
277 void PairBase::intoStringBuffer(misc::StringBuffer *sb)
278 {
279 sb->append("<pair: ");
280
281 if (first)
282 first->intoStringBuffer(sb);
283 else
284 sb->append("(nil)");
285
286 sb->append(",");
287
288 if (second)
289 second->intoStringBuffer(sb);
290 else
291 sb->append("(nil)");
292
293 sb->append(">");
294 }
295
296 size_t PairBase::sizeOf()
297 {
298 size_t size = 0;
299
300 if (first)
301 size += first->sizeOf();
302 if (second)
303 size += second->sizeOf();
304
305 return size;
306 }
307
308 } // namespace object
309
310 } // namespace lout
0 #ifndef __LOUT_OBJECT_HH__
1 #define __LOUT_OBJECT_HH__
2
3 #include <stdlib.h>
4 #include <string.h>
5
6 #include "misc.hh"
7
8 /**
9 * \brief Here, some common classes (or interfaces) are defined, to standardize
10 * the access to other classes.
11 */
12 namespace lout {
13
14 namespace object {
15
16 /**
17 * \brief This is the base class for many other classes, which defines very
18 * common virtual methods.
19 *
20 * For convenience, none of them are abstract, but they
21 * must be defined, when they are needed, especially for containers.
22 */
23 class Object
24 {
25 public:
26 virtual ~Object();
27 virtual bool equals(Object *other);
28 virtual int hashValue();
29 virtual Object *clone();
30 virtual void intoStringBuffer(misc::StringBuffer *sb);
31 const char *toString();
32 virtual size_t sizeOf();
33 };
34
35 /**
36 * \brief An object::Object wrapper for void pointers.
37 */
38 class Pointer: public Object
39 {
40 private:
41 void *value;
42
43 public:
44 Pointer(void *value) { this->value = value; }
45 bool equals(Object *other);
46 int hashValue();
47 void intoStringBuffer(misc::StringBuffer *sb);
48 inline void *getValue() { return value; }
49 };
50
51 /**
52 * \brief A typed version of object::Pointer.
53 */
54 template <class T> class TypedPointer: public Pointer
55 {
56 public:
57 inline TypedPointer(T *value) : Pointer ((void*)value) { }
58 inline T *getTypedValue() { return (T*)getValue(); }
59 };
60
61
62 /**
63 * \brief An object::Object wrapper for int's.
64 */
65 class Integer: public Object, misc::Comparable
66 {
67 int value;
68
69 public:
70 Integer(int value) { this->value = value; }
71 bool equals(Object *other);
72 int hashValue();
73 void intoStringBuffer(misc::StringBuffer *sb);
74 int compareTo(Comparable *other);
75 inline int getValue() { return value; }
76 };
77
78
79 /**
80 * \brief An object::Object wrapper for constant strings (char*).
81 *
82 * As opposed to object::String, the char array is not copied.
83 */
84 class ConstString: public Object, misc::Comparable
85 {
86 protected:
87 const char *str;
88
89 public:
90 ConstString(const char *str) { this->str = str; }
91 bool equals(Object *other);
92 int hashValue();
93 int compareTo(Comparable *other);
94 void intoStringBuffer(misc::StringBuffer *sb);
95
96 inline const char *chars() { return str; }
97
98 static int hashValue(const char *str);
99 };
100
101
102 /**
103 * \brief An object::Object wrapper for strings (char*).
104 *
105 * As opposed to object::ConstantString, the char array is copied.
106 */
107 class String: public ConstString
108 {
109 public:
110 String(const char *str);
111 ~String();
112 };
113
114 /**
115 * \todo Comment
116 */
117 class PairBase: public Object
118 {
119 protected:
120 Object *first, *second;
121
122 public:
123 PairBase(Object *first, Object *second);
124 ~PairBase();
125
126 bool equals(Object *other);
127 int hashValue();
128 void intoStringBuffer(misc::StringBuffer *sb);
129 size_t sizeOf();
130 };
131
132 /**
133 * \todo Comment
134 */
135 class Pair: public PairBase
136 {
137 public:
138 Pair(Object *first, Object *second): PairBase (first, second) { }
139
140 inline Object *getFirst () { return first; }
141 inline Object *getSecond () { return second; }
142 };
143
144 /**
145 * \todo Comment
146 */
147 template <class F, class S> class TypedPair: public PairBase
148 {
149 public:
150 TypedPair(F *first, S *second): PairBase (first, second) { }
151
152 inline F *getFirst () { return first; }
153 inline S *getSecond () { return second; }
154 };
155
156 } // namespace object
157
158 } // namespace lout
159
160 #endif // __LOUT_OBJECT_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "signal.hh"
22
23 namespace lout {
24 namespace signal {
25
26 using namespace container::typed;
27
28 // ------------
29 // Emitter
30 // ------------
31
32 Emitter::Emitter ()
33 {
34 receivers = new List <Receiver> (false);
35 }
36
37 Emitter::~Emitter ()
38 {
39 for (Iterator<Receiver> it = receivers->iterator (); it.hasNext (); ) {
40 Receiver *receiver = it.getNext ();
41 receiver->unconnectFrom (this);
42 }
43 delete receivers;
44 }
45
46 void Emitter::intoStringBuffer(misc::StringBuffer *sb)
47 {
48 sb->append ("<emitter: ");
49 receivers->intoStringBuffer (sb);
50 sb->append (">");
51 }
52
53 void Emitter::unconnect (Receiver *receiver)
54 {
55 receivers->removeRef (receiver);
56 }
57
58 /**
59 * \brief Connect a receiver to the emitter.
60 *
61 * This is protected, a sub class should define a wrapper, with the respective
62 * receiver as an argument, to gain type safety.
63 */
64 void Emitter::connect (Receiver *receiver)
65 {
66 receivers->append (receiver);
67 receiver->connectTo (this);
68 }
69
70 /**
71 * \brief Emit a void signal.
72 *
73 * This method should be called by a wrapper (return value void), which
74 * \em folds the signal, and delegates the emission to here.
75 */
76 void Emitter::emitVoid (int signalNo, int argc, Object **argv)
77 {
78 for (Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) {
79 Receiver *receiver = it.getNext();
80 emitToReceiver (receiver, signalNo, argc, argv);
81 }
82 }
83
84 /**
85 * \brief Emit a boolean signal.
86 *
87 * This method should be called by a wrapper, which \em folds the signal,
88 * delegates the emission to here, and returns the same boolean value.
89 */
90 bool Emitter::emitBool (int signalNo, int argc, Object **argv)
91 {
92 bool b = false, bt;
93
94 for (Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) {
95 Receiver *receiver = it.getNext();
96 // Note: All receivers are called, even if one returns true.
97 // Therefore, something like
98 // b = b || emitToReceiver (receiver, signalNo, argc, argv);
99 // does not work.
100 bt = emitToReceiver (receiver, signalNo, argc, argv);
101 b = b || bt;
102 }
103
104 return b;
105 }
106
107
108 // --------------
109 // Receiver
110 // --------------
111
112 Receiver::Receiver()
113 {
114 emitters = new List <Emitter> (false);
115 }
116
117 Receiver::~Receiver()
118 {
119 for (Iterator<Emitter> it = emitters->iterator(); it.hasNext(); ) {
120 Emitter *emitter = it.getNext();
121 emitter->unconnect (this);
122 }
123 delete emitters;
124 }
125
126 void Receiver::intoStringBuffer(misc::StringBuffer *sb)
127 {
128 // emitters are not listed, to prevent recursion
129 sb->append ("<receiver>");
130 }
131
132 void Receiver::connectTo(Emitter *emitter)
133 {
134 emitters->append (emitter);
135 }
136
137 void Receiver::unconnectFrom(Emitter *emitter)
138 {
139 emitters->removeRef (emitter);
140 }
141
142 // ------------------------
143 // ObservedObject
144 // ------------------------
145
146 bool ObservedObject::DeletionEmitter::emitToReceiver (Receiver *receiver,
147 int signalNo,
148 int argc, Object **argv)
149 {
150 object::TypedPointer <ObservedObject> *p =
151 (object::TypedPointer<ObservedObject>*)argv[0];
152 ((DeletionReceiver*)receiver)->deleted (p->getTypedValue ());
153 return false;
154 }
155
156 void ObservedObject::DeletionEmitter::emitDeletion (ObservedObject *obj)
157 {
158 object::TypedPointer <ObservedObject> p(obj);
159 object::Object *argv[1] = { &p };
160 emitVoid (0, 1, argv);
161 }
162
163 ObservedObject::~ObservedObject()
164 {
165 deletionEmitter.emitDeletion (this);
166 }
167
168 } // namespace signal
169 } // namespace lout
0 #ifndef __LOUT_SIGNALS_HH__
1 #define __LOUT_SIGNALS_HH__
2
3 #include "object.hh"
4 #include "container.hh"
5
6 /**
7 * \brief This namespace provides base classes to define signals.
8 *
9 * By using signals, objects may be connected at run-time, e.g. a general
10 * button widget may be connected to another application-specific object,
11 * which reacts on the operations on the button by the user. In this case,
12 * the button e.g. defines a signal "clicked", which is "emitted" each
13 * time the user clicks on the button. After the application-specific
14 * object has been connected to this signal, a specific method of it will
15 * be called each time, this button emits the "clicked" signal.
16 *
17 * Below, we will call the level, on which signals are defined, the
18 * "general level", and the level, on which the signals are connected,
19 * the "caller level".
20 *
21 * <h3>Defining Signals</h3>
22 *
23 * Typically, signals are grouped. To define a signal group \em bar for your
24 * class \em Foo, you have to define two classes, the emitter and the
25 * receiver (BarEmitter and BarReceiver), and instantiate the emitter:
26 *
27 * \dot
28 * digraph G {
29 * node [shape=record, fontname=Helvetica, fontsize=10];
30 * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
31 * labelfontsize=10, color="#404040", labelfontcolor="#000080"];
32 * fontname=Helvetica; fontsize=10;
33 *
34 * subgraph cluster_signal {
35 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
36 * label="signal";
37 *
38 * Emitter [color="#a0a0a0", URL="\ref signal::Emitter"];
39 * Receiver [color="#a0a0a0", URL="\ref signal::Receiver"];
40 * }
41 *
42 * subgraph cluster_foo {
43 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
44 * label="General (foo)";
45 *
46 * Foo;
47 * BarEmitter;
48 * BarReceiver [color="#a0a0a0"];
49 * }
50 *
51 * Emitter -> BarEmitter;
52 * Receiver -> BarReceiver;
53 * Foo -> BarEmitter [arrowhead="open", arrowtail="none",
54 * headlabel="1", taillabel="1"];
55 * }
56 * \enddot
57 *
58 * <center>[\ref uml-legend "legend"]</center>
59 *
60 * BarEmitter (class and instance) may be kept private, but BarReceiver must
61 * be public, since the caller of Foo must create a sub class of it. For
62 * BarEmitter, several methods must be implemented, see signal::Emitter for
63 * details. In BarReceiver, only some virtual abstract methods are defined,
64 * which the caller must implement. In this case, it is recommended to define
65 * a connectBar(BarReceiver*) method in Foo, which is delegated to the
66 * BarEmitter.
67 *
68 * <h3>Connecting to Signals</h3>
69 *
70 * A caller, which wants to connect to a signal, must define a sub class of
71 * the receiver, and implement the virtual methods. A typical design looks
72 * like this:
73 *
74 * \dot
75 * digraph G {
76 * node [shape=record, fontname=Helvetica, fontsize=10];
77 * edge [arrowhead="open", arrowtail="none", labelfontname=Helvetica,
78 * labelfontsize=10, color="#404040", labelfontcolor="#000080"];
79 * fontname=Helvetica; fontsize=10;
80 *
81 * subgraph cluster_foo {
82 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
83 * label="Generall (foo)";
84 *
85 * BarReceiver [color="#a0a0a0"];
86 * }
87 *
88 * subgraph cluster_qix {
89 * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
90 * label="Caller (qix)";
91 *
92 * Qix;
93 * QixBarReceiver;
94 * }
95 *
96 * BarReceiver -> QixBarReceiver [arrowhead="none", arrowtail="empty"];
97 * QixBarReceiver -> Qix [headlabel="1", taillabel="*"];
98 * }
99 * \enddot
100 *
101 * <center>[\ref uml-legend "legend"]</center>
102 *
103 * (We skip "baz" in the canon, for better readability.)
104 *
105 * Here, the QixBarReceiver is connected to the Qix, so that the signals can
106 * be delegated to the Qix. Notice that the receiver gets automatically
107 * disconnected, when deleted (see signal::Receiver::~Receiver).
108 *
109 * <h3>Void and Boolean Signals</h3>
110 *
111 * In the simplest case, signal emitting involves calling a list of
112 * signal receivers (void signals). For boolean signals, the receivers return
113 * a boolean value, and the result of the signal emission (the return value of
114 * signal::Emitter::emitBool) returns the disjunction of the values returned
115 * by the receivers. Typically, a receiver states with its return value,
116 * whether the signal was used in any way, the resulting return value so
117 * indicates, whether at least one receiver has used the signal.
118 *
119 * In Dw, events are processed this way. In the simplest case, they are
120 * delegated to the parent widget, if the widget does not process them (by
121 * returning false). As an addition, signals are emitted, and if a receiver
122 * processes the event, this is handled the same way, as if the widget itself
123 * would have processed it.
124 *
125 * Notice, that also for boolean signals, all receivers are called, even
126 * after one receiver has already returned true.
127 *
128 * <h3>Memory Management</h3>
129 *
130 * <h4>Emitters</h4>
131 *
132 * Emitters are typically instantiated one, for one object emitting the
133 * signals. In the example above, the class Foo will contain a field
134 * "BarEmitter barEmitter" (not as a pointer, "BarEmitter *barEmitter").
135 *
136 * <h4>Receivers</h4>
137 *
138 * It is important, that a emitter never deletes a receiver, it just removes
139 * them from the receivers list. Likewise, when a receiver is deleted, it
140 * unconnects itself from all emitters. (The same receiver instance can indeed
141 * be connected to multiple emitters.) So, the caller has to care about
142 * deleting receivers.
143 *
144 * In the example above, something like that will work:
145 *
146 * \code
147 * class Qix
148 * {
149 * private:
150 * class QixBarReceiver
151 * {
152 * public:
153 * Qix *qix;
154 * // ...
155 * };
156 *
157 * QixBarReceiver barReceiver;
158 *
159 * // ...
160 * };
161 * \endcode
162 *
163 * The constructor of Qix should then set \em qix:
164 *
165 * \code
166 * Qix::Qix ()
167 * {
168 * barReceiver.qix = this.
169 * // ...
170 * }
171 * \endcode
172 *
173 * After this, &\em barReceiver can be connected to all instances of
174 * BarEmitter, also multiple times.
175 */
176 namespace lout {
177
178 namespace signal {
179
180 class Receiver;
181
182 /**
183 * \brief The base class for signal emitters.
184 *
185 * If defining a signal group, a sub class of this class must be defined,
186 * with
187 *
188 * <ul>
189 * <li> a definition of the different signals (as enumeration),
190 * <li> an implementation of signal::Emitter::emitToReceiver,
191 * <li> wrappers for signal::Emitter::emitVoid and signal::Emitter::emitBool,
192 * respectively (one for each signal), and
193 * <li> a wrapper for signal::Emitter::connect.
194 * </ul>
195 *
196 * There are two representations of signals:
197 *
198 * <ul>
199 * <li> In the \em unfolded representation, the signal itself is represented
200 * by the method itself (in the emitter or the receiver), and the
201 * arguments are represented as normal C++ types.
202 *
203 * <li> \em Folding signals means to represent the signal itself by an integer
204 * number (enumeration), and translate the arguments in an object::Object*
205 * array. (If a given argument is not an instance of object::Object*,
206 * the wrappers in ::object can be used.)
207 * </ul>
208 *
209 * \sa ::signal
210 */
211 class Emitter: public object::Object
212 {
213 friend class Receiver;
214
215 private:
216 container::typed::List <Receiver> *receivers;
217
218 void unconnect (Receiver *receiver);
219
220 protected:
221 void emitVoid (int signalNo, int argc, Object **argv);
222 bool emitBool (int signalNo, int argc, Object **argv);
223 void connect(Receiver *receiver);
224
225 /**
226 * \brief A sub class must implement this for a call to a single
227 * receiver.
228 *
229 * This methods gets the signal in a \em folded representation, it has
230 * to unfold it, and pass it to a single receiver. For boolean signals,
231 * the return value of the receiver must be returned, for void signals,
232 * the return value is discarded.
233 */
234 virtual bool emitToReceiver (Receiver *receiver, int signalNo,
235 int argc, Object **argv) = 0;
236
237 public:
238 Emitter();
239 ~Emitter();
240
241 void intoStringBuffer(misc::StringBuffer *sb);
242 };
243
244 /**
245 * \brief The base class for signal receiver base classes.
246 *
247 * If defining a signal group, a sub class of this class must be defined,
248 * in which only the abstract signal methods must be defined.
249 *
250 * \sa ::signal
251 */
252 class Receiver: public object::Object
253 {
254 friend class Emitter;
255
256 private:
257 container::typed::List<Emitter> *emitters;
258
259 void connectTo(Emitter *emitter);
260 void unconnectFrom(Emitter *emitter);
261
262 public:
263 Receiver();
264 ~Receiver();
265
266 void intoStringBuffer(misc::StringBuffer *sb);
267 };
268
269 /**
270 * \brief An observed object has a signal emitter, which tells the
271 * receivers, when the object is deleted.
272 */
273 class ObservedObject
274 {
275 public:
276 class DeletionReceiver: public signal::Receiver
277 {
278 public:
279 virtual void deleted (ObservedObject *object) = 0;
280 };
281
282 private:
283 class DeletionEmitter: public signal::Emitter
284 {
285 protected:
286 bool emitToReceiver (signal::Receiver *receiver, int signalNo,
287 int argc, Object **argv);
288
289 public:
290 inline void connectDeletion (DeletionReceiver *receiver)
291 { connect (receiver); }
292
293 void emitDeletion (ObservedObject *obj);
294 };
295
296 DeletionEmitter deletionEmitter;
297
298 public:
299 virtual ~ObservedObject();
300
301 inline void connectDeletion (DeletionReceiver *receiver)
302 { deletionEmitter.connectDeletion (receiver); }
303 };
304
305 } // namespace signal
306
307 } // namespace lout
308
309 #endif // __LOUT_SIGNALS_HH__
+0
-357
missing less more
0 #! /bin/sh
1 # Common stub for a few missing GNU programs while installing.
2
3 scriptversion=2005-02-08.22
4
5 # Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005
6 # Free Software Foundation, Inc.
7 # Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
8
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2, or (at your option)
12 # any later version.
13
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22 # 02111-1307, USA.
23
24 # As a special exception to the GNU General Public License, if you
25 # distribute this file as part of a program that contains a
26 # configuration script generated by Autoconf, you may include it under
27 # the same distribution terms that you use for the rest of that program.
28
29 if test $# -eq 0; then
30 echo 1>&2 "Try \`$0 --help' for more information"
31 exit 1
32 fi
33
34 run=:
35
36 # In the cases where this matters, `missing' is being run in the
37 # srcdir already.
38 if test -f configure.ac; then
39 configure_ac=configure.ac
40 else
41 configure_ac=configure.in
42 fi
43
44 msg="missing on your system"
45
46 case "$1" in
47 --run)
48 # Try to run requested program, and just exit if it succeeds.
49 run=
50 shift
51 "$@" && exit 0
52 # Exit code 63 means version mismatch. This often happens
53 # when the user try to use an ancient version of a tool on
54 # a file that requires a minimum version. In this case we
55 # we should proceed has if the program had been absent, or
56 # if --run hadn't been passed.
57 if test $? = 63; then
58 run=:
59 msg="probably too old"
60 fi
61 ;;
62
63 -h|--h|--he|--hel|--help)
64 echo "\
65 $0 [OPTION]... PROGRAM [ARGUMENT]...
66
67 Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
68 error status if there is no known handling for PROGRAM.
69
70 Options:
71 -h, --help display this help and exit
72 -v, --version output version information and exit
73 --run try to run the given command, and emulate it if it fails
74
75 Supported PROGRAM values:
76 aclocal touch file \`aclocal.m4'
77 autoconf touch file \`configure'
78 autoheader touch file \`config.h.in'
79 automake touch all \`Makefile.in' files
80 bison create \`y.tab.[ch]', if possible, from existing .[ch]
81 flex create \`lex.yy.c', if possible, from existing .c
82 help2man touch the output file
83 lex create \`lex.yy.c', if possible, from existing .c
84 makeinfo touch the output file
85 tar try tar, gnutar, gtar, then tar without non-portable flags
86 yacc create \`y.tab.[ch]', if possible, from existing .[ch]
87
88 Send bug reports to <bug-automake@gnu.org>."
89 exit $?
90 ;;
91
92 -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
93 echo "missing $scriptversion (GNU Automake)"
94 exit $?
95 ;;
96
97 -*)
98 echo 1>&2 "$0: Unknown \`$1' option"
99 echo 1>&2 "Try \`$0 --help' for more information"
100 exit 1
101 ;;
102
103 esac
104
105 # Now exit if we have it, but it failed. Also exit now if we
106 # don't have it and --version was passed (most likely to detect
107 # the program).
108 case "$1" in
109 lex|yacc)
110 # Not GNU programs, they don't have --version.
111 ;;
112
113 tar)
114 if test -n "$run"; then
115 echo 1>&2 "ERROR: \`tar' requires --run"
116 exit 1
117 elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
118 exit 1
119 fi
120 ;;
121
122 *)
123 if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
124 # We have it, but it failed.
125 exit 1
126 elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
127 # Could not run --version or --help. This is probably someone
128 # running `$TOOL --version' or `$TOOL --help' to check whether
129 # $TOOL exists and not knowing $TOOL uses missing.
130 exit 1
131 fi
132 ;;
133 esac
134
135 # If it does not exist, or fails to run (possibly an outdated version),
136 # try to emulate it.
137 case "$1" in
138 aclocal*)
139 echo 1>&2 "\
140 WARNING: \`$1' is $msg. You should only need it if
141 you modified \`acinclude.m4' or \`${configure_ac}'. You might want
142 to install the \`Automake' and \`Perl' packages. Grab them from
143 any GNU archive site."
144 touch aclocal.m4
145 ;;
146
147 autoconf)
148 echo 1>&2 "\
149 WARNING: \`$1' is $msg. You should only need it if
150 you modified \`${configure_ac}'. You might want to install the
151 \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
152 archive site."
153 touch configure
154 ;;
155
156 autoheader)
157 echo 1>&2 "\
158 WARNING: \`$1' is $msg. You should only need it if
159 you modified \`acconfig.h' or \`${configure_ac}'. You might want
160 to install the \`Autoconf' and \`GNU m4' packages. Grab them
161 from any GNU archive site."
162 files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
163 test -z "$files" && files="config.h"
164 touch_files=
165 for f in $files; do
166 case "$f" in
167 *:*) touch_files="$touch_files "`echo "$f" |
168 sed -e 's/^[^:]*://' -e 's/:.*//'`;;
169 *) touch_files="$touch_files $f.in";;
170 esac
171 done
172 touch $touch_files
173 ;;
174
175 automake*)
176 echo 1>&2 "\
177 WARNING: \`$1' is $msg. You should only need it if
178 you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
179 You might want to install the \`Automake' and \`Perl' packages.
180 Grab them from any GNU archive site."
181 find . -type f -name Makefile.am -print |
182 sed 's/\.am$/.in/' |
183 while read f; do touch "$f"; done
184 ;;
185
186 autom4te)
187 echo 1>&2 "\
188 WARNING: \`$1' is needed, but is $msg.
189 You might have modified some files without having the
190 proper tools for further handling them.
191 You can get \`$1' as part of \`Autoconf' from any GNU
192 archive site."
193
194 file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
195 test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
196 if test -f "$file"; then
197 touch $file
198 else
199 test -z "$file" || exec >$file
200 echo "#! /bin/sh"
201 echo "# Created by GNU Automake missing as a replacement of"
202 echo "# $ $@"
203 echo "exit 0"
204 chmod +x $file
205 exit 1
206 fi
207 ;;
208
209 bison|yacc)
210 echo 1>&2 "\
211 WARNING: \`$1' $msg. You should only need it if
212 you modified a \`.y' file. You may need the \`Bison' package
213 in order for those modifications to take effect. You can get
214 \`Bison' from any GNU archive site."
215 rm -f y.tab.c y.tab.h
216 if [ $# -ne 1 ]; then
217 eval LASTARG="\${$#}"
218 case "$LASTARG" in
219 *.y)
220 SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
221 if [ -f "$SRCFILE" ]; then
222 cp "$SRCFILE" y.tab.c
223 fi
224 SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
225 if [ -f "$SRCFILE" ]; then
226 cp "$SRCFILE" y.tab.h
227 fi
228 ;;
229 esac
230 fi
231 if [ ! -f y.tab.h ]; then
232 echo >y.tab.h
233 fi
234 if [ ! -f y.tab.c ]; then
235 echo 'main() { return 0; }' >y.tab.c
236 fi
237 ;;
238
239 lex|flex)
240 echo 1>&2 "\
241 WARNING: \`$1' is $msg. You should only need it if
242 you modified a \`.l' file. You may need the \`Flex' package
243 in order for those modifications to take effect. You can get
244 \`Flex' from any GNU archive site."
245 rm -f lex.yy.c
246 if [ $# -ne 1 ]; then
247 eval LASTARG="\${$#}"
248 case "$LASTARG" in
249 *.l)
250 SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
251 if [ -f "$SRCFILE" ]; then
252 cp "$SRCFILE" lex.yy.c
253 fi
254 ;;
255 esac
256 fi
257 if [ ! -f lex.yy.c ]; then
258 echo 'main() { return 0; }' >lex.yy.c
259 fi
260 ;;
261
262 help2man)
263 echo 1>&2 "\
264 WARNING: \`$1' is $msg. You should only need it if
265 you modified a dependency of a manual page. You may need the
266 \`Help2man' package in order for those modifications to take
267 effect. You can get \`Help2man' from any GNU archive site."
268
269 file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
270 if test -z "$file"; then
271 file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
272 fi
273 if [ -f "$file" ]; then
274 touch $file
275 else
276 test -z "$file" || exec >$file
277 echo ".ab help2man is required to generate this page"
278 exit 1
279 fi
280 ;;
281
282 makeinfo)
283 echo 1>&2 "\
284 WARNING: \`$1' is $msg. You should only need it if
285 you modified a \`.texi' or \`.texinfo' file, or any other file
286 indirectly affecting the aspect of the manual. The spurious
287 call might also be the consequence of using a buggy \`make' (AIX,
288 DU, IRIX). You might want to install the \`Texinfo' package or
289 the \`GNU make' package. Grab either from any GNU archive site."
290 # The file to touch is that specified with -o ...
291 file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
292 if test -z "$file"; then
293 # ... or it is the one specified with @setfilename ...
294 infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
295 file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile`
296 # ... or it is derived from the source name (dir/f.texi becomes f.info)
297 test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
298 fi
299 touch $file
300 ;;
301
302 tar)
303 shift
304
305 # We have already tried tar in the generic part.
306 # Look for gnutar/gtar before invocation to avoid ugly error
307 # messages.
308 if (gnutar --version > /dev/null 2>&1); then
309 gnutar "$@" && exit 0
310 fi
311 if (gtar --version > /dev/null 2>&1); then
312 gtar "$@" && exit 0
313 fi
314 firstarg="$1"
315 if shift; then
316 case "$firstarg" in
317 *o*)
318 firstarg=`echo "$firstarg" | sed s/o//`
319 tar "$firstarg" "$@" && exit 0
320 ;;
321 esac
322 case "$firstarg" in
323 *h*)
324 firstarg=`echo "$firstarg" | sed s/h//`
325 tar "$firstarg" "$@" && exit 0
326 ;;
327 esac
328 fi
329
330 echo 1>&2 "\
331 WARNING: I can't seem to be able to run \`tar' with the given arguments.
332 You may want to install GNU tar or Free paxutils, or check the
333 command line arguments."
334 exit 1
335 ;;
336
337 *)
338 echo 1>&2 "\
339 WARNING: \`$1' is needed, and is $msg.
340 You might have modified some files without having the
341 proper tools for further handling them. Check the \`README' file,
342 it often tells you about the needed prerequisites for installing
343 this package. You may also peek at any GNU archive site, in case
344 some other package would contain this missing \`$1' program."
345 exit 1
346 ;;
347 esac
348
349 exit 0
350
351 # Local variables:
352 # eval: (add-hook 'write-file-hooks 'time-stamp)
353 # time-stamp-start: "scriptversion="
354 # time-stamp-format: "%:y-%02m-%02d.%02H"
355 # time-stamp-end: "$"
356 # End:
00 /*
11 * File: IO.c
22 *
3 * Copyright (C) 2000, 2001, 2002 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1111 /*
12 * Dillo's signal driven IO engine
13 */
14
15 #include <pthread.h>
16
17 #include <stdio.h>
18 #include <string.h>
12 * Dillo's event driven IO engine
13 */
14
1915 #include <errno.h>
2016 #include <fcntl.h>
2117 #include <unistd.h>
22 #include <sys/stat.h>
23 #include <sys/uio.h>
24 #include <sys/socket.h>
25 #include <gdk/gdk.h>
2618 #include "../msg.h"
2719 #include "../chain.h"
28 #include "../list.h"
2920 #include "../klist.h"
3021 #include "IO.h"
31
32 #define DEBUG_LEVEL 5
33 #include "../debug.h"
34
22 #include "iowatch.hh"
3523
3624 /*
3725 * Symbolic defines for shutdown() function
3826 * (Not defined in the same header file, for all distros --Jcid)
3927 */
40 #define IO_StopRd 0
41 #define IO_StopWr 1
42 #define IO_StopRdWr 2
28 #define IO_StopRd 1
29 #define IO_StopWr 2
30 #define IO_StopRdWr (IO_StopRd | IO_StopWr)
31
32
33 typedef struct {
34 int Key; /* Primary Key (for klist) */
35 int Op; /* IORead | IOWrite */
36 int FD; /* Current File Descriptor */
37 int Flags; /* Flag array (look definitions above) */
38 int Status; /* errno code */
39 Dstr *Buf; /* Internal buffer */
40
41 void *Info; /* CCC Info structure for this IO */
42 } IOData_t;
4343
4444
4545 /*
5454 void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
5555 void *Data1, void *Data2);
5656
57 /*
58 * Debug procedure...
59
60 static void IO_print_cond_status(gchar *str, GIOCondition cond,
61 GIOChannel *gio, gint key)
62 {
63 MSG("%s FD=%d key=%d [", str, g_io_channel_unix_get_fd(gio), key);
64 MSG(cond & G_IO_IN ? "G_IO_IN " : "");
65 MSG(cond & G_IO_OUT ? "G_IO_OUT " : "");
66 MSG(cond & G_IO_PRI ? "G_IO_PRI " : "");
67 MSG(cond & G_IO_ERR ? "G_IO_ERR " : "");
68 MSG(cond & G_IO_HUP ? "G_IO_HUP " : "");
69 MSG(cond & G_IO_NVAL ? "G_IO_NVAL " : "");
70 MSG("]\n");
71 }
72 */
73
7457
7558 /* IO API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
7659
7760 /*
78 * Return a newly created, and initialized, 'io' struct
79 */
80 IOData_t *a_IO_new(gint op, gint fd)
81 {
82 IOData_t *io = g_new0(IOData_t, 1);
61 * Return a new, initialized, 'io' struct
62 */
63 static IOData_t *IO_new(int op)
64 {
65 IOData_t *io = dNew0(IOData_t, 1);
8366 io->Op = op;
84 io->FD = fd;
85 io->GioCh = g_io_channel_unix_new(fd);
67 io->FD = -1;
8668 io->Flags = 0;
8769 io->Key = 0;
70 io->Buf = dStr_sized_new(IOBufLen);
71
8872 return io;
8973 }
9074
91 /*
92 * Set the transfer buffer.
93 * (if we allocate it, we free it)
94 */
95 void a_IO_set_buf(IOData_t *io, void *Buf, size_t BufSize)
96 {
97 if (io->Flags & IOFlag_FreeIOBuf) {
98 g_free(io->BufStart);
99 io->Flags &= ~IOFlag_FreeIOBuf;
100 }
101
102 if (!Buf && BufSize) {
103 io->BufStart = io->Buf = g_malloc(BufSize);
104 io->BufSize = BufSize;
105 io->Flags |= IOFlag_FreeIOBuf;
106
107 } else {
108 io->BufStart = io->Buf = Buf;
109 io->BufSize = BufSize;
110 }
111 }
112
113 /*
114 * Add a new chunk to the transfer buffer.
115 * (we allocate it, so we free it)
116 */
117 void a_IO_add_buf(IOData_t *io, void *Buf, size_t BufSize)
118 {
119 size_t offset;
120
121 if (BufSize == 0) {
122 /* This is the last chunk */
123 io->Flags |= IOFlag_ForceClose;
124 return;
125 }
126
127 offset = (io->Buf) ? (gchar*)io->Buf - (gchar*)io->BufStart : 0;
128 io->BufStart = g_realloc(io->BufStart, offset + io->BufSize + BufSize);
129 memcpy((gchar*)io->BufStart + offset + io->BufSize, Buf, BufSize);
130 io->Buf = (gchar*)io->BufStart + offset;
131 io->BufSize += BufSize;
132 io->Flags |= IOFlag_FreeIOBuf;
133 }
134
135 /*
136 * Return transfer buffer.
137 *
138 void a_IO_get_buf(IOData_t *io, void **Buf, size_t *BufSize)
139 {
140 *Buf = io->Buf;
141 *BufSize = io->BufSize;
142 }
143 */
144
145 typedef struct {
146 pthread_t thrID; /* Thread id */
147 pthread_mutex_t mut;
148 pthread_cond_t cond;
149
150 gint FD;
151 void *Key;
152
153 void *Buf1Start;
154 void *Buf1;
155 size_t Buf1Size;
156 void *Buf2;
157 size_t Buf2Size;
158
159 gint LastChunk;
160 gint CloseFD;
161 gint Done;
162 } thr_data_t;
163
164 /* Active data for threaded chunk transfers */
165 static thr_data_t **ThrData = NULL;
166 static gint ThrDataSize = 0;
167 static gint ThrDataMax = 8;
168
169 /*
170 * Create a new ThrData node
171 */
172 static thr_data_t *IO_thrdata_new(gint FD, void *Key)
173 {
174 thr_data_t *data;
175
176 data = g_new(thr_data_t, 1);
177 pthread_mutex_init(&data->mut, NULL);
178 pthread_cond_init(&data->cond, NULL);
179 data->FD = FD;
180 data->Key = Key;
181 data->Buf1Start = data->Buf1 = data->Buf2 = NULL;
182 data->Buf1Size = data->Buf2Size = 0;
183 data->LastChunk = 0;
184 data->CloseFD = 0;
185 data->Done = 0;
186 return data;
187 }
188
189 /*
190 * Free a ThrData node
191 */
192 static void IO_thrdata_free(thr_data_t *td)
193 {
194 /* EBUSY should not happen: IO_thrdata_free is called after the
195 * thread is done */
196 while (pthread_cond_destroy(&td->cond) == EBUSY) {
197 g_warning("IO_thrdata_free: EBUSY\n");
198 if (pthread_cancel(td->thrID) == ESRCH)
199 break;
200 }
201
202 pthread_mutex_destroy(&td->mut);
203 g_free(td);
204 }
205
206 /*
207 * Search data node for a FD
208 * - The FD is searched using a Key because using the FD itself may fail
209 * due to a race condition between the FD close and its reuse.
210 * - This function also frees already closed data.
211 */
212 static thr_data_t *IO_thrdata_find(void *Key)
213 {
214 gint i, idx = -1;
215
216 _MSG("TL(%p): [", Key);
217 for (i = 0; i < ThrDataSize; ++i) {
218 _MSG(" %d%s", ThrData[i]->FD, ThrData[i]->Done ? "D" : "");
219 if (ThrData[i]->Done) {
220 IO_thrdata_free(ThrData[i]);
221 a_List_remove(ThrData, i, ThrDataSize);
222 --i;
223 } else if (ThrData[i]->Key == Key) {
224 idx = i;
225 _MSG("*");
226 }
227 }
228 _MSG("]\n");
229
230 if (idx != -1)
231 return ThrData[idx];
232 return NULL;
233 }
234
235
236 /*
237 * Write the data buffer through a FD.
238 * [This function runs on its own thread]
239 */
240 static void *IO_write_chunk(void *ptr)
241 {
242 gint lock = 0;
243 thr_data_t *data = ptr;
244 ssize_t St;
245 gint st, done;
246
247 _MSG("thr::\n");
248 _MSG(" [thrID:%lu]\n", (gulong)data->thrID);
249
250 while (1) {
251 _MSG("thr:: trying to lock mutex\n");
252 if (!lock) {
253 pthread_mutex_lock(&data->mut);
254 lock = 1;
255 }
256 _MSG("thr:: mutex locked!\n");
257 _MSG("thr:: Buf1:%p Buf2:%p LastChunk:%d Done:%d\n",
258 data->Buf1, data->Buf2, data->LastChunk, data->Done);
259 if (data->Buf2) {
260 /* Buf1 := Buf2; Buf2 := NULL */
261 g_free(data->Buf1Start);
262 data->Buf1Start = data->Buf1 = data->Buf2;
263 data->Buf1Size = data->Buf2Size;
264 data->Buf2 = NULL;
265 data->Buf2Size = 0;
266 _MSG("thr:: Buf1:%p Buf2:%p LastChunk:%d Done:%d\n",
267 data->Buf1, data->Buf2, data->LastChunk, data->Done);
268 pthread_mutex_unlock(&data->mut);
269 lock = 0;
270 _MSG("thr:: mutex unlocked!\n");
271
272 /*** write all ***/
273 done = 0;
274 while (!done) {
275 St = write(data->FD, data->Buf1, data->Buf1Size);
276 _MSG("thr:: St=%d\n", St);
277 if (St < 0) {
278 if (errno == EINTR) {
279 continue;
280 } else {
281 perror("IO_write_chunk");
282 return NULL;
283 }
284 } else if ((size_t)St < data->Buf1Size) {
285 /* Not all data written */
286 data->Buf1 = (gchar *)data->Buf1 + St;
287 data->Buf1Size -= St;
288 } else {
289 /* All data in buffer written */
290 done = 1;
291 }
292 }
293 }
294
295 /* Buf1 was written, prepare the next step... */
296 if (!lock) {
297 pthread_mutex_lock(&data->mut);
298 lock = 1;
299 }
300 if (data->Buf2)
301 continue;
302 else if (data->LastChunk) {
303 /* Only pipes are closed, sockets are left for the server to close */
304 if (data->CloseFD) {
305 do
306 st = close(data->FD);
307 while (st < 0 && errno == EINTR);
308 }
309 g_free(data->Buf1Start);
310 data->Done = 1;
311 pthread_mutex_unlock(&data->mut);
312 _MSG("thr:: LastChunk:%d Done:%d --Bailing out!\n",
313 data->LastChunk, data->Done);
314 return NULL;
315 } else {
316 _MSG("thr:: going to cond_wait...\n");
317 pthread_cond_wait(&data->cond, &data->mut);
318 lock = 1;
319 }
320
321 }/* while (1) */
322 }
323
324 /*
325 * Write a data chunk from a pthread
326 * (!Buf && !BufSize -> LastChunk -> close tansfer, don't close FD)
327 * (!Buf && BufSize == 1 -> LastChunk -> close tansfer, close FD)
328 */
329 void a_IO_write_chunk(gint FD, void *Key, void *Buf, size_t BufSize)
330 {
331 thr_data_t *data;
332 gint new_thread = 0;
333 static pthread_attr_t thrATTR;
334 static gint thrATTRInitialized = 0;
335
336 /* set the thread attribute to the detached state */
337 if (!thrATTRInitialized) {
338 pthread_attr_init(&thrATTR);
339 pthread_attr_setdetachstate(&thrATTR, PTHREAD_CREATE_DETACHED);
340 thrATTRInitialized = 1;
341 }
342
343 /* Search data node for this FD */
344 data = IO_thrdata_find(Key);
345 _MSG(" a_IO_write_chunk: data=%p Buf=%p, BufSize=%d, FD=%d\n",
346 data, Buf, BufSize, FD);
347
348 if (!data && Buf) {
349 data = IO_thrdata_new(FD, Key);
350 a_List_add(ThrData, ThrDataSize, ThrDataMax);
351 ThrData[ThrDataSize] = data;
352 ThrDataSize++;
353 new_thread = 1;
354 }
355
356 pthread_mutex_lock(&data->mut);
357 if (Buf) {
358 data->Buf2 = g_realloc(data->Buf2, data->Buf2Size + BufSize);
359 memcpy((gchar*)data->Buf2 + data->Buf2Size, Buf, BufSize);
360 data->Buf2Size = data->Buf2Size + BufSize;
361 } else {
362 data->LastChunk = 1;
363 data->CloseFD = (BufSize == 1) ? 1 : 0;
364 }
365 pthread_cond_signal(&data->cond);
366 pthread_mutex_unlock(&data->mut);
367
368 if (new_thread)
369 pthread_create(&data->thrID, &thrATTR, IO_write_chunk, data);
370 }
371
372
37375 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
37476
37577 /*
37779 */
37880 static void IO_ins(IOData_t *io)
37981 {
380 io->Key = a_Klist_insert(&ValidIOs, (gpointer)io);
82 if (io->Key == 0) {
83 io->Key = a_Klist_insert(&ValidIOs, io);
84 }
85 _MSG("IO_ins: io->Key=%d, Klist_length=%d\n",
86 io->Key, a_Klist_length(ValidIOs));
38187 }
38288
38389 /*
38591 */
38692 static void IO_del(IOData_t *io)
38793 {
388 a_Klist_remove(ValidIOs, io->Key);
94 if (io->Key != 0) {
95 a_Klist_remove(ValidIOs, io->Key);
96 }
97 io->Key = 0;
98 _MSG(" -->ValidIOs: %d\n", a_Klist_length(ValidIOs));
38999 }
390100
391101 /*
392102 * Return a io by its Key (NULL if not found)
393103 */
394 static IOData_t *IO_get(gint Key)
395 {
396 return a_Klist_get_data(ValidIOs, Key);
104 static IOData_t *IO_get(int Key)
105 {
106 return (IOData_t *)a_Klist_get_data(ValidIOs, Key);
397107 }
398108
399109 /*
401111 */
402112 static void IO_free(IOData_t *io)
403113 {
404 g_return_if_fail(IO_get(io->Key) == NULL);
405
406 if (io->Flags & IOFlag_FreeIOBuf)
407 a_IO_set_buf(io, NULL, 0);
408 g_free(io);
114 dStr_free(io->Buf, 1);
115 dFree(io);
409116 }
410117
411118 /*
412119 * Close an open FD, and remove io controls.
413120 * (This function can be used for Close and Abort operations)
414 */
415 static void IO_close_fd(IOData_t *io, gint CloseCode)
416 {
417 gint st;
121 * BUG: there's a race condition for Abort. The file descriptor is closed
122 * twice, and it could be reused for something else in between. It's simple
123 * to fix, but it'd be better to design a canonical way to Abort the CCC.
124 */
125 static void IO_close_fd(IOData_t *io, int CloseCode)
126 {
127 int st;
128 int events = 0;
129
130 _MSG("====> begin IO_close_fd (%d) Key=%d CloseCode=%d Flags=%d ",
131 io->FD, io->Key, CloseCode, io->Flags);
418132
419133 /* With HTTP, if we close the writing part, the reading one also gets
420134 * closed! (other clients may set 'IOFlag_ForceClose') */
421 if ((io->Flags & IOFlag_ForceClose) || (CloseCode != IO_StopWr))
135 if (((io->Flags & IOFlag_ForceClose) || (CloseCode == IO_StopRdWr)) &&
136 io->FD != -1) {
422137 do
423138 st = close(io->FD);
424139 while (st < 0 && errno == EINTR);
425
140 } else {
141 _MSG(" NOT CLOSING ");
142 }
426143 /* Remove this IOData_t reference, from our ValidIOs list
427144 * We don't deallocate it here, just remove from the list.*/
428145 IO_del(io);
429146
430147 /* Stop the polling on this FD */
431 g_source_remove(io->watch_id);
432 }
433
434 /*
435 * Abort an open FD.
436 * This function is called to abort a FD connection due to an IO error
437 * or just because the connection is not required anymore.
438 */
439 static gboolean IO_abort(IOData_t *io)
440 {
441 /* Close and finish this FD's activity */
442 IO_close_fd(io, IO_StopRdWr);
443
444 return FALSE;
148 if (CloseCode & IO_StopRd) {
149 events |= DIO_READ;
150 }
151 if (CloseCode & IO_StopWr) {
152 events |= DIO_WRITE;
153 }
154 a_IOwatch_remove_fd(io->FD, events);
155 _MSG(" end IO close (%d) <=====\n", io->FD);
445156 }
446157
447158 /*
448159 * Read data from a file descriptor into a specific buffer
449160 */
450 static gboolean IO_read(IOData_t *io)
451 {
161 static bool_t IO_read(IOData_t *io)
162 {
163 char Buf[IOBufLen];
452164 ssize_t St;
453 gboolean ret, DataPending;
454
455 DEBUG_MSG(3, " IO_read\n");
456
457 do {
458 ret = FALSE;
459 DataPending = FALSE;
460
461 St = read(io->FD, io->Buf, io->BufSize);
462 io->Status = St;
463 DEBUG_MSG(3, " IO_read: %s [errno %d] [St %ld]\n",
464 g_strerror(errno), errno, (glong)St);
465
466 if ( St < 0 ) {
467 /* Error */
468 io->Status = -errno;
469 if (errno == EINTR)
470 continue;
471 else if (errno == EAGAIN)
472 ret = TRUE;
473
474 } else if ( St == 0 ) {
475 /* All data read (EOF) */
476 IO_close_fd(io, IO_StopRd);
477 a_IO_ccc(OpEnd, 2, FWD, io->Info, io, NULL);
478
479 } else if ( (size_t)St < io->BufSize ){
480 /* We have all the new data */
481 a_IO_ccc(OpSend, 2, FWD, io->Info, io, NULL);
482 ret = TRUE;
483
484 } else { /* BytesRead == io->BufSize */
485 /* We have new data, and maybe more... */
486 a_IO_ccc(OpSend, 2, FWD, io->Info, io, NULL);
487 DataPending = TRUE;
488 }
489 } while (DataPending);
490
491 return ret;
492 }
493
494 /*
495 * Write data, from a specific buffer, into a file descriptor
496 * todo: Implement IOWrites.
497 */
498 static gboolean IO_write(IOData_t *io)
499 {
500 ssize_t St;
501 gboolean ret, DataPending;
502
503 DEBUG_MSG(3, " IO_write\n");
504
505 do {
506 ret = FALSE;
507 DataPending = FALSE;
508 St = write(io->FD, io->Buf, io->BufSize);
509 io->Status = St;
510
511 DEBUG_MSG(3, " IO_write: %s [errno %d] [St %ld]\n",
512 g_strerror(errno), errno, (glong)St);
513
514 if ( St < 0 ) {
515 /* Error */
516 io->Status = -errno;
165 bool_t ret = FALSE;
166 int io_key = io->Key;
167
168 _MSG(" IO_read\n");
169
170 /* this is a new read-buffer */
171 dStr_truncate(io->Buf, 0);
172 io->Status = 0;
173
174 while (1) {
175 St = read(io->FD, Buf, IOBufLen);
176 if (St > 0) {
177 dStr_append_l(io->Buf, Buf, St);
178 continue;
179 } else if (St < 0) {
517180 if (errno == EINTR) {
518181 continue;
519182 } else if (errno == EAGAIN) {
520 DEBUG_MSG(4, " IO_write: EAGAIN\n");
521183 ret = TRUE;
522 }
523 } else if ( (size_t)St < io->BufSize ){
184 break;
185 } else {
186 io->Status = errno;
187 break;
188 }
189 } else { /* St == 0 */
190 break;
191 }
192 }
193
194 if (io->Buf->len > 0) {
195 /* send what we've got so far */
196 a_IO_ccc(OpSend, 2, FWD, io->Info, io, NULL);
197 }
198 if (St == 0) {
199 /* TODO: design a general way to avoid reentrancy problems with CCC. */
200
201 /* The following check is necessary because the above CCC operation
202 * may abort the whole Chain. */
203 if ((io = IO_get(io_key))) {
204 /* All data read (EOF) */
205 _MSG("IO_read: io->Key=%d io_key=%d\n", io->Key, io_key);
206 a_IO_ccc(OpEnd, 2, FWD, io->Info, io, NULL);
207 }
208 }
209 return ret;
210 }
211
212 /*
213 * Write data, from a specific buffer, into a file descriptor
214 */
215 static bool_t IO_write(IOData_t *io)
216 {
217 ssize_t St;
218 bool_t ret = FALSE;
219
220 _MSG(" IO_write\n");
221 io->Status = 0;
222
223 while (1) {
224 St = write(io->FD, io->Buf->str, io->Buf->len);
225 if (St < 0) {
226 /* Error */
227 if (errno == EINTR) {
228 continue;
229 } else if (errno == EAGAIN) {
230 ret = TRUE;
231 break;
232 } else {
233 io->Status = errno;
234 break;
235 }
236 } else if (St < io->Buf->len) {
524237 /* Not all data written */
525 io->BufSize -= St;
526 io->Buf = (gchar *)io->Buf + St;
527 DataPending = TRUE;
528 DEBUG_MSG(4, " IO_write: Changing io->Buf (%ld)\n", (glong)St);
529
238 dStr_erase (io->Buf, 0, St);
530239 } else {
531240 /* All data in buffer written */
532 if ( io->Op == IOWrite ) {
533 /* Single write */
534 IO_close_fd(io, IO_StopWr);
535 a_IO_ccc(OpEnd, 1, FWD, io->Info, io, NULL);
536 } else if ( io->Op == IOWrites ) {
537 /* Writing in small chunks */
538 /* clear the buffer, and wait for a new chunk */
539 a_IO_set_buf(io, NULL, 0);
540 if (io->Flags & IOFlag_ForceClose) {
541 IO_close_fd(io, IO_StopWr);
542 a_IO_ccc(OpEnd, 1, FWD, io->Info, io, NULL);
543 }
544 }
241 dStr_truncate(io->Buf, 0);
242 break;
545243 }
546 } while (DataPending);
244 }
547245
548246 return ret;
549247 }
550248
551249 /*
552250 * Handle background IO for a given FD (reads | writes)
553 * (This function gets called by glib when there's activity in the FD)
554 */
555 static gboolean IO_callback(GIOChannel *src, GIOCondition cond, gpointer data)
556 {
557 gboolean ret = FALSE;
558 gint io_key = GPOINTER_TO_INT(data);
251 * (This function gets called when there's activity in the FD)
252 */
253 static int IO_callback(IOData_t *io)
254 {
255 bool_t ret = FALSE;
256
257 _MSG("IO_callback:: (%s) FD = %d\n",
258 (io->Op == IORead) ? "IORead" : "IOWrite", io->FD);
259
260 if (io->Op == IORead) { /* Read */
261 ret = IO_read(io);
262 } else if (io->Op == IOWrite) { /* Write */
263 ret = IO_write(io);
264 }
265 return (ret) ? 1 : 0;
266 }
267
268 /*
269 * Handle the READ event of a FD.
270 */
271 static void IO_fd_read_cb(int fd, void *data)
272 {
273 int io_key = VOIDP2INT(data);
559274 IOData_t *io = IO_get(io_key);
560275
561 /* IO_print_cond_status("IO_callback: ", cond, src, io_key); */
562
563 /* There should be no more glib events on already closed FDs --Jcid */
564 if ( io == NULL ) {
565 g_warning("IO_callback: call on already closed io!\n");
566 g_assert_not_reached();
567 return FALSE;
568 }
569
570 if ( cond & (G_IO_IN | G_IO_HUP) ){ /* Read */
571 ret = IO_read(io);
572 } else if ( cond & G_IO_OUT ){ /* Write */
573 ret = IO_write(io);
574 io = IO_get(io_key); /* IO_write may have freed 'io' */
575 if (io && io->Status == -EAGAIN)
576 ret = TRUE; /* wait for another G_IO_OUT event... */
577 }
578
579 if ( cond & G_IO_ERR ){ /* Error */
580 /* IO_read/IO_write may free 'io' */
581 if ((io = IO_get(io_key))) {
582 io->Status = -EIO;
583 ret = IO_abort(io);
584 } else {
585 ret = FALSE;
586 }
587 } else if ( cond & (G_IO_PRI | G_IO_NVAL) ){
588 /* Ignore these exceptional conditions */
589 ret = FALSE;
590 }
591
592 return ret;
593 }
594
595 /*
596 * Receive an IO request (IORead | IOWrite | IOWrites),
597 * Set the GIOChannel and let it flow!
276 /* There should be no more events on already closed FDs --Jcid */
277 if (io == NULL) {
278 MSG_ERR("IO_fd_read_cb: call on already closed io!\n");
279 a_IOwatch_remove_fd(fd, DIO_READ);
280
281 } else {
282 if (IO_callback(io) == 0)
283 a_IOwatch_remove_fd(fd, DIO_READ);
284 }
285 }
286
287 /*
288 * Handle the WRITE event of a FD.
289 */
290 static void IO_fd_write_cb(int fd, void *data)
291 {
292 int io_key = VOIDP2INT(data);
293 IOData_t *io = IO_get(io_key);
294
295 if (io == NULL) {
296 /* There must be no more events on already closed FDs --Jcid */
297 MSG_ERR("IO_fd_write_cb: call on already closed io!\n");
298 a_IOwatch_remove_fd(fd, DIO_WRITE);
299
300 } else {
301 if (IO_callback(io) == 0)
302 a_IOwatch_remove_fd(fd, DIO_WRITE);
303 }
304 }
305
306 /*
307 * Receive an IO request (IORead | IOWrite),
308 * Set a watch for it, and let it flow!
598309 */
599310 static void IO_submit(IOData_t *r_io)
600311 {
312 if (r_io->FD < 0) {
313 MSG_ERR("IO_submit: FD not initialized\n");
314 return;
315 }
316
601317 /* Insert this IO in ValidIOs */
602318 IO_ins(r_io);
603319
320 _MSG("IO_submit:: (%s) FD = %d\n",
321 (r_io->Op == IORead) ? "IORead" : "IOWrite", r_io->FD);
322
604323 /* Set FD to background and to close on exec. */
605 fcntl(r_io->FD, F_SETFL,
606 O_NONBLOCK | fcntl(r_io->FD, F_GETFL));
607 fcntl(r_io->FD, F_SETFD,
608 FD_CLOEXEC | fcntl(r_io->FD, F_GETFD));
609
610 if ( r_io->Op == IORead ) {
611 r_io->watch_id =
612 g_io_add_watch(r_io->GioCh, G_IO_IN | G_IO_ERR | G_IO_HUP,
613 IO_callback, GINT_TO_POINTER (r_io->Key));
614 g_io_channel_unref(r_io->GioCh);
615
616 } else if ( r_io->Op == IOWrite || r_io->Op == IOWrites ) {
617 r_io->watch_id =
618 g_io_add_watch(r_io->GioCh, G_IO_OUT | G_IO_ERR,
619 IO_callback, GINT_TO_POINTER (r_io->Key));
620 g_io_channel_unref(r_io->GioCh);
621 }
622 }
623
624 /*
625 * Receive IO request (IORead | IOWrite | IOWrites),
626 * and either start or keep it flowing.
627 */
628 static void IO_send(IOData_t *io)
629 {
630 if (!io->Key)
631 IO_submit(io);
632 }
633
324 fcntl(r_io->FD, F_SETFL, O_NONBLOCK | fcntl(r_io->FD, F_GETFL));
325 fcntl(r_io->FD, F_SETFD, FD_CLOEXEC | fcntl(r_io->FD, F_GETFD));
326
327 if (r_io->Op == IORead) {
328 a_IOwatch_add_fd(r_io->FD, DIO_READ,
329 IO_fd_read_cb, INT2VOIDP(r_io->Key));
330
331 } else if (r_io->Op == IOWrite) {
332 a_IOwatch_add_fd(r_io->FD, DIO_WRITE,
333 IO_fd_write_cb, INT2VOIDP(r_io->Key));
334 }
335 }
634336
635337 /*
636338 * CCC function for the IO module
639341 void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
640342 void *Data1, void *Data2)
641343 {
642 IOData_t *io = Data1;
643
644 a_Chain_debug_msg("a_IO_ccc", Op, Branch, Dir);
344 IOData_t *io;
345 DataBuf *dbuf;
346
347 dReturn_if_fail( a_Chain_check("a_IO_ccc", Op, Branch, Dir, Info) );
645348
646349 if (Branch == 1) {
647350 if (Dir == BCK) {
648 /* Write data */
351 /* Write data using select */
649352 switch (Op) {
650353 case OpStart:
651 io->Info = Info;
354 io = IO_new(IOWrite);
652355 Info->LocalKey = io;
653356 break;
654357 case OpSend:
655 /* this part submits the io */
656 IO_send(io);
657 break;
358 io = Info->LocalKey;
359 if (Data2 && !strcmp(Data2, "FD")) {
360 io->FD = *(int*)Data1; /* SockFD */
361 } else {
362 dbuf = Data1;
363 dStr_append_l(io->Buf, dbuf->Buf, dbuf->Size);
364 IO_submit(io);
365 }
366 break;
367 case OpEnd:
658368 case OpAbort:
659369 io = Info->LocalKey;
660 IO_abort(io);
370 if (io->Buf->len > 0) {
371 MSG_WARN("IO_write, closing with pending data not sent\n");
372 MSG_WARN(" \"%s\"\n", dStr_printable(io->Buf, 2048));
373 }
374 /* close FD, remove from ValidIOs and remove its watch */
375 IO_close_fd(io, Op == OpEnd ? IO_StopWr : IO_StopRdWr);
661376 IO_free(io);
662 g_free(Info);
663 break;
664 }
665 } else { /* FWD */
377 dFree(Info);
378 break;
379 default:
380 MSG_WARN("Unused CCC\n");
381 break;
382 }
383 } else { /* 1 FWD */
666384 /* Write-data status */
667385 switch (Op) {
668 case OpEnd:
669 a_Chain_fcb(OpEnd, Info, io, NULL);
670 IO_free(io);
671 break;
672 case OpAbort:
673 a_Chain_fcb(OpAbort, Info, NULL, NULL);
674 IO_free(io);
386 default:
387 MSG_WARN("Unused CCC\n");
675388 break;
676389 }
677390 }
681394 /* This part catches the reader's messages */
682395 switch (Op) {
683396 case OpStart:
397 io = IO_new(IORead);
684398 Info->LocalKey = io;
685399 io->Info = Info;
686 IO_submit(io);
400 break;
401 case OpSend:
402 io = Info->LocalKey;
403 if (Data2 && !strcmp(Data2, "FD")) {
404 io->FD = *(int*)Data1; /* SockFD */
405 IO_submit(io);
406 }
687407 break;
688408 case OpAbort:
689409 io = Info->LocalKey;
690 IO_abort(io);
410 IO_close_fd(io, IO_StopRdWr);
691411 IO_free(io);
692 g_free(Info);
693 break;
694 }
695 } else { /* FWD */
412 dFree(Info);
413 break;
414 default:
415 MSG_WARN("Unused CCC\n");
416 break;
417 }
418 } else { /* 2 FWD */
696419 /* Send read-data */
420 io = Data1;
697421 switch (Op) {
698 case OpStart:
699 io->Info = Info;
700 Info->LocalKey = io;
701 a_Chain_link_new(Info, a_IO_ccc, FWD, a_Cache_ccc, 2, 2);
702 a_Chain_fcb(OpStart, Info, io, io->ExtData);
703 IO_submit(io);
704 break;
705422 case OpSend:
706 a_Chain_fcb(OpSend, Info, io, NULL);
423 dbuf = a_Chain_dbuf_new(io->Buf->str, io->Buf->len, 0);
424 a_Chain_fcb(OpSend, Info, dbuf, NULL);
425 dFree(dbuf);
707426 break;
708427 case OpEnd:
709 a_Chain_fcb(OpEnd, Info, io, NULL);
428 a_Chain_fcb(OpEnd, Info, NULL, NULL);
429 IO_close_fd(io, IO_StopRdWr); /* IO_StopRd would leak FDs */
710430 IO_free(io);
711 break;
712 case OpAbort:
713 MSG(" Not implemented\n");
431 dFree(Info);
432 break;
433 default:
434 MSG_WARN("Unused CCC\n");
714435 break;
715436 }
716437 }
717
718 } else if (Branch == 3) {
719 if (Dir == BCK) {
720 /* Write data using a thread */
721 switch (Op) {
722 case OpStart:
723 {
724 gint *fd = g_new(gint, 1);
725 *fd = *(int*)Data1; /* SockFD */
726 Info->LocalKey = fd;
727 break;
728 }
729 case OpEnd:
730 _MSG(" Info=%p Info->LocalKey=%p FD=%d Data1=%p\n",
731 Info, Info->LocalKey, *(int*)Info->LocalKey, Data1);
732 a_IO_write_chunk(*(int*)Info->LocalKey, Info,
733 NULL, (Data1) ? (size_t)1 : (size_t)0);
734 g_free(Info->LocalKey);
735 break;
736 case OpSend:
737 {
738 /* this part submits the data to the thread */
739 DataBuf *dbuf = Data1;
740 a_IO_write_chunk(*(int*)Info->LocalKey, Info,
741 dbuf->Buf, (size_t)dbuf->Size);
742 break;
743 }
744 case OpAbort:
745 g_free(Info->LocalKey);
746 g_free(Info);
747 break;
748 }
749 } else { /* FWD */
750 /* Write-data status */
751 switch (Op) {
752 case OpEnd:
753 a_Chain_fcb(OpEnd, Info, io, NULL);
754 IO_free(io);
755 break;
756 case OpAbort:
757 a_Chain_fcb(OpAbort, Info, NULL, NULL);
758 IO_free(io);
759 break;
760 }
761 }
762 }
763 }
764
438 }
439 }
440
00 #ifndef __IO_H__
11 #define __IO_H__
22
3 #include<unistd.h>
4 #include<sys/uio.h>
5 #include <glib.h>
6
3 #include "d_size.h"
4 #include "../../dlib/dlib.h"
75 #include "../chain.h"
86
97 /*
119 */
1210 #define IORead 0
1311 #define IOWrite 1
14 #define IOWrites 2
15 #define IOClose 3
16 #define IOAbort 4
12 #define IOClose 2
13 #define IOAbort 3
1714
1815 /*
1916 * IO Flags
2017 */
21 #define IOFlag_FreeIOBuf (1 << 1)
22 #define IOFlag_ForceClose (1 << 2)
23 #define IOFlag_SingleWrite (1 << 3)
18 #define IOFlag_ForceClose (1 << 1)
19 #define IOFlag_SingleWrite (1 << 2)
2420
2521 /*
2622 * IO constants
2723 */
28 #define IOBufLen 4096
29 #define IOBufLen_Http 4096
30 #define IOBufLen_File 4096
31 #define IOBufLen_Proto 4096
32 #define IOBufLen_About 4096
33
34
35 typedef struct {
36 gint Key; /* Primary Key (for klist) */
37 gint Op; /* IORead | IOWrite | IOWrites */
38 gint FD; /* Current File Descriptor */
39 gint Flags; /* Flag array (look definitions above) */
40 glong Status; /* Number of bytes read, or -errno code */
41
42 void *Buf; /* Buffer place */
43 size_t BufSize; /* Buffer length */
44 void *BufStart; /* PRIVATE: only used inside IO.c! */
45
46 void *ExtData; /* External data reference (not used by IO.c) */
47 void *Info; /* CCC Info structure for this IO */
48 GIOChannel *GioCh; /* IO channel */
49 guint watch_id; /* glib's event source id */
50 } IOData_t;
24 #define IOBufLen 8192
5125
5226
5327 /*
5428 * Exported functions
5529 */
56 IOData_t* a_IO_new(gint op, gint fd);
57 void a_IO_set_buf(IOData_t *io, void *Buf, size_t BufSize);
58 void a_IO_add_buf(IOData_t *io, void *Buf, size_t BufSize);
5930 /* Note: a_IO_ccc() is defined in Url.h together with the *_ccc() set */
6031
61 void a_IO_write_chunk(gint FD, void *Key, void *Buf, size_t BufSize);
32
33 /*
34 * Exported data
35 */
36 extern const char *AboutSplash;
37
6238
6339 #endif /* __IO_H__ */
6440
0 AM_CFLAGS = @GTK_CFLAGS@
1 AM_LIBS = @GTK_LIBS@
0 AM_CPPFLAGS = \
1 -I$(top_srcdir) \
2 -DDILLO_BINDIR='"$(bindir)/"'
3 AM_CFLAGS = @LIBFLTK_CFLAGS@
4 AM_CXXFLAGS = @LIBFLTK_CXXFLAGS@
25
3 noinst_LIBRARIES = libDio.a
6 noinst_LIBRARIES = libDiof.a
47
5 libDio_a_SOURCES = \
8 libDiof_a_SOURCES = \
69 mime.c \
710 mime.h \
811 about.c \
9 Url.c \
1012 Url.h \
11 proto.c \
1213 http.c \
1314 dpi.c \
1415 IO.c \
16 iowatch.cc \
17 iowatch.hh \
1518 IO.h
+0
-431
src/IO/Makefile.in less more
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 SOURCES = $(libDio_a_SOURCES)
17
18 srcdir = @srcdir@
19 top_srcdir = @top_srcdir@
20 VPATH = @srcdir@
21 pkgdatadir = $(datadir)/@PACKAGE@
22 pkglibdir = $(libdir)/@PACKAGE@
23 pkgincludedir = $(includedir)/@PACKAGE@
24 top_builddir = ../..
25 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
26 INSTALL = @INSTALL@
27 install_sh_DATA = $(install_sh) -c -m 644
28 install_sh_PROGRAM = $(install_sh) -c
29 install_sh_SCRIPT = $(install_sh) -c
30 INSTALL_HEADER = $(INSTALL_DATA)
31 transform = $(program_transform_name)
32 NORMAL_INSTALL = :
33 PRE_INSTALL = :
34 POST_INSTALL = :
35 NORMAL_UNINSTALL = :
36 PRE_UNINSTALL = :
37 POST_UNINSTALL = :
38 build_triplet = @build@
39 host_triplet = @host@
40 target_triplet = @target@
41 subdir = src/IO
42 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
43 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
44 am__aclocal_m4_deps = $(top_srcdir)/configure.in
45 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
46 $(ACLOCAL_M4)
47 mkinstalldirs = $(install_sh) -d
48 CONFIG_HEADER = $(top_builddir)/config.h
49 CONFIG_CLEAN_FILES =
50 LIBRARIES = $(noinst_LIBRARIES)
51 AR = ar
52 ARFLAGS = cru
53 libDio_a_AR = $(AR) $(ARFLAGS)
54 libDio_a_LIBADD =
55 am_libDio_a_OBJECTS = mime.$(OBJEXT) about.$(OBJEXT) Url.$(OBJEXT) \
56 proto.$(OBJEXT) http.$(OBJEXT) dpi.$(OBJEXT) IO.$(OBJEXT)
57 libDio_a_OBJECTS = $(am_libDio_a_OBJECTS)
58 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
59 depcomp = $(SHELL) $(top_srcdir)/depcomp
60 am__depfiles_maybe = depfiles
61 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
62 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
63 CCLD = $(CC)
64 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
65 SOURCES = $(libDio_a_SOURCES)
66 DIST_SOURCES = $(libDio_a_SOURCES)
67 ETAGS = etags
68 CTAGS = ctags
69 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
70 ACLOCAL = @ACLOCAL@
71 AMDEP_FALSE = @AMDEP_FALSE@
72 AMDEP_TRUE = @AMDEP_TRUE@
73 AMTAR = @AMTAR@
74 AUTOCONF = @AUTOCONF@
75 AUTOHEADER = @AUTOHEADER@
76 AUTOMAKE = @AUTOMAKE@
77 AWK = @AWK@
78 CC = @CC@
79 CCDEPMODE = @CCDEPMODE@
80 CFLAGS = @CFLAGS@
81 CPP = @CPP@
82 CPPFLAGS = @CPPFLAGS@
83 CXX = @CXX@
84 CXXDEPMODE = @CXXDEPMODE@
85 CXXFLAGS = @CXXFLAGS@
86 CYGPATH_W = @CYGPATH_W@
87 DEFS = @DEFS@
88 DEPDIR = @DEPDIR@
89 DLGUI_FALSE = @DLGUI_FALSE@
90 DLGUI_TRUE = @DLGUI_TRUE@
91 ECHO_C = @ECHO_C@
92 ECHO_N = @ECHO_N@
93 ECHO_T = @ECHO_T@
94 EGREP = @EGREP@
95 EXEEXT = @EXEEXT@
96 GLIB_CFLAGS = @GLIB_CFLAGS@
97 GLIB_CONFIG = @GLIB_CONFIG@
98 GLIB_LIBS = @GLIB_LIBS@
99 GTK_CFLAGS = @GTK_CFLAGS@
100 GTK_CONFIG = @GTK_CONFIG@
101 GTK_LIBS = @GTK_LIBS@
102 INSTALL_DATA = @INSTALL_DATA@
103 INSTALL_PROGRAM = @INSTALL_PROGRAM@
104 INSTALL_SCRIPT = @INSTALL_SCRIPT@
105 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
106 LDFLAGS = @LDFLAGS@
107 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
108 LIBFLTK_LIBS = @LIBFLTK_LIBS@
109 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
110 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
111 LIBJPEG_LIBS = @LIBJPEG_LIBS@
112 LIBOBJS = @LIBOBJS@
113 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
114 LIBPNG_LIBS = @LIBPNG_LIBS@
115 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
116 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
117 LIBS = @LIBS@
118 LIBSSL_LIBS = @LIBSSL_LIBS@
119 LIBZ_LIBS = @LIBZ_LIBS@
120 LTLIBOBJS = @LTLIBOBJS@
121 MAKEINFO = @MAKEINFO@
122 OBJEXT = @OBJEXT@
123 PACKAGE = @PACKAGE@
124 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
125 PACKAGE_NAME = @PACKAGE_NAME@
126 PACKAGE_STRING = @PACKAGE_STRING@
127 PACKAGE_TARNAME = @PACKAGE_TARNAME@
128 PACKAGE_VERSION = @PACKAGE_VERSION@
129 PATH_SEPARATOR = @PATH_SEPARATOR@
130 RANLIB = @RANLIB@
131 SET_MAKE = @SET_MAKE@
132 SHELL = @SHELL@
133 STRIP = @STRIP@
134 VERSION = @VERSION@
135 ac_ct_CC = @ac_ct_CC@
136 ac_ct_CXX = @ac_ct_CXX@
137 ac_ct_RANLIB = @ac_ct_RANLIB@
138 ac_ct_STRIP = @ac_ct_STRIP@
139 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
140 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
141 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
142 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
143 am__include = @am__include@
144 am__leading_dot = @am__leading_dot@
145 am__quote = @am__quote@
146 am__tar = @am__tar@
147 am__untar = @am__untar@
148 bindir = @bindir@
149 build = @build@
150 build_alias = @build_alias@
151 build_cpu = @build_cpu@
152 build_os = @build_os@
153 build_vendor = @build_vendor@
154 datadir = @datadir@
155 exec_prefix = @exec_prefix@
156 host = @host@
157 host_alias = @host_alias@
158 host_cpu = @host_cpu@
159 host_os = @host_os@
160 host_vendor = @host_vendor@
161 includedir = @includedir@
162 infodir = @infodir@
163 install_sh = @install_sh@
164 libdir = @libdir@
165 libexecdir = @libexecdir@
166 localstatedir = @localstatedir@
167 mandir = @mandir@
168 mkdir_p = @mkdir_p@
169 oldincludedir = @oldincludedir@
170 prefix = @prefix@
171 program_transform_name = @program_transform_name@
172 sbindir = @sbindir@
173 sharedstatedir = @sharedstatedir@
174 sysconfdir = @sysconfdir@
175 target = @target@
176 target_alias = @target_alias@
177 target_cpu = @target_cpu@
178 target_os = @target_os@
179 target_vendor = @target_vendor@
180 AM_CFLAGS = @GTK_CFLAGS@
181 AM_LIBS = @GTK_LIBS@
182 noinst_LIBRARIES = libDio.a
183 libDio_a_SOURCES = \
184 mime.c \
185 mime.h \
186 about.c \
187 Url.c \
188 Url.h \
189 proto.c \
190 http.c \
191 dpi.c \
192 IO.c \
193 IO.h
194
195 all: all-am
196
197 .SUFFIXES:
198 .SUFFIXES: .c .o .obj
199 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
200 @for dep in $?; do \
201 case '$(am__configure_deps)' in \
202 *$$dep*) \
203 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
204 && exit 0; \
205 exit 1;; \
206 esac; \
207 done; \
208 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/IO/Makefile'; \
209 cd $(top_srcdir) && \
210 $(AUTOMAKE) --gnu src/IO/Makefile
211 .PRECIOUS: Makefile
212 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
213 @case '$?' in \
214 *config.status*) \
215 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
216 *) \
217 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
218 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
219 esac;
220
221 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
222 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
223
224 $(top_srcdir)/configure: $(am__configure_deps)
225 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
226 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
227 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
228
229 clean-noinstLIBRARIES:
230 -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
231 libDio.a: $(libDio_a_OBJECTS) $(libDio_a_DEPENDENCIES)
232 -rm -f libDio.a
233 $(libDio_a_AR) libDio.a $(libDio_a_OBJECTS) $(libDio_a_LIBADD)
234 $(RANLIB) libDio.a
235
236 mostlyclean-compile:
237 -rm -f *.$(OBJEXT)
238
239 distclean-compile:
240 -rm -f *.tab.c
241
242 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IO.Po@am__quote@
243 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Url.Po@am__quote@
244 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/about.Po@am__quote@
245 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpi.Po@am__quote@
246 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http.Po@am__quote@
247 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mime.Po@am__quote@
248 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proto.Po@am__quote@
249
250 .c.o:
251 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
252 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
253 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
254 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
255 @am__fastdepCC_FALSE@ $(COMPILE) -c $<
256
257 .c.obj:
258 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
259 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
260 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
261 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
262 @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
263 uninstall-info-am:
264
265 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
266 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
267 unique=`for i in $$list; do \
268 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
269 done | \
270 $(AWK) ' { files[$$0] = 1; } \
271 END { for (i in files) print i; }'`; \
272 mkid -fID $$unique
273 tags: TAGS
274
275 TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
276 $(TAGS_FILES) $(LISP)
277 tags=; \
278 here=`pwd`; \
279 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
280 unique=`for i in $$list; do \
281 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
282 done | \
283 $(AWK) ' { files[$$0] = 1; } \
284 END { for (i in files) print i; }'`; \
285 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
286 test -n "$$unique" || unique=$$empty_fix; \
287 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
288 $$tags $$unique; \
289 fi
290 ctags: CTAGS
291 CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
292 $(TAGS_FILES) $(LISP)
293 tags=; \
294 here=`pwd`; \
295 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
296 unique=`for i in $$list; do \
297 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
298 done | \
299 $(AWK) ' { files[$$0] = 1; } \
300 END { for (i in files) print i; }'`; \
301 test -z "$(CTAGS_ARGS)$$tags$$unique" \
302 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
303 $$tags $$unique
304
305 GTAGS:
306 here=`$(am__cd) $(top_builddir) && pwd` \
307 && cd $(top_srcdir) \
308 && gtags -i $(GTAGS_ARGS) $$here
309
310 distclean-tags:
311 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
312
313 distdir: $(DISTFILES)
314 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
315 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
316 list='$(DISTFILES)'; for file in $$list; do \
317 case $$file in \
318 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
319 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
320 esac; \
321 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
322 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
323 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
324 dir="/$$dir"; \
325 $(mkdir_p) "$(distdir)$$dir"; \
326 else \
327 dir=''; \
328 fi; \
329 if test -d $$d/$$file; then \
330 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
331 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
332 fi; \
333 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
334 else \
335 test -f $(distdir)/$$file \
336 || cp -p $$d/$$file $(distdir)/$$file \
337 || exit 1; \
338 fi; \
339 done
340 check-am: all-am
341 check: check-am
342 all-am: Makefile $(LIBRARIES)
343 installdirs:
344 install: install-am
345 install-exec: install-exec-am
346 install-data: install-data-am
347 uninstall: uninstall-am
348
349 install-am: all-am
350 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
351
352 installcheck: installcheck-am
353 install-strip:
354 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
355 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
356 `test -z '$(STRIP)' || \
357 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
358 mostlyclean-generic:
359
360 clean-generic:
361
362 distclean-generic:
363 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
364
365 maintainer-clean-generic:
366 @echo "This command is intended for maintainers to use"
367 @echo "it deletes files that may require special tools to rebuild."
368 clean: clean-am
369
370 clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am
371
372 distclean: distclean-am
373 -rm -rf ./$(DEPDIR)
374 -rm -f Makefile
375 distclean-am: clean-am distclean-compile distclean-generic \
376 distclean-tags
377
378 dvi: dvi-am
379
380 dvi-am:
381
382 html: html-am
383
384 info: info-am
385
386 info-am:
387
388 install-data-am:
389
390 install-exec-am:
391
392 install-info: install-info-am
393
394 install-man:
395
396 installcheck-am:
397
398 maintainer-clean: maintainer-clean-am
399 -rm -rf ./$(DEPDIR)
400 -rm -f Makefile
401 maintainer-clean-am: distclean-am maintainer-clean-generic
402
403 mostlyclean: mostlyclean-am
404
405 mostlyclean-am: mostlyclean-compile mostlyclean-generic
406
407 pdf: pdf-am
408
409 pdf-am:
410
411 ps: ps-am
412
413 ps-am:
414
415 uninstall-am: uninstall-info-am
416
417 .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
418 clean-noinstLIBRARIES ctags distclean distclean-compile \
419 distclean-generic distclean-tags distdir dvi dvi-am html \
420 html-am info info-am install install-am install-data \
421 install-data-am install-exec install-exec-am install-info \
422 install-info-am install-man install-strip installcheck \
423 installcheck-am installdirs maintainer-clean \
424 maintainer-clean-generic mostlyclean mostlyclean-compile \
425 mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
426 uninstall-am uninstall-info-am
427
428 # Tell versions [3.59,3.63) of GNU make to not export all variables.
429 # Otherwise a system limit (for SysV at least) may be exceeded.
430 .NOEXPORT:
+0
-114
src/IO/Url.c less more
0 /*
1 * File: Url.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 Randall Maas <randym@acm.org>,
5 * Copyright (C) 1999, 2000 Jorge Arellano Cid <jcid@dillo.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 /* Heavily modified by Jcid, Dec 1999 & Jun 2000
14 *
15 * This module selects the apropriate CCC-function for a given URL.
16 */
17
18
19 #include <glib.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include "../list.h"
23 #include "../misc.h"
24 #include "Url.h"
25
26 typedef struct {
27 const char *Name; /* Method name */
28 UrlOpener_t Data; /* Pointer to a function */
29 } UrlMethod_t;
30
31 typedef struct {
32 const char *Name; /* Method name */
33 ChainFunction_t cccFunction; /* Pointer to a CCC function */
34 } UrlMethod2_t;
35
36
37 /*
38 * Local data
39 */
40 /*
41 static gint UrlOpenersSize = 0, UrlOpenersMax = 8;
42 static UrlOpener_t *UrlOpeners = NULL;
43
44 static gint UrlMethodsSize = 0, UrlMethodsMax = 8;
45 static UrlMethod_t *UrlMethods = NULL;
46 */
47
48 /*
49 * Add a method for Url handling
50 * (currently we have File, About & Http)
51 * -- Currently not used
52 */
53 /*
54 static gint Url_add_method(const char *Name, UrlOpener_t Method)
55 {
56 a_List_add(UrlMethods, UrlMethodsSize, UrlMethodsMax);
57 UrlMethods[UrlMethodsSize].Name = Name;
58 UrlMethods[UrlMethodsSize].Data = Method;
59 UrlMethodsSize++;
60 return 0;
61 }
62 */
63
64 /*
65 * Adds method 'F' to the list of URL openers.
66 * UrlOpeners are called in LIFO order until success.
67 * -- Currently not used
68 */
69 /*
70 static gint Url_add_opener(UrlOpener_t F)
71 {
72 a_List_add(UrlOpeners, UrlOpenersSize, UrlOpenersMax);
73 UrlOpeners[UrlOpenersSize] = F;
74 UrlOpenersSize++;
75 return 0;
76 }
77 */
78
79 /*
80 * Search the cccList for a matching ccc function.
81 */
82 ChainFunction_t a_Url_get_ccc_funct(const DilloUrl *Url)
83 {
84 static UrlMethod2_t cccList[] = { {"http" , a_Http_ccc},
85 {"file" , a_Dpi_ccc},
86 {"about", a_About_ccc},
87 {"dpi" , a_Dpi_ccc},
88 {"ftp" , a_Dpi_ccc},
89 {"https" , a_Dpi_ccc},
90 {"data" , a_Dpi_ccc} };
91 #define LSIZE (sizeof(cccList) / sizeof(cccList[0]))
92
93 const gchar *Key;
94 gint KeyLen, j;
95 size_t i;
96
97 /* Use the method inside the URL as a Key to decide what cccFunct matches */
98 if ( !URL_SCHEME_(Url) )
99 return NULL;
100
101 Key = URL_SCHEME_(Url);
102 KeyLen = strlen(Key);
103
104 for ( i = 0; i < LSIZE; ++i ) {
105 for ( j = 0; j < KeyLen; ++j )
106 if ( tolower(Key[j]) != cccList[i].Name[j] )
107 break;
108 if ( j == KeyLen )
109 return cccList[i].cccFunction;
110 }
111 return NULL;
112 }
113
22
33 #include "../chain.h"
44 #include "../url.h"
5
5 #include "../../dlib/dlib.h"
66
77 #ifdef __cplusplus
88 extern "C" {
99 #endif /* __cplusplus */
1010
11 /* Returns a file descriptor to read data from */
12 typedef gint (*UrlOpener_t) (const DilloUrl *url, void *data);
13
14 /*
15 * Module functions
16 */
17 ChainFunction_t a_Url_get_ccc_funct(const DilloUrl *Url);
18
19
2011 /*
2112 * External functions
2213 */
2314 extern void a_Http_freeall(void);
24 gint a_Http_init(void);
25 gint a_Http_proxy_auth(void);
26 void a_Http_set_proxy_passwd(gchar *str);
27 gchar *a_Http_make_query_str(const DilloUrl *url, gboolean use_proxy);
15 int a_Http_init(void);
16 int a_Http_proxy_auth(void);
17 void a_Http_set_proxy_passwd(const char *str);
18 char *a_Http_make_connect_str(const DilloUrl *url);
19 const char *a_Http_get_proxy_urlstr();
20 Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy);
2821
2922 void a_Http_ccc (int Op, int Branch, int Dir, ChainLink *Info,
30 void *Data1, void *Data2);
31 void a_About_ccc(int Op, int Branch, int Dir, ChainLink *Info,
3223 void *Data1, void *Data2);
3324 void a_IO_ccc (int Op, int Branch, int Dir, ChainLink *Info,
3425 void *Data1, void *Data2);
3526 void a_Dpi_ccc (int Op, int Branch, int Dir, ChainLink *Info,
3627 void *Data1, void *Data2);
3728
38 char *a_Dpi_send_blocking_cmd(const gchar *server_name, const gchar *cmd);
39 void a_Dpi_bye_dpid(void);
29 char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd);
30 void a_Dpi_dillo_exit(void);
31 void a_Dpi_init(void);
4032
4133
4234 #ifdef __cplusplus
00 /*
11 * File: about.c
22 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999, 2001 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 1999-2007 Jorge Arellano Cid <jcid@dillo.org>
54 *
65 * This program is free software; you can redistribute it and/or modify
76 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
98 * (at your option) any later version.
109 */
1110
1211 #include <config.h>
13 #include "IO.h"
14 #include "Url.h"
15 #include "../nav.h"
16 #include "../web.h"
17
18 typedef struct _SplashInfo SplashInfo_t;
19
20 struct _SplashInfo {
21 gint FD_Read;
22 gint FD_Write;
23 };
24
2512
2613 /*
2714 * HTML text for startup screen
2815 */
29 static char *Splash=
30 "Content-type: text/html\n"
31 "\n"
16 const char *const AboutSplash=
3217 "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
3318 "<html>\n"
3419 "<head>\n"
7560 " <table border='0' cellspacing='0' cellpadding='2'><tr>\n"
7661 " <td>\n"
7762 " <td>\n"
78 " <a href='http://www.dillo.org/dillo-help.html'>\n"
63 " <a href='http://www.dillo.org/dillo2-help.html'>\n"
7964 " Help</a>\n"
8065 " <tr>\n"
8166 " <td>&nbsp;&nbsp;\n"
8974 " <tr>\n"
9075 " <td>&nbsp;&nbsp;\n"
9176 " <td>\n"
92 " <a href='http://www.dillo.org/ChangeLog.html'>\n"
77 " <a href='http://hg.dillo.org/dillo/file/tip/ChangeLog'>\n"
9378 " ChangeLog</a>\n"
9479 " <tr>\n"
9580 " <td>&nbsp;&nbsp;\n"
118103 " <td>\n"
119104 " <table width='100%' border='0' cellspacing='1' cellpadding='3'>\n"
120105 " <tr>\n"
121 " <td colspan='1' bgcolor='#CCCCCC'>Magazines\n"
106 " <td colspan='1' bgcolor='#CCCCCC'>News\n"
122107 "\n"
123108 " <tr>\n"
124109 " <td bgcolor='#FFFFFF'>\n"
135120 " <tr>\n"
136121 " <td>&nbsp;&nbsp;\n"
137122 " <td>\n"
138 " <a href='http://www.kuro5hin.org/?op=section;section=__all__'>KuroShin</a>\n"
123 " <a href='http://www.linux.org.uk/Portaloo.cs'>Linux.org.uk</a>\n"
124 " <tr>\n"
125 " <td>&nbsp;&nbsp;\n"
126 " <td>\n"
127 " <a href='http://www.commondreams.org/'>C.&nbsp;Dreams</a>\n"
128 " <tr>\n"
129 " <td>&nbsp;&nbsp;\n"
130 " <td>\n"
131 " <a href='http://www.voltairenet.org/en'>VoltaireNet</a>\n"
139132 " <tr>\n"
140133 " <td>&nbsp;&nbsp;\n"
141134 " <td>\n"
142135 " <a href='http://www.nexusmagazine.com/'>Nexus&nbsp;M.</a>\n"
143 " <tr>\n"
144 " <td>&nbsp;&nbsp;\n"
145 " <td>\n"
146 " <a href='http://www.gnu-darwin.org/update.html'>Monster News</a>\n"
147 " <tr>\n"
148 " <td>&nbsp;&nbsp;\n"
149 " <td>\n"
150 " <a href='http://www.theregister.co.uk/index.html'>The Register</a>\n"
151136 " </table>\n"
152137 " </table>\n"
153138 " </table>\n"
173158 " <td><a href='http://www.wikipedia.org/'>Wikipedia</a>\n"
174159 " <tr>\n"
175160 " <td>&nbsp;&nbsp;\n"
176 " <td><a href='http://www.gutenberg.org/'>P. Gutenberg</a>\n"
161 " <td><a href='http://www.gutenberg.org/'>P.&nbsp;Gutenberg</a>\n"
177162 " <tr>\n"
178163 " <td>&nbsp;&nbsp;\n"
179164 " <td><a href='http://freshmeat.net/'>FreshMeat</a>\n"
205190 " <tr><td>&nbsp;&nbsp;\n"
206191 " <td><a href='http://www.violence.de'>Peace&amp;Violence</a>\n"
207192 " <tr><td>&nbsp;&nbsp;\n"
208 " <td><a href='http://www.fsf.org/philosophy/right-to-read.html'>"
193 " <td><a href='http://www.gnu.org/philosophy/right-to-read.html'>\n"
209194 " Right to Read</a>\n"
210195 " </table>\n"
211196 " </table>\n"
231216 " <td bgcolor='#FFFFFF'>\n"
232217 " <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
233218 " <p>\n"
234 " Dillo is Free Software in the terms of the GPL.\n"
219 " Dillo is Free Software under the terms of version 3 of the\n"
220 " <a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>.\n"
235221 " This means you have four basic freedoms:\n"
236222 " <ul>\n"
237223 " <li>Freedom to use the program any way you see fit.\n"
239225 " <li>Freedom to make backup copies.\n"
240226 " <li>Freedom to redistribute it.\n"
241227 " </ul>\n"
242 " The <a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>\n"
243 " is the legal mechanism that gives you these freedoms.\n"
244 " It also protects them from being taken away: any derivative work\n"
245 " based on the program must be under the GPL.<br>\n"
228 " The GPL is the legal mechanism that gives you these freedoms.\n"
229 " It also protects you from having them taken away: any derivative work\n"
230 " based on the program must be under GPLv3 as well.<br>\n"
246231 " </table>\n"
247232 "</table>\n"
248233 "</table>\n"
254239 "<tr>\n"
255240 " <td bgcolor='#CCCCCC'>\n"
256241 " <h4>Release overview</h4>\n"
257 " April 26, 2006\n"
242 " February 11, 2010\n"
258243 "<tr>\n"
259244 " <td bgcolor='#FFFFFF'>\n"
260245 " <table border='0' cellspacing='0' cellpadding='5'>\n"
261246 " <tr>\n"
262247 " <td>\n"
263248 "<p>\n"
264 "This release is hopefully the last from the GTK1 series. The port to\n"
265 "<a href='http://www.fltk.org/'>FLTK2</a> is almost finished now!\n"
249 "This release features a major overhaul of the cookies subsystem,\n"
250 "a reimplementation of the DPI API, a configurable connection limit,\n"
251 "and various CSS improvements.\n"
266252 "<p>\n"
267 "This release comes with extensive work on the plugins side. A new dpip\n"
268 "library, a new FLTK2-based GUI for downloads!, a \"<code>data:</code>\"\n"
269 "URI handler, a important bug fix in the FTP plugin, and a cookies server\n"
270 "dpi that enables all Dillo instances to use cookies at the same time!\n"
271 "<p>\n"
272 "There's also the \"<code>./configure --disable-threaded-dns</code>\"\n"
273 "option (mainly for some non reentrant BSDs), among many other improvements.\n"
274 "<p>\n"
275 "Remember that dillo project uses a release model where every new\n"
276 "browser shall be better than the former.\n"
253 "Remember that the dillo project uses a release model where every new\n"
254 "version shall be better than the last.\n"
277255 "<EM>Keep up with the latest one!</EM>\n"
278256 " </table>\n"
279257 "</table>\n"
287265 " <td bgcolor='#CCCCCC'>\n"
288266 " <h4>ChangeLog highlights</h4>\n"
289267 " (Extracted from the\n"
290 " <a href='http://www.dillo.org/ChangeLog.html'>full\n"
268 " <a href='http://hg.dillo.org/dillo/file/tip/ChangeLog'>full\n"
291269 " ChangeLog</a>)\n"
292270 "<tr>\n"
293271 " <td bgcolor='#FFFFFF'>\n"
295273 " <tr>\n"
296274 " <td>\n"
297275 "<ul>\n"
298 "<li> Designed and implemented a dpi protocol library (libDpip.a in /dpip).\n"
299 "<li> Ported the bookmarks, download, file, https, ftp and hello plugins,\n"
300 " plus the dpid daemon and the rest of the source tree to use it.\n"
301 "<li> Improved the dpi buffer reception to handle split buffers (This was\n"
302 " required for handling arbitrary data streams with dpip).\n"
303 "<li> Fixed a serious bug with the FTP plugin that led to two downloads\n"
304 " of the same file when left-clicking a non-viewable file.\n"
305 "<li> Improved the accuracy of the illegal-character error reporting\n"
306 " for URLs.\n"
307 "<li> Added dpi/downloads.cc (Default FLTK2-based GUI for downloads dpi).\n"
308 "<li> Added \"./configure --disable-dlgui\" to build without FLTK2-GUI\n"
309 " downloads.\n"
310 "<li> Fixed dpip's tag syntax and its parsing to accept any value string.\n"
311 "<li> Added DOCTYPE parsing (for better bug-meter error messages).\n"
312 "<li> Fixed bookmarks dpi to escape ' in URLs and &<>\"' in titles\n"
313 " (BUG#655).\n"
314 "<li> Added a check for malicious image sizes in IMG tags.\n"
315 "\n"
316 "<li> Added a datauri dpi to handle \"data:\" URIs (RFC-2397).\n"
317 "<li> Moved the cookies management into a dpi server: cookies.dpi.\n"
318 "<li> Removed the restriction of only one dillo with cookies enabled!\n"
319 "<li> Added \"./configure --disable-threaded-dns\" (for some non\n"
320 " reentrant BSDs).\n"
276 "<li>Added keybindings for scrolling.\n"
277 "<li>Help button and local help file.\n"
278 "<li>Add support for multiple class names in CSS.\n"
279 "<li>Fix X11 coordinate overflows.\n"
280 "<li>Improve CSS font parsing.\n"
281 "<li>Enable font face setting via &lt;font&gt; element.\n"
282 "<li>Ignore XML comment markers in CSS.\n"
283 "<li>Fix user agent style for nested &lt;ul&gt;.\n"
284 "<li>Handle signed chars. Added dIsspace() and dIsalnum() to dlib.\n"
285 "<li>Changed the CCCs to build in one step (for both HTTP and DPI).\n"
286 "<li>Remove the empty cache entry lingering after connection abort.\n"
287 "<li>Fixed URL unescaping in the datauri DPI.\n"
288 "<li>Changed and reimplemented the DPI API.\n"
289 "<li>Allow linebreaks around Chinese/Japanese characters.\n"
290 "<li>Fix scrolling for text search.\n"
291 "<li>Tooltips.\n"
292 "<li>Enable popup menu below bottom of page content.\n"
293 "<li>Handle JPEGs with CMYK color space.\n"
294 "<li>General cookies overhaul.\n"
295 "<li>Fixed a bug in w3c_mode.\n"
296 "<li>Limit number of simultaneous connections.\n"
321297 "</ul>\n"
322298 " </table>\n"
323299 "</table>\n"
338314 "<ul>\n"
339315 " <li> There's a\n"
340316 " <a href='http://www.dillo.org/dillorc'>dillorc</a>\n"
341 " (readable config) file within the tarball; It is well commented\n"
317 " (readable config) file within the tarball; It is well-commented\n"
342318 " and has plenty of options to customize dillo, so <STRONG>copy\n"
343319 " it</STRONG> to your <STRONG>~/.dillo/</STRONG> directory, and\n"
344 " modify to your taste.\n"
345 " <li> There's documentation for developers in the <CODE>/doc</CODE>\n"
320 " modify it to your taste.\n"
321 " <li> Documentation for developers is in the <CODE>/doc</CODE>\n"
346322 " dir within the tarball; you can find directions on everything\n"
347323 " else at the home page.\n"
348 " <li> Dillo has context sensitive menus using the\n"
349 " right mouse button (available on pages, links, images,\n"
350 " the Back and Forward buttons, and bug meter).\n"
324 " <li> The right mouse button brings up a context-sensitive menu\n"
325 " (available on pages, links, images, forms, the Back and Forward buttons,\n"
326 " and the bug meter).\n"
351327 " <li> Dillo behaves very nicely when browsing local files, images, and HTML.\n"
352 " It's also very good for Internet searching (try Google!).\n"
353 " <li> This release is mainly intended <strong>for developers</strong>\n"
354 " and <em>advanced users</em>.\n"
328 " It's also very good for Internet searching.\n"
329 " <li> This release is mainly intended for <strong>developers</strong>\n"
330 " and <strong>advanced users</strong>.\n"
355331 " <li> Frames, Java and Javascript are not supported.\n"
356332 "</ul>\n"
357333 "<br>\n"
387363 "</body>\n"
388364 "</html>\n";
389365
390
391
392 /*
393 * Send the splash screen through the IO using a pipe.
394 */
395 static gint About_send_splash(ChainLink *Info, DilloUrl *Url)
396 {
397 gint SplashPipe[2];
398 IOData_t *io1;
399 SplashInfo_t *SpInfo;
400
401 if (pipe(SplashPipe))
402 return -1;
403
404 SpInfo = g_new(SplashInfo_t, 1);
405 SpInfo->FD_Read = SplashPipe[0];
406 SpInfo->FD_Write = SplashPipe[1];
407 Info->LocalKey = SpInfo;
408
409 /* send splash */
410 io1 = a_IO_new(IOWrite, SpInfo->FD_Write);
411 a_IO_set_buf(io1, Splash, strlen(Splash));
412 io1->Flags |= (IOFlag_ForceClose + IOFlag_SingleWrite);
413 a_Chain_link_new(Info, a_About_ccc, BCK, a_IO_ccc, 1, 1);
414 a_Chain_bcb(OpStart, Info, io1, NULL);
415 a_Chain_bcb(OpSend, Info, io1, NULL);
416
417 /* Tell the cache to receive answer */
418 a_Chain_fcb(OpSend, Info, &SpInfo->FD_Read, NULL);
419 return SpInfo->FD_Read;
420 }
421
422 /*
423 * Push the right URL for each supported "about"
424 * ( Data1 = Requested URL; Data2 = Web structure )
425 */
426 static gint About_get(ChainLink *Info, void *Data1, void *Data2)
427 {
428 char *loc;
429 const char *tail;
430 DilloUrl *Url = Data1;
431 DilloWeb *web = Data2;
432 DilloUrl *LocUrl;
433
434 /* Don't allow the "about:" method for non-root URLs */
435 if (!(web->flags & WEB_RootUrl))
436 return -1;
437
438 tail = URL_PATH(Url);
439
440 if (!strcmp(tail, "splash")) {
441 return About_send_splash(Info, Url);
442 }
443
444 if (!strcmp(tail, "jwz"))
445 loc = "http://www.jwz.org/";
446 else if (!strcmp(tail, "raph"))
447 loc = "http://www.levien.com/";
448 else if (!strcmp(tail, "yosh"))
449 loc = "http://yosh.gimp.org/";
450 else if (!strcmp(tail, "snorfle"))
451 loc = "http://www.snorfle.net/";
452 else if (!strcmp(tail, "dillo"))
453 loc = "http://www.dillo.org/";
454 else if (!strcmp(tail, "help"))
455 loc = "http://www.dillo.org/dillo-help.html";
456 else
457 loc = "http://www.google.com/";
458
459 LocUrl = a_Url_new(loc, NULL, 0, 0, 0);
460 a_Nav_push(web->bw, LocUrl);
461 a_Url_free(LocUrl);
462 return -1;
463 }
464
465 /*
466 * CCC function for the ABOUT module
467 */
468 void a_About_ccc(int Op, int Branch, int Dir, ChainLink *Info,
469 void *Data1, void *Data2)
470 {
471 int FD;
472
473 a_Chain_debug_msg("a_About_ccc", Op, Branch, Dir);
474
475 if ( Branch == 1 ) {
476 /* Start about method */
477 if (Dir == BCK) {
478 switch (Op) {
479 case OpStart:
480 /* (Data1 = Url; Data2 = Web) */
481 // Info->LocalKey gets set in About_get
482 if ((FD = About_get(Info, Data1, Data2)) == -1)
483 a_Chain_fcb(OpAbort, Info, NULL, NULL);
484 break;
485 case OpAbort:
486 a_Chain_bcb(OpAbort, Info, NULL, NULL);
487 g_free(Info->LocalKey);
488 g_free(Info);
489 break;
490 }
491 } else { /* FWD */
492 switch (Op) {
493 case OpSend:
494 /* This means the sending framework was set OK */
495 FD = ((SplashInfo_t *)Info->LocalKey)->FD_Read;
496 a_Chain_fcb(OpSend, Info, &FD, NULL);
497 break;
498 case OpEnd:
499 /* Everything sent! */
500 a_Chain_del_link(Info, BCK);
501 g_free(Info->LocalKey);
502 a_Chain_fcb(OpEnd, Info, NULL, NULL);
503 break;
504 case OpAbort:
505 g_free(Info->LocalKey);
506 a_Chain_fcb(OpAbort, Info, NULL, NULL);
507 break;
508 }
509 }
510 }
511 }
512
00 /*
11 * File: dpi.c
22 *
3 * Copyright (C) 2002, 2003 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 2002-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1111 /*
12 * (Prototype code)
12 * Dillo plugins (small programs that interact with dillo)
1313 *
14 * Dillo plugins (small programs that interact with dillo)
15 * This should be able to handle:
16 * bookmarks, cookies, FTP, downloads, preferences, https and
17 * a lot of any-to-html filters.
14 * Dillo plugins are designed to handle:
15 * bookmarks, cookies, FTP, downloads, files, preferences, https,
16 * datauri and a lot of any-to-html filters.
1817 */
1918
2019
2120 #include <unistd.h>
2221 #include <stdlib.h>
2322 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
2623 #include <string.h>
2724 #include <stdio.h>
2825 #include <errno.h> /* for errno */
29
30 #include <stdio.h>
26 #include <fcntl.h>
27 #include <ctype.h> /* isxdigit */
28
3129 #include <sys/socket.h>
3230 #include <sys/un.h>
3331 #include <netinet/in.h>
32 #include <netinet/tcp.h>
3433 #include <arpa/inet.h>
3534 #include <netdb.h>
3635
3736 #include "../msg.h"
37 #include "../klist.h"
3838 #include "IO.h"
3939 #include "Url.h"
40 #include "../misc.h"
4140 #include "../../dpip/dpip.h"
42
43 /* #define DEBUG_LEVEL 2 */
44 #define DEBUG_LEVEL 4
45 #include "../debug.h"
4641
4742 /* This one is tricky, some sources state it should include the byte
4843 * for the terminating NULL, and others say it shouldn't. */
5651
5752
5853 typedef struct {
59 gint FreeBuf;
60 gint InTag, InData;
61 gint PipeActive;
62 gint Send2EOF;
63
64 gint DataTotalSize;
65 gint DataRecvSize;
66
67 void *Buf;
68 gint BufIdx;
69 gint BufSize;
70
71 gint TokIdx;
72 gint TokSize;
73 gint TokIsTag;
54 int InTag;
55 int Send2EOF;
56
57 int DataTotalSize;
58 int DataRecvSize;
59
60 Dstr *Buf;
61
62 int BufIdx;
63 int TokIdx;
64 int TokSize;
65 int TokIsTag;
7466
7567 ChainLink *InfoRecv;
76 } conn_data_t;
68 int Key;
69 } dpi_conn_t;
7770
7871
7972 /*
8073 * Local data
8174 */
82
83 /* list for dpi connections waiting for a FD;
84 * it holds pointers to Info structures (i.e. CCC Nodes). */
85 static GSList *PendingNodes = NULL;
86
87 /*
88 * Forward references
89 */
90
75 static Klist_t *ValidConns = NULL; /* Active connections list. It holds
76 * pointers to dpi_conn_t structures. */
77 static char SharedKey[32];
78
79 /*
80 * Initialize local data
81 */
82 void a_Dpi_init(void)
83 {
84 /* empty */
85 }
9186
9287 /*
9388 * Close a FD handling EINTR
9489 */
95 static void Dpi_close_fd(gint fd)
96 {
97 gint st;
98
90 static void Dpi_close_fd(int fd)
91 {
92 int st;
93
94 dReturn_if (fd < 0);
9995 do
10096 st = close(fd);
10197 while (st < 0 && errno == EINTR);
104100 /*
105101 * Create a new connection data structure
106102 */
107 static conn_data_t *Dpi_conn_data_new(ChainLink *Info)
108 {
109 conn_data_t *conn = g_new0(conn_data_t, 1);
110
111 conn->Buf = NULL;
103 static dpi_conn_t *Dpi_conn_new(ChainLink *Info)
104 {
105 dpi_conn_t *conn = dNew0(dpi_conn_t, 1);
106
107 conn->Buf = dStr_sized_new(8*1024);
112108 conn->InfoRecv = Info;
109 conn->Key = a_Klist_insert(&ValidConns, conn);
110
113111 return conn;
114112 }
115113
116114 /*
117115 * Free a connection data structure
118116 */
119 static void Dpi_conn_data_free(conn_data_t *conn)
120 {
121 if (conn->FreeBuf)
122 g_free(conn->Buf);
123 g_free(conn);
124 }
125
126 /*
127 * Append the new buffer in 'io' to Buf in 'conn'
128 */
129 static void Dpi_append_io_buf(conn_data_t *conn, IOData_t *io)
130 {
131 if (io->Status > 0) {
132 conn->Buf = g_realloc(conn->Buf, conn->BufSize + (gulong)io->Status);
133 memcpy((gchar*)conn->Buf + conn->BufSize, io->Buf, (size_t)io->Status);
134 conn->BufSize += io->Status;
135 conn->FreeBuf = 0;
117 static void Dpi_conn_free(dpi_conn_t *conn)
118 {
119 a_Klist_remove(ValidConns, conn->Key);
120 dStr_free(conn->Buf, 1);
121 dFree(conn);
122 }
123
124 /*
125 * Check whether a conn is still valid.
126 * Return: 1 if found, 0 otherwise
127 */
128 static int Dpi_conn_valid(int key)
129 {
130 return (a_Klist_get_data(ValidConns, key)) ? 1 : 0;
131 }
132
133 /*
134 * Append the new buffer in 'dbuf' to Buf in 'conn'
135 */
136 static void Dpi_append_dbuf(dpi_conn_t *conn, DataBuf *dbuf)
137 {
138 if (dbuf->Code == 0 && dbuf->Size > 0) {
139 dStr_append_l(conn->Buf, dbuf->Buf, dbuf->Size);
136140 }
137141 }
138142
146150 *
147151 * TODO: define an API and move this function into libDpip.a.
148152 */
149 static gint Dpi_get_token(conn_data_t *conn)
150 {
151 gint i, resp = -1;
152 gchar *buf = conn->Buf;
153
154 if (conn->FreeBuf || conn->BufIdx == conn->BufSize) {
155 g_free(conn->Buf);
156 conn->Buf = NULL;
157 conn->BufIdx = conn->BufSize = 0;
158 conn->FreeBuf = 0;
153 static int Dpi_get_token(dpi_conn_t *conn)
154 {
155 int i, resp = -1;
156 char *buf = conn->Buf->str;
157
158 if (conn->BufIdx == conn->Buf->len) {
159 dStr_truncate(conn->Buf, 0);
160 conn->BufIdx = 0;
159161 return resp;
160162 }
161163
162164 if (conn->Send2EOF) {
163165 conn->TokIdx = conn->BufIdx;
164 conn->TokSize = conn->BufSize - conn->BufIdx;
165 conn->BufIdx = conn->BufSize;
166 conn->TokSize = conn->Buf->len - conn->BufIdx;
167 conn->BufIdx = conn->Buf->len;
166168 return 0;
167169 }
168170
169 if (!conn->InTag && !conn->InData) {
171 _MSG("conn->BufIdx = %d; conn->Buf->len = %d\nbuf: [%s]\n",
172 conn->BufIdx,conn->Buf->len, conn->Buf->str + conn->BufIdx);
173
174 if (!conn->InTag) {
170175 /* search for start of tag */
171 /*
172 gchar *pbuf=NULL;
173 MSG("conn->BufIdx = %d; conn->BufSize = %d\n", conn->BufIdx,conn->BufSize);
174 pbuf = g_strndup(buf, conn->BufSize - conn->BufIdx);
175 MSG("buf: [%s]\n", pbuf);
176 g_free(pbuf);
177 */
178 while (conn->BufIdx < conn->BufSize && buf[conn->BufIdx] != '<')
176 while (conn->BufIdx < conn->Buf->len && buf[conn->BufIdx] != '<')
179177 ++conn->BufIdx;
180 if (conn->BufIdx < conn->BufSize) {
178 if (conn->BufIdx < conn->Buf->len) {
181179 /* found */
182180 conn->InTag = 1;
183181 conn->TokIdx = conn->BufIdx;
184182 } else {
185 MSG("ERROR: [Dpi_get_token] Can't find token start\n");
186 conn->FreeBuf = 1;
187 return Dpi_get_token(conn);
183 MSG_ERR("[Dpi_get_token] Can't find token start\n");
188184 }
189185 }
190186
191187 if (conn->InTag) {
192188 /* search for end of tag (EOT=" '>") */
193 for (i = conn->BufIdx; i < conn->BufSize; ++i)
189 for (i = conn->BufIdx; i < conn->Buf->len; ++i)
194190 if (buf[i] == '>' && i >= 2 && buf[i-1] == '\'' && buf[i-2] == ' ')
195191 break;
196192 conn->BufIdx = i;
197193
198 if (conn->BufIdx < conn->BufSize) {
194 if (conn->BufIdx < conn->Buf->len) {
199195 /* found EOT */
200196 conn->TokIsTag = 1;
201197 conn->TokSize = conn->BufIdx - conn->TokIdx + 1;
205201 }
206202 }
207203
208 if (conn->InData) {
209 conn->TokIsTag = 0;
210 if (conn->DataRecvSize + conn->BufSize - conn->BufIdx <
211 conn-> DataTotalSize) {
212 conn->TokSize += conn->BufSize - conn->BufIdx;
213 conn->DataRecvSize += conn->BufSize - conn->BufIdx;
214 conn->FreeBuf = 1;
215 resp = 0;
216 } else {
217 /* srch end of data */
218 MSG("ERROR: [Dpi_get_token] *** NULL code here ***\n");
219 while (conn->BufIdx < conn->BufSize)
220 ++conn->BufIdx;
221 resp = -1;
222 }
223 }
224
225204 return resp;
226205 }
227206
228207 /*
229208 * Parse a dpi tag and take the appropriate actions
230209 */
231 static void Dpi_parse_token(conn_data_t *conn)
232 {
233 gchar *tag, *cmd, *msg, *urlstr;
210 static void Dpi_parse_token(dpi_conn_t *conn)
211 {
212 char *tag, *cmd, *msg, *urlstr;
234213 DataBuf *dbuf;
214 char *Tok = conn->Buf->str + conn->TokIdx;
235215
236216 if (conn->Send2EOF) {
237217 /* we're receiving data chunks from a HTML page */
238 dbuf = a_Chain_dbuf_new(conn->Buf + conn->TokIdx, conn->TokSize, 0);
218 dbuf = a_Chain_dbuf_new(Tok, conn->TokSize, 0);
239219 a_Chain_fcb(OpSend, conn->InfoRecv, dbuf, "send_page_2eof");
240 g_free(dbuf);
220 dFree(dbuf);
241221 return;
242222 }
243223
244 tag = g_strndup(conn->Buf + conn->TokIdx, (guint)conn->TokSize);
245 MSG("Dpi_parse_token: {%s}\n", tag);
246
247 cmd = a_Dpip_get_attr(conn->Buf + conn->TokIdx, conn->TokSize, "cmd");
224 tag = dStrndup(Tok, (size_t)conn->TokSize);
225 _MSG("Dpi_parse_token: {%s}\n", tag);
226
227 cmd = a_Dpip_get_attr_l(Tok, conn->TokSize, "cmd");
248228 if (strcmp(cmd, "send_status_message") == 0) {
249 msg = a_Dpip_get_attr(conn->Buf + conn->TokIdx, conn->TokSize, "msg");
229 msg = a_Dpip_get_attr_l(Tok, conn->TokSize, "msg");
250230 a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
251 g_free(msg);
231 dFree(msg);
252232
253233 } else if (strcmp(cmd, "chat") == 0) {
254 msg = a_Dpip_get_attr(conn->Buf + conn->TokIdx, conn->TokSize, "msg");
234 msg = a_Dpip_get_attr_l(Tok, conn->TokSize, "msg");
255235 a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
256 g_free(msg);
236 dFree(msg);
257237
258238 } else if (strcmp(cmd, "dialog") == 0) {
259239 /* For now will send the dpip tag... */
260240 a_Chain_fcb(OpSend, conn->InfoRecv, tag, cmd);
261241
262242 } else if (strcmp(cmd, "start_send_page") == 0) {
263 urlstr = a_Dpip_get_attr(conn->Buf + conn->TokIdx, conn->TokSize, "url");
243 conn->Send2EOF = 1;
244 urlstr = a_Dpip_get_attr_l(Tok, conn->TokSize, "url");
264245 a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
265 g_free(urlstr);
266 /* todo: a_Dpip_get_attr(conn->Buf + conn->TokIdx,
267 * conn->TokSize, "send_mode") */
268 conn->Send2EOF = 1;
246 dFree(urlstr);
247 /* TODO: a_Dpip_get_attr_l(Tok, conn->TokSize, "send_mode") */
269248
270249 } else if (strcmp(cmd, "reload_request") == 0) {
271 urlstr = a_Dpip_get_attr(conn->Buf + conn->TokIdx, conn->TokSize, "url");
250 urlstr = a_Dpip_get_attr_l(Tok, conn->TokSize, "url");
272251 a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
273 g_free(urlstr);
274 }
275 g_free(cmd);
276
277 g_free(tag);
278 }
279
280 /*
281 * Compare function for searching a CCC Node with a 'web' pointer.
282 */
283 static gint Dpi_node_cmp(gconstpointer node, gconstpointer key)
284 {
285 return a_Url_cmp(key, ((ChainLink *)node)->LocalKey);
252 dFree(urlstr);
253 }
254 dFree(cmd);
255
256 dFree(tag);
286257 }
287258
288259
289260 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
290261
291262 /*
292 * Get a new data buffer (within an 'io'), save it into local data,
263 * Write data into a file descriptor taking care of EINTR
264 * and possible data splits.
265 * Return value: 1 on success, -1 on error.
266 */
267 static int Dpi_blocking_write(int fd, const char *msg, int msg_len)
268 {
269 int st, sent = 0;
270
271 while (sent < msg_len) {
272 st = write(fd, msg + sent, msg_len - sent);
273 if (st < 0) {
274 if (errno == EINTR) {
275 continue;
276 } else {
277 MSG_ERR("[Dpi_blocking_write] %s\n", dStrerror(errno));
278 break;
279 }
280 }
281 sent += st;
282 }
283
284 return (sent == msg_len) ? 1 : -1;
285 }
286
287 /*
288 * Read all the available data from a filedescriptor.
289 * This is intended for short answers, i.e. when we know the server
290 * will write it all before being preempted. For answers that may come
291 * as an stream with delays, non-blocking is better.
292 * Return value: read data, or NULL on error and no data.
293 */
294 static char *Dpi_blocking_read(int fd)
295 {
296 int st;
297 const int buf_sz = 8*1024;
298 char buf[buf_sz], *msg = NULL;
299 Dstr *dstr = dStr_sized_new(buf_sz);
300
301 do {
302 st = read(fd, buf, buf_sz);
303 if (st < 0) {
304 if (errno == EINTR) {
305 continue;
306 } else {
307 MSG_ERR("[Dpi_blocking_read] %s\n", dStrerror(errno));
308 break;
309 }
310 } else if (st > 0) {
311 dStr_append_l(dstr, buf, st);
312 }
313 } while (st == buf_sz);
314
315 msg = (dstr->len > 0) ? dstr->str : NULL;
316 dStr_free(dstr, (dstr->len > 0) ? FALSE : TRUE);
317 return msg;
318 }
319
320 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
321
322 /*
323 * Get a new data buffer (within a 'dbuf'), save it into local data,
293324 * split in tokens and parse the contents.
294325 */
295 static void Dpi_process_io(int Op, void *Data1, conn_data_t *conn)
296 {
297 IOData_t *io = Data1;
326 static void Dpi_process_dbuf(int Op, void *Data1, dpi_conn_t *conn)
327 {
328 DataBuf *dbuf = Data1;
329 int key = conn->Key;
298330
299331 /* Very useful for debugging: show the data stream as received. */
300 /* fwrite(io->Buf, io->Status, 1, stdout); */
332 /* fwrite(dbuf->Buf, dbuf->Size, 1, stdout); */
301333
302334 if (Op == IORead) {
303 Dpi_append_io_buf(conn, io);
304 while (Dpi_get_token(conn) != -1) {
335 Dpi_append_dbuf(conn, dbuf);
336 /* 'conn' has to be validated because Dpi_parse_token() MAY call abort */
337 while (Dpi_conn_valid(key) && Dpi_get_token(conn) != -1) {
305338 Dpi_parse_token(conn);
306339 }
307340
308341 } else if (Op == IOClose) {
309 MSG("Dpi: [Dpi_process_io] IOClose\n");
342 /* unused */
310343 }
311344 }
312345
314347 * Start dpid.
315348 * Return: 0 starting now, 1 Error.
316349 */
317 static gint Dpi_start_dpid(void)
350 static int Dpi_start_dpid(void)
318351 {
319352 pid_t pid;
320 gint st_pipe[2], n, ret = 1;
321 gchar buf[16];
353 int st_pipe[2], ret = 1;
354 char *answer;
322355
323356 /* create a pipe to track our child's status */
324357 if (pipe(st_pipe))
327360 pid = fork();
328361 if (pid == 0) {
329362 /* This is the child process. Execute the command. */
330 gchar *path1 = a_Misc_prepend_user_home(".dillo/dpid");
363 char *path1 = dStrconcat(dGethomedir(), "/.dillo/dpid", NULL);
331364 Dpi_close_fd(st_pipe[0]);
332 if (execl(path1, "dpid", NULL) == -1) {
333 g_free(path1);
334 if (execlp("dpid", "dpid", NULL) == -1) {
335 DEBUG_MSG(4, "Dpi_start_dpid (child): %s\n", g_strerror(errno));
336 do
337 n = write(st_pipe[1], "ERROR", 5);
338 while (n == -1 && errno == EINTR);
339 Dpi_close_fd(st_pipe[1]);
340 _exit (EXIT_FAILURE);
365 if (execl(path1, "dpid", (char*)NULL) == -1) {
366 dFree(path1);
367 path1 = dStrconcat(DILLO_BINDIR, "dpid", NULL);
368 if (execl(path1, "dpid", (char*)NULL) == -1) {
369 dFree(path1);
370 if (execlp("dpid", "dpid", (char*)NULL) == -1) {
371 MSG("Dpi_start_dpid (child): %s\n", dStrerror(errno));
372 if (Dpi_blocking_write(st_pipe[1], "ERROR", 5) == -1) {
373 MSG("Dpi_start_dpid (child): can't write to pipe.\n");
374 }
375 Dpi_close_fd(st_pipe[1]);
376 _exit (EXIT_FAILURE);
377 }
341378 }
342379 }
343380 } else if (pid < 0) {
344381 /* The fork failed. Report failure. */
345 DEBUG_MSG(4, "Dpi_start_dpid: %s\n", g_strerror(errno));
382 MSG("Dpi_start_dpid: %s\n", dStrerror(errno));
346383 /* close the unused pipe */
347384 Dpi_close_fd(st_pipe[0]);
348385 Dpi_close_fd(st_pipe[1]);
350387 } else {
351388 /* This is the parent process, check our child status... */
352389 Dpi_close_fd(st_pipe[1]);
353 do
354 n = read(st_pipe[0], buf, 16);
355 while (n == -1 && errno == EINTR);
356 DEBUG_MSG(2, "Dpi_start_dpid: n = %d\n", n);
357 if (n != 5)
390 if ((answer = Dpi_blocking_read(st_pipe[0])) != NULL) {
391 MSG("Dpi_start_dpid: can't start dpid\n");
392 dFree(answer);
393 } else {
358394 ret = 0;
359 else
360 DEBUG_MSG(4, "Dpi_start_dpid: %s\n", g_strerror(errno));
395 }
396 Dpi_close_fd(st_pipe[0]);
361397 }
362398
363399 return ret;
364400 }
365401
366402 /*
367 * Make a connection test for a UDS.
368 * Return: 0 OK, 1 Not working.
369 */
370 static gint Dpi_check_uds(gchar *uds_name)
371 {
372 struct sockaddr_un pun;
373 gint SockFD, ret = 1;
374
375 if (access(uds_name, W_OK) == 0) {
376 /* socket connection test */
377 memset(&pun, 0, sizeof(struct sockaddr_un));
378 pun.sun_family = AF_LOCAL;
379 strncpy(pun.sun_path, uds_name, sizeof (pun.sun_path));
380
381 if ((SockFD = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1 ||
382 connect(SockFD, (void*)&pun, D_SUN_LEN(&pun)) == -1) {
383 DEBUG_MSG(4, "Dpi_check_uds: %s %s\n", g_strerror(errno), uds_name);
403 * Read dpid's communication keys from its saved file.
404 * Return value: 1 on success, -1 on error.
405 */
406 static int Dpi_read_comm_keys(int *port)
407 {
408 FILE *In;
409 char *fname, *rcline = NULL, *tail;
410 int i, ret = -1;
411
412 fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
413 if ((In = fopen(fname, "r")) == NULL) {
414 MSG_ERR("[Dpi_read_comm_keys] %s\n", dStrerror(errno));
415 } else if ((rcline = dGetline(In)) == NULL) {
416 MSG_ERR("[Dpi_read_comm_keys] empty file: %s\n", fname);
417 } else {
418 *port = strtol(rcline, &tail, 10);
419 for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
420 SharedKey[i] = tail[i+1];
421 SharedKey[i] = 0;
422 ret = 1;
423 }
424 if (In)
425 fclose(In);
426 dFree(rcline);
427 dFree(fname);
428
429 return ret;
430 }
431
432 /*
433 * Return a socket file descriptor
434 */
435 static int Dpi_make_socket_fd()
436 {
437 int fd, one = 1, ret = -1;
438
439 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
440 /* avoid delays when sending small pieces of data */
441 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
442 ret = fd;
443 }
444 return ret;
445 }
446
447 /*
448 * Make a connection test for a IDS.
449 * Return: 1 OK, -1 Not working.
450 */
451 static int Dpi_check_dpid_ids()
452 {
453 struct sockaddr_in sin;
454 const socklen_t sin_sz = sizeof(sin);
455 int sock_fd, dpid_port, ret = -1;
456
457 /* socket connection test */
458 memset(&sin, 0, sizeof(sin));
459 sin.sin_family = AF_INET;
460 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
461
462 if (Dpi_read_comm_keys(&dpid_port) != -1) {
463 sin.sin_port = htons(dpid_port);
464 if ((sock_fd = Dpi_make_socket_fd()) == -1) {
465 MSG("Dpi_check_dpid_ids: sock_fd=%d %s\n", sock_fd, dStrerror(errno));
466 } else if (connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
467 MSG("Dpi_check_dpid_ids: %s\n", dStrerror(errno));
384468 } else {
385 Dpi_close_fd(SockFD);
386 ret = 0;
469 Dpi_close_fd(sock_fd);
470 ret = 1;
387471 }
388472 }
389473 return ret;
390 }
391
392 /*
393 * Return the directory where the UDS are in,
394 * NULL if it can't be found.
395 */
396 static gchar *Dpi_get_dpid_uds_dir(void)
397 {
398 FILE *in;
399 gchar *saved_name_filename; /* :) */
400 gchar dpid_uds_dir[256], *p;
401
402 saved_name_filename =
403 g_strconcat(g_get_home_dir(), "/", ".dillo/dpi_socket_dir", NULL);
404 in = fopen(saved_name_filename, "r");
405 g_free(saved_name_filename);
406
407 if (in != NULL) {
408 fgets(dpid_uds_dir, 256, in);
409 fclose(in);
410 if ((p = strchr(dpid_uds_dir, '\n')))
411 *p = 0;
412 if (access(dpid_uds_dir, F_OK) == 0)
413 p = g_strdup(dpid_uds_dir);
414 _MSG("Dpi_get_dpid_uds_dir:: %s\n", p);
415 return p;
416 }
417
418 _MSG("Dpi_get_dpid_uds_dir: %s \n", g_strerror(errno));
419 return NULL;
420 }
421
422 /*
423 * Return the dpid's UDS name, NULL on failure.
424 */
425 static gchar *Dpi_get_dpid_uds_name(void)
426 {
427 gchar *dpid_uds_dir, *dpid_uds_name = NULL;
428
429 if ((dpid_uds_dir = Dpi_get_dpid_uds_dir()) != NULL)
430 dpid_uds_name= g_strconcat(dpid_uds_dir, "/", "dpid.srs", NULL);
431
432 g_free(dpid_uds_dir);
433 return dpid_uds_name;
434474 }
435475
436476 /*
437477 * Confirm that the dpid is running. If not, start it.
438478 * Return: 0 running OK, 1 starting (EAGAIN), 2 Error.
439479 */
440 static gint Dpi_check_dpid(void)
441 {
442 static gint starting = 0;
443 gchar *dpid_uds_name;
444 gint check_st = 1, ret = 2;
445
446 if ((dpid_uds_name = Dpi_get_dpid_uds_name()))
447 check_st = Dpi_check_uds(dpid_uds_name);
448
449 _MSG("Dpi_check_dpid: dpid_uds_name=%s, check_st=%d\n",
450 dpid_uds_name, check_st);
451
452 if (check_st == 0) {
480 static int Dpi_check_dpid(int num_tries)
481 {
482 static int starting = 0;
483 int check_st = 1, ret = 2;
484
485 check_st = Dpi_check_dpid_ids();
486 _MSG("Dpi_check_dpid: check_st=%d\n", check_st);
487
488 if (check_st == 1) {
453489 /* connection test with dpi server passed */
454490 starting = 0;
455491 ret = 0;
456 } else if (!dpid_uds_name || check_st) {
492 } else {
457493 if (!starting) {
458494 /* start dpid */
459495 if (Dpi_start_dpid() == 0) {
460496 starting = 1;
461497 ret = 1;
462498 }
463 } else if (++starting < 25) {
499 } else if (++starting < num_tries) {
500 /* starting */
464501 ret = 1;
465502 } else {
466503 /* we waited too much, report an error... */
468505 }
469506 }
470507
471 g_free(dpid_uds_name);
472 DEBUG_MSG(2, "Dpi_check_dpid:: %s\n",
473 (ret == 0) ? "OK" : (ret == 1 ? "EAGAIN" : "ERROR"));
508 _MSG("Dpi_check_dpid:: %s\n",
509 (ret == 0) ? "OK" : (ret == 1 ? "EAGAIN" : "ERROR"));
474510 return ret;
475511 }
476512
477513 /*
478 * Return the UDS name of a dpi server.
514 * Confirm that the dpid is running. If not, start it.
515 * Return: 0 running OK, 2 Error.
516 */
517 static int Dpi_blocking_start_dpid(void)
518 {
519 int cst, try = 0,
520 n_tries = 12; /* 3 seconds */
521
522 /* test the dpid, and wait a bit for it to start if necessary */
523 while ((cst = Dpi_check_dpid(n_tries)) == 1) {
524 MSG("Dpi_blocking_start_dpid: try %d\n", ++try);
525 usleep(250000); /* 1/4 sec */
526 }
527 return cst;
528 }
529
530 /*
531 * Return the dpi server's port number, or -1 on error.
479532 * (A query is sent to dpid and then its answer parsed)
480533 * note: as the available servers and/or the dpi socket directory can
481534 * change at any time, we'll ask each time. If someday we find
482535 * that connecting each time significantly degrades performance,
483536 * an optimized approach can be tried.
484537 */
485 static gchar *Dpi_get_server_uds_name(const gchar *server_name)
486 {
487 gchar *dpid_uds_dir, *dpid_uds_name = NULL,
488 *server_uds_name = NULL;
489 gint st;
490
491 g_return_val_if_fail (server_name != NULL, NULL);
492 DEBUG_MSG(2, "Dpi_get_server_uds_name:: server_name = [%s]\n", server_name);
493
494 dpid_uds_dir = Dpi_get_dpid_uds_dir();
495 if (dpid_uds_dir) {
496 struct sockaddr_un dpid;
497 gint sock, req_sz, rdlen;
498 gchar buf[128], *cmd, *request, *rply;
499 size_t buflen;
500
501 /* Get the server's uds name from dpid */
502 sock = socket(AF_LOCAL, SOCK_STREAM, 0);
503 dpid.sun_family = AF_LOCAL;
504 dpid_uds_name = g_strconcat(dpid_uds_dir, "/", "dpid.srs", NULL);
505 _MSG("dpid_uds_name = [%s]\n", dpid_uds_name);
506 strncpy(dpid.sun_path, dpid_uds_name, sizeof(dpid.sun_path));
507
508 if (connect(sock, (struct sockaddr *) &dpid, sizeof(dpid)) == -1)
509 perror("connect");
510 /* ask dpid to check the server plugin and send its UDS name back */
538 static int Dpi_get_server_port(const char *server_name)
539 {
540 int sock_fd = -1, dpi_port = -1;
541 int dpid_port, ok = 0;
542 struct sockaddr_in sin;
543 char *cmd, *request, *rply = NULL, *port_str;
544 socklen_t sin_sz;
545
546 dReturn_val_if_fail (server_name != NULL, dpi_port);
547 _MSG("Dpi_get_server_port:: server_name = [%s]\n", server_name);
548
549 /* Read dpid's port from saved file */
550 if (Dpi_read_comm_keys(&dpid_port) != -1) {
551 ok = 1;
552 }
553 if (ok) {
554 /* Connect a socket with dpid */
555 ok = 0;
556 sin_sz = sizeof(sin);
557 memset(&sin, 0, sizeof(sin));
558 sin.sin_family = AF_INET;
559 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
560 sin.sin_port = htons(dpid_port);
561 if ((sock_fd = Dpi_make_socket_fd()) == -1 ||
562 connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
563 MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
564 } else {
565 ok = 1;
566 }
567 }
568 if (ok) {
569 /* ask dpid to check the dpi and send its port number back */
570 ok = 0;
511571 request = a_Dpip_build_cmd("cmd=%s msg=%s", "check_server", server_name);
512 DEBUG_MSG(2, "[%s]\n", request);
513 do
514 st = write(sock, request, strlen(request));
515 while (st < 0 && errno == EINTR);
516 if (st < 0 && errno != EINTR)
517 perror("writing request");
518 g_free(request);
519 shutdown(sock, 1); /* signals no more writes to dpid */
520
572 _MSG("[%s]\n", request);
573
574 if (Dpi_blocking_write(sock_fd, request, strlen(request)) == -1) {
575 MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
576 } else {
577 ok = 1;
578 }
579 dFree(request);
580 }
581 if (ok) {
521582 /* Get the reply */
522 rply = NULL;
523 buf[0] = '\0';
524 buflen = sizeof(buf)/sizeof(buf[0]);
525 for (req_sz = 0; (rdlen = read(sock, buf, buflen)) != 0;
526 req_sz += rdlen) {
527 if (rdlen == -1 && errno == EINTR)
528 continue;
529 if (rdlen == -1) {
530 perror(" ** Dpi_get_server_uds_name **");
531 break;
532 }
533 rply = g_realloc(rply, (guint)(req_sz + rdlen + 1));
534 if (req_sz == 0)
535 rply[0] = '\0';
536 strncat(rply, buf, (size_t)rdlen);
537 }
538 Dpi_close_fd(sock);
539 DEBUG_MSG(2, "rply = [%s]\n", rply);
540
583 ok = 0;
584 if ((rply = Dpi_blocking_read(sock_fd)) == NULL) {
585 MSG("Dpi_get_server_port: can't read server port from dpid.\n");
586 } else {
587 ok = 1;
588 }
589 }
590 if (ok) {
541591 /* Parse reply */
542 if (rdlen == 0 && rply) {
543 cmd = a_Dpip_get_attr(rply, (gint)strlen(rply), "cmd");
544 if (strcmp(cmd, "send_data") == 0)
545 server_uds_name = a_Dpip_get_attr(rply, (gint)strlen(rply), "msg");
546 g_free(cmd);
547 g_free(rply);
548 }
549 }
550 g_free(dpid_uds_dir);
551 g_free(dpid_uds_name);
552 DEBUG_MSG(2, "Dpi_get_server_uds_name:: %s\n", server_uds_name);
553 return server_uds_name;
592 ok = 0;
593 cmd = a_Dpip_get_attr(rply, "cmd");
594 if (strcmp(cmd, "send_data") == 0) {
595 port_str = a_Dpip_get_attr(rply, "msg");
596 _MSG("Dpi_get_server_port: rply=%s\n", rply);
597 _MSG("Dpi_get_server_port: port_str=%s\n", port_str);
598 dpi_port = strtol(port_str, NULL, 10);
599 dFree(port_str);
600 ok = 1;
601 }
602 dFree(cmd);
603 }
604 dFree(rply);
605 Dpi_close_fd(sock_fd);
606
607 return ok ? dpi_port : -1;
554608 }
555609
556610
557611 /*
558612 * Connect a socket to a dpi server and return the socket's FD.
559 * We have to ask 'dpid' (dpi daemon) for the UDS of the target dpi server.
613 * We have to ask 'dpid' (dpi daemon) for the port of the target dpi server.
560614 * Once we have it, then the proper file descriptor is returned (-1 on error).
561615 */
562 static gint Dpi_connect_socket(const gchar *server_name, gint retry)
563 {
564 char *server_uds_name;
565 struct sockaddr_un pun;
566 gint SockFD, err;
567
568 /* Query dpid for the UDS name for this server */
569 server_uds_name = Dpi_get_server_uds_name(server_name);
570 DEBUG_MSG(2, "server_uds_name = [%s]\n", server_uds_name);
571
572 if (access(server_uds_name, F_OK) != 0) {
573 MSG("server socket was NOT found\n");
616 static int Dpi_connect_socket(const char *server_name, int retry)
617 {
618 struct sockaddr_in sin;
619 int sock_fd, err, dpi_port, ret=-1;
620 char *cmd = NULL;
621
622 /* Query dpid for the port number for this server */
623 if ((dpi_port = Dpi_get_server_port(server_name)) == -1) {
624 _MSG("Dpi_connect_socket:: can't get port number for %s\n", server_name);
574625 return -1;
575626 }
627 _MSG("Dpi_connect_socket: server=%s port=%d\n", server_name, dpi_port);
576628
577629 /* connect with this server's socket */
578 memset(&pun, 0, sizeof(struct sockaddr_un));
579 pun.sun_family = AF_LOCAL;
580 strncpy(pun.sun_path, server_uds_name, sizeof (pun.sun_path));
581 g_free(server_uds_name);
582
583 if ((SockFD = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1)
630 memset(&sin, 0, sizeof(sin));
631 sin.sin_family = AF_INET;
632 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
633 sin.sin_port = htons(dpi_port);
634
635 if ((sock_fd = Dpi_make_socket_fd()) == -1) {
584636 perror("[dpi::socket]");
585 else if (connect(SockFD, (void*)&pun, D_SUN_LEN(&pun)) == -1) {
637 } else if (connect(sock_fd, (void*)&sin, sizeof(sin)) == -1) {
586638 err = errno;
587 SockFD = -1;
588 MSG("[dpi::connect] errno:%d %s\n", errno, g_strerror(errno));
639 sock_fd = -1;
640 MSG("[dpi::connect] errno:%d %s\n", errno, dStrerror(errno));
589641 if (retry) {
590642 switch (err) {
591643 case ECONNREFUSED: case EBADF: case ENOTSOCK: case EADDRNOTAVAIL:
592 /* the server may crash and its socket name survive */
593 unlink(pun.sun_path);
594 SockFD = Dpi_connect_socket(server_name, FALSE);
644 sock_fd = Dpi_connect_socket(server_name, FALSE);
595645 break;
596646 }
597647 }
598 }
599
600 return SockFD;
601 }
602
648
649 /* send authentication Key (the server closes sock_fd on error) */
650 } else if (!(cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey))) {
651 MSG_ERR("[Dpi_connect_socket] Can't make auth message.\n");
652 } else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
653 MSG_ERR("[Dpi_connect_socket] Can't send auth message.\n");
654 } else {
655 ret = sock_fd;
656 }
657 dFree(cmd);
658
659 return ret;
660 }
603661
604662 /*
605663 * CCC function for the Dpi module
607665 void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
608666 void *Data1, void *Data2)
609667 {
610 GSList *list;
611 gint SockFD = -1, st;
612
613 a_Chain_debug_msg("a_Dpi_ccc", Op, Branch, Dir);
668 dpi_conn_t *conn;
669 int SockFD = -1, st;
670
671 dReturn_if_fail( a_Chain_check("a_Dpi_ccc", Op, Branch, Dir, Info) );
614672
615673 if (Branch == 1) {
616 if (Dir == BCK) {
617 /* Cache request, return the FD. */
618 /* (Data1 = Url; Data2 = Web) */
619 switch (Op) {
620 case OpStart:
621 /* We'll know the FD later, enqueue this connection.
622 * (The Url is used as an identifier for the connection) */
623 Info->LocalKey = a_Url_dup(Data1);
624 PendingNodes = g_slist_append(PendingNodes, (gpointer)Info);
625 break;
626 }
627 } else { /* FWD */
628 switch (Op) {
629 case OpEnd:
630 /* End this requesting branch */
631 a_Url_free(Info->LocalKey);
632 a_Chain_fcb(OpEnd, Info, NULL, NULL);
633 break;
634 case OpAbort:
635 list = g_slist_find_custom(
636 PendingNodes, Info->LocalKey, Dpi_node_cmp);
637 if (list) {
638 /* The connection is not pending anymore */
639 PendingNodes = g_slist_remove(PendingNodes, list->data);
640 }
641 a_Url_free(Info->LocalKey);
642 a_Chain_fcb(OpAbort, Info, NULL, NULL);
643 break;
644 }
645 }
646
647 } else if (Branch == 2) {
648674 if (Dir == BCK) {
649675 /* Send commands to dpi-server */
650676 switch (Op) {
651677 case OpStart:
652 if ((st = Dpi_check_dpid()) == 0) {
678 if ((st = Dpi_blocking_start_dpid()) == 0) {
653679 SockFD = Dpi_connect_socket(Data1, TRUE);
654680 if (SockFD != -1) {
655 gint *fd = g_new(gint, 1);
681 int *fd = dNew(int, 1);
656682 *fd = SockFD;
657683 Info->LocalKey = fd;
658 a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 3, 2);
659 a_Chain_bcb(OpStart, Info, Info->LocalKey, NULL);
660 /* tell the capi to start the receiving branch */
661 a_Chain_fcb(OpSend, Info, Info->LocalKey, "SockFD");
684 a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 1, 1);
685 a_Chain_bcb(OpStart, Info, NULL, NULL);
662686 }
663687 }
664688
665 if (st == 0 && SockFD != -1)
666 a_Chain_fcb(OpSend, Info, NULL, (void*)"DpidOK");
667 else if (st == 1)
668 a_Chain_fcb(OpSend, Info, NULL, (void*)"DpidEAGAIN");
669 else {
670 DEBUG_MSG(4, "dpi.c: ERROR, can't start dpi daemon\n");
671 a_Dpi_ccc(OpAbort, 2, FWD, Info, "ERR_dpid", NULL);
689 if (st == 0 && SockFD != -1) {
690 a_Chain_bcb(OpSend, Info, &SockFD, "FD");
691 a_Chain_fcb(OpSend, Info, &SockFD, "FD");
692 a_Chain_fcb(OpSend, Info, NULL, "DpidOK");
693 } else {
694 MSG_ERR("dpi.c: can't start dpi daemon\n");
695 a_Dpi_ccc(OpAbort, 1, FWD, Info, NULL, "DpidERROR");
672696 }
673697 break;
674698 case OpSend:
676700 break;
677701 case OpEnd:
678702 a_Chain_bcb(OpEnd, Info, NULL, NULL);
679 g_free(Info->LocalKey);
680 g_free(Info);
703 dFree(Info->LocalKey);
704 dFree(Info);
681705 break;
682706 case OpAbort:
683 MSG("a_Dpi_ccc: OpAbort[2B], Not implemented\n");
684 g_free(Info->LocalKey);
685 g_free(Info);
707 a_Chain_bcb(OpAbort, Info, NULL, NULL);
708 dFree(Info->LocalKey);
709 dFree(Info);
710 break;
711 default:
712 MSG_WARN("Unused CCC\n");
686713 break;
687714 }
688 } else { /* FWD */
715 } else { /* 1 FWD */
689716 /* Send commands to dpi-server (status) */
690717 switch (Op) {
691 case OpEnd:
692 a_Chain_del_link(Info, BCK);
693 g_free(Info);
694 break;
695 case OpSend:
696718 case OpAbort:
697 a_Chain_fcb(OpAbort, Info, Data1, NULL);
698 g_free(Info);
719 a_Chain_fcb(OpAbort, Info, NULL, Data2);
720 dFree(Info);
721 break;
722 default:
723 MSG_WARN("Unused CCC\n");
699724 break;
700725 }
701726 }
702727
703 } else if (Branch == 3) {
728 } else if (Branch == 2) {
704729 if (Dir == FWD) {
705730 /* Receiving from server */
706731 switch (Op) {
707732 case OpSend:
708 Dpi_process_io(IORead, Data1, Info->LocalKey);
733 /* Data1 = dbuf */
734 Dpi_process_dbuf(IORead, Data1, Info->LocalKey);
709735 break;
710736 case OpEnd:
711 a_Chain_del_link(Info, BCK);
712 Dpi_process_io(IOClose, Data1, Info->LocalKey);
713 Dpi_conn_data_free(Info->LocalKey);
714737 a_Chain_fcb(OpEnd, Info, NULL, NULL);
715 break;
716 case OpAbort:
717 MSG(" Not implemented\n");
738 Dpi_conn_free(Info->LocalKey);
739 dFree(Info);
740 break;
741 default:
742 MSG_WARN("Unused CCC\n");
718743 break;
719744 }
720 } else { /* BCK */
745 } else { /* 2 BCK */
721746 switch (Op) {
722747 case OpStart:
723 {
724 IOData_t *io2;
725 Info->LocalKey = Dpi_conn_data_new(Info);
726 io2 = a_IO_new(IORead, *(int*)Data1); /* SockFD */
727 a_IO_set_buf(io2, NULL, IOBufLen);
728 a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 2, 3);
729 a_Chain_bcb(OpStart, Info, io2, NULL);
730 break;
748 conn = Dpi_conn_new(Info);
749 Info->LocalKey = conn;
750
751 /* Hack: for receiving HTTP through the DPI framework */
752 if (strcmp(Data2, "http") == 0) {
753 conn->Send2EOF = 1;
731754 }
755
756 a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 2, 2);
757 a_Chain_bcb(OpStart, Info, NULL, NULL); /* IORead */
758 break;
732759 case OpSend:
733 /* now that we have the FD, resume the connection.
734 * (Data1 = FD, Data2 = Url) */
735 list = g_slist_find_custom(PendingNodes, Data2, Dpi_node_cmp);
736 if (list) {
737 ChainLink *P_Info = list->data;
738 /* Tell the cache to start the receiving CCC */
739 a_Chain_fcb(OpSend, P_Info, Data1, NULL);
740 /* The connection is not pending anymore */
741 PendingNodes = g_slist_remove(PendingNodes, list->data);
742 /* End this requesting CCC */
743 a_Dpi_ccc(OpEnd, 1, FWD, P_Info, NULL, NULL);
760 if (Data2 && !strcmp(Data2, "FD")) {
761 a_Chain_bcb(OpSend, Info, Data1, Data2);
744762 }
745763 break;
746 case OpStop:
747 /* Stop transfer, abort the pending node.
748 * (Data1 = Url, Data2 = NULL) */
749 list = g_slist_find_custom(PendingNodes, Data1, Dpi_node_cmp);
750 if (list) {
751 ChainLink *P_Info = list->data;
752 /* Abort this requesting CCC */
753 a_Dpi_ccc(OpAbort, 1, FWD, P_Info, Data1, NULL);
754 }
755 break;
756764 case OpAbort:
757 MSG(" Not implemented\n");
765 a_Chain_bcb(OpAbort, Info, NULL, NULL);
766 Dpi_conn_free(Info->LocalKey);
767 dFree(Info);
768 break;
769 default:
770 MSG_WARN("Unused CCC\n");
758771 break;
759772 }
760773 }
761
762 } else if (Branch == 4) {
763 /* Unused */
764 g_assert_not_reached();
765 }
766 }
767
768 /*! Send DpiBye to dpid
769 * Note: currently disabled. Maybe it'd be better to have a
770 * dpid_idle_timeout variable in the config file.
771 */
772 void a_Dpi_bye_dpid()
773 {
774 char *DpiBye_cmd;
775 struct sockaddr_un sa;
776 size_t sun_path_len, addr_len;
777 char *srs_name;
778 int new_socket;
779
780 srs_name = Dpi_get_dpid_uds_name();
781 sun_path_len = sizeof(sa.sun_path);
782 addr_len = sizeof(sa);
783
784 sa.sun_family = AF_LOCAL;
785
786 if ((new_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
787 DEBUG_MSG(4, "a_Dpi_bye_dpid: %s\n", g_strerror(errno));
788 }
789 strncpy(sa.sun_path, srs_name, sizeof (sa.sun_path));
790 if (connect(new_socket, (struct sockaddr *) &sa, addr_len) == -1) {
791 DEBUG_MSG(4, "a_Dpi_bye_dpid: %s\n", g_strerror(errno));
792 fprintf(stderr, "%s\n", sa.sun_path);
793 }
794 DpiBye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
795 (void) write(new_socket, DpiBye_cmd, strlen(DpiBye_cmd));
796 g_free(DpiBye_cmd);
797 Dpi_close_fd(new_socket);
798 }
799
774 }
775 }
776
777 /*! Let dpid know dillo is no longer running.
778 * Note: currently disabled. It may serve to let the cookies dpi know
779 * when to expire session cookies.
780 */
781 void a_Dpi_dillo_exit()
782 {
783
784 }
800785
801786 /*
802787 * Send a command to a dpi server, and block until the answer is got.
803788 * Return value: the dpip tag answer as an string, NULL on error.
804789 */
805 char *a_Dpi_send_blocking_cmd(const gchar *server_name, const gchar *cmd)
806 {
807 gint i, cst, SockFD;
808 ssize_t st;
809 gchar buf[16384], *retval = NULL;
790 char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd)
791 {
792 int cst, sock_fd;
793 char *ret = NULL;
810794
811795 /* test the dpid, and wait a bit for it to start if necessary */
812 for (i = 0; (cst = Dpi_check_dpid()) == 1 && i < 2; ++i)
813 sleep(1);
814
815 if (cst != 0)
816 return retval;
817
818 SockFD = Dpi_connect_socket(server_name, TRUE);
819 if (SockFD != -1) {
820 /* todo: handle the case of (st < strlen(cmd)) */
821 do
822 st = write(SockFD, cmd, strlen(cmd));
823 while (st == -1 && errno == EINTR);
824
825 /* todo: if the answer is too long... */
826 do
827 st = read(SockFD, buf, 16384);
828 while (st < 0 && errno == EINTR);
829
830 if (st == -1)
831 perror("[a_Dpi_send_blocking_cmd]");
832 else if (st > 0)
833 retval = g_strndup(buf, (guint)st);
834
835 Dpi_close_fd(SockFD);
836
837 } else {
838 perror("[a_Dpi_send_blocking_cmd]");
839 }
840
841 return retval;
842 }
843
796 if ((cst = Dpi_blocking_start_dpid()) != 0) {
797 return ret;
798 }
799
800 if ((sock_fd = Dpi_connect_socket(server_name, TRUE)) == -1) {
801 MSG_ERR("[a_Dpi_send_blocking_cmd] Can't connect to server.\n");
802 } else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
803 MSG_ERR("[a_Dpi_send_blocking_cmd] Can't send message.\n");
804 } if ((ret = Dpi_blocking_read(sock_fd)) == NULL) {
805 MSG_ERR("[a_Dpi_send_blocking_cmd] Can't read message.\n");
806 }
807 Dpi_close_fd(sock_fd);
808
809 return ret;
810 }
811
00 /*
11 * File: http.c
22 *
3 * Copyright (C) 2000, 2001 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1515
1616 #include <config.h>
1717
18 #include <ctype.h> /* isdigit */
1819 #include <unistd.h>
1920 #include <errno.h> /* for errno */
20 #include <string.h> /* for strstr */
2121 #include <stdlib.h>
22 #include <signal.h>
2322 #include <fcntl.h>
24 #include <sys/wait.h>
23 #include <assert.h>
2524 #include <sys/socket.h> /* for lots of socket stuff */
2625 #include <netinet/in.h> /* for ntohl and stuff */
2726 #include <arpa/inet.h> /* for inet_ntop */
2827
2928 #include "IO.h"
3029 #include "Url.h"
30 #include "../msg.h"
3131 #include "../klist.h"
3232 #include "../dns.h"
33 #include "../cache.h"
34 #include "../web.h"
35 #include "../interface.h"
33 #include "../web.hh"
3634 #include "../cookies.h"
35 #include "../auth.h"
3736 #include "../prefs.h"
3837 #include "../misc.h"
3938
39 #include "../uicmd.hh"
40
4041 /* Used to send a message to the bw's status bar */
41 #define BW_MSG(web, root, fmt...) \
42 G_STMT_START { \
42 #define MSG_BW(web, root, ...) \
43 D_STMT_START { \
4344 if (a_Web_valid((web)) && (!(root) || (web)->flags & WEB_RootUrl)) \
44 a_Interface_msg((web)->bw, fmt); \
45 } G_STMT_END
46
47 #define DEBUG_LEVEL 5
48 #include "../debug.h"
49
50
45 a_UIcmd_set_msg((web)->bw, __VA_ARGS__); \
46 } D_STMT_END
47
48 #define _MSG_BW(web, root, ...)
49
50 static const int HTTP_SOCKET_USE_PROXY = 0x1;
51 static const int HTTP_SOCKET_QUEUED = 0x4;
52 static const int HTTP_SOCKET_TO_BE_FREED = 0x8;
5153
5254 /* 'Url' and 'web' are just references (no need to deallocate them here). */
5355 typedef struct {
54 gint SockFD;
55 const DilloUrl *Url; /* reference to original URL */
56 guint port; /* need a separate port in order to support PROXY */
57 gboolean use_proxy; /* indicates whether to use proxy or not */
56 int SockFD;
57 uint_t port; /* need a separate port in order to support PROXY */
58 uint_t flags;
5859 DilloWeb *web; /* reference to client's web structure */
59 GSList *addr_list; /* Holds the DNS answer */
60 GSList *addr_list_iter; /* Points to address currently being used */
61 GIOChannel *GioCh; /* GIOChannel to monitor the connecting process */
62 gint Err; /* Holds the errno of the connect() call */
60 Dlist *addr_list; /* Holds the DNS answer */
61 int Err; /* Holds the errno of the connect() call */
6362 ChainLink *Info; /* Used for CCC asynchronous operations */
63 char *connected_to; /* Used for per-host connection limit */
6464 } SocketData_t;
6565
66 /* Data structures and functions to queue sockets that need to be
67 * delayed due to the per host connection limit.
68 */
69 typedef struct SocketQueueEntry {
70 SocketData_t* sock;
71 struct SocketQueueEntry *next ;
72 } SocketQueueEntry_t;
73
74 typedef struct {
75 SocketQueueEntry_t *head;
76 SocketQueueEntry_t *tail;
77 } SocketQueue_t;
78
79 typedef struct {
80 char *host;
81 int active_connections;
82 SocketQueue_t queue;
83 } HostConnection_t;
84
85 static void Http_socket_queue_init(SocketQueue_t *sq);
86 static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock);
87 static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq);
88 static HostConnection_t *Http_host_connection_get(const char *host);
89 static void Http_host_connection_remove(HostConnection_t *hc);
90 static int Http_connect_socket(ChainLink *Info);
91 static void Http_socket_free(int SKey);
6692
6793 /*
6894 * Local data
6995 */
7096 static Klist_t *ValidSocks = NULL; /* Active sockets list. It holds pointers to
7197 * SocketData_t structures. */
72
7398 static DilloUrl *HTTP_Proxy = NULL;
74 static gchar *HTTP_Proxy_Auth_base64 = NULL;
75
76 /*
77 * Initialize proxy vars.
78 */
79 gint a_Http_init(void)
80 {
81 gchar *env_proxy = getenv("http_proxy");
99 static char *HTTP_Proxy_Auth_base64 = NULL;
100 static char *HTTP_Language_hdr = NULL;
101 static Dlist *host_connections;
102
103 /*
104 * Initialize proxy vars and Accept-Language header
105 */
106 int a_Http_init(void)
107 {
108 char *env_proxy = getenv("http_proxy");
109
110 HTTP_Language_hdr = prefs.http_language ?
111 dStrconcat("Accept-Language: ", prefs.http_language, "\r\n", NULL) :
112 dStrdup("");
82113
83114 if (env_proxy && strlen(env_proxy))
84 HTTP_Proxy = a_Url_new(env_proxy, NULL, 0, 0, 0);
115 HTTP_Proxy = a_Url_new(env_proxy, NULL);
85116 if (!HTTP_Proxy && prefs.http_proxy)
86117 HTTP_Proxy = a_Url_dup(prefs.http_proxy);
87118
91122 if (HTTP_Proxy && prefs.http_proxyuser && strchr(prefs.http_proxyuser, ':'))
92123 HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(prefs.http_proxyuser);
93124 */
125
126 host_connections = dList_new(5);
127
94128 return 0;
95129 }
96130
98132 * Tell whether the proxy auth is already set (user:password)
99133 * Return: 1 Yes, 0 No
100134 */
101 gint a_Http_proxy_auth(void)
135 int a_Http_proxy_auth(void)
102136 {
103137 return (HTTP_Proxy_Auth_base64 ? 1 : 0);
104138 }
106140 /*
107141 * Activate entered proxy password for HTTP.
108142 */
109 void a_Http_set_proxy_passwd(gchar *str)
110 {
111 gchar *http_proxyauth = g_strconcat(prefs.http_proxyuser, ":", str, NULL);
143 void a_Http_set_proxy_passwd(const char *str)
144 {
145 char *http_proxyauth = dStrconcat(prefs.http_proxyuser, ":", str, NULL);
112146 HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(http_proxyauth);
113 g_free(http_proxyauth);
114 }
115
116 /*
117 * Forward declarations
118 */
119 static void Http_send_query(ChainLink *Info, SocketData_t *S);
120 static int Http_connect_socket(ChainLink *Info);
147 dFree(http_proxyauth);
148 }
121149
122150 /*
123151 * Create and init a new SocketData_t struct, insert into ValidSocks,
124152 * and return a primary key for it.
125153 */
126 static gint Http_sock_new(void)
127 {
128 SocketData_t *S = g_new0(SocketData_t, 1);
129 return a_Klist_insert(&ValidSocks, (gpointer)S);
154 static int Http_sock_new(void)
155 {
156 SocketData_t *S = dNew0(SocketData_t, 1);
157 return a_Klist_insert(&ValidSocks, S);
158 }
159
160 static void Http_connect_queued_sockets(HostConnection_t *hc)
161 {
162 SocketData_t *sd;
163 while (hc->active_connections < prefs.http_max_conns &&
164 (sd = Http_socket_dequeue(&hc->queue))) {
165
166 sd->flags &= ~HTTP_SOCKET_QUEUED;
167
168 if (sd->flags & HTTP_SOCKET_TO_BE_FREED) {
169 dFree(sd);
170 } else if (a_Web_valid(sd->web)) {
171 /* start connecting the socket */
172 if (Http_connect_socket(sd->Info) < 0) {
173 MSG_BW(sd->web, 1, "ERROR: %s", dStrerror(sd->Err));
174 a_Chain_bfcb(OpAbort, sd->Info, NULL, "Both");
175 dFree(sd->Info);
176 Http_socket_free(VOIDP2INT(sd->Info->LocalKey));
177 } else {
178 sd->connected_to = hc->host;
179 hc->active_connections++;
180 }
181 }
182 }
130183 }
131184
132185 /*
133186 * Free SocketData_t struct
134187 */
135 static void Http_socket_free(gint SKey)
188 static void Http_socket_free(int SKey)
136189 {
137190 SocketData_t *S;
138191
139192 if ((S = a_Klist_get_data(ValidSocks, SKey))) {
140193 a_Klist_remove(ValidSocks, SKey);
141 if (S->GioCh)
142 g_io_channel_unref(S->GioCh);
143 g_free(S);
144 }
194
195 if (S->flags & HTTP_SOCKET_QUEUED) {
196 S->flags |= HTTP_SOCKET_TO_BE_FREED;
197 } else {
198 if (S->connected_to) {
199 HostConnection_t *hc = Http_host_connection_get(S->connected_to);
200 hc->active_connections--;
201 Http_connect_queued_sockets(hc);
202 if (hc->active_connections == 0)
203 Http_host_connection_remove(hc);
204 }
205 dFree(S);
206 }
207 }
208 }
209
210 /*
211 * Close the socket's FD
212 */
213 static void Http_socket_close(SocketData_t *S)
214 {
215 int st;
216 do
217 st = close(S->SockFD);
218 while (st < 0 && errno == EINTR);
219 }
220
221 /*
222 * Make the HTTP header's Referer line according to preferences
223 * (default is "host" i.e. "scheme://hostname/" )
224 */
225 static char *Http_get_referer(const DilloUrl *url)
226 {
227 char *referer = NULL;
228
229 if (!strcmp(prefs.http_referer, "host")) {
230 referer = dStrconcat("Referer: ", URL_SCHEME(url), "://",
231 URL_AUTHORITY(url), "/", "\r\n", NULL);
232 } else if (!strcmp(prefs.http_referer, "path")) {
233 referer = dStrconcat("Referer: ", URL_SCHEME(url), "://",
234 URL_AUTHORITY(url),
235 URL_PATH_(url) ? URL_PATH(url) : "/", "\r\n", NULL);
236 }
237 if (!referer)
238 referer = dStrdup("");
239 _MSG("http, referer='%s'\n", referer);
240 return referer;
241 }
242
243 /*
244 * Generate Content-Type header value for a POST query.
245 */
246 static Dstr *Http_make_content_type(const DilloUrl *url)
247 {
248 Dstr *dstr;
249
250 if (URL_FLAGS(url) & URL_MultipartEnc) {
251 MSG("submitting multipart/form-data!\n");
252 dstr = dStr_new("multipart/form-data; boundary=\"");
253 if (URL_DATA(url)->len > 2) {
254 /* boundary lines have "--" prepended. Skip that. */
255 const char *start = URL_DATA(url)->str + 2;
256 char *eol = strchr(start, '\r');
257 if (eol)
258 dStr_append_l(dstr, start, eol - start);
259 } else {
260 /* Zero parts; arbitrary boundary */
261 dStr_append_c(dstr, '0');
262 }
263 dStr_append_c(dstr,'"');
264 } else {
265 dstr = dStr_new("application/x-www-form-urlencoded");
266 }
267 return dstr;
145268 }
146269
147270 /*
148271 * Make the http query string
149272 */
150 gchar *a_Http_make_query_str(const DilloUrl *url, gboolean use_proxy)
151 {
152 gchar *str, *ptr, *cookies;
153 GString *s_port = g_string_new(""),
154 *query = g_string_new(""),
155 *full_path = g_string_new(""),
156 *proxy_auth = g_string_new("");
157
158 /* Sending the default port in the query may cause a 302-answer. --Jcid */
159 if (URL_PORT(url) && URL_PORT(url) != DILLO_URL_HTTP_PORT)
160 g_string_sprintfa(s_port, ":%d", URL_PORT(url));
273 Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
274 {
275 const char *auth;
276 char *ptr, *cookies, *referer;
277 Dstr *query = dStr_new(""),
278 *full_path = dStr_new(""),
279 *proxy_auth = dStr_new("");
161280
162281 if (use_proxy) {
163 g_string_sprintfa(full_path, "%s%s",
164 URL_STR(url),
165 (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
282 dStr_sprintfa(full_path, "%s%s",
283 URL_STR(url),
284 (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
166285 if ((ptr = strrchr(full_path->str, '#')))
167 g_string_truncate(full_path, ptr - full_path->str);
286 dStr_truncate(full_path, ptr - full_path->str);
168287 if (HTTP_Proxy_Auth_base64)
169 g_string_sprintf(proxy_auth, "Proxy-Authorization: Basic %s\r\n",
170 HTTP_Proxy_Auth_base64);
288 dStr_sprintf(proxy_auth, "Proxy-Authorization: Basic %s\r\n",
289 HTTP_Proxy_Auth_base64);
171290 } else {
172 g_string_sprintfa(full_path, "%s%s%s%s",
173 URL_PATH(url),
174 URL_QUERY_(url) ? "?" : "",
175 URL_QUERY(url),
176 (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
177 }
178
179 cookies = a_Cookies_get(url);
180 if ( URL_FLAGS(url) & URL_Post ){
181 g_string_sprintfa(
291 dStr_sprintfa(full_path, "%s%s%s%s",
292 URL_PATH(url),
293 URL_QUERY_(url) ? "?" : "",
294 URL_QUERY(url),
295 (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
296 }
297
298 cookies = a_Cookies_get_query(url);
299 auth = a_Auth_get_auth_str(url);
300 referer = Http_get_referer(url);
301 if (URL_FLAGS(url) & URL_Post) {
302 Dstr *content_type = Http_make_content_type(url);
303 dStr_sprintfa(
182304 query,
183 "POST %s HTTP/1.0\r\n"
184 "Host: %s%s\r\n"
305 "POST %s HTTP/1.1\r\n"
306 "Connection: close\r\n"
307 "Accept: text/*,image/*,*/*;q=0.2\r\n"
308 "Accept-Charset: utf-8,*;q=0.8\r\n"
309 "Accept-Encoding: gzip\r\n"
310 "%s" /* language */
311 "%s" /* auth */
312 "Host: %s\r\n"
185313 "%s"
186 "User-Agent: Dillo/%s\r\n"
187 "Cookie2: $Version=\"1\"\r\n"
188314 "%s"
189 "Content-type: application/x-www-form-urlencoded\r\n"
190 "Content-length: %ld\r\n"
191 "\r\n"
192 "%s",
193 full_path->str, URL_HOST(url), s_port->str,
194 proxy_auth->str, VERSION, cookies,
195 (glong)strlen(URL_DATA(url)),
196 URL_DATA(url));
197
315 "User-Agent: %s\r\n"
316 "Content-Length: %ld\r\n"
317 "Content-Type: %s\r\n"
318 "%s" /* cookies */
319 "\r\n",
320 full_path->str, HTTP_Language_hdr, auth ? auth : "",
321 URL_AUTHORITY(url), proxy_auth->str, referer, prefs.http_user_agent,
322 (long)URL_DATA(url)->len, content_type->str,
323 cookies);
324 dStr_append_l(query, URL_DATA(url)->str, URL_DATA(url)->len);
325 dStr_free(content_type, TRUE);
198326 } else {
199 g_string_sprintfa(
327 dStr_sprintfa(
200328 query,
201 "GET %s HTTP/1.0\r\n"
329 "GET %s HTTP/1.1\r\n"
202330 "%s"
203 "Host: %s%s\r\n"
331 "Connection: close\r\n"
332 "Accept: text/*,image/*,*/*;q=0.2\r\n"
333 "Accept-Charset: utf-8,*;q=0.8\r\n"
334 "Accept-Encoding: gzip\r\n"
335 "%s" /* language */
336 "%s" /* auth */
337 "Host: %s\r\n"
204338 "%s"
205 "User-Agent: Dillo/%s\r\n"
206 "Cookie2: $Version=\"1\"\r\n"
207339 "%s"
340 "User-Agent: %s\r\n"
341 "%s" /* cookies */
208342 "\r\n",
209343 full_path->str,
210 (URL_FLAGS(url) & URL_E2EReload) ?
344 (URL_FLAGS(url) & URL_E2EQuery) ?
211345 "Cache-Control: no-cache\r\nPragma: no-cache\r\n" : "",
212 URL_HOST(url), s_port->str,
213 proxy_auth->str,
214 VERSION,
215 cookies);
216 }
217 g_free(cookies);
218
219 str = query->str;
220 g_string_free(query, FALSE);
221 g_string_free(s_port, TRUE);
222 g_string_free(full_path, TRUE);
223 g_string_free(proxy_auth, TRUE);
224 DEBUG_MSG(4, "Query:\n%s", str);
225 return str;
226 }
227
228 /*
229 * This function is called after the socket has been successfuly connected,
230 * or upon an error condition on the connecting process.
231 * Task: use the socket to send the HTTP-query and expect its answer
232 */
233 static gboolean
234 Http_use_socket(GIOChannel *src, GIOCondition cond, gpointer data)
235 {
236 ChainLink *Info;
237 SocketData_t *S;
238 gint SKey = GPOINTER_TO_INT(data);
239
240 DEBUG_MSG(3, "Http_use_socket: %s [errno %d] [GIOcond %d]\n",
241 g_strerror(errno), errno, cond);
242
243 /* This check is required because glib may asynchronously
244 * call this function with data that's no longer used --Jcid */
245 if ( !(S = a_Klist_get_data(ValidSocks, SKey)) )
246 return FALSE;
247
248 Info = S->Info;
249 if ( cond & G_IO_HUP ) {
250 DEBUG_MSG(3, "--Connection broken\n");
251 /* get rid of S->GioCh */
252 g_io_channel_close(S->GioCh);
253 g_io_channel_unref(S->GioCh);
254 S->GioCh = NULL;
255 S->addr_list_iter = S->addr_list_iter->next;
256 if (!S->addr_list_iter || Http_connect_socket(Info) < 0) {
257 BW_MSG(S->web, 0, "ERROR: unable to connect to remote host");
258 Http_socket_free(SKey);
259 a_Chain_fcb(OpAbort, Info, NULL, NULL);
260 }
261 } else if ( S->Err ) {
262 BW_MSG(S->web, 0, "ERROR: %s", g_strerror(S->Err));
263 DEBUG_MSG(3, "Http_use_socket ERROR: %s\n", g_strerror(S->Err));
264 a_Chain_fcb(OpAbort, Info, NULL, NULL);
265 g_io_channel_close(S->GioCh);
266 Http_socket_free(SKey);
267 } else if ( cond & G_IO_OUT ) {
268 DEBUG_MSG(3, "--Connection established\n");
269 g_io_channel_unref(S->GioCh);
270 S->GioCh = NULL;
271 Http_send_query(Info, S);
272 }
273 return FALSE;
346 HTTP_Language_hdr, auth ? auth : "", URL_AUTHORITY(url),
347 proxy_auth->str, referer, prefs.http_user_agent, cookies);
348 }
349 dFree(referer);
350 dFree(cookies);
351
352 dStr_free(full_path, TRUE);
353 dStr_free(proxy_auth, TRUE);
354 _MSG("Query: {%s}\n", dStr_printable(query, 8192));
355 return query;
356 }
357
358 /*
359 * Create and submit the HTTP query to the IO engine
360 */
361 static void Http_send_query(ChainLink *Info, SocketData_t *S)
362 {
363 Dstr *query;
364 DataBuf *dbuf;
365
366 /* Create the query */
367 query = a_Http_make_query_str(S->web->url, S->flags & HTTP_SOCKET_USE_PROXY);
368 dbuf = a_Chain_dbuf_new(query->str, query->len, 0);
369
370 /* actually this message is sent too early.
371 * It should go when the socket is ready for writing (i.e. connected) */
372 _MSG_BW(S->web, 1, "Sending query to %s...", URL_HOST_(S->web->url));
373
374 /* send query */
375 a_Chain_bcb(OpSend, Info, dbuf, NULL);
376 dFree(dbuf);
377 dStr_free(query, 1);
274378 }
275379
276380 /*
280384 */
281385 static int Http_connect_socket(ChainLink *Info)
282386 {
283 gint status;
284 guint watch_id;
387 int i, status;
285388 #ifdef ENABLE_IPV6
286389 struct sockaddr_in6 name;
287390 #else
291394 DilloHost *dh;
292395 socklen_t socket_len = 0;
293396
294 S = a_Klist_get_data(ValidSocks, GPOINTER_TO_INT(Info->LocalKey));
295
296 dh = S->addr_list_iter->data;
297
298 if ( (S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0 ) {
299 S->Err = errno;
300 DEBUG_MSG(5, "Http_connect_socket ERROR: %s\n", g_strerror(errno));
301 return -1;
302 }
303 /* set NONBLOCKING and close on exec. */
304 fcntl(S->SockFD, F_SETFL,
305 O_NONBLOCK | fcntl(S->SockFD, F_GETFL));
306 fcntl(S->SockFD, F_SETFD,
307 FD_CLOEXEC | fcntl(S->SockFD, F_GETFD));
308
309 /* Some OSes require this... */
310 memset(&name, 0, sizeof(name));
311 /* Set remaining parms. */
312 switch (dh->af) {
313 case AF_INET:
314 {
315 struct sockaddr_in *sin = (struct sockaddr_in *)&name;
316 socket_len = sizeof(struct sockaddr_in);
317 sin->sin_family = dh->af;
318 sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
319 memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
320 if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
321 DEBUG_MSG(5, "Connecting to %s\n", inet_ntoa(sin->sin_addr));
322 break;
323 }
397 S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey));
398
399 /* TODO: iterate this address list until success, or end-of-list */
400 for (i = 0; (dh = dList_nth_data(S->addr_list, i)); ++i) {
401 if ((S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0) {
402 S->Err = errno;
403 MSG("Http_connect_socket ERROR: %s\n", dStrerror(errno));
404 continue;
405 }
406 /* set NONBLOCKING and close on exec. */
407 fcntl(S->SockFD, F_SETFL, O_NONBLOCK | fcntl(S->SockFD, F_GETFL));
408 fcntl(S->SockFD, F_SETFD, FD_CLOEXEC | fcntl(S->SockFD, F_GETFD));
409
410 /* Some OSes require this... */
411 memset(&name, 0, sizeof(name));
412 /* Set remaining parms. */
413 switch (dh->af) {
414 case AF_INET:
415 {
416 struct sockaddr_in *sin = (struct sockaddr_in *)&name;
417 socket_len = sizeof(struct sockaddr_in);
418 sin->sin_family = dh->af;
419 sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
420 memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
421 if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
422 MSG("Connecting to %s\n", inet_ntoa(sin->sin_addr));
423 break;
424 }
324425 #ifdef ENABLE_IPV6
325 case AF_INET6:
326 {
327 char buf[128];
328 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name;
329 socket_len = sizeof(struct sockaddr_in6);
330 sin6->sin6_family = dh->af;
331 sin6->sin6_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
332 memcpy(&sin6->sin6_addr, dh->data, dh->alen);
333 inet_ntop(dh->af, dh->data, buf, sizeof(buf));
334 if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
335 DEBUG_MSG(5, "Connecting to %s\n", buf);
336 break;
337 }
426 case AF_INET6:
427 {
428 char buf[128];
429 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name;
430 socket_len = sizeof(struct sockaddr_in6);
431 sin6->sin6_family = dh->af;
432 sin6->sin6_port =
433 S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
434 memcpy(&sin6->sin6_addr, dh->data, dh->alen);
435 inet_ntop(dh->af, dh->data, buf, sizeof(buf));
436 if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
437 MSG("Connecting to %s\n", buf);
438 break;
439 }
338440 #endif
339 }
340
341 if (!S->GioCh)
342 S->GioCh = g_io_channel_unix_new(S->SockFD);
343 watch_id = g_io_add_watch(S->GioCh, G_IO_ERR | G_IO_HUP | G_IO_OUT,
344 Http_use_socket, Info->LocalKey);
345 status = connect(S->SockFD, (struct sockaddr *)&name, socket_len);
346 if ( status == -1 && errno != EINPROGRESS ) {
347 DEBUG_MSG(5, "Http_connect_socket ERROR: %s\n", g_strerror(errno));
348 g_io_channel_close(S->GioCh);
349 g_source_remove(watch_id);
350 S->Err = errno;
351 return -1;
352 }
353
354 BW_MSG(S->web, 1, "Contacting host...");
355
356 return 0; /* Success */
357 }
358
359
360 /*
361 * Create and submit the HTTP query to the IO engine
362 */
363 static void Http_send_query(ChainLink *Info, SocketData_t *S)
364 {
365 IOData_t *io;
366 gchar *query;
367 void *link;
368
369 /* Create the query */
370 query = a_Http_make_query_str(S->Url, S->use_proxy);
371
372 /* send query */
373 BW_MSG(S->web, 1, "Sending query to %s...", URL_HOST_(S->Url));
374 io = a_IO_new(IOWrite, S->SockFD);
375 a_IO_set_buf(io, query, strlen(query));
376 io->Flags |= IOFlag_FreeIOBuf;
377 link = a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 1, 2);
378 a_Chain_bcb(OpStart, Info, io, NULL);
379 a_Chain_bcb(OpSend, Info, io, NULL);
380
381 /* Tell the cache to start the receiving CCC for the answer */
382 a_Chain_fcb(OpSend, Info, &S->SockFD, NULL);
441 }/*switch*/
442
443 MSG_BW(S->web, 1, "Contacting host...");
444 status = connect(S->SockFD, (struct sockaddr *)&name, socket_len);
445 if (status == -1 && errno != EINPROGRESS) {
446 S->Err = errno;
447 Http_socket_close(S);
448 MSG("Http_connect_socket ERROR: %s\n", dStrerror(S->Err));
449 } else {
450 a_Chain_bcb(OpSend, Info, &S->SockFD, "FD");
451 a_Chain_fcb(OpSend, Info, &S->SockFD, "FD");
452 Http_send_query(S->Info, S);
453 return 0; /* Success */
454 }
455 }
456
457 return -1;
383458 }
384459
385460 /*
386461 * Test proxy settings and check the no_proxy domains list
387462 * Return value: whether to use proxy or not.
388463 */
389 static gint Http_must_use_proxy(const DilloUrl *url)
390 {
391 gint i;
392 gchar **NoProxyVec = prefs.no_proxy_vec;
464 static int Http_must_use_proxy(const DilloUrl *url)
465 {
466 char *np, *p, *tok;
467 int ret = 0;
393468
394469 if (HTTP_Proxy) {
395 if (NoProxyVec)
396 for (i = 0; NoProxyVec[i]; ++i)
397 if (strstr(URL_AUTHORITY(url), NoProxyVec[i]))
398 return 0;
399 return 1;
400 }
401 return 0;
470 ret = 1;
471 if (prefs.no_proxy) {
472 const char *host = URL_HOST(url);
473 size_t host_len = strlen(host);
474
475 np = dStrdup(prefs.no_proxy);
476 for (p = np; (tok = dStrsep(&p, " ")); ) {
477 int start = host_len - strlen(tok);
478
479 if (start >= 0 && dStrcasecmp(host + start, tok) == 0) {
480 /* no_proxy token is suffix of host string */
481 ret = 0;
482 break;
483 }
484 }
485 dFree(np);
486 }
487 }
488 _MSG("Http_must_use_proxy: %s\n %s\n", URL_STR(url), ret ? "YES":"NO");
489 return ret;
490 }
491
492 /*
493 * Return a new string for the request used to tunnel HTTPS through a proxy.
494 * As of 2009, the best reference appears to be section 5 of RFC 2817.
495 */
496 char *a_Http_make_connect_str(const DilloUrl *url)
497 {
498 Dstr *dstr;
499 const char *auth1;
500 int auth_len;
501 char *auth2, *proxy_auth, *retstr;
502
503 dReturn_val_if_fail(Http_must_use_proxy(url), NULL);
504
505 dstr = dStr_new("");
506 auth1 = URL_AUTHORITY(url);
507 auth_len = strlen(auth1);
508 if (auth_len > 0 && !isdigit(auth1[auth_len - 1]))
509 /* if no port number, add HTTPS port */
510 auth2 = dStrconcat(auth1, ":443", NULL);
511 else
512 auth2 = dStrdup(auth1);
513 proxy_auth = HTTP_Proxy_Auth_base64 ?
514 dStrconcat ("Proxy-Authorization: Basic ",
515 HTTP_Proxy_Auth_base64, "\r\n", NULL) :
516 dStrdup("");
517 dStr_sprintfa(
518 dstr,
519 "CONNECT %s HTTP/1.1\r\n"
520 "Host: %s\r\n"
521 "%s"
522 "\r\n",
523 auth2,
524 auth2,
525 proxy_auth);
526
527 dFree(auth2);
528 dFree(proxy_auth);
529 retstr = dstr->str;
530 dStr_free(dstr, 0);
531 return retstr;
532 }
533
534 /*
535 * Return URL string of HTTP proxy, if any
536 */
537 const char *a_Http_get_proxy_urlstr()
538 {
539 return HTTP_Proxy ? URL_STR(HTTP_Proxy) : NULL;
540 }
541
542 /*
543 * Callback function for the DNS resolver.
544 * Continue connecting the socket, or abort upon error condition.
545 * S->web is checked to assert the operation wasn't aborted while waiting.
546 */
547 static void Http_dns_cb(int Status, Dlist *addr_list, void *data)
548 {
549 int SKey = VOIDP2INT(data);
550 SocketData_t *S;
551 HostConnection_t *hc;
552
553 S = a_Klist_get_data(ValidSocks, SKey);
554 if (S) {
555 if (!a_Web_valid(S->web)) {
556 a_Chain_bfcb(OpAbort, S->Info, NULL, "Both");
557 dFree(S->Info);
558 Http_socket_free(SKey);
559
560 } else if (Status == 0 && addr_list) {
561 /* Successful DNS answer; save the IP */
562 S->addr_list = addr_list;
563 S->flags |= HTTP_SOCKET_QUEUED;
564 if (S->flags & HTTP_SOCKET_USE_PROXY)
565 hc = Http_host_connection_get(URL_HOST(HTTP_Proxy));
566 else
567 hc = Http_host_connection_get(URL_HOST(S->web->url));
568 Http_socket_enqueue(&hc->queue, S);
569 Http_connect_queued_sockets(hc);
570 } else {
571 /* DNS wasn't able to resolve the hostname */
572 MSG_BW(S->web, 0, "ERROR: Dns can't resolve %s",
573 (S->flags & HTTP_SOCKET_USE_PROXY) ? URL_HOST_(HTTP_Proxy) :
574 URL_HOST_(S->web->url));
575 a_Chain_bfcb(OpAbort, S->Info, NULL, "Both");
576 dFree(S->Info);
577 Http_socket_free(SKey);
578 }
579 }
402580 }
403581
404582 /*
405583 * Asynchronously create a new http connection for 'Url'
406584 * We'll set some socket parameters; the rest will be set later
407585 * when the IP is known.
408 * ( Data1 = Requested Url; Data2 = Web structure )
586 * ( Data1 = Web structure )
409587 * Return value: 0 on success, -1 otherwise
410588 */
411 static gint Http_get(ChainLink *Info, void *Data1, void *Data2)
412 {
413 void *link;
414 const DilloUrl *Url = Data1;
589 static int Http_get(ChainLink *Info, void *Data1)
590 {
415591 SocketData_t *S;
416 gchar *hostname;
417
418 S = a_Klist_get_data(ValidSocks, GPOINTER_TO_INT(Info->LocalKey));
419
592 char *hostname;
593
594 S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey));
595 /* Reference Web data */
596 S->web = Data1;
420597 /* Reference Info data */
421598 S->Info = Info;
422 /* Reference Web data */
423 S->web = Data2;
424599
425600 /* Proxy support */
426 if (Http_must_use_proxy(Url)) {
427 hostname = g_strdup(URL_HOST(HTTP_Proxy));
601 if (Http_must_use_proxy(S->web->url)) {
602 hostname = dStrdup(URL_HOST(HTTP_Proxy));
428603 S->port = URL_PORT(HTTP_Proxy);
429 S->use_proxy = TRUE;
604 S->flags |= HTTP_SOCKET_USE_PROXY;
430605 } else {
431 hostname = g_strdup(URL_HOST(Url));
432 S->port = URL_PORT(Url);
433 S->use_proxy = FALSE;
434 }
435
436 /* Set more socket parameters */
437 S->Url = Url;
606 hostname = dStrdup(URL_HOST(S->web->url));
607 S->port = URL_PORT(S->web->url);
608 S->flags &= ~HTTP_SOCKET_USE_PROXY;
609 }
438610
439611 /* Let the user know what we'll do */
440 BW_MSG(S->web, 1, "DNS solving %s", URL_HOST_(S->Url));
441
442 /* Let the DNS engine solve the hostname, and when done,
443 * we'll try to connect the socket */
444 link = a_Chain_link_new(Info, a_Http_ccc, BCK, a_Dns_ccc, 1, 1);
445 a_Chain_bcb(OpStart, Info, hostname, NULL);
446
447 g_free(hostname);
612 MSG_BW(S->web, 1, "DNS resolving %s", URL_HOST_(S->web->url));
613
614 /* Let the DNS engine resolve the hostname, and when done,
615 * we'll try to connect the socket from the callback function */
616 a_Dns_resolve(hostname, Http_dns_cb, Info->LocalKey);
617
618 dFree(hostname);
448619 return 0;
449620 }
450621
454625 void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
455626 void *Data1, void *Data2)
456627 {
457 gint SKey = GPOINTER_TO_INT(Info->LocalKey);
458 SocketData_t *S = a_Klist_get_data(ValidSocks, SKey);
459
460 a_Chain_debug_msg("a_Http_ccc", Op, Branch, Dir);
461
462 if ( Branch == 1 ) {
628 int SKey = VOIDP2INT(Info->LocalKey);
629
630 (void)Data2; /* suppress unused parameter warning */
631
632 dReturn_if_fail( a_Chain_check("a_Http_ccc", Op, Branch, Dir, Info) );
633
634 if (Branch == 1) {
463635 if (Dir == BCK) {
464 /* HTTP query branch */
636 /* HTTP query branch */
465637 switch (Op) {
466638 case OpStart:
639 /* ( Data1 = Web ) */
467640 SKey = Http_sock_new();
468 Info->LocalKey = GINT_TO_POINTER(SKey);
469 if (Http_get(Info, Data1, Data2) < 0) {
470 S = a_Klist_get_data(ValidSocks, SKey);
471 BW_MSG(S->web, 1, "ERROR: %s", g_strerror(S->Err));
472 Http_socket_free(SKey);
473 a_Chain_fcb(OpAbort, Info, NULL, NULL);
474 }
641 Info->LocalKey = INT2VOIDP(SKey);
642 /* link IO */
643 a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 1, 1);
644 a_Chain_bcb(OpStart, Info, NULL, NULL);
645 /* async. connection */
646 Http_get(Info, Data1);
647 break;
648 case OpEnd:
649 /* finished the HTTP query branch */
650 a_Chain_bcb(OpEnd, Info, NULL, NULL);
651 Http_socket_free(SKey);
652 dFree(Info);
475653 break;
476654 case OpAbort:
477655 /* something bad happened... */
478 DEBUG_MSG(2, "Http: OpAbort [1B]\n");
656 a_Chain_bcb(OpAbort, Info, NULL, NULL);
479657 Http_socket_free(SKey);
480 a_Chain_bcb(OpAbort, Info, NULL, NULL);
481 g_free(Info);
658 dFree(Info);
482659 break;
483660 }
484 } else { /* FWD */
485 /* DNS answer branch */
661 } else { /* 1 FWD */
662 /* HTTP send-query status branch */
486663 switch (Op) {
487 case OpSend:
488 /* Successful DNS answer; save the IP */
489 if (S) {
490 S->addr_list = (GSList *)Data1;
491 S->addr_list_iter = S->addr_list;
492 }
493 break;
494 case OpEnd:
495 if (S) {
496 /* Unlink DNS_Info */
497 a_Chain_del_link(Info, BCK);
498 /* start connecting the socket */
499 if (Http_connect_socket(Info) < 0) {
500 BW_MSG(S->web, 1, "ERROR: %s", g_strerror(S->Err));
501 Http_socket_free(SKey);
502 a_Chain_fcb(OpAbort, Info, NULL, NULL);
503 }
504 }
505 break;
506 case OpAbort:
507 /* DNS wasn't able to resolve the hostname */
508 if (S) {
509 /* Unlink DNS_Info */
510 a_Chain_del_link(Info, BCK);
511 BW_MSG(S->web, 0, "ERROR: Dns can't solve %s",
512 (S->use_proxy) ? URL_HOST_(HTTP_Proxy) : URL_HOST_(S->Url));
513 Http_socket_free(SKey);
514 /* send abort message to higher-level functions */
515 a_Chain_fcb(OpAbort, Info, NULL, NULL);
516 }
664 default:
665 MSG_WARN("Unused CCC\n");
517666 break;
518667 }
519668 }
520
521 } else if ( Branch == 2 ) {
522 if (Dir == FWD) {
523 /* IO send-query status branch */
524 switch (Op) {
525 case OpStart:
526 /* LocalKey was set by branch 1 */
527 break;
528 case OpSend:
529 /* Not used */
530 break;
531 case OpEnd:
532 /* finished sending the HTTP query */
533 if (S) {
534 a_Chain_del_link(Info, BCK);
535 a_Chain_fcb(OpEnd, Info, &S->SockFD, (void *)S->Url);
536 BW_MSG(S->web, 1, "Query sent, waiting for reply...");
537 Http_socket_free(SKey);
538 }
539 break;
540 case OpAbort:
541 /* something bad happened... */
542 /* unlink IO_Info */
543 if (S) {
544 a_Chain_del_link(Info, BCK);
545 Http_socket_free(SKey);
546 a_Chain_fcb(OpAbort, Info, NULL, NULL);
547 }
548 break;
549 }
550 }
551 }
552 }
553
554
669 }
670 }
671
672
673 static void Http_socket_queue_init(SocketQueue_t *sq)
674 {
675 sq->head = NULL;
676 sq->tail = NULL;
677 }
678
679 static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock)
680 {
681 SocketQueueEntry_t *se = dNew(SocketQueueEntry_t, 1);
682
683 se->sock = sock;
684 se->next = NULL;
685
686 if (sq->tail)
687 sq->tail->next = se;
688 sq->tail = se;
689
690 if (! sq->head)
691 sq->head = se;
692 }
693
694 static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq)
695 {
696 SocketQueueEntry_t *se = sq->head;
697 SocketData_t *sd = NULL;
698
699 if (se) {
700 sq->head = se->next;
701 if (sq->tail == se)
702 sq->tail = NULL;
703 sd = se->sock;
704 dFree(se);
705 }
706
707 return sd;
708 }
709
710 static HostConnection_t *Http_host_connection_get(const char *host)
711 {
712 int i;
713 HostConnection_t *hc;
714
715 for (i = 0; i < dList_length(host_connections); i++) {
716 hc = (HostConnection_t*) dList_nth_data(host_connections, i);
717
718 if (dStrcasecmp(host, hc->host) == 0)
719 return hc;
720 }
721
722 hc = dNew0(HostConnection_t, 1);
723 Http_socket_queue_init(&hc->queue);
724 hc->host = dStrdup(host);
725 dList_append(host_connections, hc);
726
727 return hc;
728 }
729
730 static void Http_host_connection_remove(HostConnection_t *hc)
731 {
732 assert(hc->queue.head == NULL);
733 dList_remove_fast(host_connections, hc);
734 dFree(hc->host);
735 dFree(hc);
736 }
737
738 static void Http_host_connection_remove_all()
739 {
740 HostConnection_t *hc;
741
742 while (dList_length(host_connections) > 0) {
743 hc = (HostConnection_t*) dList_nth_data(host_connections, 0);
744 while (Http_socket_dequeue(&hc->queue));
745 Http_host_connection_remove(hc);
746 }
747 dList_free(host_connections);
748 }
555749
556750 /*
557751 * Deallocate memory used by http module
559753 */
560754 void a_Http_freeall(void)
561755 {
756 Http_host_connection_remove_all();
562757 a_Klist_free(&ValidSocks);
563758 a_Url_free(HTTP_Proxy);
564 g_free(HTTP_Proxy_Auth_base64);
565 }
759 dFree(HTTP_Proxy_Auth_base64);
760 dFree(HTTP_Language_hdr);
761 }
0 /*
1 * File: iowatch.cc
2 *
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 // Simple ADT for watching file descriptor activity
12
13 #include <FL/Fl.H>
14 #include "iowatch.hh"
15
16 //
17 // Hook a Callback for a certain activities in a FD
18 //
19 void a_IOwatch_add_fd(int fd, int when, Fl_FD_Handler Callback,
20 void *usr_data = 0)
21 {
22 if (fd >= 0)
23 Fl::add_fd(fd, when, Callback, usr_data);
24 }
25
26 //
27 // Remove a Callback for a given FD (or just remove some events)
28 //
29 void a_IOwatch_remove_fd(int fd, int when)
30 {
31 if (fd >= 0)
32 Fl::remove_fd(fd, when);
33 }
34
0 #ifndef __IO_WATCH_H__
1 #define __IO_WATCH_H__
2
3 /*
4 * BUG: enum {READ = 1, WRITE = 4, EXCEPT = 8} borrowed from FL/Enumerations.H
5 */
6 #define DIO_READ 1
7 #define DIO_WRITE 4
8 #define DIO_EXCEPT 8
9
10 typedef void (*CbFunction_t)(int fd, void *data);
11
12 #ifdef __cplusplus
13 extern "C" {
14 #endif /* __cplusplus */
15
16 void a_IOwatch_add_fd(int fd,int when,CbFunction_t Callback,void *usr_data);
17 void a_IOwatch_remove_fd(int fd,int when);
18
19 #ifdef __cplusplus
20 }
21 #endif /* __cplusplus */
22
23 #endif /* __IO_WATCH_H__ */
24
00 /*
11 * File: mime.c
22 *
3 * Copyright (C) 2000 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1111 #include "mime.h"
12 #include "../msg.h"
1312 #include "../list.h"
1413
1514
2221 /*
2322 * Local data
2423 */
25 static gint MimeMinItemsSize = 0, MimeMinItemsMax = 8;
24 static int MimeMinItemsSize = 0, MimeMinItemsMax = 8;
2625 static MimeItem_t *MimeMinItems = NULL;
2726
28 static gint MimeMajItemsSize = 0, MimeMajItemsMax = 8;
27 static int MimeMajItemsSize = 0, MimeMajItemsMax = 8;
2928 static MimeItem_t *MimeMajItems = NULL;
3029
3130
3433 * 'Key' is the content-type string that identifies the MIME type
3534 * 'Method' is the function that handles it
3635 */
37 static gint Mime_add_minor_type(const char *Key, Viewer_t Method)
36 static int Mime_add_minor_type(const char *Key, Viewer_t Method)
3837 {
3938 a_List_add(MimeMinItems, MimeMinItemsSize, MimeMinItemsMax);
4039 MimeMinItems[MimeMinItemsSize].Name = Key;
4847 * 'Key' is the content-type string that identifies the MIME type
4948 * 'Method' is the function that handles it
5049 */
51 static gint Mime_add_major_type(const char *Key, Viewer_t Method)
50 static int Mime_add_major_type(const char *Key, Viewer_t Method)
5251 {
5352 a_List_add(MimeMajItems, MimeMajItemsSize, MimeMajItemsMax);
5453 MimeMajItems[MimeMajItemsSize].Name = Key;
6160 * Search the list of specific MIME viewers, for a Method that matches 'Key'
6261 * 'Key' is the content-type string that identifies the MIME type
6362 */
64 static Viewer_t Mime_minor_type_fetch(const char *Key, guint Size)
63 static Viewer_t Mime_minor_type_fetch(const char *Key, uint_t Size)
6564 {
66 gint i;
65 int i;
6766
6867 if (Size) {
6968 for ( i = 0; i < MimeMinItemsSize; ++i )
70 if ( g_strncasecmp(Key, MimeMinItems[i].Name, Size) == 0 )
69 if (dStrncasecmp(Key, MimeMinItems[i].Name, Size) == 0)
7170 return MimeMinItems[i].Data;
7271 }
7372 return NULL;
7776 * Search the list of major MIME viewers, for a Method that matches 'Key'
7877 * 'Key' is the content-type string that identifies the MIME type
7978 */
80 static Viewer_t Mime_major_type_fetch(const char *Key, guint Size)
79 static Viewer_t Mime_major_type_fetch(const char *Key, uint_t Size)
8180 {
82 gint i;
81 int i;
8382
8483 if (Size) {
8584 for ( i = 0; i < MimeMajItemsSize; ++i )
86 if ( g_strncasecmp(Key, MimeMajItems[i].Name, Size) == 0 )
85 if (dStrncasecmp(Key, MimeMajItems[i].Name, Size) == 0)
8786 return MimeMajItems[i].Data;
8887 }
8988 return NULL;
9695 void a_Mime_init()
9796 {
9897 #ifdef ENABLE_GIF
99 Mime_add_minor_type("image/gif", a_Gif_image);
98 Mime_add_minor_type("image/gif", a_Dicache_gif_image);
10099 #endif
101100 #ifdef ENABLE_JPEG
102 Mime_add_minor_type("image/jpeg", a_Jpeg_image);
103 Mime_add_minor_type("image/pjpeg", a_Jpeg_image);
104 Mime_add_minor_type("image/jpg", a_Jpeg_image);
101 Mime_add_minor_type("image/jpeg", a_Dicache_jpeg_image);
102 Mime_add_minor_type("image/pjpeg", a_Dicache_jpeg_image);
103 Mime_add_minor_type("image/jpg", a_Dicache_jpeg_image);
105104 #endif
106105 #ifdef ENABLE_PNG
107 Mime_add_minor_type("image/png", a_Png_image);
108 Mime_add_minor_type("image/x-png", a_Png_image); /* deprecated */
106 Mime_add_minor_type("image/png", a_Dicache_png_image);
107 Mime_add_minor_type("image/x-png", a_Dicache_png_image); /* deprecated */
109108 #endif
110109 Mime_add_minor_type("text/html", a_Html_text);
110 Mime_add_minor_type("application/xhtml+xml", a_Html_text);
111111
112112 /* Add a major type to handle all the text stuff */
113113 Mime_add_major_type("text", a_Plain_text);
121121 * On success: a new Dw (and Call and Data properly set).
122122 * On failure: NULL (and Call and Data untouched).
123123 */
124 DwWidget *a_Mime_set_viewer(const char *content_type, void *Ptr,
125 CA_Callback_t *Call, void **Data)
124 void *a_Mime_set_viewer(const char *content_type, void *Ptr,
125 CA_Callback_t *Call, void **Data)
126126 {
127127
128128 Viewer_t viewer;
129 guint MinSize, MajSize, i;
129 uint_t MinSize, MajSize, i;
130130 const char *str = content_type;
131131
132132 MajSize = 0;
00 /*
1 MIME type handlers
2 Randall Maas
3 1999
4 */
1 * File: mime.h
2 *
3 * Copyright (C) 2005 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
511 #ifndef __MIME_H__
612 #define __MIME_H__
713
814 #include <config.h>
9 #include <stddef.h>
10 #include <gtk/gtk.h>
11 #include "../dw_widget.h"
15
16 #ifdef __cplusplus
17 extern "C" {
18 #endif /* __cplusplus */
19
20
1221 #include "../cache.h"
1322
14 typedef DwWidget* (*Viewer_t) (const char*, void*, CA_Callback_t*, void**);
23 typedef void* (*Viewer_t) (const char*, void*, CA_Callback_t*, void**);
1524
1625 /*
1726 * Function prototypes defined elsewhere
1827 */
19 DwWidget* a_Html_text (const char *Type,void *web, CA_Callback_t *Call,
28 void *a_Html_text (const char *Type,void *web, CA_Callback_t *Call,
2029 void **Data);
21 DwWidget* a_Plain_text(const char *Type,void *web, CA_Callback_t *Call,
30 void *a_Plain_text(const char *Type,void *web, CA_Callback_t *Call,
2231 void **Data);
23 #ifdef ENABLE_JPEG
24 DwWidget* a_Jpeg_image(const char *Type,void *web, CA_Callback_t *Call,
25 void **Data);
26 #endif
27 #ifdef ENABLE_PNG
28 DwWidget* a_Png_image (const char *Type,void *web, CA_Callback_t *Call,
29 void **Data);
30 #endif
31 #ifdef ENABLE_GIF
32 DwWidget* a_Gif_image (const char *Type,void *web, CA_Callback_t *Call,
33 void **Data);
34 #endif
32 void *a_Dicache_png_image (const char *Type,void *web, CA_Callback_t *Call,
33 void **Data);
34 void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
35 void **Data);
36 void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
37 void **Data);
3538
3639 /*
3740 * Functions defined inside Mime module
3841 */
3942 void a_Mime_init(void);
40 DwWidget* a_Mime_set_viewer(const char *content_type, void *Ptr,
41 CA_Callback_t *Call, void **Data);
43 void *a_Mime_set_viewer(const char *content_type, void *Ptr,
44 CA_Callback_t *Call, void **Data);
45
46 #ifdef __cplusplus
47 }
48 #endif /* __cplusplus */
4249
4350 #endif /* __MIME_H__ */
00 /*
11 * File: proto.c
22 *
3 * Copyright (C) 2003 Jorge Arellano Cid <jcid@dillo.org>,
3 * Copyright (C) 2003-2007 Jorge Arellano Cid <jcid@dillo.org>,
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
0 AM_CPPFLAGS=-DDILLORC_SYS='"$(sysconfdir)/dillorc"' @LIBJPEG_CPPFLAGS@
1 AM_CFLAGS = @GTK_CFLAGS@ @LIBPNG_CFLAGS@
0 AM_CPPFLAGS= \
1 -I$(top_srcdir) \
2 -DDILLO_SYSCONF='"$(sysconfdir)/"' \
3 -DDILLO_DOCDIR='"$(docdir)/"' \
4 @LIBJPEG_CPPFLAGS@
5 AM_CFLAGS = @LIBPNG_CFLAGS@
6 AM_CXXFLAGS = @LIBPNG_CFLAGS@ @LIBFLTK_CXXFLAGS@
27
38 SUBDIRS = IO
49
510 bin_PROGRAMS = dillo
611
7 dillo_LDADD = ../dpip/libDpip.a IO/libDio.a @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @GTK_LIBS@ @LIBZ_LIBS@ @LIBPTHREAD_LIBS@
8 dillo_LDFLAGS = @LIBJPEG_LDFLAGS@ @LIBPTHREAD_LDFLAGS@
12 dillo_LDADD = \
13 $(top_builddir)/dlib/libDlib.a \
14 $(top_builddir)/dpip/libDpip.a \
15 IO/libDiof.a \
16 $(top_builddir)/dw/libDw-widgets.a \
17 $(top_builddir)/dw/libDw-fltk.a \
18 $(top_builddir)/dw/libDw-core.a \
19 $(top_builddir)/lout/liblout.a \
20 @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @LIBFLTK_LIBS@ @LIBZ_LIBS@ \
21 @LIBICONV_LIBS@ @LIBPTHREAD_LIBS@
922
1023 dillo_SOURCES = \
11 msg.h \
12 acconfig.h \
13 chain.h \
14 chain.c \
15 commands.c \
16 commands.h \
17 cache.c \
18 cache.h \
19 capi.c \
20 capi.h \
21 debug.h \
22 dw.h \
23 dw.c \
24 dw_aligned_page.c \
25 dw_aligned_page.h \
26 dw_bullet.c \
27 dw_bullet.h \
28 dw_button.c \
29 dw_button.h \
30 dw_container.c \
31 dw_container.h \
32 dw_embed_gtk.c \
33 dw_embed_gtk.h \
34 dw_ext_iterator.h \
35 dw_ext_iterator.c \
36 dw_gtk_scrolled_frame.c \
37 dw_gtk_scrolled_frame.h \
38 dw_gtk_scrolled_window.c \
39 dw_gtk_scrolled_window.h \
40 dw_gtk_statuslabel.c \
41 dw_gtk_statuslabel.h \
42 dw_gtk_viewport.c \
43 dw_gtk_viewport.h \
44 dw_hruler.c \
45 dw_hruler.h \
46 dw_image.c \
47 dw_image.h \
48 dw_list_item.c \
49 dw_list_item.h \
50 dw_marshal.c \
51 dw_marshal.h \
52 dw_page.c \
53 dw_page.h \
54 dw_style.c \
55 dw_style.h \
56 dw_table.c \
57 dw_table.h \
58 dw_table_cell.c \
59 dw_table_cell.h \
60 dw_tooltip.c \
61 dw_tooltip.h \
62 dw_widget.c \
63 dw_widget.h \
64 findtext.c \
65 findtext.h \
66 selection.c \
67 selection.h \
68 web.c \
69 web.h \
70 progressbar.c \
71 progressbar.h \
72 dillo.c \
73 dillo.h \
74 bookmark.c \
75 bookmark.h \
76 browser.h \
77 dicache.c \
78 dicache.h \
79 dns.c \
80 dns.h \
81 gtk_ext_button.c \
82 gtk_ext_button.h \
83 gtk_ext_menu.c \
84 gtk_ext_menu.h \
85 gtk_ext_menu_item.c \
86 gtk_ext_menu_item.h \
87 gtk_menu_title.c \
88 gtk_menu_title.h \
89 gif.c \
90 jpeg.c \
91 png.c \
92 html.c \
93 html.h \
94 image.c \
95 image.h \
24 dillo.cc \
25 paths.cc \
26 paths.hh \
27 ui.cc \
28 ui.hh \
29 uicmd.cc \
30 uicmd.hh \
31 bw.h \
32 bw.c \
33 cookies.c \
34 cookies.h \
35 auth.c \
36 auth.h \
37 colors.c \
38 colors.h \
39 binaryconst.h \
9640 misc.c \
9741 misc.h \
98 interface.h \
99 interface.c \
42 history.h \
10043 history.c \
101 history.h \
102 nav.c \
103 nav.h \
104 plain.c \
105 menu.c \
106 menu.h \
10744 prefs.c \
10845 prefs.h \
109 colors.c \
110 colors.h \
46 prefsparser.cc \
47 prefsparser.hh \
48 keys.cc \
49 keys.hh \
50 msg.h \
51 list.h \
52 url.c \
53 url.h \
11154 bitvec.c \
11255 bitvec.h \
11356 klist.c \
11457 klist.h \
115 strbuf.c \
116 strbuf.h \
117 url.c \
118 url.h \
119 cookies.c \
120 cookies.h \
121 list.h \
122 binaryconst.h \
58 chain.c \
59 chain.h \
60 utf8.cc \
61 utf8.hh \
62 timeout.cc \
63 timeout.hh \
64 dialog.cc \
65 dialog.hh \
66 \
67 \
68 web.cc \
69 web.hh \
70 nav.c \
71 nav.h \
72 cache.c \
73 cache.h \
74 decode.c \
75 decode.h \
76 dicache.c \
77 dicache.h \
78 capi.c \
79 capi.h \
80 css.cc \
81 css.hh \
82 cssparser.cc \
83 cssparser.hh \
84 doctree.hh \
85 styleengine.cc \
86 styleengine.hh \
87 plain.cc \
88 html.cc \
89 html.hh \
90 html_common.hh \
91 form.cc \
92 form.hh \
93 table.cc \
94 table.hh \
95 bookmark.c \
96 bookmark.h \
97 dns.c \
98 dns.h \
99 gif.c \
100 dgif.h \
101 jpeg.c \
102 djpeg.h \
103 png.c \
104 dpng.h \
105 imgbuf.cc \
106 imgbuf.hh \
107 image.cc \
108 image.hh \
109 menu.hh \
110 menu.cc \
111 dpiapi.c \
112 dpiapi.h \
123113 pixmaps.h \
124 dpiapi.c \
125 dpiapi.h
114 findbar.cc \
115 findbar.hh \
116 xembed.cc \
117 xembed.hh
126118
127 EXTRA_DIST = chg srch
119 EXTRA_DIST = chg srch keysrc
120 sysconf_DATA = keysrc
+0
-741
src/Makefile.in less more
0 # Makefile.in generated by automake 1.9.5 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 # 2003, 2004, 2005 Free Software Foundation, Inc.
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 SOURCES = $(dillo_SOURCES)
17
18 srcdir = @srcdir@
19 top_srcdir = @top_srcdir@
20 VPATH = @srcdir@
21 pkgdatadir = $(datadir)/@PACKAGE@
22 pkglibdir = $(libdir)/@PACKAGE@
23 pkgincludedir = $(includedir)/@PACKAGE@
24 top_builddir = ..
25 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
26 INSTALL = @INSTALL@
27 install_sh_DATA = $(install_sh) -c -m 644
28 install_sh_PROGRAM = $(install_sh) -c
29 install_sh_SCRIPT = $(install_sh) -c
30 INSTALL_HEADER = $(INSTALL_DATA)
31 transform = $(program_transform_name)
32 NORMAL_INSTALL = :
33 PRE_INSTALL = :
34 POST_INSTALL = :
35 NORMAL_UNINSTALL = :
36 PRE_UNINSTALL = :
37 POST_UNINSTALL = :
38 build_triplet = @build@
39 host_triplet = @host@
40 target_triplet = @target@
41 bin_PROGRAMS = dillo$(EXEEXT)
42 subdir = src
43 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
44 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
45 am__aclocal_m4_deps = $(top_srcdir)/configure.in
46 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
47 $(ACLOCAL_M4)
48 mkinstalldirs = $(install_sh) -d
49 CONFIG_HEADER = $(top_builddir)/config.h
50 CONFIG_CLEAN_FILES =
51 am__installdirs = "$(DESTDIR)$(bindir)"
52 binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
53 PROGRAMS = $(bin_PROGRAMS)
54 am_dillo_OBJECTS = chain.$(OBJEXT) commands.$(OBJEXT) cache.$(OBJEXT) \
55 capi.$(OBJEXT) dw.$(OBJEXT) dw_aligned_page.$(OBJEXT) \
56 dw_bullet.$(OBJEXT) dw_button.$(OBJEXT) dw_container.$(OBJEXT) \
57 dw_embed_gtk.$(OBJEXT) dw_ext_iterator.$(OBJEXT) \
58 dw_gtk_scrolled_frame.$(OBJEXT) \
59 dw_gtk_scrolled_window.$(OBJEXT) dw_gtk_statuslabel.$(OBJEXT) \
60 dw_gtk_viewport.$(OBJEXT) dw_hruler.$(OBJEXT) \
61 dw_image.$(OBJEXT) dw_list_item.$(OBJEXT) dw_marshal.$(OBJEXT) \
62 dw_page.$(OBJEXT) dw_style.$(OBJEXT) dw_table.$(OBJEXT) \
63 dw_table_cell.$(OBJEXT) dw_tooltip.$(OBJEXT) \
64 dw_widget.$(OBJEXT) findtext.$(OBJEXT) selection.$(OBJEXT) \
65 web.$(OBJEXT) progressbar.$(OBJEXT) dillo.$(OBJEXT) \
66 bookmark.$(OBJEXT) dicache.$(OBJEXT) dns.$(OBJEXT) \
67 gtk_ext_button.$(OBJEXT) gtk_ext_menu.$(OBJEXT) \
68 gtk_ext_menu_item.$(OBJEXT) gtk_menu_title.$(OBJEXT) \
69 gif.$(OBJEXT) jpeg.$(OBJEXT) png.$(OBJEXT) html.$(OBJEXT) \
70 image.$(OBJEXT) misc.$(OBJEXT) interface.$(OBJEXT) \
71 history.$(OBJEXT) nav.$(OBJEXT) plain.$(OBJEXT) menu.$(OBJEXT) \
72 prefs.$(OBJEXT) colors.$(OBJEXT) bitvec.$(OBJEXT) \
73 klist.$(OBJEXT) strbuf.$(OBJEXT) url.$(OBJEXT) \
74 cookies.$(OBJEXT) dpiapi.$(OBJEXT)
75 dillo_OBJECTS = $(am_dillo_OBJECTS)
76 dillo_DEPENDENCIES = ../dpip/libDpip.a IO/libDio.a
77 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
78 depcomp = $(SHELL) $(top_srcdir)/depcomp
79 am__depfiles_maybe = depfiles
80 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
81 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
82 CCLD = $(CC)
83 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
84 SOURCES = $(dillo_SOURCES)
85 DIST_SOURCES = $(dillo_SOURCES)
86 RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
87 html-recursive info-recursive install-data-recursive \
88 install-exec-recursive install-info-recursive \
89 install-recursive installcheck-recursive installdirs-recursive \
90 pdf-recursive ps-recursive uninstall-info-recursive \
91 uninstall-recursive
92 ETAGS = etags
93 CTAGS = ctags
94 DIST_SUBDIRS = $(SUBDIRS)
95 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
96 ACLOCAL = @ACLOCAL@
97 AMDEP_FALSE = @AMDEP_FALSE@
98 AMDEP_TRUE = @AMDEP_TRUE@
99 AMTAR = @AMTAR@
100 AUTOCONF = @AUTOCONF@
101 AUTOHEADER = @AUTOHEADER@
102 AUTOMAKE = @AUTOMAKE@
103 AWK = @AWK@
104 CC = @CC@
105 CCDEPMODE = @CCDEPMODE@
106 CFLAGS = @CFLAGS@
107 CPP = @CPP@
108 CPPFLAGS = @CPPFLAGS@
109 CXX = @CXX@
110 CXXDEPMODE = @CXXDEPMODE@
111 CXXFLAGS = @CXXFLAGS@
112 CYGPATH_W = @CYGPATH_W@
113 DEFS = @DEFS@
114 DEPDIR = @DEPDIR@
115 DLGUI_FALSE = @DLGUI_FALSE@
116 DLGUI_TRUE = @DLGUI_TRUE@
117 ECHO_C = @ECHO_C@
118 ECHO_N = @ECHO_N@
119 ECHO_T = @ECHO_T@
120 EGREP = @EGREP@
121 EXEEXT = @EXEEXT@
122 GLIB_CFLAGS = @GLIB_CFLAGS@
123 GLIB_CONFIG = @GLIB_CONFIG@
124 GLIB_LIBS = @GLIB_LIBS@
125 GTK_CFLAGS = @GTK_CFLAGS@
126 GTK_CONFIG = @GTK_CONFIG@
127 GTK_LIBS = @GTK_LIBS@
128 INSTALL_DATA = @INSTALL_DATA@
129 INSTALL_PROGRAM = @INSTALL_PROGRAM@
130 INSTALL_SCRIPT = @INSTALL_SCRIPT@
131 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
132 LDFLAGS = @LDFLAGS@
133 LIBFLTK_CXXFLAGS = @LIBFLTK_CXXFLAGS@
134 LIBFLTK_LIBS = @LIBFLTK_LIBS@
135 LIBJPEG_CPPFLAGS = @LIBJPEG_CPPFLAGS@
136 LIBJPEG_LDFLAGS = @LIBJPEG_LDFLAGS@
137 LIBJPEG_LIBS = @LIBJPEG_LIBS@
138 LIBOBJS = @LIBOBJS@
139 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
140 LIBPNG_LIBS = @LIBPNG_LIBS@
141 LIBPTHREAD_LDFLAGS = @LIBPTHREAD_LDFLAGS@
142 LIBPTHREAD_LIBS = @LIBPTHREAD_LIBS@
143 LIBS = @LIBS@
144 LIBSSL_LIBS = @LIBSSL_LIBS@
145 LIBZ_LIBS = @LIBZ_LIBS@
146 LTLIBOBJS = @LTLIBOBJS@
147 MAKEINFO = @MAKEINFO@
148 OBJEXT = @OBJEXT@
149 PACKAGE = @PACKAGE@
150 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
151 PACKAGE_NAME = @PACKAGE_NAME@
152 PACKAGE_STRING = @PACKAGE_STRING@
153 PACKAGE_TARNAME = @PACKAGE_TARNAME@
154 PACKAGE_VERSION = @PACKAGE_VERSION@
155 PATH_SEPARATOR = @PATH_SEPARATOR@
156 RANLIB = @RANLIB@
157 SET_MAKE = @SET_MAKE@
158 SHELL = @SHELL@
159 STRIP = @STRIP@
160 VERSION = @VERSION@
161 ac_ct_CC = @ac_ct_CC@
162 ac_ct_CXX = @ac_ct_CXX@
163 ac_ct_RANLIB = @ac_ct_RANLIB@
164 ac_ct_STRIP = @ac_ct_STRIP@
165 am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
166 am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
167 am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
168 am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
169 am__include = @am__include@
170 am__leading_dot = @am__leading_dot@
171 am__quote = @am__quote@
172 am__tar = @am__tar@
173 am__untar = @am__untar@
174 bindir = @bindir@
175 build = @build@
176 build_alias = @build_alias@
177 build_cpu = @build_cpu@
178 build_os = @build_os@
179 build_vendor = @build_vendor@
180 datadir = @datadir@
181 exec_prefix = @exec_prefix@
182 host = @host@
183 host_alias = @host_alias@
184 host_cpu = @host_cpu@
185 host_os = @host_os@
186 host_vendor = @host_vendor@
187 includedir = @includedir@
188 infodir = @infodir@
189 install_sh = @install_sh@
190 libdir = @libdir@
191 libexecdir = @libexecdir@
192 localstatedir = @localstatedir@
193 mandir = @mandir@
194 mkdir_p = @mkdir_p@
195 oldincludedir = @oldincludedir@
196 prefix = @prefix@
197 program_transform_name = @program_transform_name@
198 sbindir = @sbindir@
199 sharedstatedir = @sharedstatedir@
200 sysconfdir = @sysconfdir@
201 target = @target@
202 target_alias = @target_alias@
203 target_cpu = @target_cpu@
204 target_os = @target_os@
205 target_vendor = @target_vendor@
206 AM_CPPFLAGS = -DDILLORC_SYS='"$(sysconfdir)/dillorc"' @LIBJPEG_CPPFLAGS@
207 AM_CFLAGS = @GTK_CFLAGS@ @LIBPNG_CFLAGS@
208 SUBDIRS = IO
209 dillo_LDADD = ../dpip/libDpip.a IO/libDio.a @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @GTK_LIBS@ @LIBZ_LIBS@ @LIBPTHREAD_LIBS@
210 dillo_LDFLAGS = @LIBJPEG_LDFLAGS@ @LIBPTHREAD_LDFLAGS@
211 dillo_SOURCES = \
212 msg.h \
213 acconfig.h \
214 chain.h \
215 chain.c \
216 commands.c \
217 commands.h \
218 cache.c \
219 cache.h \
220 capi.c \
221 capi.h \
222 debug.h \
223 dw.h \
224 dw.c \
225 dw_aligned_page.c \
226 dw_aligned_page.h \
227 dw_bullet.c \
228 dw_bullet.h \
229 dw_button.c \
230 dw_button.h \
231 dw_container.c \
232 dw_container.h \
233 dw_embed_gtk.c \
234 dw_embed_gtk.h \
235 dw_ext_iterator.h \
236 dw_ext_iterator.c \
237 dw_gtk_scrolled_frame.c \
238 dw_gtk_scrolled_frame.h \
239 dw_gtk_scrolled_window.c \
240 dw_gtk_scrolled_window.h \
241 dw_gtk_statuslabel.c \
242 dw_gtk_statuslabel.h \
243 dw_gtk_viewport.c \
244 dw_gtk_viewport.h \
245 dw_hruler.c \
246 dw_hruler.h \
247 dw_image.c \
248 dw_image.h \
249 dw_list_item.c \
250 dw_list_item.h \
251 dw_marshal.c \
252 dw_marshal.h \
253 dw_page.c \
254 dw_page.h \
255 dw_style.c \
256 dw_style.h \
257 dw_table.c \
258 dw_table.h \
259 dw_table_cell.c \
260 dw_table_cell.h \
261 dw_tooltip.c \
262 dw_tooltip.h \
263 dw_widget.c \
264 dw_widget.h \
265 findtext.c \
266 findtext.h \
267 selection.c \
268 selection.h \
269 web.c \
270 web.h \
271 progressbar.c \
272 progressbar.h \
273 dillo.c \
274 dillo.h \
275 bookmark.c \
276 bookmark.h \
277 browser.h \
278 dicache.c \
279 dicache.h \
280 dns.c \
281 dns.h \
282 gtk_ext_button.c \
283 gtk_ext_button.h \
284 gtk_ext_menu.c \
285 gtk_ext_menu.h \
286 gtk_ext_menu_item.c \
287 gtk_ext_menu_item.h \
288 gtk_menu_title.c \
289 gtk_menu_title.h \
290 gif.c \
291 jpeg.c \
292 png.c \
293 html.c \
294 html.h \
295 image.c \
296 image.h \
297 misc.c \
298 misc.h \
299 interface.h \
300 interface.c \
301 history.c \
302 history.h \
303 nav.c \
304 nav.h \
305 plain.c \
306 menu.c \
307 menu.h \
308 prefs.c \
309 prefs.h \
310 colors.c \
311 colors.h \
312 bitvec.c \
313 bitvec.h \
314 klist.c \
315 klist.h \
316 strbuf.c \
317 strbuf.h \
318 url.c \
319 url.h \
320 cookies.c \
321 cookies.h \
322 list.h \
323 binaryconst.h \
324 pixmaps.h \
325 dpiapi.c \
326 dpiapi.h
327
328 EXTRA_DIST = chg srch
329 all: all-recursive
330
331 .SUFFIXES:
332 .SUFFIXES: .c .o .obj
333 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
334 @for dep in $?; do \
335 case '$(am__configure_deps)' in \
336 *$$dep*) \
337 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
338 && exit 0; \
339 exit 1;; \
340 esac; \
341 done; \
342 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
343 cd $(top_srcdir) && \
344 $(AUTOMAKE) --gnu src/Makefile
345 .PRECIOUS: Makefile
346 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
347 @case '$?' in \
348 *config.status*) \
349 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
350 *) \
351 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
352 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
353 esac;
354
355 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
356 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
357
358 $(top_srcdir)/configure: $(am__configure_deps)
359 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
360 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
361 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
362 install-binPROGRAMS: $(bin_PROGRAMS)
363 @$(NORMAL_INSTALL)
364 test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
365 @list='$(bin_PROGRAMS)'; for p in $$list; do \
366 p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
367 if test -f $$p \
368 ; then \
369 f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
370 echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
371 $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
372 else :; fi; \
373 done
374
375 uninstall-binPROGRAMS:
376 @$(NORMAL_UNINSTALL)
377 @list='$(bin_PROGRAMS)'; for p in $$list; do \
378 f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
379 echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
380 rm -f "$(DESTDIR)$(bindir)/$$f"; \
381 done
382
383 clean-binPROGRAMS:
384 -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
385 dillo$(EXEEXT): $(dillo_OBJECTS) $(dillo_DEPENDENCIES)
386 @rm -f dillo$(EXEEXT)
387 $(LINK) $(dillo_LDFLAGS) $(dillo_OBJECTS) $(dillo_LDADD) $(LIBS)
388
389 mostlyclean-compile:
390 -rm -f *.$(OBJEXT)
391
392 distclean-compile:
393 -rm -f *.tab.c
394
395 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitvec.Po@am__quote@
396 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bookmark.Po@am__quote@
397 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache.Po@am__quote@
398 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/capi.Po@am__quote@
399 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chain.Po@am__quote@
400 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/colors.Po@am__quote@
401 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/commands.Po@am__quote@
402 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cookies.Po@am__quote@
403 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dicache.Po@am__quote@
404 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dillo.Po@am__quote@
405 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns.Po@am__quote@
406 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dpiapi.Po@am__quote@
407 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw.Po@am__quote@
408 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_aligned_page.Po@am__quote@
409 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_bullet.Po@am__quote@
410 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_button.Po@am__quote@
411 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_container.Po@am__quote@
412 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_embed_gtk.Po@am__quote@
413 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_ext_iterator.Po@am__quote@
414 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_gtk_scrolled_frame.Po@am__quote@
415 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_gtk_scrolled_window.Po@am__quote@
416 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_gtk_statuslabel.Po@am__quote@
417 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_gtk_viewport.Po@am__quote@
418 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_hruler.Po@am__quote@
419 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_image.Po@am__quote@
420 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_list_item.Po@am__quote@
421 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_marshal.Po@am__quote@
422 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_page.Po@am__quote@
423 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_style.Po@am__quote@
424 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_table.Po@am__quote@
425 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_table_cell.Po@am__quote@
426 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_tooltip.Po@am__quote@
427 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw_widget.Po@am__quote@
428 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/findtext.Po@am__quote@
429 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gif.Po@am__quote@
430 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk_ext_button.Po@am__quote@
431 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk_ext_menu.Po@am__quote@
432 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk_ext_menu_item.Po@am__quote@
433 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk_menu_title.Po@am__quote@
434 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/history.Po@am__quote@
435 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/html.Po@am__quote@
436 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image.Po@am__quote@
437 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interface.Po@am__quote@
438 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg.Po@am__quote@
439 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/klist.Po@am__quote@
440 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu.Po@am__quote@
441 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
442 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nav.Po@am__quote@
443 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plain.Po@am__quote@
444 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/png.Po@am__quote@
445 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs.Po@am__quote@
446 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/progressbar.Po@am__quote@
447 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/selection.Po@am__quote@
448 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strbuf.Po@am__quote@
449 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Po@am__quote@
450 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web.Po@am__quote@
451
452 .c.o:
453 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
454 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
455 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
456 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
457 @am__fastdepCC_FALSE@ $(COMPILE) -c $<
458
459 .c.obj:
460 @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
461 @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
462 @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
463 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
464 @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
465 uninstall-info-am:
466
467 # This directory's subdirectories are mostly independent; you can cd
468 # into them and run `make' without going through this Makefile.
469 # To change the values of `make' variables: instead of editing Makefiles,
470 # (1) if the variable is set in `config.status', edit `config.status'
471 # (which will cause the Makefiles to be regenerated when you run `make');
472 # (2) otherwise, pass the desired values on the `make' command line.
473 $(RECURSIVE_TARGETS):
474 @failcom='exit 1'; \
475 for f in x $$MAKEFLAGS; do \
476 case $$f in \
477 *=* | --[!k]*);; \
478 *k*) failcom='fail=yes';; \
479 esac; \
480 done; \
481 dot_seen=no; \
482 target=`echo $@ | sed s/-recursive//`; \
483 list='$(SUBDIRS)'; for subdir in $$list; do \
484 echo "Making $$target in $$subdir"; \
485 if test "$$subdir" = "."; then \
486 dot_seen=yes; \
487 local_target="$$target-am"; \
488 else \
489 local_target="$$target"; \
490 fi; \
491 (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
492 || eval $$failcom; \
493 done; \
494 if test "$$dot_seen" = "no"; then \
495 $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
496 fi; test -z "$$fail"
497
498 mostlyclean-recursive clean-recursive distclean-recursive \
499 maintainer-clean-recursive:
500 @failcom='exit 1'; \
501 for f in x $$MAKEFLAGS; do \
502 case $$f in \
503 *=* | --[!k]*);; \
504 *k*) failcom='fail=yes';; \
505 esac; \
506 done; \
507 dot_seen=no; \
508 case "$@" in \
509 distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
510 *) list='$(SUBDIRS)' ;; \
511 esac; \
512 rev=''; for subdir in $$list; do \
513 if test "$$subdir" = "."; then :; else \
514 rev="$$subdir $$rev"; \
515 fi; \
516 done; \
517 rev="$$rev ."; \
518 target=`echo $@ | sed s/-recursive//`; \
519 for subdir in $$rev; do \
520 echo "Making $$target in $$subdir"; \
521 if test "$$subdir" = "."; then \
522 local_target="$$target-am"; \
523 else \
524 local_target="$$target"; \
525 fi; \
526 (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
527 || eval $$failcom; \
528 done && test -z "$$fail"
529 tags-recursive:
530 list='$(SUBDIRS)'; for subdir in $$list; do \
531 test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
532 done
533 ctags-recursive:
534 list='$(SUBDIRS)'; for subdir in $$list; do \
535 test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
536 done
537
538 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
539 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
540 unique=`for i in $$list; do \
541 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
542 done | \
543 $(AWK) ' { files[$$0] = 1; } \
544 END { for (i in files) print i; }'`; \
545 mkid -fID $$unique
546 tags: TAGS
547
548 TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
549 $(TAGS_FILES) $(LISP)
550 tags=; \
551 here=`pwd`; \
552 if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
553 include_option=--etags-include; \
554 empty_fix=.; \
555 else \
556 include_option=--include; \
557 empty_fix=; \
558 fi; \
559 list='$(SUBDIRS)'; for subdir in $$list; do \
560 if test "$$subdir" = .; then :; else \
561 test ! -f $$subdir/TAGS || \
562 tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
563 fi; \
564 done; \
565 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
566 unique=`for i in $$list; do \
567 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
568 done | \
569 $(AWK) ' { files[$$0] = 1; } \
570 END { for (i in files) print i; }'`; \
571 if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
572 test -n "$$unique" || unique=$$empty_fix; \
573 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
574 $$tags $$unique; \
575 fi
576 ctags: CTAGS
577 CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
578 $(TAGS_FILES) $(LISP)
579 tags=; \
580 here=`pwd`; \
581 list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
582 unique=`for i in $$list; do \
583 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
584 done | \
585 $(AWK) ' { files[$$0] = 1; } \
586 END { for (i in files) print i; }'`; \
587 test -z "$(CTAGS_ARGS)$$tags$$unique" \
588 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
589 $$tags $$unique
590
591 GTAGS:
592 here=`$(am__cd) $(top_builddir) && pwd` \
593 && cd $(top_srcdir) \
594 && gtags -i $(GTAGS_ARGS) $$here
595
596 distclean-tags:
597 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
598
599 distdir: $(DISTFILES)
600 @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
601 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
602 list='$(DISTFILES)'; for file in $$list; do \
603 case $$file in \
604 $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
605 $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
606 esac; \
607 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
608 dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
609 if test "$$dir" != "$$file" && test "$$dir" != "."; then \
610 dir="/$$dir"; \
611 $(mkdir_p) "$(distdir)$$dir"; \
612 else \
613 dir=''; \
614 fi; \
615 if test -d $$d/$$file; then \
616 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
617 cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
618 fi; \
619 cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
620 else \
621 test -f $(distdir)/$$file \
622 || cp -p $$d/$$file $(distdir)/$$file \
623 || exit 1; \
624 fi; \
625 done
626 list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
627 if test "$$subdir" = .; then :; else \
628 test -d "$(distdir)/$$subdir" \
629 || $(mkdir_p) "$(distdir)/$$subdir" \
630 || exit 1; \
631 distdir=`$(am__cd) $(distdir) && pwd`; \
632 top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
633 (cd $$subdir && \
634 $(MAKE) $(AM_MAKEFLAGS) \
635 top_distdir="$$top_distdir" \
636 distdir="$$distdir/$$subdir" \
637 distdir) \
638 || exit 1; \
639 fi; \
640 done
641 check-am: all-am
642 check: check-recursive
643 all-am: Makefile $(PROGRAMS)
644 installdirs: installdirs-recursive
645 installdirs-am:
646 for dir in "$(DESTDIR)$(bindir)"; do \
647 test -z "$$dir" || $(mkdir_p) "$$dir"; \
648 done
649 install: install-recursive
650 install-exec: install-exec-recursive
651 install-data: install-data-recursive
652 uninstall: uninstall-recursive
653
654 install-am: all-am
655 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
656
657 installcheck: installcheck-recursive
658 install-strip:
659 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
660 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
661 `test -z '$(STRIP)' || \
662 echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
663 mostlyclean-generic:
664
665 clean-generic:
666
667 distclean-generic:
668 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
669
670 maintainer-clean-generic:
671 @echo "This command is intended for maintainers to use"
672 @echo "it deletes files that may require special tools to rebuild."
673 clean: clean-recursive
674
675 clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
676
677 distclean: distclean-recursive
678 -rm -rf ./$(DEPDIR)
679 -rm -f Makefile
680 distclean-am: clean-am distclean-compile distclean-generic \
681 distclean-tags
682
683 dvi: dvi-recursive
684
685 dvi-am:
686
687 html: html-recursive
688
689 info: info-recursive
690
691 info-am:
692
693 install-data-am:
694
695 install-exec-am: install-binPROGRAMS
696
697 install-info: install-info-recursive
698
699 install-man:
700
701 installcheck-am:
702
703 maintainer-clean: maintainer-clean-recursive
704 -rm -rf ./$(DEPDIR)
705 -rm -f Makefile
706 maintainer-clean-am: distclean-am maintainer-clean-generic
707
708 mostlyclean: mostlyclean-recursive
709
710 mostlyclean-am: mostlyclean-compile mostlyclean-generic
711
712 pdf: pdf-recursive
713
714 pdf-am:
715
716 ps: ps-recursive
717
718 ps-am:
719
720 uninstall-am: uninstall-binPROGRAMS uninstall-info-am
721
722 uninstall-info: uninstall-info-recursive
723
724 .PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \
725 clean clean-binPROGRAMS clean-generic clean-recursive ctags \
726 ctags-recursive distclean distclean-compile distclean-generic \
727 distclean-recursive distclean-tags distdir dvi dvi-am html \
728 html-am info info-am install install-am install-binPROGRAMS \
729 install-data install-data-am install-exec install-exec-am \
730 install-info install-info-am install-man install-strip \
731 installcheck installcheck-am installdirs installdirs-am \
732 maintainer-clean maintainer-clean-generic \
733 maintainer-clean-recursive mostlyclean mostlyclean-compile \
734 mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \
735 tags tags-recursive uninstall uninstall-am \
736 uninstall-binPROGRAMS uninstall-info-am
737
738 # Tell versions [3.59,3.63) of GNU make to not export all variables.
739 # Otherwise a system limit (for SysV at least) may be exceeded.
740 .NOEXPORT:
+0
-2
src/acconfig.h less more
0 #undef PACKAGE
1 #undef VERSION
0 /*
1 * File: auth.c
2 *
3 * Copyright 2008 Jeremy Henty <onepoint@starurchin.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 /* Handling of HTTP AUTH takes place here.
12 * This implementation aims to follow RFC 2617:
13 * http://www.ietf.org/rfc/rfc2617.txt
14 */
15
16
17 #include <ctype.h> /* iscntrl */
18 #include "auth.h"
19 #include "msg.h"
20 #include "misc.h"
21 #include "dialog.hh"
22 #include "../dlib/dlib.h"
23
24
25 typedef struct {
26 int ok;
27 const char *realm;
28 } AuthParse_t;
29
30 typedef struct {
31 char *name;
32 Dlist *paths; /* stripped of any trailing '/', so the root path is "" */
33 char *authorization; /* the authorization request header */
34 } AuthRealm_t;
35
36 typedef struct {
37 char *scheme;
38 char *authority;
39 Dlist *realms;
40 } AuthHost_t;
41
42 typedef struct {
43 const char *realm_name;
44 const DilloUrl *url;
45 } AuthDialogData_t;
46
47 /*
48 * Local data
49 */
50 static Dlist *auth_hosts;
51
52 /*
53 * Initialize the auth module.
54 */
55 void a_Auth_init(void)
56 {
57 auth_hosts = dList_new(1);
58 }
59
60 static AuthParse_t *Auth_parse_new()
61 {
62 AuthParse_t *auth_parse = dNew(AuthParse_t, 1);
63 auth_parse->ok = 0;
64 auth_parse->realm = NULL;
65 return auth_parse;
66 }
67
68 static void Auth_parse_free(AuthParse_t *auth_parse)
69 {
70 if (auth_parse) {
71 dFree((void *)auth_parse->realm);
72 dFree(auth_parse);
73 }
74 }
75
76 static int Auth_path_is_inside(const char *path1, const char *path2, int len)
77 {
78 /*
79 * path2 is effectively truncated to length len. Typically len will be
80 * strlen(path2), or 1 less when we want to ignore a trailing '/'.
81 */
82 return
83 strncmp(path1, path2, len) == 0 &&
84 (path1[len] == '\0' || path1[len] == '/');
85 }
86
87 /*
88 * Check valid chars.
89 * Return: 0 if invalid, 1 otherwise.
90 */
91 static int Auth_is_token_char(char c)
92 {
93 const char *invalid = "\"()<>@,;:\\[]?=/{} \t";
94 return (strchr(invalid, c) || iscntrl((uchar_t)c)) ? 0 : 1;
95 }
96
97 /*
98 * Unquote the content of a quoted string.
99 * Return: newly allocated unquoted content.
100 *
101 * Arguments:
102 * quoted: pointer to the first char *after* the initial double quote.
103 * size: the number of chars in the result, *not* including a final '\0'.
104 *
105 * Preconditions:
106 * quoted points to a correctly quoted and escaped string.
107 * size is the number of characters in the quoted string, *after*
108 * removing escape characters.
109 *
110 */
111 static const char *Auth_unquote_value(const char *quoted, int size)
112 {
113 char c, *value, *value_ptr;
114 value_ptr = value = dNew(char, size + 1);
115 while ((c = *quoted++) != '"')
116 *value_ptr++ = (c == '\\') ? *quoted++ : c;
117 *value_ptr = '\0';
118 return value;
119 }
120
121 /*
122 * Parse a quoted string. Save the result as the auth realm if required.
123 * Return: 1 if the parse succeeds, 0 otherwise.
124 */
125 static int Auth_parse_quoted_string(AuthParse_t *auth_parse, int set_realm,
126 char **auth)
127 {
128 char *value;
129 int size;
130
131 /* parse the '"' */
132 switch (*(*auth)++) {
133 case '"':
134 break;
135 case '\0':
136 case ',':
137 MSG("auth.c: missing Basic auth token value after '='\n");
138 return 0;
139 break;
140 default:
141 MSG("auth.c: garbage in Basic auth after '='\n");
142 return 0;
143 break;
144 }
145
146 /* parse the rest */
147 value = *auth;
148 size = 0;
149 while (1) {
150 switch (*(*auth)++) {
151 case '"':
152 if (set_realm) {
153 dFree((void *)auth_parse->realm);
154 auth_parse->realm = Auth_unquote_value(value, size);
155 auth_parse->ok = 1;
156 }
157 return 1;
158 break;
159 case '\0':
160 MSG("auth.c: auth string ended inside quoted string value\n");
161 return 0;
162 break;
163 case '\\':
164 /* end of string? */
165 if (!*(*auth)++) {
166 MSG("auth.c: "
167 "auth string ended inside quoted string value "
168 "immediately after \\\n");
169 return 0;
170 }
171 /* fall through to the next case */
172 default:
173 size++;
174 break;
175 }
176 }
177 }
178
179 /*
180 * Parse a token-value pair.
181 * Return: 1 if the parse succeeds, 0 otherwise.
182 */
183 static int Auth_parse_token_value(AuthParse_t *auth_parse, char **auth)
184 {
185 char *token;
186 int token_size, set_realm;
187 static const char realm_token[] = "realm";
188
189 /* parse a token */
190 token = *auth;
191 token_size = 0;
192 while (Auth_is_token_char(**auth)) {
193 (*auth)++;
194 token_size++;
195 }
196 if (token_size == 0) {
197 MSG("auth.c: Auth_parse_token_value: "
198 "missing Basic auth token\n");
199 return 0;
200 }
201
202 /* skip space characters */
203 while (**auth == ' ')
204 (*auth)++;
205
206 /* parse the '=' */
207 switch (*(*auth)++) {
208 case '=':
209 break;
210 case '\0':
211 case ',':
212 MSG("auth.c: Auth_parse_token_value: "
213 "missing Basic auth token value\n");
214 return 0;
215 break;
216 default:
217 MSG("auth.c: Auth_parse_token_value: "
218 "garbage after Basic auth token\n");
219 return 0;
220 break;
221 }
222
223 /* skip space characters */
224 while (**auth == ' ')
225 (*auth)++;
226
227 /* is this value the realm? */
228 set_realm =
229 auth_parse->realm == NULL &&
230 dStrncasecmp(realm_token,token,token_size) == 0 &&
231 strlen(realm_token) == (size_t)token_size;
232
233 return Auth_parse_quoted_string(auth_parse, set_realm, auth);
234 }
235
236 static void Auth_parse_auth_basic(AuthParse_t *auth_parse, char **auth)
237 {
238 int token_value_pairs_found;
239
240 /* parse comma-separated token-value pairs */
241 token_value_pairs_found = 0;
242 while (1) {
243 /* skip space and comma characters */
244 while (**auth == ' ' || **auth == ',')
245 (*auth)++;
246 /* end of string? */
247 if (!**auth)
248 break;
249 /* parse token-value pair */
250 if (!Auth_parse_token_value(auth_parse, auth))
251 break;
252 token_value_pairs_found = 1;
253 }
254
255 if (!token_value_pairs_found) {
256 MSG("auth.c: Auth_parse_auth_basic: "
257 "missing Basic auth token-value pairs\n");
258 return;
259 }
260
261 if (!auth_parse->realm) {
262 MSG("auth.c: Auth_parse_auth_basic: "
263 "missing Basic auth realm\n");
264 return;
265 }
266 }
267
268 static void Auth_parse_auth(AuthParse_t *auth_parse, char *auth)
269 {
270 _MSG("auth.c: Auth_parse_auth: auth = '%s'\n", auth);
271 if (dStrncasecmp(auth, "Basic ", 6) == 0) {
272 auth += 6;
273 Auth_parse_auth_basic(auth_parse, &auth);
274 } else {
275 MSG("auth.c: Auth_parse_auth: "
276 "unknown authorization scheme: auth = {%s}\n",
277 auth);
278 }
279 }
280
281 /*
282 * Return the host that contains a URL, or NULL if there is no such host.
283 */
284 static AuthHost_t *Auth_host_by_url(const DilloUrl *url)
285 {
286 AuthHost_t *host;
287 int i;
288
289 for (i = 0; (host = dList_nth_data(auth_hosts, i)); i++)
290 if (((dStrcasecmp(URL_SCHEME(url), host->scheme) == 0) &&
291 (dStrcasecmp(URL_AUTHORITY(url), host->authority) == 0)))
292 return host;
293
294 return NULL;
295 }
296
297 /*
298 * Search all realms for the one with the given name.
299 */
300 static AuthRealm_t *Auth_realm_by_name(const AuthHost_t *host,
301 const char *name)
302 {
303 AuthRealm_t *realm;
304 int i;
305
306 for (i = 0; (realm = dList_nth_data(host->realms, i)); i++)
307 if (strcmp(realm->name,name) == 0)
308 return realm;
309
310 return NULL;
311 }
312
313 /*
314 * Search all realms for the one with the best-matching path.
315 */
316 static AuthRealm_t *Auth_realm_by_path(const AuthHost_t *host,
317 const char *path)
318 {
319 AuthRealm_t *realm_best, *realm;
320 int i, j;
321 int match_length;
322
323 match_length = 0;
324 realm_best = NULL;
325 for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) {
326 char *realm_path;
327
328 for (j = 0; (realm_path = dList_nth_data(realm->paths, j)); j++) {
329 int realm_path_length = strlen(realm_path);
330 if (Auth_path_is_inside(path, realm_path, realm_path_length) &&
331 !(realm_best && match_length >= realm_path_length)) {
332 realm_best = realm;
333 match_length = realm_path_length;
334 }
335 }
336 }
337
338 return realm_best;
339 }
340
341 static int Auth_realm_includes_path(const AuthRealm_t *realm, const char *path)
342 {
343 int i;
344 char *realm_path;
345
346 for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++)
347 if (Auth_path_is_inside(path, realm_path, strlen(realm_path)))
348 return 1;
349
350 return 0;
351 }
352
353 static void Auth_realm_add_path(AuthRealm_t *realm, const char *path)
354 {
355 int len, i;
356 char *realm_path, *n_path;
357
358 n_path = dStrdup(path);
359 len = strlen(n_path);
360
361 /* remove trailing '/' */
362 if (len && n_path[len - 1] == '/')
363 n_path[--len] = 0;
364
365 /* delete existing paths that are inside the new one */
366 for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++) {
367 if (Auth_path_is_inside(realm_path, path, len)) {
368 dList_remove_fast(realm->paths, realm_path);
369 dFree(realm_path);
370 i--; /* reconsider this slot */
371 }
372 }
373
374 dList_append(realm->paths, n_path);
375 }
376
377 /*
378 * Return the authorization header for an HTTP query.
379 */
380 const char *a_Auth_get_auth_str(const DilloUrl *url)
381 {
382 AuthHost_t *host;
383 AuthRealm_t *realm;
384
385 return
386 ((host = Auth_host_by_url(url)) &&
387 (realm = Auth_realm_by_path(host, URL_PATH(url)))) ?
388 realm->authorization : NULL;
389 }
390
391 /*
392 * Determine whether the user needs to authenticate.
393 */
394 static int Auth_do_auth_required(const char *realm_name, const DilloUrl *url)
395 {
396 /*
397 * TO DO: I dislike the way that this code must decide whether we
398 * sent authentication during the request and trust us to resend it
399 * after the reload. Could it be more robust if every DilloUrl
400 * recorded its authentication, and whether it was accepted? (JCH)
401 */
402
403 AuthHost_t *host;
404 AuthRealm_t *realm;
405
406 /*
407 * The size of the following comments reflects the concerns in the
408 * TO DO at the top of this function. It should not be so hard to
409 * explain why code is correct! (JCH)
410 */
411
412 /*
413 * If we have authentication but did not send it (because we did
414 * not know this path was in the realm) then we update the realm.
415 * We do not re-authenticate because our authentication is probably
416 * OK. Thanks to the updated realm the forthcoming reload will
417 * make us send the authentication. If our authentication is not
418 * OK the server will challenge us again after the reload and then
419 * we will re-authenticate.
420 */
421 if ((host = Auth_host_by_url(url)) &&
422 (realm = Auth_realm_by_name(host, realm_name)) &&
423 (!Auth_realm_includes_path(realm, URL_PATH(url)))) {
424 _MSG("Auth_do_auth_required: updating realm '%s' with URL '%s'\n",
425 realm_name, URL_STR(url));
426 Auth_realm_add_path(realm, URL_PATH(url));
427 return 0;
428 }
429
430 /*
431 * Either we had no authentication or we sent it and the server
432 * rejected it, so we must re-authenticate.
433 */
434 return 1;
435 }
436
437 static void Auth_do_auth_dialog_cb(const char *user, const char *password,
438 void *vData)
439 {
440 AuthDialogData_t *data;
441 AuthHost_t *host;
442 AuthRealm_t *realm;
443 char *user_password, *response, *authorization, *authorization_old;
444
445 data = (AuthDialogData_t *)vData;
446
447 /* find or create the host */
448 if (!(host = Auth_host_by_url(data->url))) {
449 /* create a new host */
450 host = dNew(AuthHost_t, 1);
451 host->scheme = dStrdup(URL_SCHEME(data->url));
452 host->authority = dStrdup(URL_AUTHORITY(data->url));
453 host->realms = dList_new(1);
454 dList_append(auth_hosts, host);
455 }
456
457 /* find or create the realm */
458 if (!(realm = Auth_realm_by_name(host, data->realm_name))) {
459 /* create a new realm */
460 realm = dNew(AuthRealm_t, 1);
461 realm->name = dStrdup(data->realm_name);
462 realm->paths = dList_new(1);
463 realm->authorization = NULL;
464 dList_append(host->realms, realm);
465 }
466
467 Auth_realm_add_path(realm, URL_PATH(data->url));
468
469 /* create and set the authorization */
470 user_password = dStrconcat(user, ":", password, NULL);
471 response = a_Misc_encode_base64(user_password);
472 authorization =
473 dStrconcat("Authorization: Basic ", response, "\r\n", NULL);
474 authorization_old = realm->authorization;
475 realm->authorization = authorization;
476 dFree(authorization_old);
477 dFree(user_password);
478 dFree(response);
479 }
480
481 static int Auth_do_auth_dialog(const char *realm, const DilloUrl *url)
482 {
483 int ret;
484 char *message;
485 AuthDialogData_t *data;
486
487 _MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", realm);
488 message = dStrconcat("The server at ", URL_HOST(url), " requires a username"
489 " and password for \"", realm, "\".", NULL);
490 data = dNew(AuthDialogData_t, 1);
491 data->realm_name = dStrdup(realm);
492 data->url = a_Url_dup(url);
493 ret = a_Dialog_user_password(message, Auth_do_auth_dialog_cb, data);
494 dFree(message);
495 dFree((void*)data->realm_name);
496 a_Url_free((void*)data->url);
497 dFree(data);
498 return ret;
499 }
500
501 /*
502 * Do authorization for an auth string.
503 */
504 static int Auth_do_auth(char *auth, const DilloUrl *url)
505 {
506 int reload;
507 AuthParse_t *auth_parse;
508
509 _MSG("auth.c: Auth_do_auth: auth={%s}\n", auth);
510 reload = 0;
511 auth_parse = Auth_parse_new();
512 Auth_parse_auth(auth_parse, auth);
513 if (auth_parse->ok)
514 reload =
515 Auth_do_auth_required(auth_parse->realm, url) ?
516 Auth_do_auth_dialog(auth_parse->realm, url)
517 : 1;
518 Auth_parse_free(auth_parse);
519
520 return reload;
521 }
522
523 /*
524 * Do authorization for a set of auth strings.
525 */
526 int a_Auth_do_auth(Dlist *auths, const DilloUrl *url)
527 {
528 int reload, i;
529 char *auth;
530
531 reload = 0;
532 for (i = 0; (auth = dList_nth_data(auths, i)); ++i)
533 if (Auth_do_auth(auth, url))
534 reload = 1;
535
536 return reload;
537 }
538
0 #ifndef __AUTH_H__
1 #define __AUTH_H__
2
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7 #include "url.h"
8
9
10 const char *a_Auth_get_auth_str(const DilloUrl *request_url);
11 int a_Auth_do_auth(Dlist *auth_string, const DilloUrl *url);
12 void a_Auth_init(void);
13
14
15 #ifdef __cplusplus
16 }
17 #endif /* __cplusplus */
18 #endif /* !__AUTH_H__ */
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1212 * A simple ADT for bit arrays
1313 */
1414
15 #include "../dlib/dlib.h"
1516 #include "bitvec.h"
1617
1718
1819 /*
1920 * Create a new bitvec with 'num_bits' size
2021 */
21 bitvec_t *a_Bitvec_new(gint num_bits)
22 bitvec_t *a_Bitvec_new(int num_bits)
2223 {
23 bitvec_t *bvec = g_new(bitvec_t, 1);
24 bitvec_t *bvec = dNew(bitvec_t, 1);
2425
25 bvec->vec = g_new0(guchar, num_bits/BVEC_SIZE + 1);
26 bvec->vec = dNew0(uchar_t, num_bits/BVEC_SIZE + 1);
2627 bvec->len = num_bits;
2728 return bvec;
29 }
30
31 /*
32 * Clear a bitvec
33 */
34 void a_Bitvec_clear(bitvec_t *bvec)
35 {
36 memset(bvec->vec, 0, sizeof(uchar_t) * bvec->len/BVEC_SIZE + 1);
2837 }
2938
3039 /*
3342 void a_Bitvec_free(bitvec_t *bvec)
3443 {
3544 if (bvec) {
36 g_free(bvec->vec);
37 g_free(bvec);
45 dFree(bvec->vec);
46 dFree(bvec);
3847 }
3948 }
4049
4150 /*
4251 * Get a bit
4352 */
44 gint a_Bitvec_get_bit(bitvec_t *bvec, gint pos)
53 int a_Bitvec_get_bit(bitvec_t *bvec, int pos)
4554 {
46 g_return_val_if_fail (pos < bvec->len, 0);
55 dReturn_val_if_fail (pos < bvec->len, 0);
4756 return (bvec->vec[pos/BVEC_SIZE] & 1 << pos % BVEC_SIZE);
4857 }
4958
5059 /*
5160 * Set a bit
5261 */
53 void a_Bitvec_set_bit(bitvec_t *bvec, gint pos)
62 void a_Bitvec_set_bit(bitvec_t *bvec, int pos)
5463 {
55 g_return_if_fail (pos < bvec->len);
64 dReturn_if_fail (pos < bvec->len);
5665 bvec->vec[pos/BVEC_SIZE] |= 1 << (pos % BVEC_SIZE);
5766 }
00 #ifndef __BITVEC_H__
11 #define __BITVEC_H__
22
3 #include <glib.h>
3 #include "d_size.h"
44
5 #define BVEC_TYPE guchar
5 #define BVEC_TYPE uchar_t
66 #define BVEC_SIZE sizeof(BVEC_TYPE)
77
88 typedef struct _bitvec bitvec_t;
99
1010 struct _bitvec {
1111 BVEC_TYPE *vec;
12 gint len; /* number of bits [1 based] */
12 int len; /* number of bits [1 based] */
1313 };
1414
1515
1616 /*
1717 * Function prototypes
1818 */
19 bitvec_t *a_Bitvec_new(gint bits);
19 bitvec_t *a_Bitvec_new(int bits);
2020 void a_Bitvec_free(bitvec_t *bvec);
21 gint a_Bitvec_get_bit(bitvec_t *bvec, gint pos);
22 void a_Bitvec_set_bit(bitvec_t *bvec, gint pos);
21 int a_Bitvec_get_bit(bitvec_t *bvec, int pos);
22 void a_Bitvec_set_bit(bitvec_t *bvec, int pos);
23 void a_Bitvec_clear(bitvec_t *bvec);
2324
2425 /*
2526 #define a_Bitvec_get_bit(bvec,pos) \
00 /*
11 * File: bookmark.c
22 *
3 * Copyright 2002 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright 2002-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
11 #include <errno.h>
12 #include <stdio.h>
1311 #include <stdlib.h>
14 #include <string.h>
1512
1613 #include "msg.h"
17 #include "browser.h"
1814 #include "history.h"
19 #include "menu.h"
2015 #include "capi.h"
21 #include "nav.h"
22 #include "misc.h"
2316 #include "bookmark.h" /* for prototypes */
2417 #include "../dpip/dpip.h"
2518
2619
27
28 /*
29 * Initialize the bookmarks module
30 */
31 void a_Bookmarks_init(void)
32 {
33 /* simple isn't it? ;) */
34 }
3520
3621 /*
3722 * Have a short chat with the bookmarks server,
5540 if (Bw)
5641 bw = Bw;
5742 if (!cmd4 && Cmd)
58 cmd4 = g_strdup(Cmd);
43 cmd4 = dStrdup(Cmd);
5944
6045 if (!answer) {
6146 a_Capi_dpi_send_cmd(NULL, bw, cmd1, "bookmarks", 1);
7257 } else if (*answer == 'O') {
7358 /* "OK, send it!" */
7459 a_Capi_dpi_send_cmd(NULL, bw, cmd4, "bookmarks", 0);
75 g_free(cmd4);
60 dFree(cmd4);
7661 cmd4 = NULL;
7762 }
7863 }
8267 /*
8368 * Add the new bookmark through the bookmarks server
8469 */
85 void a_Bookmarks_add(GtkWidget *widget, gpointer client_data)
70 void a_Bookmarks_add(BrowserWindow *bw, const DilloUrl *url)
8671 {
87 BrowserWindow *bw = (BrowserWindow *)client_data;
88 DilloUrl *url;
89 const gchar *title;
90 gchar *cmd;
72 const char *title;
73 char *cmd;
9174
92 url = a_Menu_popup_get_url(bw);
93 g_return_if_fail(url != NULL);
75 dReturn_if_fail(url != NULL);
9476
9577 /* if the page has no title, we'll use the url string */
9678 title = a_History_get_title_by_url(url, 1);
9880 cmd = a_Dpip_build_cmd("cmd=%s url=%s title=%s",
9981 "add_bookmark", URL_STR(url), title);
10082 a_Bookmarks_chat_add(bw, cmd, NULL);
101 g_free(cmd);
83 dFree(cmd);
10284 }
10385
104 /*
105 * Request the server to show the bookmarks
106 */
107 void a_Bookmarks_show(BrowserWindow *bw)
108 {
109 DilloUrl *url;
110
111 url = a_Url_new("dpi:/bm/", NULL, 0, 0, 0);
112 a_Nav_push(bw, url);
113 a_Url_free(url);
114 }
115
00 #ifndef __BOOKMARK_H__
11 #define __BOOKMARK_H__
22
3 #include "browser.h"
3 #include "bw.h"
44
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
58
6 void a_Bookmarks_init(void);
7 void a_Bookmarks_add(GtkWidget *widget, gpointer client_data);
8 void a_Bookmarks_show(BrowserWindow *bw);
9 void a_Bookmarks_add(BrowserWindow *bw, const DilloUrl *url);
910
10 /* todo: this is for testing purposes */
11 /* TODO: this is for testing purposes */
1112 void a_Bookmarks_chat_add(BrowserWindow *Bw, char *Cmd, char *answer);
1213
14 #ifdef __cplusplus
15 }
16 #endif /* __cplusplus */
17
1318 #endif /* __BOOKMARK_H__ */
+0
-147
src/browser.h less more
0 #ifndef __BROWSER_H__
1 #define __BROWSER_H__
2
3 #include <sys/types.h>
4 #include <gdk/gdk.h>
5 #include <gtk/gtk.h>
6
7 #include "url.h" /* for DilloUrl */
8
9
10 typedef struct _BrowserWindow BrowserWindow;
11 typedef struct _DilloMenuPopup DilloMenuPopup;
12
13 typedef struct {
14 DilloUrl *Url; /* URL-key for this cache connection */
15 gint Flags; /* {WEB_RootUrl, WEB_Image, WEB_Download} */
16 } BwUrls;
17
18 /* The popup menus so that we can call them. */
19 struct _DilloMenuPopup
20 {
21 GtkWidget *over_page;
22 GtkWidget *over_link;
23 GtkWidget *over_image;
24 GtkWidget *over_back;
25 GtkWidget *over_forw;
26 GtkWidget *over_bug;
27 DilloUrl *url;
28 DilloUrl *url2;
29 GtkWidget *ol_oi_submenu;
30 };
31
32 /* browser_window contains all widgets to create a single window */
33 struct _BrowserWindow
34 {
35 /* Control-Panel handleboxes --used for hiding */
36 GSList *PanelHandles;
37 /* Flag: TRUE when control-panel is hidden */
38 gboolean fullwindow;
39
40 /* widgets for the main window */
41 GtkWidget *main_window;
42 GtkWidget *back_button;
43 GtkWidget *forw_button;
44 GtkWidget *home_button;
45 GtkWidget *reload_button;
46 GtkWidget *save_button;
47 GtkWidget *stop_button;
48 GtkWidget *bookmarks_button;
49 GtkWidget *menubar;
50 GtkWidget *clear_url_button;
51 GtkWidget *location;
52 GtkWidget *search_button;
53 GtkWidget *progress_box;
54 GtkWidget *status_box;
55 GtkWidget *status;
56 GtkWidget *status_bug_meter;
57 gint status_is_link;
58 GtkWidget *imgprogress;
59 GtkWidget *progress;
60
61 /* the keyboard accelerator table */
62 GtkAccelGroup *accel_group;
63
64 /* Popup menu for this BrowserWindow */
65 DilloMenuPopup menu_popup;
66
67 /* The "Headings" and "Anchors" menus */
68 GtkWidget *pagemarks_menuitem;
69 GtkWidget *pagemarks_menu;
70 GtkWidget *pagemarks_last;
71
72 /* "View page Bugs" menuitem so we can set its sensitivity */
73 GtkWidget *viewbugs_menuitem;
74
75 /* This is the main document widget. (HTML rendering or whatever) */
76 GtkWidget *docwin;
77
78 /* Current cursor type */
79 GdkCursorType CursorType;
80
81 /* A list of active cache clients in the window (The primary Key) */
82 gint *RootClients;
83 gint NumRootClients;
84 gint MaxRootClients;
85
86 /* Image Keys for all active connections in the window */
87 gint *ImageClients;
88 gint NumImageClients;
89 gint MaxImageClients;
90 /* Number of different images in the page */
91 gint NumImages;
92 /* Number of different images already loaded */
93 gint NumImagesGot;
94
95 /* List of all Urls requested by this page (and its types) */
96 BwUrls *PageUrls;
97 gint NumPageUrls;
98 gint MaxPageUrls;
99
100 /* widgets for dialog boxes off main window */
101 GtkWidget *open_dialog_window;
102 GtkWidget *open_dialog_entry;
103 GtkWidget *openfile_dialog_window;
104 GtkWidget *quit_dialog_window;
105 GtkWidget *save_dialog_window;
106 GtkWidget *save_link_dialog_window;
107 GtkWidget *findtext_dialog_window;
108 GtkWidget *findtext_dialog_check;
109 GtkWidget *findtext_dialog_entry;
110 GtkWidget *search_dialog_window;
111 GtkWidget *search_dialog_entry;
112 GtkWidget *proxy_passwd_dialog_window;
113 GtkWidget *proxy_passwd_dialog_entry;
114 GtkWidget *question_dialog_window;
115 gpointer question_dialog_data;
116 gpointer question_dialog_answer;
117 GtkWidget *viewsource_window;
118 GtkWidget *pagebugs_window;
119 GtkWidget *full_screen_off_button;
120
121 /* Dillo navigation stack (holds indexes to history list) */
122 gint *nav_stack;
123 gint nav_stack_size; /* [1 based] */
124 gint nav_stack_size_max;
125 /* 'nav_stack_ptr' refers to what's being displayed */
126 gint nav_stack_ptr; /* [0 based] */
127 /* When the user clicks a link, the URL isn't pushed directly to history;
128 * nav_expect_url holds it until the first answer-bytes are got. Only then
129 * it is sent to history and referenced in 'nav_stack[++nav_stack_ptr]' */
130 DilloUrl *nav_expect_url;
131 /* 'nav_expecting' is true if the last URL is being loaded for
132 * the first time and has not gotten the dw yet. */
133 gboolean nav_expecting;
134
135 /* Counter for the number of hops on a redirection. Used to stop
136 * redirection loops (accounts for WEB_RootUrl only) */
137 gint redirect_level;
138
139 /* The id for the idle function that sets button sensitivity. */
140 guint sens_idle_id;
141 };
142
143
144
145 #endif /* __BROWSER_H__ */
146
0 /*
1 * File: bw.c
2 *
3 * Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 /* Data structures for each browser window */
12
13
14 #include "bw.h"
15 #include "msg.h"
16 #include "list.h"
17 #include "capi.h"
18 #include "uicmd.hh"
19
20
21 /*
22 * Local Data
23 */
24 /* A list of working browser windows */
25 static BrowserWindow **bws;
26 static int num_bws, num_bws_max;
27
28
29 /*
30 * Initialize global data
31 */
32 void a_Bw_init(void)
33 {
34 num_bws = 0;
35 num_bws_max = 16;
36 bws = NULL;
37 }
38
39 /*
40 * Create a new browser window and return it.
41 * (the new window is stored in browser_window[])
42 */
43 BrowserWindow *a_Bw_new()
44 {
45 BrowserWindow *bw;
46
47 /* We use dNew0() to zero the memory */
48 bw = dNew0(BrowserWindow, 1);
49 a_List_add(bws, num_bws, num_bws_max);
50 bws[num_bws++] = bw;
51
52 /* Initialize nav_stack */
53 bw->nav_stack = dList_new(8);
54 bw->nav_stack_ptr = -1;
55
56 /* Init expect */
57 bw->nav_expecting = FALSE;
58 bw->nav_expect_url = NULL;
59
60 bw->redirect_level = 0;
61 bw->meta_refresh_status = 0;
62 bw->meta_refresh_url = NULL;
63
64 bw->RootClients = dList_new(8);
65 bw->ImageClients = dList_new(8);
66 bw->NumImages = 0;
67 bw->NumImagesGot = 0;
68 bw->NumPendingStyleSheets = 0;
69 bw->PageUrls = dList_new(8);
70 bw->Docs = dList_new(8);
71
72 bw->num_page_bugs = 0;
73 bw->page_bugs = dStr_new("");
74
75 /* now that the bw is made, let's customize it.. */
76 //Interface_browser_window_customize(bw);
77
78 return bw;
79 }
80
81 /*
82 * Free resources associated to a bw.
83 */
84 void a_Bw_free(BrowserWindow *bw)
85 {
86 int i, j;
87
88 for (i = 0; i < num_bws; i++) {
89 if (bws[i] == bw) {
90 a_List_remove(bws, i, num_bws);
91
92 dList_free(bw->RootClients);
93 dList_free(bw->ImageClients);
94 dList_free(bw->Docs);
95
96 a_Url_free(bw->nav_expect_url);
97 for (j = 0; j < dList_length(bw->PageUrls); ++j)
98 a_Url_free(dList_nth_data(bw->PageUrls, j));
99 dList_free(bw->PageUrls);
100
101 for (j = 0; j < dList_length(bw->nav_stack); ++j)
102 dFree(dList_nth_data(bw->nav_stack, j));
103 dList_free(bw->nav_stack);
104
105 a_Url_free(bw->meta_refresh_url);
106
107 dStr_free(bw->page_bugs, 1);
108 dFree(bw);
109 break;
110 }
111 }
112 }
113
114 /*- Clients ----------------------------------------------------------------*/
115 /*
116 * Add a reference to a cache-client. It is kept int this bw's list.
117 * This helps us keep track of which are active in the window so that it's
118 * possible to abort/stop them.
119 * (Root: Flag, whether a Root URL or not)
120 *
121 * TODO: Make NumImages count different images.
122 */
123 void a_Bw_add_client(BrowserWindow *bw, int Key, int Root)
124 {
125 dReturn_if_fail ( bw != NULL );
126
127 if (Root) {
128 dList_append(bw->RootClients, INT2VOIDP(Key));
129 } else {
130 dList_append(bw->ImageClients, INT2VOIDP(Key));
131 bw->NumImages++;
132 /* --Images progress-bar stuff-- */
133 a_UIcmd_set_img_prog(bw, bw->NumImagesGot, bw->NumImages, 1);
134 }
135 if (dList_length(bw->RootClients) + dList_length(bw->ImageClients) == 1)
136 a_UIcmd_set_buttons_sens(bw);
137 }
138
139 /*
140 * Remove the cache-client from the bw's list
141 * (client can be a image or a html page)
142 * Return: 0 if found, 1 otherwise.
143 */
144 int a_Bw_remove_client(BrowserWindow *bw, int ClientKey)
145 {
146 void *data;
147
148 if ((data = dList_find(bw->RootClients, INT2VOIDP(ClientKey)))) {
149 dList_remove_fast(bw->RootClients, data);
150 } else if ((data = dList_find(bw->ImageClients, INT2VOIDP(ClientKey)))) {
151 dList_remove_fast(bw->ImageClients, data);
152 ++bw->NumImagesGot;
153 }
154 return data ? 0 : 1;
155 }
156
157 /*
158 * Close a cache-client upon successful retrieval.
159 * Remove the cache-client from the bw list and update the meters.
160 * (client can be a image or a html page)
161 */
162 void a_Bw_close_client(BrowserWindow *bw, int ClientKey)
163 {
164 if (a_Bw_remove_client(bw, ClientKey) == 0) {
165 a_UIcmd_set_img_prog(bw, bw->NumImagesGot, bw->NumImages, 1);
166 if (bw->NumImagesGot == bw->NumImages)
167 a_UIcmd_set_img_prog(bw, 0, 0, 0);
168 if (dList_length(bw->RootClients) == 0)
169 a_UIcmd_set_buttons_sens(bw);
170 }
171 }
172
173 /*
174 * Stop the active clients of this bw's top page.
175 * Note: rendering stops, but the cache continues to be fed.
176 */
177 void a_Bw_stop_clients(BrowserWindow *bw, int flags)
178 {
179 void *data;
180
181 if (flags & BW_Root) {
182 /* Remove root clients */
183 while ((data = dList_nth_data(bw->RootClients, 0))) {
184 a_Capi_stop_client(VOIDP2INT(data), (flags & BW_Force));
185 dList_remove_fast(bw->RootClients, data);
186 }
187 }
188
189 if (flags & BW_Img) {
190 /* Remove image clients */
191 while ((data = dList_nth_data(bw->ImageClients, 0))) {
192 a_Capi_stop_client(VOIDP2INT(data), (flags & BW_Force));
193 dList_remove_fast(bw->ImageClients, data);
194 }
195 }
196 }
197
198 /*- Page -------------------------------------------------------------------*/
199 /*
200 * Add an URL to the browser window's list.
201 * This helps us keep track of page-requested URLs so that it's
202 * possible to stop, abort and reload them.
203 */
204 void a_Bw_add_url(BrowserWindow *bw, const DilloUrl *Url)
205 {
206 dReturn_if_fail ( bw != NULL && Url != NULL );
207
208 if (!dList_find_custom(bw->PageUrls, Url, (dCompareFunc)a_Url_cmp)) {
209 dList_append(bw->PageUrls, a_Url_dup(Url));
210 }
211 }
212
213 /*
214 * Add a document to the browser window's list.
215 */
216 void a_Bw_add_doc(BrowserWindow *bw, void *vdoc)
217 {
218 dReturn_if_fail ( bw != NULL && vdoc != NULL);
219
220 dList_append(bw->Docs, vdoc);
221 }
222
223 /*
224 * Get current document.
225 */
226 void *a_Bw_get_current_doc(BrowserWindow *bw)
227 {
228 void *doc = NULL;
229 int len = dList_length(bw->Docs);
230
231 if (len == 1)
232 doc = dList_nth_data(bw->Docs, 0);
233 else if (len > 1)
234 MSG("a_Bw_get_current_doc() multiple docs not implemented\n");
235
236 return doc;
237 }
238
239 /*
240 * Get document by URL.
241 *
242 * This is currently used by popup menus that need to ensure that the
243 * page has not changed while the menu was popped up.
244 */
245 void *a_Bw_get_url_doc(BrowserWindow *bw, const DilloUrl *url)
246 {
247 void *doc = NULL;
248
249 if (url && dList_find_custom(bw->PageUrls, url, (dCompareFunc)a_Url_cmp)) {
250 doc = a_Bw_get_current_doc(bw);
251 }
252 return doc;
253 }
254
255 /*
256 * Remove a document from the bw's list
257 */
258 void a_Bw_remove_doc(BrowserWindow *bw, void *vdoc)
259 {
260 void *data;
261
262 if ((data = dList_find(bw->Docs, vdoc))) {
263 dList_remove_fast(bw->Docs, data);
264 }
265 }
266
267 /*- Cleanup ----------------------------------------------------------------*/
268 /*
269 * Empty RootClients, ImageClients and PageUrls lists and
270 * reset progress bar data.
271 */
272 void a_Bw_cleanup(BrowserWindow *bw)
273 {
274 void *data;
275
276 /* Remove root clients */
277 while ((data = dList_nth_data(bw->RootClients, 0))) {
278 dList_remove_fast(bw->RootClients, data);
279 }
280 /* Remove image clients */
281 while ((data = dList_nth_data(bw->ImageClients, 0))) {
282 dList_remove_fast(bw->ImageClients, data);
283 }
284 /* Remove PageUrls */
285 while ((data = dList_nth_data(bw->PageUrls, 0))) {
286 a_Url_free(data);
287 dList_remove_fast(bw->PageUrls, data);
288 }
289
290 /* Zero image-progress data */
291 bw->NumImages = 0;
292 bw->NumImagesGot = 0;
293
294 /* Zero stylesheet counter */
295 bw->NumPendingStyleSheets = 0;
296 }
297
298 /*--------------------------------------------------------------------------*/
299
300 int a_Bw_num()
301 {
302 return num_bws;
303 }
304
305 /*
306 * Return a bw by index
307 */
308 BrowserWindow *a_Bw_get(int i)
309 {
310 if (i >= 0 && i < num_bws)
311 return bws[i];
312 return NULL;
313 }
314
0 #ifndef __BW_H__
1 #define __BW_H__
2
3 #include "url.h" /* for DilloUrl */
4
5 /*
6 * Flag Defines for a_Bw_stop_clients()
7 */
8 #define BW_Root (1) /* Root URLs */
9 #define BW_Img (2) /* Image URLs */
10 #define BW_Force (4) /* Stop connection too */
11
12
13 typedef struct _BrowserWindow BrowserWindow;
14
15
16 /* browser_window contains the specific data for a single window */
17 struct _BrowserWindow
18 {
19 /* Pointer to the UI object this bw belongs to */
20 void *ui;
21
22 /* All the rendering is done by this.
23 * It is defined as a void pointer to avoid C++ in this structure.
24 * C++ sources have to include "dw/core.hh" and cast it into an object. */
25 void *render_layout;
26
27 /* Root document(s). Currently only used by DilloHtml */
28 Dlist *Docs;
29
30 /* A list of active cache clients in the window (The primary Key) */
31 Dlist *RootClients;
32 /* Image Keys for all active connections in the window */
33 Dlist *ImageClients;
34 /* Number of images in the page */
35 int NumImages;
36 /* Number of images already loaded */
37 int NumImagesGot;
38 /* Number of not yet arrived style sheets */
39 int NumPendingStyleSheets;
40 /* List of all Urls requested by this page (and its types) */
41 Dlist *PageUrls;
42
43 /* The navigation stack (holds indexes to history list) */
44 Dlist *nav_stack;
45 /* 'nav_stack_ptr' refers to what's being displayed */
46 int nav_stack_ptr; /* [0 based; -1 = empty] */
47 /* When the user clicks a link, the URL isn't pushed directly to history;
48 * nav_expect_url holds it until the first answer-bytes are got. Only then
49 * it is sent to history and referenced at the top of nav_stack */
50 DilloUrl *nav_expect_url;
51 /* 'nav_expecting' is true while dillo waits for non-cached URL data,
52 * until a dw is assigned to it. */
53 bool_t nav_expecting;
54
55 /* Counter for the number of hops on a redirection. Used to stop
56 * redirection loops (accounts for WEB_RootUrl only) */
57 int redirect_level;
58
59 /* Url for zero-delay redirections in the META element */
60 int meta_refresh_status;
61 DilloUrl *meta_refresh_url;
62
63 /* HTML-bugs detected at parse time */
64 int num_page_bugs;
65 Dstr *page_bugs;
66 };
67
68
69 #ifdef __cplusplus
70 extern "C" {
71 #endif /* __cplusplus */
72
73
74 void a_Bw_init(void);
75 BrowserWindow *a_Bw_new();
76 void a_Bw_free(BrowserWindow *bw);
77 BrowserWindow *a_Bw_get(int i);
78 int a_Bw_num();
79
80 void a_Bw_add_client(BrowserWindow *bw, int Key, int Root);
81 int a_Bw_remove_client(BrowserWindow *bw, int ClientKey);
82 void a_Bw_close_client(BrowserWindow *bw, int ClientKey);
83 void a_Bw_stop_clients(BrowserWindow *bw, int flags);
84 void a_Bw_add_doc(BrowserWindow *bw, void *vdoc);
85 void *a_Bw_get_current_doc(BrowserWindow *bw);
86 void *a_Bw_get_url_doc(BrowserWindow *bw, const DilloUrl *Url);
87 void a_Bw_remove_doc(BrowserWindow *bw, void *vdoc);
88 void a_Bw_add_url(BrowserWindow *bw, const DilloUrl *Url);
89 void a_Bw_cleanup(BrowserWindow *bw);
90
91 typedef void (*BwCallback_t)(BrowserWindow *bw, const void *data);
92
93 #ifdef __cplusplus
94 }
95 #endif /* __cplusplus */
96
97 #endif /* __BROWSER_H__ */
98
00 /*
11 * File: cache.c
22 *
3 * Copyright 2000, 2001, 2002, 2003, 2004 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1515 #include <ctype.h> /* for tolower */
1616 #include <sys/types.h>
1717
18 #include <sys/stat.h>
1918 #include <stdlib.h>
2019 #include <string.h>
21 #include <fcntl.h>
22 #include <unistd.h>
2320
2421 #include "msg.h"
25 #include "list.h"
2622 #include "IO/Url.h"
2723 #include "IO/IO.h"
28 #include "web.h"
24 #include "web.hh"
2925 #include "dicache.h"
30 #include "interface.h"
3126 #include "nav.h"
3227 #include "cookies.h"
3328 #include "misc.h"
29 #include "capi.h"
30 #include "decode.h"
31 #include "auth.h"
32
33 #include "timeout.hh"
34 #include "uicmd.hh"
3435
3536 #define NULLKey 0
3637
37 #define DEBUG_LEVEL 5
38 #include "debug.h"
38 /* Maximum initial size for the automatically-growing data buffer */
39 #define MAX_INIT_BUF 1024*1024
40 /* Maximum filesize for a URL, before offering a download */
41 #define HUGE_FILESIZE 15*1024*1024
3942
4043 /*
4144 * Local data types
4548 const DilloUrl *Url; /* Cached Url. Url is used as a primary Key */
4649 char *TypeDet; /* MIME type string (detected from data) */
4750 char *TypeHdr; /* MIME type string as from the HTTP Header */
48 GString *Header; /* HTTP header */
51 char *TypeMeta; /* MIME type string from META HTTP-EQUIV */
52 char *TypeNorm; /* MIME type string normalized */
53 Dstr *Header; /* HTTP header */
4954 const DilloUrl *Location; /* New URI for redirects */
50 void *Data; /* Pointer to raw data */
51 size_t ValidSize, /* Actually size of valid range */
52 TotalSize, /* Goal size of the whole data (0 if unknown) */
53 BuffSize; /* Buffer Size for unknown length transfers */
54 guint Flags; /* Look Flag Defines in cache.h */
55 IOData_t *io; /* Pointer to IO data */
56 ChainLink *CCCQuery; /* CCC link for querying branch */
57 ChainLink *CCCAnswer; /* CCC link for answering branch */
58 } CacheData_t;
55 Dlist *Auth; /* Authentication fields */
56 Dstr *Data; /* Pointer to raw data */
57 Dstr *UTF8Data; /* Data after charset translation */
58 int DataRefcount; /* Reference count */
59 Decode *TransferDecoder; /* Transfer decoder (e.g., chunked) */
60 Decode *ContentDecoder; /* Data decoder (e.g., gzip) */
61 Decode *CharsetDecoder; /* Translates text to UTF-8 encoding */
62 int ExpectedSize; /* Goal size of the HTTP transfer (0 if unknown)*/
63 int TransferSize; /* Actual length of the HTTP transfer */
64 uint_t Flags; /* See Flag Defines in cache.h */
65 } CacheEntry_t;
5966
6067
6168 /*
6269 * Local data
6370 */
64 /* A hash for cached data. Holds pointers to CacheData_t structs */
65 static GHashTable *CacheHash = NULL;
71 /* A sorted list for cached data. Holds pointers to CacheEntry_t structs */
72 static Dlist *CachedURLs;
6673
6774 /* A list for cache clients.
6875 * Although implemented as a list, we'll call it ClientQueue --Jcid */
69 static GSList *ClientQueue = NULL;
76 static Dlist *ClientQueue;
7077
7178 /* A list for delayed clients (it holds weak pointers to cache entries,
7279 * which are used to make deferred calls to Cache_process_queue) */
73 static GSList *DelayedQueue = NULL;
74 static guint DelayedQueueIdleId = 0;
80 static Dlist *DelayedQueue;
81 static uint_t DelayedQueueIdleId = 0;
7582
7683
7784 /*
7885 * Forward declarations
7986 */
80 static void Cache_process_queue(CacheData_t *entry);
81 static void Cache_delayed_process_queue(CacheData_t *entry);
82 static void Cache_stop_client(gint Key, gint force);
83
84 /*
85 * Determine if two hash entries are equal (used by GHashTable)
86 */
87 static gint Cache_hash_entry_equal(gconstpointer v1, gconstpointer v2)
88 {
89 return (!a_Url_cmp((DilloUrl *)v1, (DilloUrl *)v2));
90 }
91
92 /*
93 * Determine the hash value given the key (used by GHashTable)
94 */
95 static guint Cache_url_hash(gconstpointer key)
96 {
97 const char *p = URL_STR((DilloUrl *)key);
98 guint h = *p;
99
100 if (h)
101 for (p += 1; *p != '\0' && *p != '#'; p++)
102 h = (h << 5) - h + *p;
103
104 return h;
87 static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry);
88 static void Cache_delayed_process_queue(CacheEntry_t *entry);
89 static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw);
90 static void Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds);
91
92 /*
93 * Determine if two cache entries are equal (used by CachedURLs)
94 */
95 static int Cache_entry_cmp(const void *v1, const void *v2)
96 {
97 const CacheEntry_t *d1 = v1, *d2 = v2;
98
99 return a_Url_cmp(d1->Url, d2->Url);
100 }
101
102 /*
103 * Determine if two cache entries are equal, using a URL as key.
104 */
105 static int Cache_entry_by_url_cmp(const void *v1, const void *v2)
106 {
107 const DilloUrl *u1 = ((CacheEntry_t*)v1)->Url;
108 const DilloUrl *u2 = v2;
109
110 return a_Url_cmp(u1, u2);
105111 }
106112
107113 /*
109115 */
110116 void a_Cache_init(void)
111117 {
112 CacheHash = g_hash_table_new(Cache_url_hash, Cache_hash_entry_equal);
118 ClientQueue = dList_new(32);
119 DelayedQueue = dList_new(32);
120 CachedURLs = dList_new(256);
121
122 /* inject the splash screen in the cache */
123 {
124 DilloUrl *url = a_Url_new("about:splash", NULL);
125 Dstr *ds = dStr_new(AboutSplash);
126 Cache_entry_inject(url, ds);
127 dStr_free(ds, 1);
128 a_Url_free(url);
129 }
113130 }
114131
115132 /* Client operations ------------------------------------------------------ */
116133
117134 /*
118 * Make a unique primary-key for cache clients
119 */
120 static gint Cache_client_make_key(void)
121 {
122 static gint ClientKey = 0; /* Provide a primary key for each client */
123
124 if ( ++ClientKey < 0 ) ClientKey = 1;
125 return ClientKey;
126 }
127
128 /*
129135 * Add a client to ClientQueue.
130 * - Every client-camp is just a reference (except 'Web').
136 * - Every client-field is just a reference (except 'Web').
131137 * - Return a unique number for identifying the client.
132138 */
133 static gint Cache_client_enqueue(const DilloUrl *Url, DilloWeb *Web,
139 static int Cache_client_enqueue(const DilloUrl *Url, DilloWeb *Web,
134140 CA_Callback_t Callback, void *CbData)
135141 {
136 gint ClientKey;
142 static int ClientKey = 0; /* Provide a primary key for each client */
137143 CacheClient_t *NewClient;
138144
139 NewClient = g_new(CacheClient_t, 1);
140 ClientKey = Cache_client_make_key();
145 if (++ClientKey <= 0)
146 ClientKey = 1;
147
148 NewClient = dNew(CacheClient_t, 1);
141149 NewClient->Key = ClientKey;
142150 NewClient->Url = Url;
151 NewClient->Version = 0;
143152 NewClient->Buf = NULL;
153 NewClient->BufSize = 0;
144154 NewClient->Callback = Callback;
145155 NewClient->CbData = CbData;
146156 NewClient->Web = Web;
147157
148 ClientQueue = g_slist_append(ClientQueue, NewClient);
158 dList_append(ClientQueue, NewClient);
149159
150160 return ClientKey;
151161 }
153163 /*
154164 * Compare function for searching a Client by its key
155165 */
156 static gint Cache_client_key_cmp(gconstpointer client, gconstpointer key)
157 {
158 return ( GPOINTER_TO_INT(key) != ((CacheClient_t *)client)->Key );
159 }
160
161 /*
162 * Compare function for searching a Client by its URL
163 */
164 static gint Cache_client_url_cmp(gconstpointer client, gconstpointer url)
165 {
166 return a_Url_cmp((DilloUrl *)url, ((CacheClient_t *)client)->Url);
167 }
168
169 /*
170 * Compare function for searching a Client by hostname
171 */
172 static gint Cache_client_host_cmp(gconstpointer client, gconstpointer hostname)
173 {
174 return g_strcasecmp(URL_HOST(((CacheClient_t *)client)->Url),
175 (gchar *)hostname );
166 static int Cache_client_by_key_cmp(const void *client, const void *key)
167 {
168 return ((CacheClient_t *)client)->Key - VOIDP2INT(key);
176169 }
177170
178171 /*
179172 * Remove a client from the queue
180173 */
181 static void Cache_client_dequeue(CacheClient_t *Client, gint Key)
182 {
183 GSList *List;
184
185 if (!Client &&
186 (List = g_slist_find_custom(ClientQueue, GINT_TO_POINTER(Key),
187 Cache_client_key_cmp)))
188 Client = List->data;
189
190 if ( Client ) {
191 ClientQueue = g_slist_remove(ClientQueue, Client);
174 static void Cache_client_dequeue(CacheClient_t *Client, int Key)
175 {
176 if (!Client) {
177 Client = dList_find_custom(ClientQueue, INT2VOIDP(Key),
178 Cache_client_by_key_cmp);
179 }
180 if (Client) {
181 dList_remove(ClientQueue, Client);
192182 a_Web_free(Client->Web);
193 g_free(Client);
183 dFree(Client);
194184 }
195185 }
196186
200190 /*
201191 * Set safe values for a new cache entry
202192 */
203 static void Cache_entry_init(CacheData_t *NewEntry, const DilloUrl *Url)
193 static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url)
204194 {
205195 NewEntry->Url = a_Url_dup(Url);
206196 NewEntry->TypeDet = NULL;
207197 NewEntry->TypeHdr = NULL;
208 NewEntry->Header = g_string_new("");
198 NewEntry->TypeMeta = NULL;
199 NewEntry->TypeNorm = NULL;
200 NewEntry->Header = dStr_new("");
209201 NewEntry->Location = NULL;
210 NewEntry->Data = NULL;
211 NewEntry->ValidSize = 0;
212 NewEntry->TotalSize = 0;
213 NewEntry->BuffSize = 4096;
214 NewEntry->Flags = 0;
215 NewEntry->io = NULL;
216 NewEntry->CCCQuery = a_Chain_new();
217 NewEntry->CCCAnswer = NULL;
202 NewEntry->Auth = NULL;
203 NewEntry->Data = dStr_sized_new(8*1024);
204 NewEntry->UTF8Data = NULL;
205 NewEntry->DataRefcount = 0;
206 NewEntry->TransferDecoder = NULL;
207 NewEntry->ContentDecoder = NULL;
208 NewEntry->CharsetDecoder = NULL;
209 NewEntry->ExpectedSize = 0;
210 NewEntry->TransferSize = 0;
211 NewEntry->Flags = CA_IsEmpty;
218212 }
219213
220214 /*
221215 * Get the data structure for a cached URL (using 'Url' as the search key)
222216 * If 'Url' isn't cached, return NULL
223217 */
224 static CacheData_t *Cache_entry_search(const DilloUrl *Url)
225 {
226 return g_hash_table_lookup(CacheHash, Url);
218 static CacheEntry_t *Cache_entry_search(const DilloUrl *Url)
219 {
220 return dList_find_sorted(CachedURLs, Url, Cache_entry_by_url_cmp);
221 }
222
223 /*
224 * Given a URL, find its cache entry, following redirections.
225 */
226 static CacheEntry_t *Cache_entry_search_with_redirect(const DilloUrl *Url)
227 {
228 int i;
229 CacheEntry_t *entry;
230
231 for (i = 0; (entry = Cache_entry_search(Url)); ++i) {
232
233 /* Test for a redirection loop */
234 if (entry->Flags & CA_RedirectLoop || i == 3) {
235 _MSG_WARN("Redirect loop for URL: >%s<\n", URL_STR_(Url));
236 break;
237 }
238 /* Test for a working redirection */
239 if (entry && entry->Flags & CA_Redirect && entry->Location) {
240 Url = entry->Location;
241 } else
242 break;
243 }
244 return entry;
227245 }
228246
229247 /*
230248 * Allocate and set a new entry in the cache list
231249 */
232 static CacheData_t *Cache_entry_add(const DilloUrl *Url)
233 {
234 CacheData_t *new_entry = g_new(CacheData_t, 1);
235
236 if (Cache_entry_search(Url))
237 DEBUG_MSG(5, "WARNING: Cache_entry_add, leaking an entry.\n");
238
250 static CacheEntry_t *Cache_entry_add(const DilloUrl *Url)
251 {
252 CacheEntry_t *old_entry, *new_entry;
253
254 if ((old_entry = Cache_entry_search(Url))) {
255 MSG_WARN("Cache_entry_add, leaking an entry.\n");
256 dList_remove(CachedURLs, old_entry);
257 }
258
259 new_entry = dNew(CacheEntry_t, 1);
239260 Cache_entry_init(new_entry, Url); /* Set safe values */
240 g_hash_table_insert(CacheHash, (gpointer)new_entry->Url, new_entry);
261 dList_insert_sorted(CachedURLs, new_entry, Cache_entry_cmp);
241262 return new_entry;
242263 }
243264
244265 /*
245 * Free the components of a CacheData_t struct.
246 */
247 static void Cache_entry_free(CacheData_t *entry)
266 * Inject full page content directly into the cache.
267 * Used for "about:splash". May be used for "about:cache" too.
268 */
269 static void Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds)
270 {
271 CacheEntry_t *entry;
272
273 if (!(entry = Cache_entry_search(Url)))
274 entry = Cache_entry_add(Url);
275 entry->Flags |= CA_GotData + CA_GotHeader + CA_GotLength + CA_InternalUrl;
276 if (data_ds->len)
277 entry->Flags &= ~CA_IsEmpty;
278 dStr_truncate(entry->Data, 0);
279 dStr_append_l(entry->Data, data_ds->str, data_ds->len);
280 dStr_fit(entry->Data);
281 entry->ExpectedSize = entry->TransferSize = entry->Data->len;
282 }
283
284 /*
285 * Free Authentication fields.
286 */
287 static void Cache_auth_free(Dlist *auth)
288 {
289 int i;
290 void *auth_field;
291 for (i = 0; (auth_field = dList_nth_data(auth, i)); ++i)
292 dFree(auth_field);
293 dList_free(auth);
294 }
295
296 /*
297 * Free the components of a CacheEntry_t struct.
298 */
299 static void Cache_entry_free(CacheEntry_t *entry)
248300 {
249301 a_Url_free((DilloUrl *)entry->Url);
250 g_free(entry->TypeDet);
251 g_free(entry->TypeHdr);
252 g_string_free(entry->Header, TRUE);
302 dFree(entry->TypeDet);
303 dFree(entry->TypeHdr);
304 dFree(entry->TypeMeta);
305 dFree(entry->TypeNorm);
306 dStr_free(entry->Header, TRUE);
253307 a_Url_free((DilloUrl *)entry->Location);
254 g_free(entry->Data);
255 g_free(entry);
256 /* CCCQuery and CCCAnswer are just references */
257 }
258
259 /*
260 * Remove an entry from the CacheList (no CCC-function is called)
261 */
262 static gint Cache_entry_remove_raw(CacheData_t *entry, DilloUrl *url)
263 {
308 Cache_auth_free(entry->Auth);
309 dStr_free(entry->Data, 1);
310 dStr_free(entry->UTF8Data, 1);
311 if (entry->CharsetDecoder)
312 a_Decode_free(entry->CharsetDecoder);
313 if (entry->TransferDecoder)
314 a_Decode_free(entry->TransferDecoder);
315 if (entry->ContentDecoder)
316 a_Decode_free(entry->ContentDecoder);
317 dFree(entry);
318 }
319
320 /*
321 * Remove an entry, from the cache.
322 * All the entry clients are removed too! (it may stop rendering of this
323 * same resource on other windows, but nothing more).
324 */
325 static void Cache_entry_remove(CacheEntry_t *entry, DilloUrl *url)
326 {
327 int i;
328 CacheClient_t *Client;
329
264330 if (!entry && !(entry = Cache_entry_search(url)))
265 return 0;
266
267 /* There MUST NOT be any clients */
268 g_return_val_if_fail(
269 !g_slist_find_custom(
270 ClientQueue, (gpointer)entry->Url, Cache_client_url_cmp), 0);
331 return;
332 if (entry->Flags & CA_InternalUrl)
333 return;
334
335 /* remove all clients for this entry */
336 for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) {
337 if (Client->Url == entry->Url) {
338 a_Cache_stop_client(Client->Key);
339 --i;
340 }
341 }
271342
272343 /* remove from DelayedQueue */
273 DelayedQueue = g_slist_remove(DelayedQueue, entry);
344 dList_remove(DelayedQueue, entry);
274345
275346 /* remove from dicache */
276347 a_Dicache_invalidate_entry(entry->Url);
277348
278349 /* remove from cache */
279 g_hash_table_remove(CacheHash, entry->Url);
350 dList_remove(CachedURLs, entry);
280351 Cache_entry_free(entry);
281 return 1;
282 }
283
284 /*
285 * Remove an entry, using the CCC if necessary.
286 * (entry SHOULD NOT have clients)
287 */
288 static void Cache_entry_remove(CacheData_t *entry, DilloUrl *url)
289 {
290 ChainLink *InfoQuery, *InfoAnswer;
291
292 if (!entry && !(entry = Cache_entry_search(url)))
293 return;
294
295 InfoQuery = entry->CCCQuery;
296 InfoAnswer = entry->CCCAnswer;
297
298 if (InfoQuery) {
299 DEBUG_MSG(2, "## Aborting CCCQuery\n");
300 a_Cache_ccc(OpAbort, 1, BCK, InfoQuery, NULL, NULL);
301 } else if (InfoAnswer) {
302 DEBUG_MSG(2, "## Aborting CCCAnswer\n");
303 a_Cache_ccc(OpAbort, 2, BCK, InfoAnswer, NULL, NULL);
304 } else {
305 DEBUG_MSG(2, "## Aborting raw2\n");
306 Cache_entry_remove_raw(entry, NULL);
307 }
308 }
309
352 }
353
354 /*
355 * Wrapper for capi.
356 */
357 void a_Cache_entry_remove_by_url(DilloUrl *url)
358 {
359 Cache_entry_remove(NULL, url);
360 }
310361
311362 /* Misc. operations ------------------------------------------------------- */
312
313 /*
314 * Given an entry (url), remove all its clients (by url or host).
315 */
316 static void Cache_stop_clients(DilloUrl *url, gint ByHost)
317 {
318 guint i;
319 CacheClient_t *Client;
320
321 for (i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i) {
322 if ( (ByHost && Cache_client_host_cmp(Client, URL_HOST(url)) == 0) ||
323 (!ByHost && Cache_client_url_cmp(Client, url) == 0) ) {
324 Cache_stop_client(Client->Key, 0);
325 --i;
326 }
327 }
328 }
329
330 /*
331 * Prepare a reload by stopping clients and removing the entry
332 * Return value: 1 if on success, 0 otherwise
333 */
334 static gint Cache_prepare_reload(DilloUrl *url)
335 {
336 CacheClient_t *Client;
337 DilloWeb *ClientWeb;
338 guint i;
339
340 for (i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i){
341 if (Cache_client_url_cmp(Client, url) == 0 &&
342 (ClientWeb = Client->Web) && !(ClientWeb->flags & WEB_Download))
343 Cache_stop_client(Client->Key, 0);
344 }
345
346 if (!g_slist_find_custom(ClientQueue, url, Cache_client_url_cmp)) {
347 /* There're no clients for this entry */
348 DEBUG_MSG(2, "## No more clients for this entry\n");
349 Cache_entry_remove(NULL, url);
350 return 1;
351 } else {
352 MSG("Cache_prepare_reload: ERROR, entry still has clients\n");
353 }
354
355 return 0;
356 }
357363
358364 /*
359365 * Try finding the url in the cache. If it hits, send the cache contents
360366 * from there. If it misses, set up a new connection.
361 * Return value: A primary key for identifying the client.
362 */
363 static gint Cache_open_url(DilloWeb *Web, CA_Callback_t Call, void *CbData)
364 {
365 void *link;
366 gint ClientKey;
367 ChainFunction_t cccFunct;
367 *
368 * - 'Web' is an auxiliary data structure with misc. parameters.
369 * - 'Call' is the callback that receives the data
370 * - 'CbData' is custom data passed to 'Call'
371 * Note: 'Call' and/or 'CbData' can be NULL, in that case they get set
372 * later by a_Web_dispatch_by_type, based on content/type and 'Web' data.
373 *
374 * Return value: A primary key for identifying the client,
375 */
376 int a_Cache_open_url(void *web, CA_Callback_t Call, void *CbData)
377 {
378 int ClientKey;
379 CacheEntry_t *entry;
380 DilloWeb *Web = web;
368381 DilloUrl *Url = Web->url;
369 CacheData_t *entry = Cache_entry_search(Url);
370
371 _MSG("Cache_open_url: %s %sFOUND\n", URL_STR(Url), entry ? "" : "NOT ");
372
373 if ( entry ) {
382
383 if (URL_FLAGS(Url) & URL_E2EQuery) {
384 /* remove current entry */
385 Cache_entry_remove(NULL, Url);
386 }
387
388 if ((entry = Cache_entry_search(Url))) {
374389 /* URL is cached: feed our client with cached data */
375390 ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData);
376391 Cache_delayed_process_queue(entry);
380395 * and open a new connection */
381396 entry = Cache_entry_add(Url);
382397 ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData);
383 a_Cache_ccc(OpStart, 1, BCK, entry->CCCQuery, NULL, (void *)entry->Url);
384 cccFunct = a_Url_get_ccc_funct(entry->Url);
385 if ( cccFunct ) {
386 link = a_Chain_link_new(entry->CCCQuery,
387 a_Cache_ccc, BCK, cccFunct, 1, 1);
388 a_Chain_bcb(OpStart, entry->CCCQuery, (void *)entry->Url, Web);
398 }
399
400 return ClientKey;
401 }
402
403 /*
404 * Get cache entry status
405 */
406 uint_t a_Cache_get_flags(const DilloUrl *url)
407 {
408 CacheEntry_t *entry = Cache_entry_search(url);
409 return (entry ? entry->Flags : 0);
410 }
411
412 /*
413 * Get cache entry status (following redirections).
414 */
415 uint_t a_Cache_get_flags_with_redirection(const DilloUrl *url)
416 {
417 CacheEntry_t *entry = Cache_entry_search_with_redirect(url);
418 return (entry ? entry->Flags : 0);
419 }
420
421 /*
422 * Reference the cache data.
423 */
424 static void Cache_ref_data(CacheEntry_t *entry)
425 {
426 if (entry) {
427 entry->DataRefcount++;
428 _MSG("DataRefcount++: %d\n", entry->DataRefcount);
429 if (entry->CharsetDecoder &&
430 (!entry->UTF8Data || entry->DataRefcount == 1)) {
431 dStr_free(entry->UTF8Data, 1);
432 entry->UTF8Data = a_Decode_process(entry->CharsetDecoder,
433 entry->Data->str,
434 entry->Data->len);
435 }
436 }
437 }
438
439 /*
440 * Unreference the cache data.
441 */
442 static void Cache_unref_data(CacheEntry_t *entry)
443 {
444 if (entry) {
445 entry->DataRefcount--;
446 _MSG("DataRefcount--: %d\n", entry->DataRefcount);
447
448 if (entry->CharsetDecoder) {
449 if (entry->DataRefcount == 0) {
450 dStr_free(entry->UTF8Data, 1);
451 entry->UTF8Data = NULL;
452 } else if (entry->DataRefcount < 0) {
453 MSG_ERR("Cache_unref_data: negative refcount\n");
454 entry->DataRefcount = 0;
455 }
456 }
457 }
458 }
459
460 /*
461 * Get current content type.
462 */
463 static const char *Cache_current_content_type(CacheEntry_t *entry)
464 {
465 return entry->TypeNorm ? entry->TypeNorm : entry->TypeMeta ? entry->TypeMeta
466 : entry->TypeHdr ? entry->TypeHdr : entry->TypeDet;
467 }
468
469 /*
470 * Get current Content-Type for cache entry found by URL.
471 */
472 const char *a_Cache_get_content_type(const DilloUrl *url)
473 {
474 CacheEntry_t *entry = Cache_entry_search_with_redirect(url);
475
476 return (entry) ? Cache_current_content_type(entry) : NULL;
477 }
478
479 /*
480 * Get pointer to entry's data.
481 */
482 static Dstr *Cache_data(CacheEntry_t *entry)
483 {
484 return entry->UTF8Data ? entry->UTF8Data : entry->Data;
485 }
486
487 /*
488 * Change Content-Type for cache entry found by url.
489 * from = { "http" | "meta" }
490 * Return new content type.
491 */
492 const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype,
493 const char *from)
494 {
495 const char *curr;
496 char *major, *minor, *charset;
497 CacheEntry_t *entry = Cache_entry_search(url);
498
499 dReturn_val_if_fail (entry != NULL, NULL);
500
501 _MSG("a_Cache_set_content_type {%s} {%s}\n", ctype, URL_STR(url));
502
503 curr = Cache_current_content_type(entry);
504 if (entry->TypeMeta || (*from == 'h' && entry->TypeHdr) ) {
505 /* Type is already been set. Do nothing.
506 * BTW, META overrides TypeHdr */
507 } else {
508 if (*from == 'h') {
509 /* Content-Type from HTTP header */
510 entry->TypeHdr = dStrdup(ctype);
389511 } else {
390 a_Interface_msg(Web->bw, "ERROR: unsupported protocol");
391 a_Cache_ccc(OpAbort, 1, FWD, entry->CCCQuery, NULL, NULL);
392 ClientKey = 0; /* aborted */
393 }
394 }
395 return ClientKey;
396 }
397
398 /*
399 * Try finding the url in the cache. If it hits, send the cache contents
400 * from there. If it misses, set up a new connection.
401 *
402 * - 'Web' is an auxiliar data structure with misc. parameters.
403 * - 'Call' is the callback that receives the data
404 * - 'CbData' is custom data passed to 'Call'
405 * Note: 'Call' and/or 'CbData' can be NULL, in that case they get set
406 * later by a_Web_dispatch_by_type, based on content/type and 'Web' data.
407 *
408 * Return value: A primary key for identifying the client.
409 */
410 gint a_Cache_open_url(void *web, CA_Callback_t Call, void *CbData)
411 {
412 gint ClientKey;
413 DICacheEntry *DicEntry;
414 CacheData_t *entry;
415 DilloWeb *Web = web;
416 DilloUrl *Url = Web->url;
417
418 if (URL_FLAGS(Url) & URL_E2EReload) {
419 /* Reload operation */
420 Cache_prepare_reload(Url);
421 }
422
423 if ( Call ) {
424 /* This is a verbatim request */
425 ClientKey = Cache_open_url(Web, Call, CbData);
426
427 } else if ( (DicEntry = a_Dicache_get_entry(Url)) &&
428 (entry = Cache_entry_search(Url)) ) {
429 /* We have it in the Dicache! */
430 ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData);
431 Cache_delayed_process_queue(entry);
432
433 } else {
434 /* It can be anything; let's request it and decide what to do
435 when we get more info... */
436 ClientKey = Cache_open_url(Web, Call, CbData);
437 }
438
439 if (g_slist_find_custom(ClientQueue, GINT_TO_POINTER(ClientKey),
440 Cache_client_key_cmp))
441 return ClientKey;
442 else
443 return 0; /* Aborted */
512 /* Content-Type from META */
513 entry->TypeMeta = dStrdup(ctype);
514 }
515 if (a_Misc_content_type_cmp(curr, ctype)) {
516 /* ctype gives one different from current */
517 a_Misc_parse_content_type(ctype, &major, &minor, &charset);
518 if (*from == 'm' && charset &&
519 ((!major || !*major) && (!minor || !*minor))) {
520 /* META only gives charset; use detected MIME type too */
521 entry->TypeNorm = dStrconcat(entry->TypeDet, ctype, NULL);
522 } else if (*from == 'm' &&
523 !dStrncasecmp(ctype, "text/xhtml", 10)) {
524 /* WORKAROUND: doxygen uses "text/xhtml" in META */
525 entry->TypeNorm = dStrdup(entry->TypeDet);
526 }
527 if (charset) {
528 if (entry->CharsetDecoder)
529 a_Decode_free(entry->CharsetDecoder);
530 entry->CharsetDecoder = a_Decode_charset_init(charset);
531 curr = Cache_current_content_type(entry);
532
533 /* Invalidate UTF8Data */
534 dStr_free(entry->UTF8Data, 1);
535 entry->UTF8Data = NULL;
536 }
537 dFree(major); dFree(minor); dFree(charset);
538 }
539 }
540 return curr;
444541 }
445542
446543 /*
447544 * Get the pointer to the URL document, and its size, from the cache entry.
448545 * Return: 1 cached, 0 not cached.
449546 */
450 gint a_Cache_get_buf(const DilloUrl *Url, gchar **PBuf, gint *BufSize)
451 {
452 CacheData_t *entry;
453
454 while ((entry = Cache_entry_search(Url))) {
455
456 /* Test for a redirection loop */
457 if (entry->Flags & CA_RedirectLoop) {
458 g_warning ("Redirect loop for URL: >%s<\n", URL_STR_(Url));
459 break;
460 }
461 /* Test for a working redirection */
462 if (entry && entry->Flags & CA_Redirect && entry->Location) {
463 Url = entry->Location;
464 } else
465 break;
466 }
467
468 *BufSize = (entry) ? entry->ValidSize : 0;
469 *PBuf = (entry) ? (gchar *) entry->Data : NULL;
547 int a_Cache_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
548 {
549 CacheEntry_t *entry = Cache_entry_search_with_redirect(Url);
550 if (entry) {
551 Dstr *data;
552 Cache_ref_data(entry);
553 data = Cache_data(entry);
554 *PBuf = data->str;
555 *BufSize = data->len;
556 } else {
557 *PBuf = NULL;
558 *BufSize = 0;
559 }
470560 return (entry ? 1 : 0);
471561 }
562
563 /*
564 * Unreference the data buffer when no longer using it.
565 */
566 void a_Cache_unref_buf(const DilloUrl *Url)
567 {
568 Cache_unref_data(Cache_entry_search_with_redirect(Url));
569 }
570
472571
473572 /*
474573 * Extract a single field from the header, allocating and storing the value
475574 * in 'field'. ('fieldname' must not include the trailing ':')
476575 * Return a new string with the field-content if found (NULL on error)
477 * (This function expects a '\r' stripped header)
576 * (This function expects a '\r'-stripped header, with one-line header fields)
478577 */
479578 static char *Cache_parse_field(const char *header, const char *fieldname)
480579 {
481580 char *field;
482 guint i, j;
483
484 for ( i = 0; header[i]; i++ ) {
581 uint_t i, j;
582
583 for (i = 0; header[i]; i++) {
485584 /* Search fieldname */
486585 for (j = 0; fieldname[j]; j++)
487 if ( tolower(fieldname[j]) != tolower(header[i + j]))
586 if (tolower(fieldname[j]) != tolower(header[i + j]))
488587 break;
489 if ( fieldname[j] ) {
588 if (fieldname[j]) {
490589 /* skip to next line */
491590 for ( i += j; header[i] != '\n'; i++);
492591 continue;
493592 }
494593
495594 i += j;
496 while (header[i] == ' ') i++;
497 if (header[i] == ':' ) {
595 if (header[i] == ':') {
498596 /* Field found! */
499 while (header[++i] == ' ');
597 while (header[++i] == ' ' || header[i] == '\t');
500598 for (j = 0; header[i + j] != '\n'; j++);
501 field = g_strndup(header + i, j);
599 while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t'))
600 j--;
601 field = dStrndup(header + i, j);
502602 return field;
503603 }
604 while (header[i] != '\n') i++;
504605 }
505606 return NULL;
506607 }
507608
508 #ifndef DISABLE_COOKIES
509609 /*
510610 * Extract multiple fields from the header.
511611 */
512 static GList *Cache_parse_multiple_fields(const char *header,
612 static Dlist *Cache_parse_multiple_fields(const char *header,
513613 const char *fieldname)
514614 {
515 guint i, j;
516 GList *fields = NULL;
615 uint_t i, j;
616 Dlist *fields = dList_new(8);
517617 char *field;
518618
519619 for (i = 0; header[i]; i++) {
528628 }
529629
530630 i += j;
531 for ( ; header[i] == ' '; i++);
532 if (header[i] == ':' ) {
631 if (header[i] == ':') {
533632 /* Field found! */
534 while (header[++i] == ' ');
633 while (header[++i] == ' ' || header[i] == '\t');
535634 for (j = 0; header[i + j] != '\n'; j++);
536 field = g_strndup(header + i, j);
537 fields = g_list_append(fields, field);
538 }
635 while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t'))
636 j--;
637 field = dStrndup(header + i, j);
638 dList_append(fields, field);
639 } else {
640 while (header[i] != '\n') i++;
641 }
642 }
643
644 if (dList_length(fields) == 0) {
645 dList_free(fields);
646 fields = NULL;
539647 }
540648 return fields;
541649 }
542 #endif /* !DISABLE_COOKIES */
543650
544651 /*
545652 * Scan, allocate, and set things according to header info.
546653 * (This function needs the whole header to work)
547654 */
548 static void Cache_parse_header(CacheData_t *entry, IOData_t *io, gint HdrLen)
549 {
550 gchar *header = entry->Header->str;
551 gchar *Length, *Type, *location_str;
655 static void Cache_parse_header(CacheEntry_t *entry)
656 {
657 char *header = entry->Header->str;
658 char *Length, *Type, *location_str, *encoding;
552659 #ifndef DISABLE_COOKIES
553 GList *Cookies;
660 Dlist *Cookies;
554661 #endif
555
556 if ( HdrLen < 12 ) {
557 /* Not enough info. */
558
559 } if ( header[9] == '3' && header[10] == '0' ) {
560 /* 30x: URL redirection */
561 entry->Flags |= CA_Redirect;
562 if ( header[11] == '1' )
563 entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */
564 else if ( header[11] == '2' )
565 entry->Flags |= CA_TempRedirect; /* 302 Temporal Redirect */
566
567 location_str = Cache_parse_field(header, "Location");
568 entry->Location = a_Url_new(location_str, URL_STR_(entry->Url), 0, 0, 0);
569 g_free(location_str);
570
571 } else if ( strncmp(header + 9, "404", 3) == 0 ) {
572 entry->Flags |= CA_NotFound;
573 }
574
575 entry->ValidSize = io->Status - HdrLen;
576 if ( (Length = Cache_parse_field(header, "Content-Length")) != NULL ) {
577 entry->Flags |= CA_GotLength;
578 entry->TotalSize = strtol(Length, NULL, 10);
579 g_free(Length);
580 if (entry->TotalSize < entry->ValidSize)
581 entry->TotalSize = 0;
582 }
662 Dlist *warnings;
663 void *data;
664 int i;
665
666 _MSG("Cache_parse_header\n");
667
668 if (entry->Header->len > 12) {
669 if (header[9] == '1' && header[10] == '0' && header[11] == '0') {
670 /* 100: Continue. The "real" header has not come yet. */
671 MSG("An actual 100 Continue header!\n");
672 entry->Flags &= ~CA_GotHeader;
673 dStr_free(entry->Header, 1);
674 entry->Header = dStr_new("");
675 return;
676 }
677 if (header[9] == '3' && header[10] == '0') {
678 /* 30x: URL redirection */
679 if ((location_str = Cache_parse_field(header, "Location"))) {
680 DilloUrl *location_url;
681
682 entry->Flags |= CA_Redirect;
683 if (header[11] == '1')
684 entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */
685 else if (header[11] == '2')
686 entry->Flags |= CA_TempRedirect; /* 302 Temporary Redirect */
687
688 location_url = a_Url_new(location_str, URL_STR_(entry->Url));
689 if (URL_FLAGS(location_url) & (URL_Post + URL_Get) &&
690 dStrcasecmp(URL_SCHEME(location_url), "dpi") == 0 &&
691 dStrcasecmp(URL_SCHEME(entry->Url), "dpi") != 0) {
692 /* Forbid dpi GET and POST from non dpi-generated urls */
693 MSG("Redirection Denied! '%s' -> '%s'\n",
694 URL_STR(entry->Url), URL_STR(location_url));
695 a_Url_free(location_url);
696 } else {
697 entry->Location = location_url;
698 }
699 dFree(location_str);
700 }
701 } else if (strncmp(header + 9, "401", 3) == 0) {
702 entry->Auth =
703 Cache_parse_multiple_fields(header, "WWW-Authenticate");
704 } else if (strncmp(header + 9, "404", 3) == 0) {
705 entry->Flags |= CA_NotFound;
706 }
707 }
708
709 if ((warnings = Cache_parse_multiple_fields(header, "Warning"))) {
710 for (i = 0; (data = dList_nth_data(warnings, i)); ++i) {
711 MSG_HTTP("%s\n", (char *)data);
712 dFree(data);
713 }
714 dList_free(warnings);
715 }
716
717 /*
718 * Get Transfer-Encoding and initialize decoder
719 */
720 encoding = Cache_parse_field(header, "Transfer-Encoding");
721 entry->TransferDecoder = a_Decode_transfer_init(encoding);
722
723
724 if ((Length = Cache_parse_field(header, "Content-Length")) != NULL) {
725 if (encoding) {
726 /*
727 * If Transfer-Encoding is present, Content-Length must be ignored.
728 * If the Transfer-Encoding is non-identity, it is an error.
729 */
730 if (dStrcasecmp(encoding, "identity"))
731 MSG_HTTP("Content-Length and non-identity Transfer-Encoding "
732 "headers both present.\n");
733 } else {
734 entry->Flags |= CA_GotLength;
735 entry->ExpectedSize = MAX(strtol(Length, NULL, 10), 0);
736 }
737 dFree(Length);
738 }
739
740 dFree(encoding); /* free Transfer-Encoding */
583741
584742 #ifndef DISABLE_COOKIES
585 /* BUG: If a server feels like mixing Set-Cookie2 and Set-Cookie
586 * responses which aren't identical, then we have a problem. I don't
587 * know if that is a real issue though. */
588 if ( (Cookies = Cache_parse_multiple_fields(header, "Set-Cookie2")) ||
589 (Cookies = Cache_parse_multiple_fields(header, "Set-Cookie")) ) {
590 a_Cookies_set(Cookies, entry->Url);
591 g_list_foreach(Cookies, (GFunc)g_free, NULL);
592 g_list_free(Cookies);
743 if ((Cookies = Cache_parse_multiple_fields(header, "Set-Cookie"))) {
744 char *server_date = Cache_parse_field(header, "Date");
745
746 a_Cookies_set(Cookies, entry->Url, server_date);
747 for (i = 0; (data = dList_nth_data(Cookies, i)); ++i)
748 dFree(data);
749 dList_free(Cookies);
750 dFree(server_date);
593751 }
594752 #endif /* !DISABLE_COOKIES */
595753
596 if ( entry->TotalSize > 0 && entry->TotalSize >= entry->ValidSize ) {
597 entry->Data = g_malloc(entry->TotalSize);
598 memcpy(entry->Data, (char*)io->Buf+HdrLen, (size_t)io->Status-HdrLen);
599 /* Prepare next read */
600 a_IO_set_buf(io, (char *)entry->Data + entry->ValidSize,
601 entry->TotalSize - entry->ValidSize);
602 } else {
603 /* We don't know the size of the transfer; A lazy server? ;) */
604 entry->Data = g_malloc(entry->ValidSize + entry->BuffSize);
605 memcpy(entry->Data, (char *)io->Buf+HdrLen, entry->ValidSize);
606 /* Prepare next read */
607 a_IO_set_buf(io, (char *)entry->Data + entry->ValidSize,
608 entry->BuffSize);
754 /*
755 * Get Content-Encoding and initialize decoder
756 */
757 encoding = Cache_parse_field(header, "Content-Encoding");
758 entry->ContentDecoder = a_Decode_content_init(encoding);
759 dFree(encoding);
760
761 if (entry->ExpectedSize > 0) {
762 if (entry->ExpectedSize > HUGE_FILESIZE) {
763 entry->Flags |= CA_HugeFile;
764 }
765 /* Avoid some reallocs. With MAX_INIT_BUF we avoid a SEGFAULT
766 * with huge files (e.g. iso files).
767 * Note: the buffer grows automatically. */
768 dStr_free(entry->Data, 1);
769 entry->Data = dStr_sized_new(MIN(entry->ExpectedSize, MAX_INIT_BUF));
609770 }
610771
611772 /* Get Content-Type */
612 if ( (Type = Cache_parse_field(header, "Content-Type")) == NULL ) {
613 MSG_HTTP("Server didn't send Content-Type in header.\n");
614 } else {
615 entry->TypeHdr = Type;
616 /* This Content-Type is not trusted. It's checked against real data
617 * in Cache_process_queue(); only then CA_GotContentType becomes true.
618 */
619 }
773 if ((Type = Cache_parse_field(header, "Content-Type"))) {
774 /* This HTTP Content-Type is not trusted. It's checked against real data
775 * in Cache_process_queue(); only then CA_GotContentType becomes true. */
776 a_Cache_set_content_type(entry->Url, Type, "http");
777 _MSG("TypeHdr {%s} {%s}\n", Type, URL_STR(entry->Url));
778 _MSG("TypeMeta {%s}\n", entry->TypeMeta);
779 dFree(Type);
780 }
781 Cache_ref_data(entry);
620782 }
621783
622784 /*
623785 * Consume bytes until the whole header is got (up to a "\r\n\r\n" sequence)
624 * (Also strip '\r' chars from header)
625 */
626 static gint Cache_get_header(IOData_t *io, CacheData_t *entry)
627 {
628 gint N, i;
629 GString *hdr = entry->Header;
630 guchar *data = io->Buf;
786 * (Also unfold multi-line fields and strip '\r' chars from header)
787 */
788 static int Cache_get_header(CacheEntry_t *entry,
789 const char *buf, size_t buf_size)
790 {
791 size_t N, i;
792 Dstr *hdr = entry->Header;
631793
632794 /* Header finishes when N = 2 */
633795 N = (hdr->len && hdr->str[hdr->len - 1] == '\n');
634 for ( i = 0; i < io->Status && N < 2; ++i ) {
635 if ( data[i] == '\r' || !data[i] )
796 for (i = 0; i < buf_size && N < 2; ++i) {
797 if (buf[i] == '\r' || !buf[i])
636798 continue;
637 N = (data[i] == '\n') ? N + 1 : 0;
638 g_string_append_c(hdr, data[i]);
639 }
640
641 if ( N == 2 ){
799 if (N == 1 && (buf[i] == ' ' || buf[i] == '\t')) {
800 /* unfold multiple-line header */
801 _MSG("Multiple-line header!\n");
802 dStr_erase(hdr, hdr->len - 1, 1);
803 }
804 N = (buf[i] == '\n') ? N + 1 : 0;
805 dStr_append_c(hdr, buf[i]);
806 }
807
808 if (N == 2) {
642809 /* Got whole header */
643 _MSG("Header [io_len=%d]\n%s", i, hdr->str);
810 _MSG("Header [buf_size=%d]\n%s", i, hdr->str);
644811 entry->Flags |= CA_GotHeader;
645 /* Return number of original-header bytes in this io [1 based] */
812 dStr_fit(hdr);
813 /* Return number of header bytes in 'buf' [1 based] */
646814 return i;
647815 }
648816 return 0;
656824 * 'Op' is the operation to perform
657825 * 'VPtr' is a (void) pointer to the IO control structure
658826 */
659 static void Cache_process_io(int Op, void *VPtr)
660 {
661 gint Status, len;
662 IOData_t *io = VPtr;
663 const DilloUrl *Url = io->ExtData;
664 CacheData_t *entry = Cache_entry_search(Url);
827 void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
828 const DilloUrl *Url)
829 {
830 int offset, len;
831 const char *str;
832 Dstr *dstr1, *dstr2, *dstr3;
833 CacheEntry_t *entry = Cache_entry_search(Url);
665834
666835 /* Assert a valid entry (not aborted) */
667 if ( !entry )
668 return;
669
670 /* Keep track of this entry's io */
671 entry->io = io;
672
673 if ( Op == IOClose ) {
674 if (entry->Flags & CA_GotLength && entry->TotalSize != entry->ValidSize){
836 dReturn_if_fail (entry != NULL);
837
838 _MSG("__a_Cache_process_dbuf__\n");
839
840 if (Op == IORead) {
841 /*
842 * Cache_get_header() will set CA_GotHeader if it has a full header, and
843 * Cache_parse_header() will unset it if the header ends being
844 * merely an informational response from the server (i.e., 100 Continue)
845 */
846 for (offset = 0; !(entry->Flags & CA_GotHeader) &&
847 (len = Cache_get_header(entry, buf + offset, buf_size - offset));
848 Cache_parse_header(entry) ) {
849 offset += len;
850 }
851
852 if (entry->Flags & CA_GotHeader) {
853 str = buf + offset;
854 len = buf_size - offset;
855 entry->TransferSize += len;
856 dstr1 = dstr2 = dstr3 = NULL;
857
858 /* Decode arrived data (<= 3 stages) */
859 if (entry->TransferDecoder) {
860 dstr1 = a_Decode_process(entry->TransferDecoder, str, len);
861 str = dstr1->str;
862 len = dstr1->len;
863 }
864 if (entry->ContentDecoder) {
865 dstr2 = a_Decode_process(entry->ContentDecoder, str, len);
866 str = dstr2->str;
867 len = dstr2->len;
868 }
869 dStr_append_l(entry->Data, str, len);
870 if (entry->CharsetDecoder && entry->UTF8Data) {
871 dstr3 = a_Decode_process(entry->CharsetDecoder, str, len);
872 dStr_append_l(entry->UTF8Data, dstr3->str, dstr3->len);
873 }
874 dStr_free(dstr1, 1);
875 dStr_free(dstr2, 1);
876 dStr_free(dstr3, 1);
877
878 if (entry->Data->len)
879 entry->Flags &= ~CA_IsEmpty;
880
881 entry = Cache_process_queue(entry);
882 }
883 } else if (Op == IOClose) {
884 if ((entry->ExpectedSize || entry->TransferSize) &&
885 entry->TypeHdr == NULL) {
886 MSG_HTTP("Message with a body lacked Content-Type header.\n");
887 }
888 if ((entry->Flags & CA_GotLength) &&
889 (entry->ExpectedSize != entry->TransferSize)) {
675890 MSG_HTTP("Content-Length does NOT match message body,\n"
676891 " at: %s\n", URL_STR_(entry->Url));
892 MSG("entry->ExpectedSize = %d, entry->TransferSize = %d\n",
893 entry->ExpectedSize, entry->TransferSize);
677894 }
678895 entry->Flags |= CA_GotData;
679896 entry->Flags &= ~CA_Stopped; /* it may catch up! */
680 entry->TotalSize = entry->ValidSize;
681 entry->io = NULL;
682 entry->CCCAnswer = NULL;
683 Cache_process_queue(entry);
684 return;
685 } else if ( Op == IOAbort ) {
686 /* todo: implement Abort
687 * (eliminate cache entry and anything related) */
688 DEBUG_MSG(5, "Cache_process_io Op = IOAbort; not implemented yet\n");
689 entry->io = NULL;
690 entry->CCCAnswer = NULL;
691 return;
692 }
693
694 if ( !(entry->Flags & CA_GotHeader) ) {
695 /* Haven't got the whole header yet */
696 len = Cache_get_header(io, entry);
697 if ( entry->Flags & CA_GotHeader ) {
698 /* Let's scan, allocate, and set things according to header info */
699 Cache_parse_header(entry, io, len);
700 /* Now that we have it parsed, let's update our clients */
701 Cache_process_queue(entry);
702 }
703 return;
704 }
705
706 Status = io->Status;
707 entry->ValidSize += Status;
708 if ( Status < (gint)io->BufSize ) {
709 /* An incomplete buffer; update buffer & size */
710 a_IO_set_buf(io, (char *)io->Buf + Status, io->BufSize - Status);
711
712 } else if ( Status == (gint)io->BufSize ) {
713 /* A full buffer! */
714 if ( !entry->TotalSize ) {
715 /* We are receiving in small chunks... */
716 entry->Data = g_realloc(entry->Data,entry->ValidSize+entry->BuffSize);
717 a_IO_set_buf(io, (char *)entry->Data + entry->ValidSize,
718 entry->BuffSize);
719 } else {
720 /* We have a preallocated buffer! */
721 a_IO_set_buf(io, (char *)io->Buf + Status, io->BufSize - Status);
722 }
723 }
724 Cache_process_queue(entry);
897 if (entry->TransferDecoder) {
898 a_Decode_free(entry->TransferDecoder);
899 entry->TransferDecoder = NULL;
900 }
901 if (entry->ContentDecoder) {
902 a_Decode_free(entry->ContentDecoder);
903 entry->ContentDecoder = NULL;
904 }
905 dStr_fit(entry->Data); /* fit buffer size! */
906
907 if ((entry = Cache_process_queue(entry))) {
908 if (entry->Flags & CA_GotHeader) {
909 Cache_unref_data(entry);
910 }
911 }
912 } else if (Op == IOAbort) {
913 /* unused */
914 MSG("a_Cache_process_dbuf Op = IOAbort; not implemented!\n");
915 }
725916 }
726917
727918 /*
728919 * Process redirections (HTTP 30x answers)
729920 * (This is a work in progress --not finished yet)
730921 */
731 static gint Cache_redirect(CacheData_t *entry, gint Flags, BrowserWindow *bw)
922 static int Cache_redirect(CacheEntry_t *entry, int Flags, BrowserWindow *bw)
732923 {
733924 DilloUrl *NewUrl;
734925
739930 entry->Flags |= CA_RedirectLoop;
740931
741932 if (entry->Flags & CA_RedirectLoop) {
742 a_Interface_msg(bw, "ERROR: redirect loop for: %s", URL_STR_(entry->Url));
933 a_UIcmd_set_msg(bw, "ERROR: redirect loop for: %s", URL_STR_(entry->Url));
743934 bw->redirect_level = 0;
744935 return 0;
745936 }
746937
747938 if ((entry->Flags & CA_Redirect && entry->Location) &&
748939 (entry->Flags & CA_ForceRedirect || entry->Flags & CA_TempRedirect ||
749 !entry->ValidSize || entry->ValidSize < 1024 )) {
750
751 _MSG(">>>Redirect from: %s\n to %s\n",
940 !entry->Data->len || entry->Data->len < 1024)) {
941
942 _MSG(">>>> Redirect from: %s\n to %s <<<<\n",
752943 URL_STR_(entry->Url), URL_STR_(entry->Location));
753944 _MSG("%s", entry->Header->str);
754945
755946 if (Flags & WEB_RootUrl) {
756947 /* Redirection of the main page */
757 NewUrl = a_Url_new(URL_STR_(entry->Location), URL_STR_(entry->Url),
758 0, 0, 0);
948 NewUrl = a_Url_new(URL_STR_(entry->Location), URL_STR_(entry->Url));
759949 if (entry->Flags & CA_TempRedirect)
760 a_Url_set_flags(NewUrl, URL_FLAGS(NewUrl) | URL_E2EReload);
761 a_Nav_push(bw, NewUrl);
950 a_Url_set_flags(NewUrl, URL_FLAGS(NewUrl) | URL_E2EQuery);
951 a_Nav_push(bw, NewUrl, entry->Url);
762952 a_Url_free(NewUrl);
763953 } else {
764954 /* Sub entity redirection (most probably an image) */
765 if ( !entry->ValidSize ) {
766 DEBUG_MSG(3,">>>Image redirection without entity-content<<<\n");
955 if (!entry->Data->len) {
956 _MSG(">>>> Image redirection without entity-content <<<<\n");
767957 } else {
768 DEBUG_MSG(3, ">>>Image redirection with entity-content<<<\n");
958 _MSG(">>>> Image redirection with entity-content <<<<\n");
769959 }
770960 }
771961 }
772962 return 0;
963 }
964
965 typedef struct {
966 Dlist *auth;
967 DilloUrl *url;
968 BrowserWindow *bw;
969 } CacheAuthData_t;
970
971 /*
972 * Ask for user/password and reload the page.
973 */
974 static void Cache_auth_callback(void *vdata)
975 {
976 CacheAuthData_t *data = (CacheAuthData_t *)vdata;
977 if (a_Auth_do_auth(data->auth, data->url))
978 a_Nav_reload(data->bw);
979 Cache_auth_free(data->auth);
980 a_Url_free(data->url);
981 dFree(data);
982 Cache_auth_entry(NULL, NULL);
983 a_Timeout_remove();
984 }
985
986 /*
987 * Set a timeout function to ask for user/password.
988 */
989 static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw)
990 {
991 static int busy = 0;
992 CacheAuthData_t *data;
993
994 if (!entry) {
995 busy = 0;
996 } else if (busy) {
997 MSG_WARN("Cache_auth_entry: caught busy!\n");
998 } else if (entry->Auth) {
999 busy = 1;
1000 data = dNew(CacheAuthData_t, 1);
1001 data->auth = entry->Auth;
1002 data->url = a_Url_dup(entry->Url);
1003 data->bw = bw;
1004 entry->Auth = NULL;
1005 a_Timeout_add(0.0, Cache_auth_callback, data);
1006 }
7731007 }
7741008
7751009 /*
7761010 * Check whether a URL scheme is downloadable.
7771011 * Return: 1 enabled, 0 disabled.
7781012 */
779 int Cache_download_enabled(const DilloUrl *url)
780 {
781 if (!strcasecmp(URL_SCHEME(url), "http") ||
782 !strcasecmp(URL_SCHEME(url), "https") ||
783 !strcasecmp(URL_SCHEME(url), "ftp") )
1013 int a_Cache_download_enabled(const DilloUrl *url)
1014 {
1015 if (!dStrcasecmp(URL_SCHEME(url), "http") ||
1016 !dStrcasecmp(URL_SCHEME(url), "https") ||
1017 !dStrcasecmp(URL_SCHEME(url), "ftp"))
7841018 return 1;
7851019 return 0;
7861020 }
7901024 * (Currently used to handle WEB_RootUrl redirects,
7911025 * and to ignore image redirects --Jcid)
7921026 */
793 void a_Cache_null_client(int Op, CacheClient_t *Client)
1027 static void Cache_null_client(int Op, CacheClient_t *Client)
7941028 {
7951029 DilloWeb *Web = Client->Web;
7961030
7981032 if (Op == CA_Close) {
7991033 if (Web->flags & WEB_RootUrl) {
8001034 /* Remove this client from our active list */
801 a_Interface_close_client(Web->bw, Client->Key);
1035 a_Bw_close_client(Web->bw, Client->Key);
8021036 }
8031037 }
8041038
8051039 /* else ignore */
8061040
8071041 return;
1042 }
1043
1044 typedef struct {
1045 BrowserWindow *bw;
1046 DilloUrl *url;
1047 } Cache_savelink_t;
1048
1049 /*
1050 * Save link from behind a timeout so that Cache_process_queue() can
1051 * get on with its work.
1052 */
1053 static void Cache_savelink_cb(void *vdata)
1054 {
1055 Cache_savelink_t *data = (Cache_savelink_t*) vdata;
1056
1057 a_UIcmd_save_link(data->bw, data->url);
1058 a_Url_free(data->url);
1059 dFree(data);
8081060 }
8091061
8101062 /*
8151067 * - Remove clients when done
8161068 * - Call redirect handler
8171069 *
818 * todo: Implement CA_Abort Op in client callback
819 */
820 static void Cache_process_queue(CacheData_t *entry)
821 {
822 guint i;
823 gint st;
824 const gchar *Type;
1070 * Return: Cache entry, which may be NULL if it has been removed.
1071 *
1072 * TODO: Implement CA_Abort Op in client callback
1073 */
1074 static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry)
1075 {
1076 uint_t i;
1077 int st;
1078 const char *Type;
1079 Dstr *data;
8251080 CacheClient_t *Client;
8261081 DilloWeb *ClientWeb;
8271082 BrowserWindow *Client_bw = NULL;
828 static gboolean Busy = FALSE;
829 gboolean AbortEntry = FALSE;
830 gboolean OfferDownload = FALSE;
831 gboolean TypeMismatch = FALSE;
832
833 if ( Busy )
834 DEBUG_MSG(5, "FATAL!:*** >>>> Cache_process_queue Caught busy!!!\n");
1083 static bool_t Busy = FALSE;
1084 bool_t AbortEntry = FALSE;
1085 bool_t OfferDownload = FALSE;
1086 bool_t TypeMismatch = FALSE;
1087
1088 if (Busy)
1089 MSG_ERR("FATAL!: >>>> Cache_process_queue Caught busy!!! <<<<\n");
8351090 if (!(entry->Flags & CA_GotHeader))
836 return;
1091 return entry;
8371092 if (!(entry->Flags & CA_GotContentType)) {
8381093 st = a_Misc_get_content_type_from_data(
839 entry->Data, entry->ValidSize, &Type);
1094 entry->Data->str, entry->Data->len, &Type);
1095 _MSG("Cache: detected Content-Type '%s'\n", Type);
8401096 if (st == 0 || entry->Flags & CA_GotData) {
8411097 if (a_Misc_content_type_check(entry->TypeHdr, Type) < 0) {
8421098 MSG_HTTP("Content-Type '%s' doesn't match the real data.\n",
8431099 entry->TypeHdr);
8441100 TypeMismatch = TRUE;
8451101 }
846 entry->TypeDet = g_strdup(Type);
1102 entry->TypeDet = dStrdup(Type);
8471103 entry->Flags |= CA_GotContentType;
8481104 } else
849 return; /* i.e., wait for more data */
1105 return entry; /* i.e., wait for more data */
8501106 }
8511107
8521108 Busy = TRUE;
853 for ( i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i ) {
854 if ( Client->Url == entry->Url ) {
1109 for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) {
1110 if (Client->Url == entry->Url) {
8551111 ClientWeb = Client->Web; /* It was a (void*) */
8561112 Client_bw = ClientWeb->bw; /* 'bw' in a local var */
8571113
8581114 if (ClientWeb->flags & WEB_RootUrl) {
8591115 if (!(entry->Flags & CA_MsgErased)) {
8601116 /* clear the "expecting for reply..." message */
861 a_Interface_msg(Client_bw, "");
1117 a_UIcmd_set_msg(Client_bw, "");
8621118 entry->Flags |= CA_MsgErased;
8631119 }
864 if (TypeMismatch)
865 a_Interface_msg(Client_bw,"HTTP warning: Content-Type '%s' "
1120 if (TypeMismatch) {
1121 a_UIcmd_set_msg(Client_bw,"HTTP warning: Content-Type '%s' "
8661122 "doesn't match the real data.", entry->TypeHdr);
1123 OfferDownload = TRUE;
1124 }
8671125 if (entry->Flags & CA_Redirect) {
8681126 if (!Client->Callback) {
869 Client->Callback = a_Cache_null_client;
1127 Client->Callback = Cache_null_client;
8701128 Client_bw->redirect_level++;
8711129 }
8721130 } else {
8731131 Client_bw->redirect_level = 0;
8741132 }
1133 if (entry->Flags & CA_HugeFile) {
1134 a_UIcmd_set_msg(Client_bw,"Huge file! (%dMB)",
1135 entry->ExpectedSize / (1024*1024));
1136 AbortEntry = OfferDownload = TRUE;
1137 }
8751138 } else {
8761139 /* For non root URLs, ignore redirections and 404 answers */
8771140 if (entry->Flags & CA_Redirect || entry->Flags & CA_NotFound)
878 Client->Callback = a_Cache_null_client;
1141 Client->Callback = Cache_null_client;
8791142 }
8801143
8811144 /* Set the client function */
8821145 if (!Client->Callback) {
1146 Client->Callback = Cache_null_client;
8831147 if (TypeMismatch) {
8841148 AbortEntry = TRUE;
885 Client->Callback = a_Cache_null_client;
8861149 } else {
887 st = a_Web_dispatch_by_type(
888 entry->TypeHdr ? entry->TypeHdr : entry->TypeDet,
889 ClientWeb, &Client->Callback, &Client->CbData);
1150 const char *curr_type = Cache_current_content_type(entry);
1151 st = a_Web_dispatch_by_type(curr_type, ClientWeb,
1152 &Client->Callback, &Client->CbData);
8901153 if (st == -1) {
8911154 /* MIME type is not viewable */
892 Client->Callback = a_Cache_null_client;
8931155 if (ClientWeb->flags & WEB_RootUrl) {
894 /* Unhandled MIME type, prepare a download offer... */
895 AbortEntry = TRUE;
896 OfferDownload = TRUE;
1156 MSG("Content-Type '%s' not viewable.\n", curr_type);
1157 /* prepare a download offer... */
1158 AbortEntry = OfferDownload = TRUE;
8971159 } else {
8981160 /* TODO: Resource Type not handled.
8991161 * Not aborted to avoid multiple connections on the same
9001162 * resource. A better idea is to abort the connection and
9011163 * to keep a failed-resource flag in the cache entry. */
902 MSG_HTTP("Unhandled MIME type: <%s>\n",
903 entry->TypeHdr ? entry->TypeHdr:entry->TypeDet);
9041164 }
9051165 }
9061166 }
9071167 if (AbortEntry) {
908 a_Interface_remove_client(Client_bw, Client->Key);
1168 a_Bw_remove_client(Client_bw, Client->Key);
9091169 Cache_client_dequeue(Client, NULLKey);
9101170 --i; /* Keep the index value in the next iteration */
9111171 continue;
9131173 }
9141174
9151175 /* Send data to our client */
916 if ( (Client->BufSize = entry->ValidSize) > 0) {
917 Client->Buf = (guchar *)entry->Data;
1176 if (ClientWeb->flags & WEB_Download) {
1177 /* for download, always provide original data, not translated */
1178 data = entry->Data;
1179 } else {
1180 data = Cache_data(entry);
1181 }
1182 if ((Client->BufSize = data->len) > 0) {
1183 Client->Buf = data->str;
9181184 (Client->Callback)(CA_Send, Client);
1185 if (ClientWeb->flags & WEB_RootUrl) {
1186 /* show size of page received */
1187 a_UIcmd_set_page_prog(Client_bw, entry->Data->len, 1);
1188 }
9191189 }
9201190
9211191 /* Remove client when done */
922 if ( (entry->Flags & CA_GotData) ) {
1192 if (entry->Flags & CA_GotData) {
9231193 /* Copy flags to a local var */
924 gint flags = ClientWeb->flags;
1194 int flags = ClientWeb->flags;
9251195 /* We finished sending data, let the client know */
926 if (!Client->Callback)
927 DEBUG_MSG(3, "Client Callback is NULL");
928 else
929 (Client->Callback)(CA_Close, Client);
1196 (Client->Callback)(CA_Close, Client);
1197 if (ClientWeb->flags & WEB_RootUrl)
1198 a_UIcmd_set_page_prog(Client_bw, 0, 0);
9301199 Cache_client_dequeue(Client, NULLKey);
9311200 --i; /* Keep the index value in the next iteration */
9321201
933 /* call Cache_redirect() from this 'if' to assert one call only. */
934 if ( entry->Flags & CA_Redirect )
1202 /* within CA_GotData, we assert just one redirect call */
1203 if (entry->Flags & CA_Redirect)
9351204 Cache_redirect(entry, flags, Client_bw);
936
937 _MSG("Cache_process_queue: NumRootClients=%d sens_idle_id = %d\n",
938 Client_bw->NumRootClients, Client_bw->sens_idle_id);
9391205 }
9401206 }
9411207 } /* for */
9421208
9431209 if (AbortEntry) {
944 /* Abort the entry, remove it from cache, and maybe offer download.
945 * (the dialog is made before 'entry' is freed) */
946 if (OfferDownload && Cache_download_enabled(entry->Url))
947 a_Interface_offer_link_download(Client_bw, entry->Url);
948 Cache_entry_remove(entry, NULL);
1210 /* Abort the entry, remove it from cache, and maybe offer download. */
1211 DilloUrl *url = a_Url_dup(entry->Url);
1212 a_Capi_conn_abort_by_url(url);
1213 entry = NULL;
1214 if (OfferDownload) {
1215 /* Remove entry when 'conn' is already done */
1216 Cache_entry_remove(NULL, url);
1217 if (a_Cache_download_enabled(url)) {
1218 Cache_savelink_t *data = dNew(Cache_savelink_t, 1);
1219 data->bw = Client_bw;
1220 data->url = a_Url_dup(url);
1221 a_Timeout_add(0.0, Cache_savelink_cb, data);
1222 }
1223 }
1224 a_Url_free(url);
1225 } else if (entry->Auth && (entry->Flags & CA_GotData)) {
1226 Cache_auth_entry(entry, Client_bw);
1227 }
1228
1229 /* Trigger cleanup when there are no cache clients */
1230 if (dList_length(ClientQueue) == 0) {
1231 _MSG(" a_Dicache_cleanup()\n");
1232 a_Dicache_cleanup();
9491233 }
9501234
9511235 Busy = FALSE;
952 DEBUG_MSG(1, "QueueSize ====> %d\n", g_slist_length(ClientQueue));
1236 _MSG("QueueSize ====> %d\n", dList_length(ClientQueue));
1237 return entry;
9531238 }
9541239
9551240 /*
9561241 * Callback function for Cache_delayed_process_queue.
9571242 */
958 static gint Cache_delayed_process_queue_callback(gpointer data)
959 {
960 gpointer entry;
961
962 while ((entry = g_slist_nth_data(DelayedQueue, 0))) {
963 Cache_process_queue( (CacheData_t *)entry );
964 /* note that if Cache_process_queue removes the entry,
965 * the following g_slist_remove has no effect. */
966 DelayedQueue = g_slist_remove(DelayedQueue, entry);
1243 static void Cache_delayed_process_queue_callback()
1244 {
1245 CacheEntry_t *entry;
1246
1247 while ((entry = (CacheEntry_t *)dList_nth_data(DelayedQueue, 0))) {
1248 Cache_ref_data(entry);
1249 if ((entry = Cache_process_queue(entry))) {
1250 Cache_unref_data(entry);
1251 dList_remove(DelayedQueue, entry);
1252 }
9671253 }
9681254 DelayedQueueIdleId = 0;
969 return FALSE;
970 }
971
972 /*
973 * Set a call to Cache_process_queue from the gtk_main cycle.
974 */
975 static void Cache_delayed_process_queue(CacheData_t *entry)
1255 a_Timeout_remove();
1256 }
1257
1258 /*
1259 * Set a call to Cache_process_queue from the main cycle.
1260 */
1261 static void Cache_delayed_process_queue(CacheEntry_t *entry)
9761262 {
9771263 /* there's no need to repeat entries in the queue */
978 if (!g_slist_find(DelayedQueue, entry))
979 DelayedQueue = g_slist_prepend(DelayedQueue, entry);
980
981 if (DelayedQueueIdleId == 0)
982 DelayedQueueIdleId =
983 gtk_idle_add((GtkFunction)Cache_delayed_process_queue_callback, NULL);
984 }
985
986
987 /*
988 * Remove a cache client
989 * todo: beware of downloads
990 */
991 static void Cache_remove_client_raw(CacheClient_t *Client, gint Key)
992 {
993 Cache_client_dequeue(Client, Key);
994 }
995
996 /*
997 * Remove every Interface-client of a single Url.
998 * todo: beware of downloads
999 * (this is transitory code)
1000 */
1001 static void Cache_remove_interface_clients(const DilloUrl *Url)
1002 {
1003 guint i;
1004 DilloWeb *Web;
1264 if (!dList_find(DelayedQueue, entry))
1265 dList_append(DelayedQueue, entry);
1266
1267 if (DelayedQueueIdleId == 0) {
1268 _MSG(" Setting timeout callback\n");
1269 a_Timeout_add(0.0, Cache_delayed_process_queue_callback, NULL);
1270 DelayedQueueIdleId = 1;
1271 }
1272 }
1273
1274 /*
1275 * Last Client for this entry?
1276 * Return: Client if true, NULL otherwise
1277 * (cache.c has only one call to a capi function. This avoids a second one)
1278 */
1279 CacheClient_t *a_Cache_client_get_if_unique(int Key)
1280 {
1281 int i, n = 0;
1282 CacheClient_t *Client, *iClient;
1283
1284 if ((Client = dList_find_custom(ClientQueue, INT2VOIDP(Key),
1285 Cache_client_by_key_cmp))) {
1286 for (i = 0; (iClient = dList_nth_data(ClientQueue, i)); ++i) {
1287 if (iClient->Url == Client->Url) {
1288 ++n;
1289 }
1290 }
1291 }
1292 return (n == 1) ? Client : NULL;
1293 }
1294
1295 /*
1296 * Remove a client from the client queue
1297 * TODO: notify the dicache and upper layers
1298 */
1299 void a_Cache_stop_client(int Key)
1300 {
10051301 CacheClient_t *Client;
1006
1007 for ( i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i ) {
1008 if ( Client->Url == Url ) {
1009 Web = Client->Web;
1010 a_Interface_remove_client(Web->bw, Client->Key);
1011 }
1012 }
1013 }
1014
1015 /*
1016 * Remove a client from the client queue
1017 * todo: notify the dicache and upper layers
1018 */
1019 static void Cache_stop_client(gint Key, gint force)
1302 CacheEntry_t *entry;
1303 DICacheEntry *DicEntry;
1304
1305 /* The client can be in both queues at the same time */
1306 if ((Client = dList_find_custom(ClientQueue, INT2VOIDP(Key),
1307 Cache_client_by_key_cmp))) {
1308 /* Dicache */
1309 if ((DicEntry = a_Dicache_get_entry(Client->Url, Client->Version)))
1310 a_Dicache_unref(Client->Url, Client->Version);
1311
1312 /* DelayedQueue */
1313 if ((entry = Cache_entry_search(Client->Url)))
1314 dList_remove(DelayedQueue, entry);
1315
1316 /* Main queue */
1317 Cache_client_dequeue(Client, NULLKey);
1318
1319 } else {
1320 _MSG("WARNING: Cache_stop_client, nonexistent client\n");
1321 }
1322 }
1323
1324
1325 /*
1326 * Memory deallocator (only called at exit time)
1327 */
1328 void a_Cache_freeall(void)
10201329 {
10211330 CacheClient_t *Client;
1022 CacheData_t *entry;
1023 GSList *List;
1024 DilloUrl *url;
1025
1026 if (!(List = g_slist_find_custom(ClientQueue, GINT_TO_POINTER(Key),
1027 Cache_client_key_cmp))){
1028 _MSG("WARNING: Cache_stop_client, inexistent client\n");
1029 return;
1030 }
1031
1032 Client = List->data;
1033 url = (DilloUrl *)Client->Url;
1034 Cache_remove_client_raw(Client, NULLKey);
1035
1036 if (force &&
1037 !g_slist_find_custom(ClientQueue, url, Cache_client_url_cmp)) {
1038 /* it was the last client of this entry */
1039 if ((entry = Cache_entry_search(url))) {
1040 if (entry->CCCQuery) {
1041 a_Cache_ccc(OpAbort, 1, BCK, entry->CCCQuery, NULL, NULL);
1042 } else if (entry->CCCAnswer) {
1043 a_Cache_ccc(OpStop, 2, BCK, entry->CCCAnswer, NULL, Client);
1044 }
1045 }
1046 }
1047 }
1048
1049 /*
1050 * Remove a client from the client queue
1051 * (It may keep feeding the cache, but nothing else)
1052 */
1053 void a_Cache_stop_client(gint Key)
1054 {
1055 Cache_stop_client(Key, 0);
1056 }
1057
1058
1059 /*
1060 * CCC function for the CACHE module
1061 */
1062 void a_Cache_ccc(int Op, int Branch, int Dir, ChainLink *Info,
1063 void *Data1, void *Data2)
1064 {
1065 CacheData_t *entry;
1066
1067 a_Chain_debug_msg("a_Cache_ccc", Op, Branch, Dir);
1068
1069 if ( Branch == 1 ) {
1070 if (Dir == BCK) {
1071 /* Querying branch */
1072 switch (Op) {
1073 case OpStart:
1074 /* Localkey = entry->Url */
1075 Info->LocalKey = Data2;
1076 break;
1077 case OpStop:
1078 break;
1079 case OpAbort:
1080 Cache_entry_remove_raw(NULL, Info->LocalKey);
1081 a_Chain_bcb(OpAbort, Info, NULL, NULL);
1082 g_free(Info);
1083 break;
1084 }
1085 } else { /* FWD */
1086 switch (Op) {
1087 case OpSend:
1088 /* Start the answer-reading chain */
1089 a_Cache_ccc(OpStart, 2, BCK, a_Chain_new(), Data1, Info->LocalKey);
1090 break;
1091 case OpEnd:
1092 /* unlink HTTP_Info */
1093 a_Chain_del_link(Info, BCK);
1094 /* 'entry->CCCQuery' and 'Info' point to the same place! */
1095 if ((entry = Cache_entry_search(Info->LocalKey)) != NULL)
1096 entry->CCCQuery = NULL;
1097 g_free(Info);
1098 break;
1099 case OpAbort:
1100 /* Unlink HTTP_Info */
1101 a_Chain_del_link(Info, BCK);
1102
1103 /* remove interface client-references of this entry */
1104 Cache_remove_interface_clients(Info->LocalKey);
1105 /* remove clients of this entry */
1106 Cache_stop_clients(Info->LocalKey, 0);
1107 /* remove the entry */
1108 Cache_entry_remove_raw(NULL, Info->LocalKey);
1109
1110 g_free(Info);
1111 break;
1112 }
1113 }
1114
1115 } else if (Branch == 2) {
1116 if (Dir == FWD) {
1117 /* Answering branch */
1118 switch (Op) {
1119 case OpStart:
1120 /* Data2 = entry->Url */
1121 Info->LocalKey = Data2;
1122 if ((entry = Cache_entry_search(Info->LocalKey))) {
1123 entry->CCCAnswer = Info;
1124 } else {
1125 /* The cache-entry was removed */
1126 a_Chain_bcb(OpAbort, Info, NULL, NULL);
1127 g_free(Info);
1128 }
1129 break;
1130 case OpSend:
1131 /* Send data */
1132 if ((entry = Cache_entry_search(Info->LocalKey))) {
1133 Cache_process_io(IORead, Data1);
1134 } else {
1135 a_Chain_bcb(OpAbort, Info, NULL, NULL);
1136 g_free(Info);
1137 }
1138 break;
1139 case OpEnd:
1140 /* Unlink HTTP_Info */
1141 a_Chain_del_link(Info, BCK);
1142 g_free(Info);
1143 Cache_process_io(IOClose, Data1);
1144 break;
1145 case OpAbort:
1146 a_Chain_del_link(Info, BCK);
1147 g_free(Info);
1148 Cache_process_io(IOAbort, Data1);
1149 break;
1150 }
1151 } else { /* BCK */
1152 switch (Op) {
1153 case OpStart:
1154 {
1155 IOData_t *io2;
1156
1157 Info->LocalKey = Data2;
1158 if ((entry = Cache_entry_search(Data2))) /* Is Data2 valid? */
1159 entry->CCCAnswer = Info;
1160
1161 /* Link IO to receive the answer */
1162 io2 = a_IO_new(IORead, *(int*)Data1);
1163 a_IO_set_buf(io2, NULL, IOBufLen);
1164 io2->ExtData = Data2; /* We have it as LocalKey */
1165 a_Chain_link_new(Info, a_Cache_ccc, BCK, a_IO_ccc, 2, 2);
1166 a_Chain_bcb(OpStart, Info, io2, NULL);
1167 break;
1168 }
1169 case OpStop:
1170 MSG(" Not implemented\n");
1171 break;
1172 case OpAbort:
1173 Cache_entry_remove_raw(NULL, Info->LocalKey);
1174 a_Chain_bcb(OpAbort, Info, NULL, NULL);
1175 g_free(Info);
1176 break;
1177 }
1178 }
1179 }
1180 }
1181
1182 static gboolean
1183 Cache_remove_hash_entry(gpointer key, gpointer value, gpointer user_data)
1184 {
1185 Cache_entry_free((CacheData_t *)value);
1186 return TRUE;
1187 }
1188
1189
1190 /*
1191 * Memory deallocator (only called at exit time)
1192 */
1193 void a_Cache_freeall(void)
1194 {
1195 CacheClient_t *Client;
1331 void *data;
11961332
11971333 /* free the client queue */
1198 while ( (Client = g_slist_nth_data(ClientQueue, 0)) )
1334 while ((Client = dList_nth_data(ClientQueue, 0)))
11991335 Cache_client_dequeue(Client, NULLKey);
12001336
1201 /* free the main cache */
12021337 /* Remove every cache entry */
1203 g_hash_table_foreach_remove(CacheHash, (GHRFunc)Cache_remove_hash_entry,
1204 NULL);
1205 /* Remove the cache hash */
1206 g_hash_table_destroy(CacheHash);
1207 }
1338 while ((data = dList_nth_data(CachedURLs, 0))) {
1339 dList_remove_fast(CachedURLs, data);
1340 Cache_entry_free(data);
1341 }
1342 /* Remove the cache list */
1343 dList_free(CachedURLs);
1344 }
00 #ifndef __CACHE_H__
11 #define __CACHE_H__
22
3 #include <glib.h>
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7
48 #include "chain.h"
59 #include "url.h"
610
1418 /*
1519 * Flag Defines
1620 */
17 #define CA_GotHeader (1) /* True after header is completely got */
18 #define CA_GotContentType (2) /* True after Content-Type is known */
19 #define CA_GotLength (4) /* True if Content-Length is known */
20 #define CA_GotData (8) /* True if we have all Data in cache */
21 #define CA_FreeData (16) /* Free the cache Data on close */
22 #define CA_Redirect (32) /* Data actually points to a redirect */
23 #define CA_ForceRedirect (64) /* Unconditional redirect */
24 #define CA_TempRedirect (128) /* Temporal redirect */
25 #define CA_NotFound (256) /* True if remote server didn't find the URL */
26 #define CA_Stopped (512) /* True if the entry has been stopped */
27 #define CA_MsgErased (1024) /* Used to erase the bw's status bar */
28 #define CA_RedirectLoop (2048) /* Redirect loop */
21 #define CA_GotHeader 0x1 /* True after header is completely got */
22 #define CA_GotContentType 0x2 /* True after Content-Type is known */
23 #define CA_GotLength 0x4 /* True if Content-Length is known */
24 #define CA_GotData 0x8 /* True if we have all Data in cache */
25 #define CA_Redirect 0x10 /* Data actually points to a redirect */
26 #define CA_ForceRedirect 0x20 /* Unconditional redirect */
27 #define CA_TempRedirect 0x40 /* Temporary redirect */
28 #define CA_NotFound 0x80 /* True if remote server didn't find the URL */
29 #define CA_Stopped 0x100 /* True if the entry has been stopped */
30 #define CA_MsgErased 0x200 /* Used to erase the bw's status bar */
31 #define CA_RedirectLoop 0x400 /* Redirect loop */
32 #define CA_InternalUrl 0x800 /* URL content is generated by dillo */
33 #define CA_HugeFile 0x1000 /* URL content is too big */
34 #define CA_IsEmpty 0x2000 /* True until a byte of content arrives */
2935
3036 /*
3137 * Callback type for cache clients
3743 * Data structure for cache clients.
3844 */
3945 struct _CacheClient {
40 gint Key; /* Primary Key for this client */
46 int Key; /* Primary Key for this client */
4147 const DilloUrl *Url; /* Pointer to a cache entry Url */
42 guchar *Buf; /* Pointer to cache-data */
43 guint BufSize; /* Valid size of cache-data */
48 int Version; /* Dicache version of this Url (0 if not used) */
49 void *Buf; /* Pointer to cache-data */
50 uint_t BufSize; /* Valid size of cache-data */
4451 CA_Callback_t Callback; /* Client function */
4552 void *CbData; /* Client function data */
4653 void *Web; /* Pointer to the Web structure of our client */
5057 * Function prototypes
5158 */
5259 void a_Cache_init(void);
53 gint a_Cache_open_url(void *Web, CA_Callback_t Call, void *CbData);
54 gint a_Cache_get_buf(const DilloUrl *Url, gchar **PBuf, gint *BufSize);
60 int a_Cache_open_url(void *Web, CA_Callback_t Call, void *CbData);
61 int a_Cache_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
62 void a_Cache_unref_buf(const DilloUrl *Url);
63 const char *a_Cache_get_content_type(const DilloUrl *url);
64 const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype,
65 const char *from);
66 uint_t a_Cache_get_flags(const DilloUrl *url);
67 uint_t a_Cache_get_flags_with_redirection(const DilloUrl *url);
68 void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
69 const DilloUrl *Url);
70 int a_Cache_download_enabled(const DilloUrl *url);
71 void a_Cache_entry_remove_by_url(DilloUrl *url);
5572 void a_Cache_freeall(void);
56 void a_Cache_null_client(int Op, CacheClient_t *Client);
57 void a_Cache_stop_client(gint Key);
73 CacheClient_t *a_Cache_client_get_if_unique(int Key);
74 void a_Cache_stop_client(int Key);
5875
5976
77 #ifdef __cplusplus
78 }
79 #endif /* __cplusplus */
6080 #endif /* __CACHE_H__ */
6181
00 /*
11 * File: capi.c
22 *
3 * Copyright 2002 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright 2002-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1515 */
1616
1717 #include <string.h>
18 #include <unistd.h> /* for pipe */
1918
2019 #include "msg.h"
2120 #include "capi.h"
21 #include "IO/IO.h" /* for IORead &friends */
2222 #include "IO/Url.h"
2323 #include "chain.h"
24 #include "list.h"
25 #include "interface.h"
2624 #include "history.h"
2725 #include "nav.h"
28 #include "misc.h"
2926 #include "dpiapi.h"
27 #include "uicmd.hh"
3028 #include "../dpip/dpip.h"
3129
3230 /* for testing dpi chat */
3331 #include "bookmark.h"
3432
35 #define DEBUG_LEVEL 5
36 #include "debug.h"
37
3833 typedef struct {
39 DilloWeb *web;
40 DilloUrl *url; /* local copy of web->url. Used when the latter is freed */
34 DilloUrl *url; /* local copy of web->url */
4135 void *bw;
42 gchar *server;
43 gchar *datastr;
44 gint SockFD;
45 gint Flags;
46 gint DpiPipe[2];
36 char *server;
37 char *datastr;
38 int SockFD;
39 int Flags;
4740 ChainLink *InfoSend;
4841 ChainLink *InfoRecv;
49 ChainLink *InfoPipe;
50
51 gint Ref;
52 } dpi_conn_t;
42
43 int Ref;
44 } capi_conn_t;
5345
5446 /* Flags for conn */
5547 enum {
6254 * Local data
6355 */
6456 /* Data list for active dpi connections */
65 static dpi_conn_t **DpiConn = NULL;
66 static gint DpiConnSize;
67 static gint DpiConnMax = 4;
68
69 /* ID for the timeout function that waits for dpid to start */
70 static guint dpi_conn_timeout_id = 0;
57 static Dlist *CapiConns; /* Data list for active connections; it holds
58 * pointers to capi_conn_t structures. */
59 /* Last URL asked for view source */
60 static DilloUrl *CapiVsUrl = NULL;
7161
7262 /*
7363 * Forward declarations
7969 /* ------------------------------------------------------------------------- */
8070
8171 /*
72 * Initialize capi&cache data
73 */
74 void a_Capi_init(void)
75 {
76 /* create an empty list */
77 CapiConns = dList_new(32);
78 /* init cache */
79 a_Cache_init();
80 }
81
82 /* ------------------------------------------------------------------------- */
83
84 /*
8285 * Create a new connection data structure
8386 */
84 static dpi_conn_t *
85 Capi_dpi_conn_new(DilloWeb *web, void *bw, char *server, gchar *datastr)
86 {
87 dpi_conn_t *conn;
88
89 conn = g_new(dpi_conn_t, 1);
90 conn->web = web;
91 conn->url = (web) ? a_Url_dup(web->url) : NULL;
87 static capi_conn_t *
88 Capi_conn_new(const DilloUrl *url, void *bw, char *server, char *datastr)
89 {
90 capi_conn_t *conn;
91
92 conn = dNew(capi_conn_t, 1);
93 conn->url = url ? a_Url_dup(url) : NULL;
9294 conn->bw = bw;
93 conn->server = g_strdup(server);
94 conn->datastr = g_strdup(datastr);
95 conn->server = dStrdup(server);
96 conn->datastr = dStrdup(datastr);
9597 conn->SockFD = -1;
96 conn->Flags = PENDING;
97 conn->InfoSend = a_Chain_new();
98 conn->InfoRecv = NULL; /* will be set later */
99 conn->InfoPipe = NULL; /* may be set later */
98 conn->Flags = (strcmp(server, "http") != 0) ? PENDING : 0;
99 conn->InfoSend = NULL;
100 conn->InfoRecv = NULL;
100101 conn->Ref = 0; /* Reference count */
101102 return conn;
102103 }
103104
104105 /*
106 * Validate a capi_conn_t pointer.
107 * Return value: NULL if not valid, conn otherwise.
108 */
109 static capi_conn_t *Capi_conn_valid(capi_conn_t *conn)
110 {
111 return dList_find(CapiConns, conn);
112 }
113
114 /*
105115 * Increment the reference count and add to the list if not present
106116 */
107 static void Capi_dpi_conn_ref(dpi_conn_t *conn)
117 static void Capi_conn_ref(capi_conn_t *conn)
108118 {
109119 if (++conn->Ref == 1) {
110120 /* add the connection data to list */
111 a_List_add(DpiConn, DpiConnSize, DpiConnMax);
112 DpiConn[DpiConnSize] = conn;
113 DpiConnSize++;
114 }
121 dList_append(CapiConns, (void *)conn);
122 }
123 _MSG(" Capi_conn_ref #%d %p\n", conn->Ref, conn);
115124 }
116125
117126 /*
118127 * Decrement the reference count (and remove from list when zero)
119128 */
120 static void Capi_dpi_conn_unref(dpi_conn_t *conn)
121 {
122 gint i, j;
123
124 --conn->Ref;
125 if (conn->Ref == 0) {
126 for (i = 0; i < DpiConnSize; ++i)
127 if (DpiConn[i] == conn) {
128 /* remove conn preserving the list order */
129 for (j = i; j + 1 < DpiConnSize; ++j)
130 DpiConn[j] = DpiConn[j + 1];
131 --DpiConnSize;
132 /* free dynamic memory */
133 a_Url_free(conn->url);
134 g_free(conn->server);
135 g_free(conn->datastr);
136 g_free(conn);
137 break;
138 }
139 }
129 static void Capi_conn_unref(capi_conn_t *conn)
130 {
131 _MSG(" Capi_conn_unref #%d %p\n", conn->Ref - 1, conn);
132
133 /* We may validate conn here, but it doesn't *seem* necessary */
134 if (--conn->Ref == 0) {
135 /* remove conn preserving the list order */
136 dList_remove(CapiConns, (void *)conn);
137 /* free dynamic memory */
138 a_Url_free(conn->url);
139 dFree(conn->server);
140 dFree(conn->datastr);
141 dFree(conn);
142 }
143 _MSG(" Capi_conn_unref CapiConns=%d\n", dList_length(CapiConns));
144 }
145
146 /*
147 * Compare function for searching a conn by server string
148 */
149 static int Capi_conn_by_server_cmp(const void *v1, const void *v2)
150 {
151 const capi_conn_t *node = v1;
152 const char *server = v2;
153 dReturn_val_if_fail(node && node->server && server, 1);
154 return strcmp(node->server, server);
140155 }
141156
142157 /*
143158 * Find connection data by server
144159 */
145 static dpi_conn_t *Capi_dpi_conn_find(gchar *server)
146 {
147 gint i;
148
149 for (i = 0; i < DpiConnSize; ++i)
150 if (strcmp(server, DpiConn[i]->server) == 0)
151 return DpiConn[i];
152
153 return NULL;
160 static capi_conn_t *Capi_conn_find(char *server)
161 {
162 return dList_find_custom(CapiConns, (void*)server, Capi_conn_by_server_cmp);
154163 }
155164
156165 /*
157166 * Resume connections that were waiting for dpid to start.
158167 */
159 static void Capi_dpi_conn_resume(void)
160 {
161 gint i;
168 static void Capi_conn_resume(void)
169 {
170 int i;
162171 DataBuf *dbuf;
163
164 for (i = 0; i < DpiConnSize; ++i)
165 if (DpiConn[i]->Flags & PENDING) {
166 dpi_conn_t *conn = DpiConn[i];
167 dbuf = a_Chain_dbuf_new(conn->datastr,(gint)strlen(conn->datastr), 0);
168 a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
169 g_free(dbuf);
172 capi_conn_t *conn;
173
174 for (i = 0; i < dList_length(CapiConns); ++i) {
175 conn = dList_nth_data (CapiConns, i);
176 if (conn->Flags & PENDING) {
177 dbuf = a_Chain_dbuf_new(conn->datastr,(int)strlen(conn->datastr), 0);
178 if (conn->InfoSend) {
179 a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
180 }
181 dFree(dbuf);
170182 conn->Flags &= ~PENDING;
171183 }
172 }
173
174 /*
175 * Test dpid and resume connections if it already started.
176 */
177 static gint Capi_dpi_conn_timeout(gpointer data)
178 {
179 static gint try = 0;
180 gint i;
181
182 ++try;
183 DEBUG_MSG(5, "Capi_dpi_conn_timeout:: try %d\n", try);
184
185 for (i = 0; i < DpiConnSize; ++i)
186 if (DpiConn[i]->Flags & PENDING) {
187 dpi_conn_t *conn = DpiConn[i];
188 a_Chain_bcb(OpStart, conn->InfoSend, conn->server, NULL);
184 }
185 }
186
187 /*
188 * Abort the connection for a given url, using its CCC.
189 * (OpAbort 2,BCK removes the cache entry)
190 * TODO: when conn is already done, the cache entry isn't removed.
191 * This may be wrong and needs a revision.
192 */
193 void a_Capi_conn_abort_by_url(const DilloUrl *url)
194 {
195 int i;
196 capi_conn_t *conn;
197
198 for (i = 0; i < dList_length(CapiConns); ++i) {
199 conn = dList_nth_data (CapiConns, i);
200 if (a_Url_cmp(conn->url, url) == 0) {
201 if (conn->InfoSend) {
202 a_Capi_ccc(OpAbort, 1, BCK, conn->InfoSend, NULL, NULL);
203 }
204 if (conn->InfoRecv) {
205 a_Capi_ccc(OpAbort, 2, BCK, conn->InfoRecv, NULL, NULL);
206 }
189207 break;
190208 }
191
192 for (i = 0; i < DpiConnSize; ++i)
193 if (DpiConn[i]->Flags & PENDING)
194 return TRUE;
195 try = 0;
196 dpi_conn_timeout_id = 0;
197 return FALSE;
209 }
198210 }
199211
200212 /* ------------------------------------------------------------------------- */
201213
202214 /*
203 * Safety test: only allow dpi-urls from dpi-generated pages.
204 */
205 static gint Capi_verify_dpi_url_request(DilloWeb *web)
206 {
207 DilloUrl *referer;
208 gint allow = FALSE;
209
210 /* test POST and GET */
211 if (strchr(URL_STR(web->url), '?') || URL_DATA_(web->url)) {
212 /* safety measure: only allow dpi requests from dpi-generated urls */
213 if (a_Nav_stack_size(web->bw)) {
214 referer = a_History_get_url(NAV_TOP(web->bw));
215 if (g_strncasecmp(URL_STR(referer), "dpi:/", 5) == 0)
216 allow = TRUE;
215 * Store the last URL requested by "view source"
216 */
217 void a_Capi_set_vsource_url(const DilloUrl *url)
218 {
219 a_Url_free(CapiVsUrl);
220 CapiVsUrl = a_Url_dup(url);
221 }
222
223 /*
224 * Safety test: only allow GET|POST dpi-urls from dpi-generated pages.
225 */
226 int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url)
227 {
228 const DilloUrl *referer;
229 int allow = FALSE;
230
231 if (dStrcasecmp(URL_SCHEME(url), "dpi") == 0) {
232 if (!(URL_FLAGS(url) & (URL_Post + URL_Get))) {
233 allow = TRUE;
234 } else if (!(URL_FLAGS(url) & URL_Post) &&
235 strncmp(URL_STR(url), "dpi:/vsource/", 13) == 0) {
236 allow = TRUE;
237 } else {
238 /* only allow GET&POST dpi-requests from dpi-generated urls */
239 if (a_Nav_stack_size(bw)) {
240 referer = a_History_get_url(NAV_TOP_UIDX(bw));
241 if (dStrcasecmp(URL_SCHEME(referer), "dpi") == 0) {
242 allow = TRUE;
243 }
244 }
217245 }
218246 } else {
219247 allow = TRUE;
220248 }
221249
222250 if (!allow) {
223 MSG("Capi_verify_dpi_url_request: Permission Denied!\n");
224 MSG(" URL_STR : %s\n", URL_STR(web->url));
225 if (URL_DATA_(web->url))
226 MSG(" URL_DATA: %s\n", URL_DATA(web->url));
251 MSG("a_Capi_dpi_verify_request: Permission Denied!\n");
252 MSG(" URL_STR : %s\n", URL_STR(url));
253 if (URL_FLAGS(url) & URL_Post) {
254 MSG(" URL_DATA: %s\n", dStr_printable(URL_DATA(url), 1024));
255 }
227256 }
228257 return allow;
229258 }
231260 /*
232261 * If the url belongs to a dpi server, return its name.
233262 */
234 gint a_Capi_url_uses_dpi(gchar *url_str, gchar **server_ptr)
235 {
236 gchar *p, *server = NULL;
237
238 if (g_strncasecmp(url_str, "dpi:/", 5) == 0) {
263 static int Capi_url_uses_dpi(DilloUrl *url, char **server_ptr)
264 {
265 char *p, *server = NULL, *url_str = URL_STR(url);
266 Dstr *tmp;
267
268 if ((dStrncasecmp(url_str, "http:", 5) == 0) ||
269 (dStrncasecmp(url_str, "about:", 6) == 0)) {
270 /* URL doesn't use dpi (server = NULL) */
271 } else if (dStrncasecmp(url_str, "dpi:/", 5) == 0) {
239272 /* dpi prefix, get this server's name */
240 if ((p = strchr(url_str + 5, '/')) != NULL)
241 server = g_strndup(url_str + 5, (guint)(p - url_str - 5));
273 if ((p = strchr(url_str + 5, '/')) != NULL) {
274 server = dStrndup(url_str + 5, (uint_t)(p - url_str - 5));
275 } else {
276 server = dStrdup("?");
277 }
278 if (strcmp(server, "bm") == 0) {
279 dFree(server);
280 server = dStrdup("bookmarks");
281 }
282 } else if ((p = strchr(url_str, ':')) != NULL) {
283 tmp = dStr_new("proto.");
284 dStr_append_l(tmp, url_str, p - url_str);
285 server = tmp->str;
286 dStr_free(tmp, 0);
287 }
288
289 return ((*server_ptr = server) ? 1 : 0);
290 }
291
292 /*
293 * Build the dpip command tag, according to URL and server.
294 */
295 static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
296 {
297 char *cmd;
298
299 if (strcmp(server, "proto.https") == 0) {
300 /* Let's be kind and make the HTTP query string for the dpi */
301 char *proxy_connect = a_Http_make_connect_str(web->url);
302 Dstr *http_query = a_Http_make_query_str(web->url, FALSE);
303 /* BUG: embedded NULLs in query data will truncate message */
304 if (proxy_connect) {
305 const char *proxy_urlstr = a_Http_get_proxy_urlstr();
306 cmd = a_Dpip_build_cmd("cmd=%s proxy_url=%s proxy_connect=%s "
307 "url=%s query=%s", "open_url", proxy_urlstr,
308 proxy_connect, URL_STR(web->url),
309 http_query->str);
310 } else {
311 cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s",
312 "open_url", URL_STR(web->url),http_query->str);
313 }
314 dFree(proxy_connect);
315 dStr_free(http_query, 1);
316
317 } else if (strcmp(server, "downloads") == 0) {
318 /* let the downloads server get it */
319 cmd = a_Dpip_build_cmd("cmd=%s url=%s destination=%s",
320 "download", URL_STR(web->url), web->filename);
321
322 } else {
323 /* For everyone else, the url string is enough... */
324 cmd = a_Dpip_build_cmd("cmd=%s url=%s", "open_url", URL_STR(web->url));
325 }
326 return cmd;
327 }
328
329 /*
330 * Send the requested URL's source to the "view source" dpi
331 */
332 static void Capi_dpi_send_source(BrowserWindow *bw, DilloUrl *url)
333 {
334 char *p, *buf, *cmd, size_str[32], *server="vsource";
335 int buf_size;
336
337 if (!(p = strchr(URL_STR(url), ':')) || !(p = strchr(p + 1, ':')))
338 return;
339
340 if (a_Capi_get_buf(CapiVsUrl, &buf, &buf_size)) {
341 /* send the page's source to this dpi connection */
342 snprintf(size_str, 32, "%d", buf_size);
343 cmd = a_Dpip_build_cmd("cmd=%s url=%s data_size=%s",
344 "start_send_page", URL_STR(url), size_str);
345 a_Capi_dpi_send_cmd(NULL, bw, cmd, server, 0);
346 a_Capi_dpi_send_data(url, bw, buf, buf_size, server, 0);
347 } else {
348 cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
349 "DpiError", "Page is NOT cached");
350 a_Capi_dpi_send_cmd(NULL, bw, cmd, server, 0);
351 }
352 dFree(cmd);
353 }
354
355 /*
356 * When dillo wants to open an URL, this can be either due to user action
357 * (e.g., typing in an URL, clicking a link), or automatic (HTTP header
358 * indicates redirection, META HTML tag with refresh attribute and 0 delay,
359 * and images and stylesheets on an HTML page when autoloading is enabled).
360 *
361 * For a user request, the action will be permitted.
362 * For an automatic request, permission to load depends on the filter set
363 * by the user.
364 */
365 static bool_t Capi_filters_test(const DilloUrl *wanted,
366 const DilloUrl *requester)
367 {
368 bool_t ret;
369
370 if (requester == NULL) {
371 /* request made by user */
372 ret = TRUE;
373 } else {
374 switch (prefs.filter_auto_requests) {
375 case PREFS_FILTER_SAME_DOMAIN:
376 {
377 const char *req_host = URL_HOST(requester),
378 *want_host = URL_HOST(wanted),
379 *req_suffix,
380 *want_suffix;
381 if (want_host[0] == '\0') {
382 ret = (req_host[0] == '\0' ||
383 !dStrcasecmp(URL_SCHEME(wanted), "data")) ? TRUE : FALSE;
384 } else {
385 /* This will regard "www.dillo.org" and "www.dillo.org." as
386 * different, but it doesn't seem worth caring about.
387 */
388 req_suffix = a_Url_host_find_public_suffix(req_host);
389 want_suffix = a_Url_host_find_public_suffix(want_host);
390
391 ret = dStrcasecmp(req_suffix, want_suffix) == 0;
392 }
393
394 MSG("Capi_filters_test: %s from '%s' to '%s'\n",
395 ret ? "ALLOW" : "DENY", req_host, want_host);
396 break;
397 }
398 case PREFS_FILTER_ALLOW_ALL:
399 default:
400 ret = TRUE;
401 break;
402 }
403 }
404 return ret;
405 }
406
407 /*
408 * Most used function for requesting a URL.
409 * TODO: clean up the ad-hoc bindings with an API that allows dynamic
410 * addition of new plugins.
411 *
412 * Return value: A primary key for identifying the client,
413 * 0 if the client is aborted in the process.
414 */
415 int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
416 {
417 int reload;
418 char *cmd, *server;
419 capi_conn_t *conn = NULL;
420 const char *scheme = URL_SCHEME(web->url);
421 int safe = 0, ret = 0, use_cache = 0;
422
423 dReturn_val_if_fail((a_Capi_get_flags(web->url) & CAPI_IsCached) ||
424 Capi_filters_test(web->url, web->requester), 0);
425
426 /* reload test */
427 reload = (!(a_Capi_get_flags(web->url) & CAPI_IsCached) ||
428 (URL_FLAGS(web->url) & URL_E2EQuery));
429
430 if (web->flags & WEB_Download) {
431 /* download request: if cached save from cache, else
432 * for http, ftp or https, use the downloads dpi */
433 if (a_Capi_get_flags_with_redirection(web->url) & CAPI_IsCached) {
434 if (web->filename) {
435 if ((web->stream = fopen(web->filename, "w"))) {
436 use_cache = 1;
437 } else {
438 MSG_WARN("Cannot open \"%s\" for writing.\n", web->filename);
439 }
440 }
441 } else if (a_Cache_download_enabled(web->url)) {
442 server = "downloads";
443 cmd = Capi_dpi_build_cmd(web, server);
444 a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
445 dFree(cmd);
446 }
447
448 } else if (Capi_url_uses_dpi(web->url, &server)) {
449 /* dpi request */
450 if ((safe = a_Capi_dpi_verify_request(web->bw, web->url))) {
451 if (dStrcasecmp(scheme, "dpi") == 0) {
452 if (strcmp(server, "vsource") == 0) {
453 /* don't reload the "view source" page */
454 } else {
455 /* make the other "dpi:/" prefixed urls always reload. */
456 a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EQuery);
457 reload = 1;
458 }
459 }
460 if (reload) {
461 a_Capi_conn_abort_by_url(web->url);
462 /* Send dpip command */
463 _MSG("a_Capi_open_url, reload url='%s'\n", URL_STR(web->url));
464 cmd = Capi_dpi_build_cmd(web, server);
465 a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
466 dFree(cmd);
467 if (strcmp(server, "vsource") == 0) {
468 Capi_dpi_send_source(web->bw, web->url);
469 }
470 }
471 use_cache = 1;
472 }
473 dFree(server);
474
475 } else if (!dStrcasecmp(scheme, "http")) {
476 /* http request */
477 if (reload) {
478 a_Capi_conn_abort_by_url(web->url);
479 /* create a new connection and start the CCC operations */
480 conn = Capi_conn_new(web->url, web->bw, "http", "none");
481 /* start the reception branch before the query one because the DNS
482 * may callback immediately. This may avoid a race condition. */
483 a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, "http");
484 a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, web);
485 }
486 use_cache = 1;
487
488 } else if (!dStrcasecmp(scheme, "about")) {
489 /* internal request */
490 use_cache = 1;
491 }
492
493 if (use_cache) {
494 if (!conn || (conn && Capi_conn_valid(conn))) {
495 /* not aborted, let's continue... */
496 ret = a_Cache_open_url(web, Call, CbData);
497 }
498 } else {
499 a_Web_free(web);
500 }
501 return ret;
502 }
503
504 /*
505 * Convert cache-defined flags to Capi ones.
506 */
507 static int Capi_map_cache_flags(uint_t flags)
508 {
509 int status = 0;
510
511 if (flags) {
512 status |= CAPI_IsCached;
513 if (flags & CA_IsEmpty)
514 status |= CAPI_IsEmpty;
515 if (flags & CA_GotData)
516 status |= CAPI_Completed;
242517 else
243 server = g_strdup("?");
244
245 if (strcmp(server, "bm") == 0) {
246 g_free(server);
247 server = g_strdup("bookmarks");
248 }
249
250 } else if (g_strncasecmp(url_str, "ftp:/", 5) == 0) {
251 server = g_strdup("ftp");
252
253 } else if (g_strncasecmp(url_str, "https:/", 7) == 0) {
254 server = g_strdup("https");
255 } else if (g_strncasecmp(url_str, "file:", 5) == 0) {
256 server = g_strdup("file");
257 } else if (g_strncasecmp(url_str, "data:", 5) == 0) {
258 server = g_strdup("datauri");
259 }
260
261 return ((*server_ptr = server) ? 1 : 0);
262 }
263
264 /*
265 * Build the dpip command tag, according to URL and server.
266 * todo: make it PROXY-aware (AFAIS, it should be easy)
267 */
268 static gchar *Capi_dpi_build_cmd(DilloUrl *url, gchar *server)
269 {
270 gchar *cmd, *http_query;
271
272 if (strcmp(server, "https") == 0) {
273 /* Let's be kind and make the HTTP query string for the dpi */
274 http_query = a_Http_make_query_str(url, FALSE);
275 cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s",
276 "open_url", URL_STR(url), http_query);
277 g_free(http_query);
278
279 } else {
280 /* For everyone else, the url_str is enough... */
281 cmd = a_Dpip_build_cmd("cmd=%s url=%s", "open_url", URL_STR(url));
282 }
283 return cmd;
284 }
285
286 /*
287 * Most used function.
288 * todo: clean up the ad-hoc bindings with an API that allows dynamic
289 * addition of new plugins.
290 */
291 gint a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
292 {
293 gint buf_size;
294 char *cmd, *server, *buf;
295 char *url_str = URL_STR(web->url);
296 gint use_cache = 0, safe = 0, reload = 0, ret = 0;
297
298 _MSG(" a_Capi_open_url:: web->Image=%p\n", web->Image);
299
300 if (a_Capi_url_uses_dpi(url_str, &server) && !Call) {
301
302 _MSG(" url_str = %s\n", url_str);
303
304 if (g_strncasecmp(url_str, "dpi:/", 5) == 0) {
305 /* safety check... */
306 safe = Capi_verify_dpi_url_request(web);
307 /* make "dpi:/" prefixed urls always reload. */
308 a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EReload);
309 reload = 1;
310 } else {
311 /* reload test */
312 reload = (!a_Capi_get_buf(web->url, &buf, &buf_size) ||
313 (URL_FLAGS(web->url) & URL_E2EReload));
314 safe = 1;
315 }
316
317 _MSG(" reload=%d URL_E2EReload=%d\n", reload,
318 (URL_FLAGS(web->url) & URL_E2EReload));
319
320 if (safe && reload) {
321 /* Send dpip command */
322 cmd = Capi_dpi_build_cmd(web->url, server);
323 a_Capi_dpi_send_cmd(web, web->bw, cmd, server, 1);
324 g_free(cmd);
325
326 /* test the new dpi-cache connection! */
327 use_cache = 1;
328
329 } else if (safe && !reload) {
330 use_cache = 1;
331 }
332 g_free(server);
333
334 } else {
335 use_cache = 1;
336 }
337
338 if (use_cache)
339 ret = a_Cache_open_url(web, Call, CbData);
340 else
341 a_Web_free(web);
342
343 return ret;
518 status |= CAPI_InProgress;
519
520 /* CAPI_Aborted is not yet used/defined */
521 }
522 return status;
523 }
524
525 /*
526 * Return status information of an URL's content-transfer process.
527 */
528 int a_Capi_get_flags(const DilloUrl *Url)
529 {
530 uint_t flags = a_Cache_get_flags(Url);
531 int status = flags ? Capi_map_cache_flags(flags) : 0;
532 return status;
533 }
534
535 /*
536 * Same as a_Capi_get_flags() but following redirections.
537 */
538 int a_Capi_get_flags_with_redirection(const DilloUrl *Url)
539 {
540 uint_t flags = a_Cache_get_flags_with_redirection(Url);
541 int status = flags ? Capi_map_cache_flags(flags) : 0;
542 return status;
344543 }
345544
346545 /*
347546 * Get the cache's buffer for the URL, and its size.
348547 * Return: 1 cached, 0 not cached.
349548 */
350 gint a_Capi_get_buf(const DilloUrl *Url, gchar **PBuf, gint *BufSize)
549 int a_Capi_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
351550 {
352551 return a_Cache_get_buf(Url, PBuf, BufSize);
552 }
553
554 /*
555 * Unref the cache's buffer when no longer using it.
556 */
557 void a_Capi_unref_buf(const DilloUrl *Url)
558 {
559 a_Cache_unref_buf(Url);
560 }
561
562 /*
563 * Get the Content-Type associated with the URL
564 */
565 const char *a_Capi_get_content_type(const DilloUrl *url)
566 {
567 return a_Cache_get_content_type(url);
568 }
569
570 /*
571 * Set the Content-Type for the URL.
572 */
573 const char *a_Capi_set_content_type(const DilloUrl *url, const char *ctype,
574 const char *from)
575 {
576 return a_Cache_set_content_type(url, ctype, from);
577 }
578
579 /*
580 * Send data to a dpi (e.g. add_bookmark, open_url, send_preferences, ...)
581 * Most of the time we send dpi commands, but it also serves for raw data
582 * as with "view source".
583 */
584 int a_Capi_dpi_send_data(const DilloUrl *url, void *bw,
585 char *data, int data_sz, char *server, int flags)
586 {
587 capi_conn_t *conn;
588 DataBuf *dbuf;
589
590 if (flags & 1) {
591 /* open a new connection to server */
592
593 /* Create a new connection data struct and add it to the list */
594 conn = Capi_conn_new(url, bw, server, data);
595 /* start the CCC operations */
596 a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, server);
597 a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, server);
598
599 } else {
600 /* Re-use an open connection */
601 conn = Capi_conn_find(server);
602 if (conn) {
603 /* found */
604 dbuf = a_Chain_dbuf_new(data, data_sz, 0);
605 a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
606 dFree(dbuf);
607 } else {
608 MSG(" ERROR: [a_Capi_dpi_send_data] No open connection found\n");
609 }
610 }
611
612 return 0;
353613 }
354614
355615 /*
356616 * Send a dpi cmd.
357617 * (For instance: add_bookmark, open_url, send_preferences, ...)
358618 */
359 gint a_Capi_dpi_send_cmd(DilloWeb *web, void *bw, char *cmd, char *server,
360 gint flags)
361 {
362 dpi_conn_t *conn;
363 DataBuf *dbuf;
364
365 if (flags & 1) {
366 /* open a new connection to server */
367
368 /* Create a new connection data struct and add it to the list */
369 conn = Capi_dpi_conn_new(web, bw, server, cmd);
370 /* start the CCC operations */
371 a_Capi_ccc(OpStart, 1, BCK, conn->InfoSend, conn, server);
372
373 } else {
374 /* Re-use an open connection */
375 conn = Capi_dpi_conn_find(server);
376 if (conn) {
377 /* found */
378 dbuf = a_Chain_dbuf_new(cmd, (gint)strlen(cmd), 0);
379 a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
380 g_free(dbuf);
381 } else {
382 MSG(" ERROR: [a_Capi_dpi_send_cmd] No open connection found\n");
383 }
384 }
385
386 return 0;
387 }
388
619 int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
620 int flags)
621 {
622 return a_Capi_dpi_send_data(url, bw, cmd, strlen(cmd), server, flags);
623 }
624
625 /*
626 * Remove a client from the cache client queue.
627 * force = also abort the CCC if this is the last client.
628 */
629 void a_Capi_stop_client(int Key, int force)
630 {
631 CacheClient_t *Client;
632
633 _MSG("a_Capi_stop_client: force=%d\n", force);
634
635 Client = a_Cache_client_get_if_unique(Key);
636 if (Client && (force || Client->BufSize == 0)) {
637 /* remove empty entries too */
638 a_Capi_conn_abort_by_url(Client->Url);
639 }
640 a_Cache_stop_client(Key);
641 }
389642
390643 /*
391644 * CCC function for the CAPI module
393646 void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
394647 void *Data1, void *Data2)
395648 {
396 dpi_conn_t *conn;
397
398 a_Chain_debug_msg("a_Capi_ccc", Op, Branch, Dir);
649 capi_conn_t *conn;
650
651 dReturn_if_fail( a_Chain_check("a_Capi_ccc", Op, Branch, Dir, Info) );
399652
400653 if (Branch == 1) {
401654 if (Dir == BCK) {
402655 /* Command sending branch */
403656 switch (Op) {
404657 case OpStart:
658 /* Data1 = conn; Data2 = {Web | server} */
405659 conn = Data1;
406 Capi_dpi_conn_ref(conn);
407 Info->LocalKey = Data1;
408 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 1);
409 a_Chain_bcb(OpStart, Info, Data2, NULL);
660 Capi_conn_ref(conn);
661 Info->LocalKey = conn;
662 conn->InfoSend = Info;
663 if (strcmp(conn->server, "http") == 0) {
664 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Http_ccc, 1, 1);
665 a_Chain_bcb(OpStart, Info, Data2, NULL);
666 } else {
667 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 1, 1);
668 a_Chain_bcb(OpStart, Info, Data2, NULL);
669 }
410670 break;
411671 case OpSend:
672 /* Data1 = dbuf */
412673 a_Chain_bcb(OpSend, Info, Data1, NULL);
413674 break;
414675 case OpEnd:
676 conn = Info->LocalKey;
677 conn->InfoSend = NULL;
415678 a_Chain_bcb(OpEnd, Info, NULL, NULL);
416 Capi_dpi_conn_unref(Info->LocalKey);
417 g_free(Info);
418 break;
419 case OpStop:
679 Capi_conn_unref(conn);
680 dFree(Info);
681 break;
420682 case OpAbort:
421 MSG(" Not implemented\n");
422 break;
423 }
424 } else { /* FWD */
683 conn = Info->LocalKey;
684 conn->InfoSend = NULL;
685 a_Chain_bcb(OpAbort, Info, NULL, NULL);
686 Capi_conn_unref(conn);
687 dFree(Info);
688 break;
689 default:
690 MSG_WARN("Unused CCC\n");
691 break;
692 }
693 } else { /* 1 FWD */
425694 /* Command sending branch (status) */
426695 switch (Op) {
427696 case OpSend:
428697 if (!Data2) {
429 g_warning("Capi.c: Opsend [1F] Data2 = NULL\n");
430 } else if (strcmp(Data2, "SockFD") == 0) {
431 /* start the receiving branch */
432 dpi_conn_t *conn = Info->LocalKey;
698 MSG_WARN("Capi.c: Opsend [1F] Data2 = NULL\n");
699 } else if (strcmp(Data2, "FD") == 0) {
700 conn = Info->LocalKey;
433701 conn->SockFD = *(int*)Data1;
434 a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(),Info->LocalKey, NULL);
702 /* communicate the FD through the answer branch */
703 a_Capi_ccc(OpSend, 2, BCK, conn->InfoRecv, &conn->SockFD, "FD");
435704 } else if (strcmp(Data2, "DpidOK") == 0) {
436 /* send the data inmediatly! */
437 Capi_dpi_conn_resume();
438 } else if (strcmp(Data2, "DpidEAGAIN") == 0) {
439 /* set a timeout function to retry later... */
440 if (dpi_conn_timeout_id == 0)
441 dpi_conn_timeout_id =
442 gtk_timeout_add(250, (GtkFunction) Capi_dpi_conn_timeout,
443 NULL);
444 }
445 break;
446 case OpStop:
705 /* resume pending dpi requests */
706 Capi_conn_resume();
707 }
708 break;
447709 case OpAbort:
448710 conn = Info->LocalKey;
449 if (Data1 && !strcmp(Data1, "ERR_dpid"))
450 a_Interface_msg(conn->bw, "ERROR: can't start dpid daemon!");
451 Capi_dpi_conn_unref(conn);
452 g_free(Info);
711 conn->InfoSend = NULL;
712 if (Data2) {
713 if (!strcmp(Data2, "DpidERROR")) {
714 a_UIcmd_set_msg(conn->bw,
715 "ERROR: can't start dpid daemon "
716 "(URL scheme = '%s')!",
717 conn->url ? URL_SCHEME(conn->url) : "");
718 } else if (!strcmp(Data2, "Both") && conn->InfoRecv) {
719 /* abort the other branch too */
720 a_Capi_ccc(OpAbort, 2, BCK, conn->InfoRecv, NULL, NULL);
721 }
722 }
723 /* if URL == expect-url */
724 a_Nav_cancel_expect_if_eq(conn->bw, conn->url);
725 /* finish conn */
726 Capi_conn_unref(conn);
727 dFree(Info);
728 break;
729 default:
730 MSG_WARN("Unused CCC\n");
453731 break;
454732 }
455733 }
456734
457735 } else if (Branch == 2) {
458736 if (Dir == BCK) {
459 /* Server listening branch (status) */
737 /* Answer branch */
460738 switch (Op) {
461739 case OpStart:
462 {
463 dpi_conn_t *conn = Data1;
464 Capi_dpi_conn_ref(conn);
465 Info->LocalKey = Data1;
740 /* Data1 = conn; Data2 = {"http" | "<dpi server name>"} */
741 conn = Data1;
742 Capi_conn_ref(conn);
743 Info->LocalKey = conn;
466744 conn->InfoRecv = Info;
467 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 3, 2);
468 a_Chain_bcb(OpStart, Info, &conn->SockFD, "SockFD");
469 break;
470 }
471 case OpStop:
745 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 2);
746 a_Chain_bcb(OpStart, Info, NULL, Data2);
747 break;
748 case OpSend:
749 /* Data1 = FD */
750 if (Data2 && strcmp(Data2, "FD") == 0) {
751 a_Chain_bcb(OpSend, Info, Data1, Data2);
752 }
753 break;
472754 case OpAbort:
473 Capi_dpi_conn_unref(Info->LocalKey);
474 MSG(" Not implemented\n");
475 break;
476 }
477 } else { /* FWD */
755 conn = Info->LocalKey;
756 conn->InfoRecv = NULL;
757 a_Chain_bcb(OpAbort, Info, NULL, NULL);
758 /* remove the cache entry for this URL */
759 a_Cache_entry_remove_by_url(conn->url);
760 a_Nav_cancel_expect_if_eq(conn->bw, conn->url);
761 Capi_conn_unref(conn);
762 dFree(Info);
763 break;
764 default:
765 MSG_WARN("Unused CCC\n");
766 break;
767 }
768 } else { /* 2 FWD */
478769 /* Server listening branch */
479770 switch (Op) {
480771 case OpSend:
481772 conn = Info->LocalKey;
482 if (conn->Flags & ABORTED ||
483 (conn->web && !a_Web_valid(conn->web))) {
484 /* there's no client for this transfer!*/
485 _MSG(" ** Capi 2F catched an invalid 'web' structure\n");
486 /* this flag is used just in case the same memory address
487 * is reused for a new 'web' and the test passes */
488 conn->Flags &= ABORTED;
489 /* Make the dpi module stop this transfer */
490 a_Chain_bcb(OpStop, Info, conn->url, NULL);
491
773 if (strcmp(Data2, "send_page_2eof") == 0) {
774 /* Data1 = dbuf */
775 DataBuf *dbuf = Data1;
776 a_Cache_process_dbuf(IORead, dbuf->Buf, dbuf->Size, conn->url);
492777 } else if (strcmp(Data2, "send_status_message") == 0) {
493 a_Interface_msg(conn->bw, "%s", Data1);
778 a_UIcmd_set_msg(conn->bw, "%s", Data1);
494779 } else if (strcmp(Data2, "chat") == 0) {
495 a_Interface_msg(conn->bw, "%s", Data1);
780 a_UIcmd_set_msg(conn->bw, "%s", Data1);
496781 a_Bookmarks_chat_add(NULL, NULL, Data1);
497782 } else if (strcmp(Data2, "dialog") == 0) {
498783 a_Dpiapi_dialog(conn->bw, conn->server, Data1);
499 } else if (strcmp(Data2, "start_send_page") == 0) {
500 /* start the pipe-writing chain */
501 a_Capi_ccc(OpStart, 3, BCK, a_Chain_new(), Info->LocalKey,NULL);
502 /* let the dpi know the reading end of the pipe */
503 a_Chain_bcb(OpSend, Info, &conn->DpiPipe[0], conn->web->url);
504 } else if (strcmp(Data2, "send_page_2eof") == 0) {
505 a_Capi_ccc(OpSend, 3, BCK, conn->InfoPipe, Data1, NULL);
506784 } else if (strcmp(Data2, "reload_request") == 0) {
507785 a_Nav_reload(conn->bw);
786 } else if (strcmp(Data2, "start_send_page") == 0) {
787 /* prepare the cache to receive the data stream for this URL
788 *
789 * a_Capi_open_url() already added a new cache entry,
790 * and a client for it.
791 */
508792 }
509793 break;
510794 case OpEnd:
511 {
512 dpi_conn_t *conn = Info->LocalKey;
513 a_Chain_del_link(Info, BCK);
795 conn = Info->LocalKey;
514796 conn->InfoRecv = NULL;
797
798 a_Cache_process_dbuf(IOClose, NULL, 0, conn->url);
799
515800 if (conn->InfoSend) {
516801 /* Propagate OpEnd to the sending branch too */
517802 a_Capi_ccc(OpEnd, 1, BCK, conn->InfoSend, NULL, NULL);
518803 }
519 if (conn->InfoPipe) {
520 /* Propagate OpEnd to the pipe branch too */
521 a_Capi_ccc(OpEnd, 3, BCK, conn->InfoPipe, NULL, NULL);
522 }
523 Capi_dpi_conn_unref(conn);
524 g_free(Info);
525 break;
526 }
527 case OpStop:
528 case OpAbort:
529 MSG(" Not implemented\n");
530 break;
531 }
532 }
533
534 } else if (Branch == 3) {
535 if (Dir == BCK) {
536 /* Pipe writing branch */
537 switch (Op) {
538 case OpStart:
539 {
540 dpi_conn_t *conn = Data1;
541 Info->LocalKey = Data1;
542 Capi_dpi_conn_ref(conn);
543 conn->InfoPipe = Info;
544 if (pipe(conn->DpiPipe)) {
545 MSG(" Error with pipe\n");
546 return;
547 }
548 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_IO_ccc, 3, 3);
549 a_Chain_bcb(OpStart, Info, &conn->DpiPipe[1], "SockFD");
550 break;
551 }
552 case OpSend:
553 a_Chain_bcb(OpSend, Info, Data1, NULL);
554 break;
555 case OpEnd:
556 a_Chain_bcb(OpEnd, Info, (void*)1, NULL);
557 Capi_dpi_conn_unref(Info->LocalKey);
558 g_free(Info);
559 break;
560 case OpStop:
561 case OpAbort:
562 Capi_dpi_conn_unref(Info->LocalKey);
563 MSG(" Not implemented\n");
564 break;
565 }
566 } else { /* FWD */
567 /* Pipe branch (status) */
568 }
569 }
570 }
804 Capi_conn_unref(conn);
805 dFree(Info);
806 break;
807 default:
808 MSG_WARN("Unused CCC\n");
809 break;
810 }
811 }
812 }
813 }
00 #ifndef __CAPI_H__
11 #define __CAPI_H__
22
3 #include <glib.h>
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7
48 #include "cache.h"
5 #include "web.h"
9 #include "web.hh"
10
11 /*
12 * Flag defines
13 */
14 #define CAPI_IsCached (0x1)
15 #define CAPI_IsEmpty (0x2)
16 #define CAPI_InProgress (0x4)
17 #define CAPI_Aborted (0x8)
18 #define CAPI_Completed (0x10)
619
720 /*
821 * Function prototypes
922 */
10 gint a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData);
11 gint a_Capi_get_buf(const DilloUrl *Url, gchar **PBuf, gint *BufSize);
12 gint a_Capi_dpi_send_cmd(DilloWeb *web, void *bw, char *cmd, char *server,
13 gint flags);
14 gint a_Capi_url_uses_dpi(gchar *url_str, gchar **server_ptr);
23 void a_Capi_init(void);
24 int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData);
25 int a_Capi_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
26 void a_Capi_unref_buf(const DilloUrl *Url);
27 const char *a_Capi_get_content_type(const DilloUrl *url);
28 const char *a_Capi_set_content_type(const DilloUrl *url, const char *ctype,
29 const char *from);
30 int a_Capi_get_flags(const DilloUrl *Url);
31 int a_Capi_get_flags_with_redirection(const DilloUrl *Url);
32 int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url);
33 int a_Capi_dpi_send_data(const DilloUrl *url, void *bw,
34 char *data, int data_sz, char *server, int flags);
35 int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
36 int flags);
37 void a_Capi_set_vsource_url(const DilloUrl *url);
38 void a_Capi_stop_client(int Key, int force);
39 void a_Capi_conn_abort_by_url(const DilloUrl *url);
1540
1641
42 #ifdef __cplusplus
43 }
44 #endif /* __cplusplus */
1745
1846 #endif /* __CAPI_H__ */
1947
22 * Concomitant control chain (CCC)
33 * Theory and code by Jorge Arellano Cid
44 *
5 * Copyright 2001, 2002 Jorge Arellano Cid <jcid@dillo.org>
5 * Copyright 2001-2007 Jorge Arellano Cid <jcid@dillo.org>
66 *
77 * This program is free software; you can redistribute it and/or modify
88 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
9 * the Free Software Foundation; either version 3 of the License, or
1010 * (at your option) any later version.
1111 */
1212
13 #include "msg.h"
1314 #include "chain.h"
15 #include "../dlib/dlib.h"
1416
1517 #define VERBOSE 0
1618
1719 /*
20 * Show debugging info
21 */
22 #if VERBOSE
23 static void Chain_debug_msg(char *FuncStr, int Op, int Branch, int Dir,
24 ChainLink *Info)
25 {
26 const char *StrOps[] = {"", "OpStart", "OpSend",
27 "OpStop", "OpEnd", "OpAbort"};
28 MSG("%-*s: %-*s [%d%s] Info=%p Flags=%d\n",
29 12, FuncStr, 7, StrOps[Op], Branch, (Dir == 1) ? "F" : "B",
30 Info, Info ? Info->Flags : -1);
31 }
32 #else
33 static void Chain_debug_msg(char *FuncStr, int Op, int Branch, int Dir,
34 ChainLink *Info) { }
35 #endif
36 /*
1837 * Create and initialize a new chain-link
1938 */
2039 ChainLink *a_Chain_new(void)
2140 {
22 return g_new0(ChainLink, 1);
41 return dNew0(ChainLink, 1);
2342 }
2443
2544 /*
3150 * => BtoA_branch: branch on which 'A' receives communications from 'B'
3251 */
3352 ChainLink *a_Chain_link_new(ChainLink *AInfo, ChainFunction_t AFunc,
34 gint Direction, ChainFunction_t BFunc,
35 gint AtoB_branch, gint BtoA_branch)
53 int Direction, ChainFunction_t BFunc,
54 int AtoB_branch, int BtoA_branch)
3655 {
3756 ChainLink *NewLink = a_Chain_new();
3857 ChainLink *OldLink = AInfo;
3958
40 if ( Direction == BCK ) {
59 if (Direction == BCK) {
4160 NewLink->Fcb = AFunc;
4261 NewLink->FcbInfo = AInfo;
4362 NewLink->FcbBranch = BtoA_branch;
5877 }
5978
6079 /*
61 * Destroy a link and update the referer.
62 * 'Direction' tells whether to delete the forward or backward link.
63 */
64 void a_Chain_del_link(ChainLink *Info, gint Direction)
65 {
66 ChainLink *DeadLink;
67
68 if ( Direction == FWD ) {
69 DeadLink = Info->FcbInfo;
80 * Unlink a previously used link.
81 * 'Direction' tells whether to unlink the forward or backward link.
82 */
83 void a_Chain_unlink(ChainLink *Info, int Direction)
84 {
85 if (Direction == FWD) {
7086 Info->Fcb = NULL;
7187 Info->FcbInfo = NULL;
7288 Info->FcbBranch = 0;
73 } else { /* BCK */
74 DeadLink = Info->BcbInfo;
89 } else { /* BCK */
7590 Info->Bcb = NULL;
7691 Info->BcbInfo = NULL;
7792 Info->BcbBranch = 0;
7893 }
79 g_free(DeadLink);
8094 }
8195
8296 /*
8397 * Issue the forward callback of the 'Info' link
84 */
85 gint a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2)
86 {
87 if ( Info->Fcb ) {
98 * Return value: 1 if OK, 0 if not operative.
99 */
100 int a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2)
101 {
102 int ret = 0;
103
104 if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
105 /* CCC is not operative */
106 } else if (Info->Fcb) {
107 /* flag the caller */
108 if (Op == OpEnd)
109 Info->Flags |= CCC_Ended;
110 else if (Op == OpAbort)
111 Info->Flags |= CCC_Aborted;
112
88113 Info->Fcb(Op, Info->FcbBranch, FWD, Info->FcbInfo, Data1, Data2);
89 return 1;
90 }
91 return 0;
114 ret = 1;
115 }
116 return ret;
92117 }
93118
94119 /*
95120 * Issue the backward callback of the 'Info' link
96 */
97 gint a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2)
98 {
99 if ( Info->Bcb ) {
121 * Return value: 1 if OK, 0 if not operative.
122 */
123 int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2)
124 {
125 int ret = 0;
126
127 if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
128 /* CCC is not operative */
129 } else if (Info->Bcb) {
130 /* flag the caller */
131 if (Op == OpEnd)
132 Info->Flags |= CCC_Ended;
133 else if (Op == OpAbort)
134 Info->Flags |= CCC_Aborted;
135
100136 Info->Bcb(Op, Info->BcbBranch, BCK, Info->BcbInfo, Data1, Data2);
101 return 1;
102 }
103 return 0;
137 ret = 1;
138 }
139 return ret;
140 }
141
142 /*
143 * Issue the backward callback of the 'Info' link and then the
144 * forward callback (used for OpAbort and OpStop).
145 * Return value: 1 if OK, 0 if not operative.
146 */
147 int a_Chain_bfcb(int Op, ChainLink *Info, void *Data1, void *Data2)
148 {
149 int ret;
150
151 ret = a_Chain_bcb(Op, Info, Data1, Data2);
152 if (ret == 1) {
153 /* we need to clear the flag to reuse this 'Info' ChainLink */
154 if (Op == OpEnd)
155 Info->Flags &= ~CCC_Ended;
156 else if (Op == OpAbort)
157 Info->Flags &= ~CCC_Aborted;
158
159 ret = a_Chain_fcb(Op, Info, Data1, Data2);
160 }
161 return ret;
104162 }
105163
106164
107165 /*
108166 * Allocate and initialize a new DataBuf structure
109167 */
110 DataBuf *a_Chain_dbuf_new(void *buf, gint size, gint code)
111 {
112 DataBuf *dbuf = g_new(DataBuf, 1);
168 DataBuf *a_Chain_dbuf_new(void *buf, int size, int code)
169 {
170 DataBuf *dbuf = dNew(DataBuf, 1);
113171 dbuf->Buf = buf;
114172 dbuf->Size = size;
115173 dbuf->Code = code;
117175 }
118176
119177 /*
120 * Show some debugging info
121 */
122 void a_Chain_debug_msg(char *FuncStr, int Op, int Branch, int Dir)
123 {
124 #if VERBOSE
125 const gchar *StrOps[] = {"", "OpStart", "OpSend",
126 "OpStop", "OpEnd", "OpAbort"};
127 g_print("%-*s: %-*s [%d%s]\n",
128 12, FuncStr, 7, StrOps[Op], Branch, (Dir == 1) ? "F" : "B");
129 #endif
130 }
178 * Check whether the CCC is operative.
179 * Also used to hook debug information.
180 *
181 * Return value: 1 if ready to use, 0 if not operative.
182 */
183 int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir,
184 ChainLink *Info)
185 {
186 int ret = 0;
187
188 /* Show status information */
189 Chain_debug_msg(FuncStr, Op, Branch, Dir, Info);
190
191 if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
192 /* CCC is not operative */
193 MSG_WARN("CCC: call on already finished chain. Flags=%s%s\n",
194 Info->Flags & CCC_Ended ? "CCC_Ended " : "",
195 Info->Flags & CCC_Aborted ? "CCC_Aborted" : "");
196 } else {
197 ret = 1;
198 }
199 return ret;
200 }
201
55 * Theory and code by Jorge Arellano Cid <jcid@dillo.org>
66 */
77
8 #include <glib.h>
98
109 /*
1110 * Supported CCC operations
1615 #define OpEnd 4
1716 #define OpAbort 5
1817
18 /*
19 * CCC flags
20 */
21 #define CCC_Stopped (1 << 0)
22 #define CCC_Ended (1 << 1)
23 #define CCC_Aborted (1 << 2)
1924
2025 /*
2126 * Linking direction
3338 struct _ChainLink {
3439 void *LocalKey;
3540
41 int Flags;
42
3643 ChainLink *FcbInfo;
3744 ChainFunction_t Fcb;
38 gint FcbBranch;
45 int FcbBranch;
3946
4047 ChainLink *BcbInfo;
4148 ChainFunction_t Bcb;
42 gint BcbBranch;
49 int BcbBranch;
4350 };
4451
4552 /* A convenience data structure for passing data chunks between nodes */
4653 struct _DataBuf {
47 gchar *Buf;
48 gint Size;
49 gint Code;
54 char *Buf;
55 int Size;
56 int Code;
5057 };
5158
5259
5663 */
5764 ChainLink *a_Chain_new(void);
5865 ChainLink *a_Chain_link_new(ChainLink *AInfo, ChainFunction_t AFunc,
59 gint Direction, ChainFunction_t BFunc,
60 gint AtoB_branch, gint BtoA_branch);
61 void a_Chain_del_link(ChainLink *Info, gint Direction);
62 gint a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2);
63 gint a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2);
66 int Direction, ChainFunction_t BFunc,
67 int AtoB_branch, int BtoA_branch);
68 void a_Chain_unlink(ChainLink *Info, int Direction);
69 int a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2);
70 int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2);
71 int a_Chain_bfcb(int Op, ChainLink *Info, void *Data1, void *Data2);
72 int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir,
73 ChainLink *Info);
6474
65 DataBuf *a_Chain_dbuf_new(void *buf, gint size, gint code);
66 void a_Chain_debug_msg(char *FuncStr, int Op, int Branch, int Dir);
67
68 /*
69 * CCC functions of subscribing modules
70 */
71 void a_Cache_ccc(int Op, int Branch, int Dir, ChainLink *Info,
72 void *Data1, void *Data2);
75 DataBuf *a_Chain_dbuf_new(void *buf, int size, int code);
7376
7477 #endif /* __CHAIN_H__ */
0 /*
1 * File: colors.c
2 *
3 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
011 #include <string.h>
112 #include <stdlib.h>
2 #include <glib.h>
313 #include <ctype.h>
414 #include "colors.h"
515
6 #define DEBUG_LEVEL 5
7 #include "debug.h"
816 #include "msg.h"
917
1018 /*
1826
1927 static const struct key {
2028 char *key;
21 gint32 val;
29 int32_t val;
2230 } color_keyword [] = {
2331 #ifdef EXTENDED_COLOR
2432 { "aliceblue", 0xf0f8ff},
195203 #define NCOLORS (sizeof(color_keyword) / sizeof(struct key))
196204
197205 /*
198 * Parse a color in hex (RRGGBB)
206 * Parse a color in hex (RRGGBB) or (RGB)
199207 *
200208 * Return Value:
201209 * parsed color if successful (err = 0),
202210 * default_color on error (err = 1).
203211 */
204 static gint32 Color_parse_hex (const char *s, gint32 default_color, gint *err)
205 {
206 gint32 ret_color;
207 gchar *tail;
212 static int32_t Color_parse_hex (const char *s, int32_t default_color, int *err)
213 {
214 int32_t ret_color;
215 char *tail;
208216
209217 *err = 1;
210218 ret_color = strtol(s, &tail, 16);
211 if ( tail - s == 6 )
219 if (tail - s == 6)
212220 *err = 0;
213 else
221 else if (tail - s == 3) { /* #RGB as allowed by CSS */
222 *err = 0;
223 ret_color = ((ret_color & 0xf00) << 12) | ((ret_color & 0xf00) << 8) |
224 ((ret_color & 0x0f0) << 8) | ((ret_color & 0x0f0) << 4) |
225 ((ret_color & 0x00f) << 4) | ((ret_color & 0x00f) << 0);
226 } else
214227 ret_color = default_color;
215228
216229 return ret_color;
225238 * Parsed color if successful,
226239 * default_color (+ error code) on error.
227240 */
228 gint32 a_Color_parse (const char *subtag, gint32 default_color, gint *err)
241 int32_t a_Color_parse (const char *subtag, int32_t default_color, int *err)
229242 {
230243 const char *cp;
231 gint32 ret_color;
232 gint ret, low, mid, high, st = 1;
244 int32_t ret_color;
245 int ret, low, mid, high, st = 1;
233246
234247 /* skip leading spaces */
235 for (cp = subtag; isspace(*cp); cp++);
248 for (cp = subtag; dIsspace(*cp); cp++);
236249
237250 ret_color = default_color;
238251 if (*cp == '#') {
248261 high = NCOLORS - 1;
249262 while (low <= high) {
250263 mid = (low + high) / 2;
251 if ((ret = g_strcasecmp(cp, color_keyword[mid].key)) < 0)
264 if ((ret = dStrcasecmp(cp, color_keyword[mid].key)) < 0)
252265 high = mid - 1;
253266 else if (ret > 0)
254267 low = mid + 1;
266279 }
267280 }
268281
269 DEBUG_MSG(3, "subtag: %s\n", subtag);
270 DEBUG_MSG(3, "color : %X\n", ret_color);
282 _MSG("subtag: %s\n", subtag);
283 _MSG("color : %X\n", ret_color);
271284
272285 *err = st;
273286 return ret_color;
311324 * if candidate has good contrast with C_txt, C_lnk and C_bg -> candidate
312325 * else another color (from the internal list)
313326 */
314 gint32 a_Color_vc(gint32 candidate, gint32 C_txt, gint32 C_lnk, gint32 C_bg)
327 int32_t a_Color_vc(int32_t candidate,
328 int32_t C_txt, int32_t C_lnk, int32_t C_bg)
315329 {
316330 /* candidate purple darkcyan darkmagenta olive */
317 static gint32 v[] = {0x000000, 0x800080, 0x008b8b, 0x8b008b, 0x808000,
331 static int32_t v[] = {0x000000, 0x800080, 0x008b8b, 0x8b008b, 0x808000,
318332 /* darkred coral black */
319333 0x8b0000, 0xff7f50, 0x000000};
320 gint v_size = sizeof(v) / sizeof(v[0]);
321 gint i, max_i, score, max_score, d_bg, d_txt, d_lnk;
334 int v_size = sizeof(v) / sizeof(v[0]);
335 int i, max_i, score, max_score, d_bg, d_txt, d_lnk;
322336
323337
324338 /* set candidate in the list */
00 #ifndef __COLORS_H__
11 #define __COLORS_H__
2
3 #include "config.h"
4 #include <stdint.h>
25
36 #ifdef __cplusplus
47 extern "C" {
58 #endif /* __cplusplus */
69
7 gint32 a_Color_parse (const char *subtag, gint32 default_color, gint *err);
8 gint32 a_Color_vc(gint32 candidate, gint32 c1, gint32 c2, gint32 c3);
10 int32_t a_Color_parse (const char *subtag, int32_t default_color, int *err);
11 int32_t a_Color_vc(int32_t candidate, int32_t c1, int32_t c2, int32_t c3);
912
1013 #ifdef __cplusplus
1114 }
+0
-413
src/commands.c less more
0 /*
1 * File: commands.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 Sammy Mannaert <nstalkie@tvd.be>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #include <gtk/gtk.h>
13 #include <stdio.h> /* for sprintf */
14 #include <sys/time.h> /* for gettimeofday (testing gorp only) */
15 #include <unistd.h>
16 #include <string.h> /* for strcat() */
17
18 #include "msg.h"
19 #include "bookmark.h"
20 #include "interface.h"
21 #include "history.h"
22 #include "nav.h"
23 #include "misc.h"
24 #include "commands.h"
25 #include "prefs.h"
26 #include "menu.h"
27 #include "capi.h"
28 #include "selection.h"
29
30 /* FILE MENU */
31
32 /*
33 * Create a new browser window
34 */
35 void a_Commands_new_callback(GtkWidget *widget, gpointer client_data)
36 {
37 BrowserWindow *nbw, *bw = (BrowserWindow *)client_data;
38
39 nbw = a_Interface_browser_window_new(bw->main_window->allocation.width,
40 bw->main_window->allocation.height, 0);
41 /* focus the location bar */
42 gtk_widget_grab_focus(nbw->location);
43 }
44
45 /*
46 * Create and show the "Open file" dialog
47 */
48 void a_Commands_openfile_callback(GtkWidget *widget, gpointer client_data)
49 {
50 BrowserWindow *bw = (BrowserWindow *) client_data;
51
52 a_Interface_openfile_dialog(bw);
53 }
54
55 /*
56 * Create and show the "Open Url" dialog window
57 */
58 void a_Commands_openurl_callback(GtkWidget *widget, gpointer client_data)
59 {
60 BrowserWindow *bw = (BrowserWindow *) client_data;
61 a_Interface_open_dialog(widget, bw);
62 }
63
64 /*
65 * ?
66 */
67 void a_Commands_prefs_callback(GtkWidget *widget, gpointer client_data)
68 {
69 }
70
71 /*
72 * Close browser window, and exit dillo if it's the last one.
73 */
74 void a_Commands_close_callback(GtkWidget *widget, gpointer client_data)
75 {
76 BrowserWindow *bw = (BrowserWindow *)client_data;
77 gtk_widget_destroy(bw->main_window);
78 }
79
80 /*
81 * Free memory and quit dillo
82 */
83 void a_Commands_exit_callback(GtkWidget *widget, gpointer client_data)
84 {
85 a_Interface_quit_all();
86 }
87
88
89 /* PAGE MENU */
90
91 /*
92 * Show current page's source code.
93 */
94 void a_Commands_viewsource_callback (GtkWidget *widget, gpointer client_data)
95 {
96 BrowserWindow *bw = (BrowserWindow *)client_data;
97 gchar *buf;
98 gint buf_size;
99
100 /* Get page source data */
101 a_Capi_get_buf(a_History_get_url(NAV_TOP(bw)), &buf, &buf_size);
102 /* Show it */
103 a_Interface_text_window (&bw->viewsource_window,
104 "View Source", "view_source",
105 buf, buf_size,
106 530, 500);
107 }
108
109 /*
110 * Show the detected HTML errors in current page.
111 */
112 void a_Commands_view_page_bugs_callback (GtkWidget *widget,
113 gpointer client_data)
114 {
115 DilloHtmlLB *html_lb = client_data;
116
117 a_Interface_text_window (&html_lb->bw->pagebugs_window,
118 "Detected HTML errors", "page_bugs",
119 html_lb->page_bugs->str,
120 html_lb->page_bugs->len,
121 530, 500);
122 }
123
124 /*
125 * ?
126 */
127 void a_Commands_selectall_callback(GtkWidget *widget, gpointer client_data)
128 {
129 }
130
131 /*
132 * Create and show the "Find Text" dialog window
133 */
134 void a_Commands_findtext_callback(GtkWidget *widget, gpointer client_data)
135 {
136 BrowserWindow *bw = (BrowserWindow *) client_data;
137
138 a_Interface_findtext_dialog( bw );
139 }
140
141 /*
142 * Print the page!
143 * ('cat page.html | html2ps | lpr -Pcool' Why bother? I think providing
144 * such an option in a configurable way should cut it --Jcid)
145 */
146 void a_Commands_print_callback(GtkWidget *widget, gpointer client_data)
147 {
148 }
149
150
151 /* TOOLBAR MENU */
152
153 /*
154 * Abort all active connections for this page
155 * (Downloads MUST keep flowing)
156 */
157 void a_Commands_stop_callback(GtkWidget *widget, gpointer client_data)
158 {
159 BrowserWindow *bw = client_data;
160 a_Nav_cancel_expect(bw);
161 a_Interface_stop(bw);
162 a_Interface_set_button_sens(bw);
163 a_Interface_msg(bw, "Stopped");
164 }
165
166 /*
167 * Back to previous page
168 */
169 void a_Commands_back_callback(GtkWidget *widget, gpointer client_data)
170 {
171 BrowserWindow *bw = (BrowserWindow *) client_data;
172
173 a_Nav_back(bw);
174 }
175
176 /*
177 * Creates the menu for the forward navigation button.
178 */
179 GtkMenu *a_Commands_forw_button_menu_creator_callback(GtkExtButton *button,
180 gpointer client_data)
181 {
182 BrowserWindow *bw = client_data;
183
184 if (bw->menu_popup.over_forw)
185 gtk_widget_destroy(bw->menu_popup.over_forw);
186 bw->menu_popup.over_forw = a_Menu_popup_history_new(bw, +1);
187 return GTK_MENU(bw->menu_popup.over_forw);
188 }
189
190 /*
191 * Creates the menu for the backward navigation button.
192 */
193 GtkMenu *a_Commands_back_button_menu_creator_callback(GtkExtButton *button,
194 gpointer client_data)
195 {
196 BrowserWindow *bw = client_data;
197
198 if (bw->menu_popup.over_back)
199 gtk_widget_destroy(bw->menu_popup.over_back);
200 bw->menu_popup.over_back = a_Menu_popup_history_new(bw, -1);
201 return GTK_MENU(bw->menu_popup.over_back);
202 }
203
204 /*
205 * Handler for menu entries in the history menus. This one is called, when
206 * the link should be opened in the same window.
207 */
208 void a_Commands_history_callback_same_bw(GtkWidget *widget,
209 gpointer client_data)
210 {
211 BrowserWindow *bw = client_data;
212
213 a_Nav_jump_callback(widget, bw, 0);
214 }
215
216 /*
217 * Handler for menu entries in the history menus. This one is called, when
218 * the link should be opened in a new window.
219 */
220 void a_Commands_history_callback_new_bw(GtkWidget *widget,
221 gpointer client_data)
222 {
223 BrowserWindow *bw = client_data;
224
225 a_Nav_jump_callback(widget, bw, 1);
226 }
227
228 /*
229 * Go to the next page in the history buffer
230 */
231 void a_Commands_forw_callback(GtkWidget *widget, gpointer client_data)
232 {
233 BrowserWindow *bw = (BrowserWindow *) client_data;
234
235 a_Nav_forw(bw);
236 }
237
238 /*
239 * Start the reload process
240 */
241 void a_Commands_reload_callback(GtkWidget *widget, gpointer client_data)
242 {
243 BrowserWindow *bw = (BrowserWindow *) client_data;
244
245 a_Nav_reload(bw);
246 }
247
248 /*
249 * Go home!
250 */
251 void a_Commands_home_callback(GtkWidget *widget, gpointer client_data)
252 {
253 BrowserWindow *bw = (BrowserWindow *) client_data;
254
255 a_Nav_home(bw);
256 }
257
258 /*
259 * Bring up the save page dialog
260 */
261 void a_Commands_save_callback(GtkWidget *widget, gpointer client_data)
262 {
263 BrowserWindow *bw = (BrowserWindow *) client_data;
264
265 a_Interface_save_dialog(widget, bw);
266 }
267
268 /*
269 * Bring up the save link dialog
270 */
271 void a_Commands_save_link_callback(GtkWidget *widget, gpointer client_data)
272 {
273 BrowserWindow *bw = (BrowserWindow *) client_data;
274
275 a_Interface_save_link_dialog(widget, bw);
276 }
277
278
279 /* BOOKMARKS MENU */
280
281 /*
282 * Add a bookmark to the current bookmark widget.
283 */
284 void a_Commands_addbm_callback(GtkWidget *widget, gpointer client_data)
285 {
286 a_Bookmarks_add(widget, client_data);
287 }
288
289 /*
290 * Show the bookmarks-file as rendered html
291 */
292 void a_Commands_viewbm_callback(GtkWidget *widget, gpointer client_data)
293 {
294 BrowserWindow *bw = (BrowserWindow *) client_data;
295
296 a_Bookmarks_show(bw);
297 }
298
299
300 /* HELP MENU */
301
302 /*
303 * This one was intended as a link to help-info on the web site, but
304 * currently points to the home page --Jcid
305 */
306 void a_Commands_helphome_callback(GtkWidget *widget, gpointer client_data)
307 {
308 BrowserWindow *bw = (BrowserWindow *) client_data;
309 DilloUrl *url = a_Url_new(DILLO_HOME, NULL, 0, 0, 0);
310
311 a_Nav_push(bw, url);
312 a_Url_free(url);
313 }
314
315
316 /* RIGHT BUTTON POP-UP MENU */
317
318 /*
319 * Open link in browser-window
320 */
321 void a_Commands_open_link_callback(GtkWidget *widget, gpointer client_data)
322 {
323 BrowserWindow *bw = (BrowserWindow *)client_data;
324
325 a_Nav_push(bw, a_Menu_popup_get_url(bw));
326 }
327
328 /*
329 * Open link in another browser-window
330 */
331 void a_Commands_open_link_nw_callback(GtkWidget *widget, gpointer client_data)
332 {
333 BrowserWindow *bw = (BrowserWindow *)client_data;
334 gint width, height;
335 BrowserWindow *newbw;
336
337 gdk_window_get_size (bw->main_window->window, &width, &height);
338 newbw = a_Interface_browser_window_new(width, height, 0);
339 a_Nav_push(newbw, a_Menu_popup_get_url(bw));
340 }
341
342 /*
343 * Called when the user wants the popup's URL for pasting.
344 */
345 void a_Commands_select_popup_url_callback(GtkWidget *widget, gpointer data)
346 {
347 BrowserWindow *bw = (BrowserWindow *)data;
348 a_Selection_set_selection(widget, URL_STR(a_Menu_popup_get_url(bw)));
349 }
350
351
352 /* BUG METER POP-UP MENU */
353
354 /*
355 * Helper function for validation.
356 */
357 static void Commands_ob_validate_page(BrowserWindow *bw, const gchar *fmt)
358 {
359 GString *gstr = g_string_sized_new(128);
360 gchar *o_url, *e_url, *n_url;
361 DilloUrl *url;
362
363 /* prepare the validation request URI */
364 o_url = URL_STR(a_History_get_url(NAV_TOP(bw)));
365 e_url = a_Misc_escape_chars(o_url, ":/?");
366 n_url = e_url ? e_url : o_url;
367 g_string_sprintf(gstr, fmt, n_url);
368 if (n_url != o_url)
369 g_free(n_url);
370 url = a_Url_new(gstr->str, NULL, 0, 0, 0);
371 g_string_free(gstr, TRUE);
372
373 a_Nav_push(bw, url);
374 a_Url_free(url);
375 }
376
377 /*
378 * Validate current page with the W3C's validator.
379 */
380 void a_Commands_ob_w3c_callback(GtkWidget *widget, gpointer client_data)
381 {
382 BrowserWindow *bw = (BrowserWindow *)client_data;
383 const gchar *w3c_fmt="http://validator.w3.org/check?uri=%s";
384
385 Commands_ob_validate_page(bw, w3c_fmt);
386 }
387
388 /*
389 * Validate current page with the WDG's validator.
390 */
391 void a_Commands_ob_wdg_callback(GtkWidget *widget, gpointer client_data)
392 {
393 BrowserWindow *bw = (BrowserWindow *)client_data;
394 const gchar *wdg_fmt=
395 "http://www.htmlhelp.org/cgi-bin/validate.cgi?url=%s&warnings=yes";
396
397 Commands_ob_validate_page(bw, wdg_fmt);
398 }
399
400 /*
401 * Request the info page for bug meter.
402 */
403 void a_Commands_ob_info_callback(GtkWidget *widget, gpointer client_data)
404 {
405 BrowserWindow *bw = (BrowserWindow *)client_data;
406 const gchar *info_str = "http://www.dillo.org/help/bug_meter.html";
407 DilloUrl *url = a_Url_new(info_str, NULL, 0, 0, 0);
408
409 a_Nav_push(bw, url);
410 a_Url_free(url);
411 }
412
+0
-50
src/commands.h less more
0 #ifndef __COMMANDS_H__
1 #define __COMMANDS_H__
2
3 #include "gtk_ext_button.h"
4
5 void a_Commands_new_callback(GtkWidget * widget, gpointer client_data);
6 void a_Commands_openfile_callback (GtkWidget *widget, gpointer client_data);
7 void a_Commands_openurl_callback (GtkWidget *widget, gpointer client_data);
8 void a_Commands_prefs_callback(GtkWidget * widget, gpointer client_data);
9 void a_Commands_close_callback(GtkWidget * widget, gpointer client_data);
10 void a_Commands_exit_callback (GtkWidget *widget, gpointer client_data);
11
12 void a_Commands_viewsource_callback (GtkWidget *widget, gpointer client_data);
13 void a_Commands_view_page_bugs_callback (GtkWidget *button,
14 gpointer client_data);
15 void a_Commands_selectall_callback (GtkWidget *widget, gpointer client_data);
16 void a_Commands_findtext_callback (GtkWidget *widget, gpointer client_data);
17 void a_Commands_print_callback (GtkWidget *widget, gpointer client_data);
18
19 GtkMenu *a_Commands_forw_button_menu_creator_callback(GtkExtButton *button,
20 gpointer client_data);
21 GtkMenu *a_Commands_back_button_menu_creator_callback(GtkExtButton *button,
22 gpointer client_data);
23 void a_Commands_history_callback_same_bw(GtkWidget *widget,
24 gpointer client_data);
25 void a_Commands_history_callback_new_bw(GtkWidget *widget,
26 gpointer client_data);
27
28 void a_Commands_back_callback (GtkWidget *widget, gpointer client_data);
29 void a_Commands_forw_callback (GtkWidget *widget, gpointer client_data);
30 void a_Commands_reload_callback (GtkWidget *widget, gpointer client_data);
31 void a_Commands_stop_callback (GtkWidget *widget, gpointer client_data);
32 void a_Commands_home_callback (GtkWidget *widget, gpointer client_data);
33 void a_Commands_save_callback (GtkWidget *widget, gpointer client_data);
34 void a_Commands_save_link_callback (GtkWidget *widget, gpointer client_data);
35
36 void a_Commands_addbm_callback (GtkWidget *widget, gpointer client_data);
37 void a_Commands_viewbm_callback (GtkWidget *widget, gpointer client_data);
38
39 void a_Commands_helphome_callback (GtkWidget *widget, gpointer client_data);
40
41 void a_Commands_open_link_callback(GtkWidget *widget, gpointer client_data);
42 void a_Commands_open_link_nw_callback(GtkWidget *widget, gpointer client_data);
43 void a_Commands_select_popup_url_callback(GtkWidget *widget, gpointer data);
44
45 void a_Commands_ob_w3c_callback(GtkWidget *widget, gpointer client_data);
46 void a_Commands_ob_wdg_callback(GtkWidget *widget, gpointer client_data);
47 void a_Commands_ob_info_callback(GtkWidget *widget, gpointer client_data);
48
49 #endif /* __COMMANDS_H__ */
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
8 * the Free Software Foundation; either version 3 of the License, or
99 * (at your option) any later version.
1010 */
1111
12 /* Handling of cookies takes place here.
13 * This implementation aims to follow RFC 2965:
14 * http://www.cis.ohio-state.edu/cs/Services/rfc/rfc-text/rfc2965.txt
15 */
16
17 #define DEBUG_LEVEL 10
18 #include "debug.h"
19
12 /* Handling of cookies takes place here. */
13
14 #include "msg.h"
2015
2116 #ifdef DISABLE_COOKIES
2217
2520 */
2621 void a_Cookies_init(void)
2722 {
28 DEBUG_MSG(10, "Cookies: absolutely disabled at compilation time.\n");
23 MSG("Cookies: absolutely disabled at compilation time.\n");
2924 }
3025
3126 #else
3732 #include <unistd.h>
3833 #include <stdlib.h>
3934 #include <stdio.h>
40 #include <time.h> /* for time() and time_t */
4135 #include <ctype.h>
42
43 #include "msg.h"
36 #include <errno.h>
37
4438 #include "IO/Url.h"
45 #include "misc.h"
4639 #include "list.h"
4740 #include "cookies.h"
4841 #include "capi.h"
49 #include "dpiapi.h"
5042 #include "../dpip/dpip.h"
5143
5244
7062 static int num_ccontrol_max = 1;
7163 static CookieControlAction default_action = COOKIE_DENY;
7264
73 static gboolean disabled;
74
75 static FILE *Cookies_fopen(const char *file, gchar *init_str);
65 static bool_t disabled;
66
67 static FILE *Cookies_fopen(const char *file, char *init_str);
7668 static CookieControlAction Cookies_control_check(const DilloUrl *url);
7769 static CookieControlAction Cookies_control_check_domain(const char *domain);
7870 static int Cookie_control_init(void);
8173 * Return a file pointer. If the file doesn't exist, try to create it,
8274 * with the optional 'init_str' as its content.
8375 */
84 static FILE *Cookies_fopen(const char *filename, gchar *init_str)
76 static FILE *Cookies_fopen(const char *filename, char *init_str)
8577 {
8678 FILE *F_in;
87 int fd;
79 int fd, rc;
8880
8981 if ((F_in = fopen(filename, "r")) == NULL) {
9082 /* Create the file */
9183 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
9284 if (fd != -1) {
93 if (init_str)
94 write(fd, init_str, strlen(init_str));
85 if (init_str) {
86 rc = write(fd, init_str, strlen(init_str));
87 if (rc == -1) {
88 MSG("Cookies: Could not write initial string to file %s: %s\n",
89 filename, dStrerror(errno));
90 }
91 }
9592 close(fd);
9693
97 DEBUG_MSG(10, "Cookies: Created file: %s\n", filename);
98 F_in = Cookies_fopen(filename, NULL);
94 MSG("Cookies: Created file: %s\n", filename);
95 F_in = fopen(filename, "r");
9996 } else {
100 DEBUG_MSG(10, "Cookies: Could not create file: %s!\n", filename);
97 MSG("Cookies: Could not create file: %s!\n", filename);
10198 }
10299 }
103100
104 /* set close on exec */
105 fcntl(fileno(F_in), F_SETFD, FD_CLOEXEC | fcntl(fileno(F_in), F_GETFD));
101 if (F_in) {
102 /* set close on exec */
103 fcntl(fileno(F_in), F_SETFD, FD_CLOEXEC | fcntl(fileno(F_in), F_GETFD));
104 }
106105
107106 return F_in;
108107 }
118117
119118 /* Read and parse the cookie control file (cookiesrc) */
120119 if (Cookie_control_init() != 0) {
121 DEBUG_MSG(10, "Disabling cookies.\n");
120 MSG("Disabling cookies.\n");
122121 return;
123122 }
124123
125 DEBUG_MSG(10, "Enabling cookies as from cookiesrc...\n");
124 MSG("Enabling cookies as from cookiesrc...\n");
126125 disabled = FALSE;
127126 }
128127
136135 /*
137136 * Set the value corresponding to the cookie string
138137 */
139 void a_Cookies_set(GList *cookie_strings, const DilloUrl *set_url)
138 void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url,
139 const char *date)
140140 {
141141 CookieControlAction action;
142 char *cmd, *path, numstr[16];
142 char *cmd, *cookie_string, *dpip_tag;
143 const char *path;
144 int i;
143145
144146 if (disabled)
145147 return;
146148
147149 action = Cookies_control_check(set_url);
148150 if (action == COOKIE_DENY) {
149 DEBUG_MSG(5, "Cookies: denied SET for %s\n", URL_HOST_(set_url));
151 _MSG("Cookies: denied SET for %s\n", URL_HOST_(set_url));
150152 return;
151153 }
152154
153 while (cookie_strings != NULL ) {
154 char *cookie_string = cookie_strings->data;
155 path = (char *) URL_PATH_(set_url);
156
157 g_snprintf(numstr, 16, "%d", URL_PORT(set_url));
158 cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s port=%s",
159 "set_cookie", cookie_string, URL_HOST_(set_url),
160 path ? path : "/", numstr);
161
162 DEBUG_MSG(5, "Cookies.c: a_Cookies_set \n\t \"%s\" \n",cmd );
163 a_Capi_dpi_send_cmd(NULL, NULL, cmd, "cookies", 1);
164 g_free(cmd);
165
166 cookie_strings = g_list_next(cookie_strings);
167 }
168
169 }
170
171 /*
172 * Return a string that contains all relevant cookies as headers.
173 */
174 char *a_Cookies_get(const DilloUrl *request_url)
175 {
176 char *cmd, *path, *dpip_tag, *cookie, numstr[16];
155 for (i = 0; (cookie_string = dList_nth_data(cookie_strings, i)); ++i) {
156 path = URL_PATH_(set_url);
157 if (date)
158 cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s date=%s",
159 "set_cookie", cookie_string,
160 URL_HOST_(set_url), path ? path : "/", date);
161 else
162 cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s",
163 "set_cookie", cookie_string,
164 URL_HOST_(set_url), path ? path : "/");
165
166 _MSG("Cookies.c: a_Cookies_set \n\t \"%s\" \n",cmd );
167 /* This call is commented because it doesn't guarantee the order
168 * in which cookies are set and got. (It may deadlock too) */
169 //a_Capi_dpi_send_cmd(NULL, NULL, cmd, "cookies", 1);
170
171 dpip_tag = a_Dpi_send_blocking_cmd("cookies", cmd);
172 _MSG("a_Cookies_set: dpip_tag = {%s}\n", dpip_tag);
173 dFree(dpip_tag);
174 dFree(cmd);
175 }
176 }
177
178 /*
179 * Return a string containing cookie data for an HTTP query.
180 */
181 char *a_Cookies_get_query(const DilloUrl *request_url)
182 {
183 char *cmd, *dpip_tag, *query;
184 const char *path;
177185 CookieControlAction action;
178186
179 cookie = g_strdup("");
180
181187 if (disabled)
182 return cookie;
188 return dStrdup("");
183189
184190 action = Cookies_control_check(request_url);
185191 if (action == COOKIE_DENY) {
186 DEBUG_MSG(5, "Cookies: denied GET for %s\n", URL_HOST_(request_url));
187 return cookie;
188 }
189 path = (char *) URL_PATH_(request_url);
190
191 g_snprintf(numstr, 16, "%d", URL_PORT(request_url));
192 cmd = a_Dpip_build_cmd("cmd=%s scheme=%s host=%s path=%s port=%s",
192 _MSG("Cookies: denied GET for %s\n", URL_HOST_(request_url));
193 return dStrdup("");
194 }
195 path = URL_PATH_(request_url);
196
197 cmd = a_Dpip_build_cmd("cmd=%s scheme=%s host=%s path=%s",
193198 "get_cookie", URL_SCHEME(request_url),
194 URL_HOST(request_url), path ? path : "/", numstr);
199 URL_HOST(request_url), path ? path : "/");
195200
196201 /* Get the answer from cookies.dpi */
202 _MSG("cookies.c: a_Dpi_send_blocking_cmd cmd = {%s}\n", cmd);
197203 dpip_tag = a_Dpi_send_blocking_cmd("cookies", cmd);
198 g_free(cmd);
204 _MSG("cookies.c: after a_Dpi_send_blocking_cmd resp={%s}\n", dpip_tag);
205 dFree(cmd);
199206
200207 if (dpip_tag != NULL) {
201 cookie = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cookie");
202 g_free(dpip_tag);
203 }
204 return cookie;
208 query = a_Dpip_get_attr(dpip_tag, "cookie");
209 dFree(dpip_tag);
210 } else {
211 query = dStrdup("");
212 }
213 return query;
205214 }
206215
207216 /* -------------------------------------------------------------
220229 {
221230 CookieControl cc;
222231 FILE *stream;
223 char *filename;
232 char *filename, *rc;
224233 char line[LINE_MAXLEN];
225234 char domain[LINE_MAXLEN];
226235 char rule[LINE_MAXLEN];
227 int i, j;
228 gboolean enabled = FALSE;
236 bool_t enabled = FALSE;
229237
230238 /* Get a file pointer */
231 filename = a_Misc_prepend_user_home(".dillo/cookiesrc");
239 filename = dStrconcat(dGethomedir(), "/.dillo/cookiesrc", NULL);
232240 stream = Cookies_fopen(filename, "DEFAULT DENY\n");
233 g_free(filename);
241 dFree(filename);
234242
235243 if (!stream)
236244 return 2;
238246 /* Get all lines in the file */
239247 while (!feof(stream)) {
240248 line[0] = '\0';
241 fgets(line, LINE_MAXLEN, stream);
249 rc = fgets(line, LINE_MAXLEN, stream);
250 if (!rc && ferror(stream)) {
251 MSG("Cookies1: Error while reading rule from cookiesrc: %s\n",
252 dStrerror(errno));
253 return 2; /* bail out */
254 }
242255
243256 /* Remove leading and trailing whitespaces */
244 g_strstrip(line);
257 dStrstrip(line);
245258
246259 if (line[0] != '\0' && line[0] != '#') {
247 i = 0;
248 j = 0;
260 int i = 0, j = 0;
249261
250262 /* Get the domain */
251 while (!isspace(line[i]))
263 while (line[i] != '\0' && !dIsspace(line[i]))
252264 domain[j++] = line[i++];
253265 domain[j] = '\0';
254266
255267 /* Skip past whitespaces */
256 i++;
257 while (isspace(line[i]))
268 while (dIsspace(line[i]))
258269 i++;
259270
260271 /* Get the rule */
261272 j = 0;
262 while (line[i] != '\0' && !isspace(line[i]))
273 while (line[i] != '\0' && !dIsspace(line[i]))
263274 rule[j++] = line[i++];
264275 rule[j] = '\0';
265276
266 if (g_strcasecmp(rule, "ACCEPT") == 0)
277 if (dStrcasecmp(rule, "ACCEPT") == 0)
267278 cc.action = COOKIE_ACCEPT;
268 else if (g_strcasecmp(rule, "ACCEPT_SESSION") == 0)
279 else if (dStrcasecmp(rule, "ACCEPT_SESSION") == 0)
269280 cc.action = COOKIE_ACCEPT_SESSION;
270 else if (g_strcasecmp(rule, "DENY") == 0)
281 else if (dStrcasecmp(rule, "DENY") == 0)
271282 cc.action = COOKIE_DENY;
272283 else {
273284 MSG("Cookies: rule '%s' for domain '%s' is not recognised.\n",
275286 continue;
276287 }
277288
278 cc.domain = g_strdup(domain);
279 if (g_strcasecmp(cc.domain, "DEFAULT") == 0) {
289 cc.domain = dStrdup(domain);
290 if (dStrcasecmp(cc.domain, "DEFAULT") == 0) {
280291 /* Set the default action */
281292 default_action = cc.action;
282 g_free(cc.domain);
293 dFree(cc.domain);
283294 } else {
295 int i;
296 uint_t len = strlen(cc.domain);
297
298 /* Insert into list such that longest rules come first. */
284299 a_List_add(ccontrol, num_ccontrol, num_ccontrol_max);
285 ccontrol[num_ccontrol++] = cc;
300 for (i = num_ccontrol++;
301 i > 0 && (len > strlen(ccontrol[i-1].domain));
302 i--) {
303 ccontrol[i] = ccontrol[i-1];
304 }
305 ccontrol[i] = cc;
286306 }
287307
288308 if (cc.action != COOKIE_DENY)
296316 }
297317
298318 /*
299 * Check the rules for an appropriate action for this domain
319 * Check the rules for an appropriate action for this domain.
320 * The rules are ordered by domain length, with longest first, so the
321 * first match is the most specific.
300322 */
301323 static CookieControlAction Cookies_control_check_domain(const char *domain)
302324 {
306328 if (ccontrol[i].domain[0] == '.') {
307329 diff = strlen(domain) - strlen(ccontrol[i].domain);
308330 if (diff >= 0) {
309 if (g_strcasecmp(domain + diff, ccontrol[i].domain) != 0)
331 if (dStrcasecmp(domain + diff, ccontrol[i].domain) != 0)
310332 continue;
311333 } else {
312334 continue;
313335 }
314336 } else {
315 if (g_strcasecmp(domain, ccontrol[i].domain) != 0)
337 if (dStrcasecmp(domain, ccontrol[i].domain) != 0)
316338 continue;
317339 }
318340
00 #ifndef __COOKIES_H__
11 #define __COOKIES_H__
22
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7
38 #ifdef DISABLE_COOKIES
4 # define a_Cookies_get(url) g_strdup("")
9 # define a_Cookies_get_query(url) dStrdup("")
10 # define a_Cookies_set() ;
11 # define a_Cookies_init() ;
512 # define a_Cookies_freeall() ;
6 void a_Cookies_init( void );
713 #else
8 char *a_Cookies_get(const DilloUrl *request_url);
9 void a_Cookies_set(GList *cookie_string, const DilloUrl *set_url);
14 char *a_Cookies_get_query(const DilloUrl *request_url);
15 void a_Cookies_set(Dlist *cookie_string, const DilloUrl *set_url,
16 const char *server_date);
1017 void a_Cookies_init( void );
1118 void a_Cookies_freeall( void );
1219 #endif
1320
21
22 #ifdef __cplusplus
23 }
24 #endif /* __cplusplus */
1425 #endif /* !__COOKIES_H__ */
0 /*
1 * File: css.cc
2 *
3 * Copyright 2008-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <stdio.h>
12 #include "../dlib/dlib.h"
13 #include "misc.h"
14 #include "msg.h"
15 #include "html_common.hh"
16 #include "css.hh"
17 #include "cssparser.hh"
18
19 using namespace dw::core::style;
20
21 void CssProperty::print () {
22 fprintf (stderr, "%s - %d\n",
23 CssParser::propertyNameString((CssPropertyName)name),
24 (int)value.intVal);
25 }
26
27 CssPropertyList::CssPropertyList (const CssPropertyList &p, bool deep) :
28 lout::misc::SimpleVector <CssProperty> (p)
29 {
30 refCount = 0;
31 if (deep) {
32 for (int i = 0; i < size (); i++) {
33 CssProperty *p = getRef(i);
34 switch (p->type) {
35 case CSS_TYPE_STRING:
36 case CSS_TYPE_SYMBOL:
37 p->value.strVal = dStrdup (p->value.strVal);
38 break;
39 default:
40 break;
41 }
42 }
43 ownerOfStrings = true;
44 } else {
45 ownerOfStrings = false;
46 }
47 };
48
49 CssPropertyList::~CssPropertyList () {
50 if (ownerOfStrings)
51 for (int i = 0; i < size (); i++)
52 getRef (i)->free ();
53 }
54
55 /**
56 * \brief Set property to a given name and type.
57 */
58 void CssPropertyList::set (CssPropertyName name, CssValueType type,
59 CssPropertyValue value) {
60 CssProperty *prop;
61
62 for (int i = 0; i < size (); i++) {
63 prop = getRef (i);
64
65 if (prop->name == name) {
66 if (ownerOfStrings)
67 prop->free ();
68 prop->type = type;
69 prop->value = value;
70 return;
71 }
72 }
73
74 increase ();
75 prop = getRef (size () - 1);
76 prop->name = name;
77 prop->type = type;
78 prop->value = value;
79 }
80
81 /**
82 * \brief Merge properties into argument property list.
83 */
84 void CssPropertyList::apply (CssPropertyList *props) {
85 for (int i = 0; i < size (); i++)
86 props->set ((CssPropertyName) getRef (i)->name,
87 (CssValueType) getRef (i)->type,
88 getRef (i)->value);
89 }
90
91 void CssPropertyList::print () {
92 for (int i = 0; i < size (); i++)
93 getRef (i)->print ();
94 }
95
96 CssSelector::CssSelector () {
97 struct CombinatorAndSelector *cs;
98
99 refCount = 0;
100 selectorList = new lout::misc::SimpleVector
101 <struct CombinatorAndSelector> (1);
102 selectorList->increase ();
103 cs = selectorList->getRef (selectorList->size () - 1);
104
105 cs->notMatchingBefore = -1;
106 cs->selector = new CssSimpleSelector ();
107 };
108
109 CssSelector::~CssSelector () {
110 for (int i = selectorList->size () - 1; i >= 0; i--)
111 delete selectorList->getRef (i)->selector;
112 delete selectorList;
113 }
114
115 /**
116 * \brief Return whether selector matches at a given node in the document tree.
117 */
118 bool CssSelector::match (Doctree *docTree, const DoctreeNode *node) {
119 CssSimpleSelector *sel;
120 Combinator comb = CHILD;
121 int *notMatchingBefore;
122 const DoctreeNode *n;
123
124 for (int i = selectorList->size () - 1; i >= 0; i--) {
125 struct CombinatorAndSelector *cs = selectorList->getRef (i);
126
127 sel = cs->selector;
128 notMatchingBefore = &cs->notMatchingBefore;
129
130 if (node == NULL)
131 return false;
132
133 switch (comb) {
134 case CHILD:
135 if (!sel->match (node))
136 return false;
137 break;
138 case DESCENDANT:
139 n = node;
140
141 while (true) {
142 if (node == NULL || node->num <= *notMatchingBefore) {
143 *notMatchingBefore = n->num;
144 return false;
145 }
146
147 if (sel->match (node))
148 break;
149
150 node = docTree->parent (node);
151 }
152 break;
153 default:
154 return false; // \todo implement other combinators
155 }
156
157 comb = cs->combinator;
158 node = docTree->parent (node);
159 }
160
161 return true;
162 }
163
164 void CssSelector::addSimpleSelector (Combinator c) {
165 struct CombinatorAndSelector *cs;
166
167 selectorList->increase ();
168 cs = selectorList->getRef (selectorList->size () - 1);
169
170 cs->combinator = c;
171 cs->notMatchingBefore = -1;
172 cs->selector = new CssSimpleSelector ();
173 }
174
175 /**
176 * \brief Return the specificity of the selector.
177 *
178 * The specificity of a CSS selector is defined in
179 * http://www.w3.org/TR/CSS21/cascade.html#specificity
180 */
181 int CssSelector::specificity () {
182 int spec = 0;
183
184 for (int i = 0; i < selectorList->size (); i++)
185 spec += selectorList->getRef (i)->selector->specificity ();
186
187 return spec;
188 }
189
190 void CssSelector::print () {
191 for (int i = 0; i < selectorList->size (); i++) {
192 selectorList->getRef (i)->selector->print ();
193
194 if (i < selectorList->size () - 1) {
195 switch (selectorList->getRef (i + 1)->combinator) {
196 case CHILD:
197 fprintf (stderr, "> ");
198 break;
199 case DESCENDANT:
200 fprintf (stderr, "\" \" ");
201 break;
202 default:
203 fprintf (stderr, "? ");
204 break;
205 }
206 }
207 }
208
209 fprintf (stderr, "\n");
210 }
211
212 CssSimpleSelector::CssSimpleSelector () {
213 element = ELEMENT_ANY;
214 klass = NULL;
215 id = NULL;
216 pseudo = NULL;
217 }
218
219 CssSimpleSelector::~CssSimpleSelector () {
220 if (klass) {
221 for (int i = 0; i < klass->size (); i++)
222 dFree (klass->get (i));
223 delete klass;
224 }
225 dFree (id);
226 dFree (pseudo);
227 }
228
229 void CssSimpleSelector::setSelect (SelectType t, const char *v) {
230 switch (t) {
231 case SELECT_CLASS:
232 if (klass == NULL)
233 klass = new lout::misc::SimpleVector <char *> (1);
234 klass->increase ();
235 klass->set (klass->size () - 1, dStrdup (v));
236 break;
237 case SELECT_PSEUDO_CLASS:
238 if (pseudo == NULL)
239 pseudo = dStrdup (v);
240 break;
241 case SELECT_ID:
242 if (id == NULL)
243 id = dStrdup (v);
244 break;
245 default:
246 break;
247 }
248 }
249
250 /**
251 * \brief Return whether simple selector matches at a given node of
252 * the document tree.
253 */
254 bool CssSimpleSelector::match (const DoctreeNode *n) {
255 if (element != ELEMENT_ANY && element != n->element)
256 return false;
257 if (pseudo != NULL &&
258 (n->pseudo == NULL || dStrcasecmp (pseudo, n->pseudo) != 0))
259 return false;
260 if (id != NULL && (n->id == NULL || dStrcasecmp (id, n->id) != 0))
261 return false;
262 if (klass != NULL) {
263 for (int i = 0; i < klass->size (); i++) {
264 bool found = false;
265 if (n->klass != NULL) {
266 for (int j = 0; j < n->klass->size (); j++) {
267 if (dStrcasecmp (klass->get(i), n->klass->get(j)) == 0) {
268 found = true;
269 break;
270 }
271 }
272 }
273 if (! found)
274 return false;
275 }
276 }
277
278 return true;
279 }
280
281 /**
282 * \brief Return the specificity of the simple selector.
283 *
284 * The result is used in CssSelector::specificity ().
285 */
286 int CssSimpleSelector::specificity () {
287 int spec = 0;
288
289 if (id)
290 spec += 1 << 20;
291 if (klass)
292 spec += klass->size() << 10;
293 if (pseudo)
294 spec += 1 << 10;
295 if (element != ELEMENT_ANY)
296 spec += 1;
297
298 return spec;
299 }
300
301 void CssSimpleSelector::print () {
302 fprintf (stderr, "Element %d, pseudo %s, id %s ",
303 element, pseudo, id);
304 if (klass != NULL) {
305 fprintf (stderr, "class ");
306 for (int i = 0; i < klass->size (); i++)
307 fprintf (stderr, ".%s", klass->get (i));
308 }
309 }
310
311 CssRule::CssRule (CssSelector *selector, CssPropertyList *props, int pos) {
312 assert (selector->size () > 0);
313
314 this->selector = selector;
315 this->selector->ref ();
316 this->props = props;
317 this->props->ref ();
318 this->pos = pos;
319 spec = selector->specificity ();
320 };
321
322 CssRule::~CssRule () {
323 selector->unref ();
324 props->unref ();
325 };
326
327 void CssRule::apply (CssPropertyList *props,
328 Doctree *docTree, const DoctreeNode *node) {
329 if (selector->match (docTree, node))
330 this->props->apply (props);
331 }
332
333 void CssRule::print () {
334 selector->print ();
335 props->print ();
336 }
337
338 /*
339 * \brief Insert rule with increasing specificity.
340 *
341 * If two rules have the same specificity, the one that was added later
342 * will be added behind the others.
343 * This gives later added rules more weight.
344 */
345 void CssStyleSheet::RuleList::insert (CssRule *rule) {
346 increase ();
347 int i = size () - 1;
348
349 while (i > 0 && rule->specificity () < get (i - 1)->specificity ()) {
350 *getRef (i) = get (i - 1);
351 i--;
352 }
353
354 *getRef (i) = rule;
355 }
356
357 CssStyleSheet::CssStyleSheet () {
358 for (int i = 0; i < ntags; i++)
359 elementTable[i] = new RuleList ();
360
361 idTable = new RuleMap ();
362 classTable = new RuleMap ();
363 anyTable = new RuleList ();
364 }
365
366 CssStyleSheet::~CssStyleSheet () {
367 for (int i = 0; i < ntags; i++)
368 delete elementTable[i];
369 delete idTable;
370 delete classTable;
371 delete anyTable;
372 }
373
374 /**
375 * \brief Insert a rule into CssStyleSheet.
376 *
377 * To improve matching performance the rules are organized into
378 * rule lists based on the topmost simple selector of their selector.
379 */
380 void CssStyleSheet::addRule (CssRule *rule) {
381 CssSimpleSelector *top = rule->selector->top ();
382 RuleList *ruleList = NULL;
383 lout::object::ConstString *string;
384
385 if (top->getId ()) {
386 string = new lout::object::ConstString (top->getId ());
387 ruleList = idTable->get (string);
388 if (ruleList == NULL) {
389 ruleList = new RuleList ();
390 idTable->put (string, ruleList);
391 } else {
392 delete string;
393 }
394 } else if (top->getClass () && top->getClass ()->size () > 0) {
395 string = new lout::object::ConstString (top->getClass ()->get (0));
396 ruleList = classTable->get (string);
397 if (ruleList == NULL) {
398 ruleList = new RuleList;
399 classTable->put (string, ruleList);
400 } else {
401 delete string;
402 }
403 } else if (top->getElement () >= 0 && top->getElement () < ntags) {
404 ruleList = elementTable[top->getElement ()];
405 } else if (top->getElement () == CssSimpleSelector::ELEMENT_ANY) {
406 ruleList = anyTable;
407 }
408
409 if (ruleList) {
410 ruleList->insert (rule);
411 } else {
412 assert (top->getElement () == CssSimpleSelector::ELEMENT_NONE);
413 delete rule;
414 }
415 }
416
417 /**
418 * \brief Apply a stylesheet to a property list.
419 *
420 * The properties are set as defined by the rules in the stylesheet that
421 * match at the given node in the document tree.
422 */
423 void CssStyleSheet::apply (CssPropertyList *props,
424 Doctree *docTree, const DoctreeNode *node) {
425 static const int maxLists = 32;
426 RuleList *ruleList[maxLists];
427 int numLists = 0, index[maxLists] = {0};
428
429 if (node->id) {
430 lout::object::ConstString idString (node->id);
431
432 ruleList[numLists] = idTable->get (&idString);
433 if (ruleList[numLists])
434 numLists++;
435 }
436
437 if (node->klass) {
438 for (int i = 0; i < node->klass->size (); i++) {
439 if (i >= maxLists - 4) {
440 MSG_WARN("Maximum number of classes per element exceeded.\n");
441 break;
442 }
443
444 lout::object::ConstString classString (node->klass->get (i));
445
446 ruleList[numLists] = classTable->get (&classString);
447 if (ruleList[numLists])
448 numLists++;
449 }
450 }
451
452 ruleList[numLists] = elementTable[node->element];
453 if (ruleList[numLists])
454 numLists++;
455
456 ruleList[numLists] = anyTable;
457 if (ruleList[numLists])
458 numLists++;
459
460 // Apply potentially matching rules from ruleList[0-numLists] with
461 // ascending specificity.
462 // If specificity is equal, rules are applied in order of appearance.
463 // Each ruleList is sorted already.
464 while (true) {
465 int minSpec = 1 << 30;
466 int minPos = 1 << 30;
467 int minSpecIndex = -1;
468
469 for (int i = 0; i < numLists; i++) {
470 if (ruleList[i] && ruleList[i]->size () > index[i] &&
471 (ruleList[i]->get(index[i])->specificity () < minSpec ||
472 (ruleList[i]->get(index[i])->specificity () == minSpec &&
473 ruleList[i]->get(index[i])->position () < minPos))) {
474
475 minSpec = ruleList[i]->get(index[i])->specificity ();
476 minPos = ruleList[i]->get(index[i])->position ();
477 minSpecIndex = i;
478 }
479 }
480
481 if (minSpecIndex >= 0) {
482 ruleList[minSpecIndex]->get (index[minSpecIndex])->apply
483 (props, docTree, node);
484 index[minSpecIndex]++;
485 } else {
486 break;
487 }
488 }
489 }
490
491 CssStyleSheet *CssContext::userAgentStyle;
492 CssStyleSheet *CssContext::userStyle;
493 CssStyleSheet *CssContext::userImportantStyle;
494
495 CssContext::CssContext () {
496 pos = 0;
497
498 for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++)
499 sheet[o] = NULL;
500
501 if (userAgentStyle == NULL) {
502 userAgentStyle = new CssStyleSheet ();
503 userStyle = new CssStyleSheet ();
504 userImportantStyle = new CssStyleSheet ();
505
506 sheet[CSS_PRIMARY_USER_AGENT] = userAgentStyle;
507 sheet[CSS_PRIMARY_USER] = userStyle;
508 sheet[CSS_PRIMARY_USER_IMPORTANT] = userImportantStyle;
509
510 buildUserAgentStyle ();
511 buildUserStyle ();
512 }
513
514 sheet[CSS_PRIMARY_USER_AGENT] = userAgentStyle;
515 sheet[CSS_PRIMARY_USER] = userStyle;
516 sheet[CSS_PRIMARY_USER_IMPORTANT] = userImportantStyle;
517 }
518
519 CssContext::~CssContext () {
520 for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++)
521 if (sheet[o] != userAgentStyle && sheet[o] != userStyle &&
522 sheet[o] != userImportantStyle)
523 delete sheet[o];
524 }
525
526 /**
527 * \brief Apply a CSS context to a property list.
528 *
529 * The stylesheets in the context are applied one after the other
530 * in the ordering defined by CSS 2.1.
531 * Stylesheets that are applied later can overwrite properties set
532 * by previous stylesheets.
533 * This allows e.g. user styles to overwrite author styles.
534 */
535 void CssContext::apply (CssPropertyList *props, Doctree *docTree,
536 DoctreeNode *node,
537 CssPropertyList *tagStyle, CssPropertyList *tagStyleImportant,
538 CssPropertyList *nonCssHints) {
539 if (sheet[CSS_PRIMARY_USER_AGENT])
540 sheet[CSS_PRIMARY_USER_AGENT]->apply (props, docTree, node);
541
542 if (sheet[CSS_PRIMARY_USER])
543 sheet[CSS_PRIMARY_USER]->apply (props, docTree, node);
544
545 if (nonCssHints)
546 nonCssHints->apply (props);
547
548 if (sheet[CSS_PRIMARY_AUTHOR])
549 sheet[CSS_PRIMARY_AUTHOR]->apply (props, docTree, node);
550
551 if (tagStyle)
552 tagStyle->apply (props);
553
554 if (sheet[CSS_PRIMARY_AUTHOR_IMPORTANT])
555 sheet[CSS_PRIMARY_AUTHOR_IMPORTANT]->apply (props, docTree, node);
556
557 if (tagStyleImportant)
558 tagStyleImportant->apply (props);
559
560 if (sheet[CSS_PRIMARY_USER_IMPORTANT])
561 sheet[CSS_PRIMARY_USER_IMPORTANT]->apply (props, docTree, node);
562 }
563
564 void CssContext::addRule (CssSelector *sel, CssPropertyList *props,
565 CssPrimaryOrder order) {
566
567 if (props->size () > 0) {
568 CssRule *rule = new CssRule (sel, props, pos++);
569
570 if (sheet[order] == NULL)
571 sheet[order] = new CssStyleSheet ();
572
573 sheet[order]->addRule (rule);
574 }
575 }
576
577 /**
578 * \brief Create the user agent style.
579 *
580 * The user agent style defines how dillo renders HTML in the absence of
581 * author or user styles.
582 */
583 void CssContext::buildUserAgentStyle () {
584 const char *cssBuf =
585 "body {margin: 5px}"
586 "big {font-size: 1.17em}"
587 "blockquote, dd {margin-left: 40px; margin-right: 40px}"
588 "center {text-align: center}"
589 "dt {font-weight: bolder}"
590 ":link {color: blue; text-decoration: underline; cursor: pointer}"
591 ":visited {color: #800080; text-decoration: underline; cursor: pointer}"
592 "h1, h2, h3, h4, h5, h6, b, strong {font-weight: bolder}"
593 "i, em, cite, address, var {font-style: italic}"
594 ":link img, :visited img {border: 1px solid}"
595 "frameset, ul, ol, dir {margin-left: 40px}"
596 "h1 {font-size: 2em; margin-top: .67em; margin-bottom: 0}"
597 "h2 {font-size: 1.5em; margin-top: .75em; margin-bottom: 0}"
598 "h3 {font-size: 1.17em; margin-top: .83em; margin-bottom: 0}"
599 "h4 {margin-top: 1.12em; margin-bottom: 0}"
600 "h5 {font-size: 0.83em; margin-top: 1.5em; margin-bottom: 0}"
601 "h6 {font-size: 0.75em; margin-top: 1.67em; margin-bottom: 0}"
602 "hr {width: 100%; border: 1px inset}"
603 "li {margin-top: 0.1em}"
604 "pre {white-space: pre}"
605 "ol {list-style-type: decimal}"
606 "ul {list-style-type: disc}"
607 "ul ul {list-style-type: circle}"
608 "ul ul ul {list-style-type: square}"
609 "ul ul ul ul {list-style-type: disc}"
610 "u {text-decoration: underline}"
611 "small, sub, sup {font-size: 0.83em}"
612 "sub {vertical-align: sub}"
613 "sup {vertical-align: super}"
614 "s, strike, del {text-decoration: line-through}"
615 "table {border-spacing: 2px}"
616 "td, th {padding: 2px}"
617 "thead, tbody, tfoot {vertical-align: middle}"
618 "th {font-weight: bolder; text-align: center}"
619 "code, tt, pre, samp, kbd {font-family: monospace}"
620 /* WORKAROUND: Reset font properties in tables as some
621 * some pages rely on it (e.g. gmail).
622 * http://developer.mozilla.org/En/Fixing_Table_Inheritance_in_Quirks_Mode
623 * has a detailed description of the issue.
624 */
625 "table, caption {font-size: medium; font-weight: normal}";
626
627 CssParser::parse (NULL, NULL, this, cssBuf, strlen (cssBuf),
628 CSS_ORIGIN_USER_AGENT);
629 }
630
631 void CssContext::buildUserStyle () {
632 Dstr *style;
633 char *filename = dStrconcat(dGethomedir(), "/.dillo/style.css", NULL);
634
635 if ((style = a_Misc_file2dstr(filename))) {
636 CssParser::parse (NULL,NULL,this,style->str, style->len,CSS_ORIGIN_USER);
637 dStr_free (style, 1);
638 }
639 dFree (filename);
640 }
0 #ifndef __CSS_HH__
1 #define __CSS_HH__
2
3 #include "dw/core.hh"
4 #include "doctree.hh"
5
6 /* Origin and weight. Used only internally.*/
7 typedef enum {
8 CSS_PRIMARY_USER_AGENT,
9 CSS_PRIMARY_USER,
10 CSS_PRIMARY_AUTHOR,
11 CSS_PRIMARY_AUTHOR_IMPORTANT,
12 CSS_PRIMARY_USER_IMPORTANT,
13 CSS_PRIMARY_LAST,
14 } CssPrimaryOrder;
15
16 typedef enum {
17 CSS_ORIGIN_USER_AGENT,
18 CSS_ORIGIN_USER,
19 CSS_ORIGIN_AUTHOR,
20 } CssOrigin;
21
22 typedef enum {
23 CSS_TYPE_INTEGER, /* This type is only used internally, for x-*
24 properties. */
25 CSS_TYPE_ENUM, /* Value is i, if represented by
26 enum_symbols[i]. */
27 CSS_TYPE_MULTI_ENUM, /* For all enum_symbols[i], 1 << i are
28 combined. */
29 CSS_TYPE_LENGTH_PERCENTAGE, /* <length> or <percentage>. Represented by
30 CssLength. */
31 CSS_TYPE_LENGTH, /* <length>, represented as CssLength.
32 Note: In some cases, CSS_TYPE_LENGTH is used
33 instead of CSS_TYPE_LENGTH_PERCENTAGE,
34 only because Dw cannot handle percentages
35 in this particular case (e.g.
36 'margin-*-width'). */
37 CSS_TYPE_SIGNED_LENGTH, /* As CSS_TYPE_LENGTH but may be negative. */
38 CSS_TYPE_LENGTH_PERCENTAGE_NUMBER, /* <length> or <percentage>, or <number> */
39 CSS_TYPE_COLOR, /* Represented as integer. */
40 CSS_TYPE_FONT_WEIGHT, /* this very special and only used by
41 'font-weight' */
42 CSS_TYPE_STRING, /* <string> */
43 CSS_TYPE_SYMBOL, /* Symbols, which are directly copied (as
44 opposed to CSS_TYPE_ENUM and
45 CSS_TYPE_MULTI_ENUM). Used for
46 'font-family'. */
47 CSS_TYPE_UNUSED /* Not yet used. Will itself get unused some
48 day. */
49 } CssValueType;
50
51 /*
52 * Lengths are represented as int in the following way:
53 *
54 * | <------ integer value ------> |
55 *
56 * +---+ - - - +---+---+- - - - - -+---+---+---+---+
57 * | integer part | type |
58 * +---+ - - - +---+---+- - - - - -+---+---+---+---+
59 * | integer part | decimal fraction | type |
60 * +---+ - - - +---+---+- - - - - -+---+---+---+---+
61 * n-1 15 14 3 2 1 0
62 *
63 * | <------ fixed point value ------> |
64 *
65 * where type is one of the CSS_LENGTH_TYPE_* values.
66 * CSS_LENGTH_TYPE_PX values are stored as
67 * 29 bit signed integer, all other types as fixed point values.
68 */
69
70 typedef int CssLength;
71
72 typedef enum {
73 CSS_LENGTH_TYPE_NONE,
74 CSS_LENGTH_TYPE_PX,
75 CSS_LENGTH_TYPE_MM, /* "cm", "in", "pt" and "pc" are converted into
76 millimeters. */
77 CSS_LENGTH_TYPE_EM,
78 CSS_LENGTH_TYPE_EX,
79 CSS_LENGTH_TYPE_PERCENTAGE,
80 CSS_LENGTH_TYPE_RELATIVE, /* This does not exist in CSS but
81 is used in HTML */
82 CSS_LENGTH_TYPE_AUTO /* This can be used as a simple value. */
83 } CssLengthType;
84
85 inline CssLength CSS_CREATE_LENGTH (float v, CssLengthType t) {
86 static const int CSS_LENGTH_FRAC_MAX = (1 << (32 - 15 - 1)) - 1;
87 static const int CSS_LENGTH_INT_MAX = (1 << (32 - 4)) - 1;
88 int iv;
89
90 switch (t) {
91 case CSS_LENGTH_TYPE_PX:
92 iv = lout::misc::roundInt(v);
93 if (iv > CSS_LENGTH_INT_MAX)
94 iv = CSS_LENGTH_INT_MAX;
95 else if (iv < -CSS_LENGTH_INT_MAX)
96 iv = -CSS_LENGTH_INT_MAX;
97 return iv << 3 | t;
98 case CSS_LENGTH_TYPE_NONE:
99 case CSS_LENGTH_TYPE_MM:
100 case CSS_LENGTH_TYPE_EM:
101 case CSS_LENGTH_TYPE_EX:
102 case CSS_LENGTH_TYPE_PERCENTAGE:
103 case CSS_LENGTH_TYPE_RELATIVE:
104 if (v > CSS_LENGTH_FRAC_MAX)
105 v = CSS_LENGTH_FRAC_MAX;
106 else if (v < -CSS_LENGTH_FRAC_MAX)
107 v = -CSS_LENGTH_FRAC_MAX;
108 return ((int) (v * (1 << 15)) & ~7 ) | t;
109 case CSS_LENGTH_TYPE_AUTO:
110 return t;
111 default:
112 assert(false);
113 return CSS_LENGTH_TYPE_AUTO;
114 }
115 }
116
117 inline CssLengthType CSS_LENGTH_TYPE (CssLength l) {
118 return (CssLengthType) (l & 7);
119 }
120
121 inline float CSS_LENGTH_VALUE (CssLength l) {
122 switch (CSS_LENGTH_TYPE(l)) {
123 case CSS_LENGTH_TYPE_PX:
124 return (float) (l >> 3);
125 case CSS_LENGTH_TYPE_NONE:
126 case CSS_LENGTH_TYPE_MM:
127 case CSS_LENGTH_TYPE_EM:
128 case CSS_LENGTH_TYPE_EX:
129 case CSS_LENGTH_TYPE_PERCENTAGE:
130 case CSS_LENGTH_TYPE_RELATIVE:
131 return ((float)(l & ~7)) / (1 << 15);
132 case CSS_LENGTH_TYPE_AUTO:
133 return 0.0;
134 default:
135 assert(false);
136 return 0.0;
137 }
138 }
139
140 typedef enum {
141 CSS_PROPERTY_BACKGROUND_ATTACHMENT,
142 CSS_PROPERTY_BACKGROUND_COLOR,
143 CSS_PROPERTY_BACKGROUND_IMAGE,
144 CSS_PROPERTY_BACKGROUND_POSITION,
145 CSS_PROPERTY_BACKGROUND_REPEAT,
146 CSS_PROPERTY_BORDER_BOTTOM_COLOR,
147 CSS_PROPERTY_BORDER_BOTTOM_STYLE,
148 CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
149 CSS_PROPERTY_BORDER_COLLAPSE,
150 CSS_PROPERTY_BORDER_LEFT_COLOR,
151 CSS_PROPERTY_BORDER_LEFT_STYLE,
152 CSS_PROPERTY_BORDER_LEFT_WIDTH,
153 CSS_PROPERTY_BORDER_RIGHT_COLOR,
154 CSS_PROPERTY_BORDER_RIGHT_STYLE,
155 CSS_PROPERTY_BORDER_RIGHT_WIDTH,
156 CSS_PROPERTY_BORDER_SPACING,
157 CSS_PROPERTY_BORDER_TOP_COLOR,
158 CSS_PROPERTY_BORDER_TOP_STYLE,
159 CSS_PROPERTY_BORDER_TOP_WIDTH,
160 CSS_PROPERTY_BOTTOM,
161 CSS_PROPERTY_CAPTION_SIDE,
162 CSS_PROPERTY_CLEAR,
163 CSS_PROPERTY_CLIP,
164 CSS_PROPERTY_COLOR,
165 CSS_PROPERTY_CONTENT,
166 CSS_PROPERTY_COUNTER_INCREMENT,
167 CSS_PROPERTY_COUNTER_RESET,
168 CSS_PROPERTY_CURSOR,
169 CSS_PROPERTY_DIRECTION,
170 CSS_PROPERTY_DISPLAY,
171 CSS_PROPERTY_EMPTY_CELLS,
172 CSS_PROPERTY_FLOAT,
173 CSS_PROPERTY_FONT_FAMILY,
174 CSS_PROPERTY_FONT_SIZE,
175 CSS_PROPERTY_FONT_SIZE_ADJUST,
176 CSS_PROPERTY_FONT_STRETCH,
177 CSS_PROPERTY_FONT_STYLE,
178 CSS_PROPERTY_FONT_VARIANT,
179 CSS_PROPERTY_FONT_WEIGHT,
180 CSS_PROPERTY_HEIGHT,
181 CSS_PROPERTY_LEFT,
182 CSS_PROPERTY_LETTER_SPACING,
183 CSS_PROPERTY_LINE_HEIGHT,
184 CSS_PROPERTY_LIST_STYLE_IMAGE,
185 CSS_PROPERTY_LIST_STYLE_POSITION,
186 CSS_PROPERTY_LIST_STYLE_TYPE,
187 CSS_PROPERTY_MARGIN_BOTTOM,
188 CSS_PROPERTY_MARGIN_LEFT,
189 CSS_PROPERTY_MARGIN_RIGHT,
190 CSS_PROPERTY_MARGIN_TOP,
191 CSS_PROPERTY_MARKER_OFFSET,
192 CSS_PROPERTY_MARKS,
193 CSS_PROPERTY_MAX_HEIGHT,
194 CSS_PROPERTY_MAX_WIDTH,
195 CSS_PROPERTY_MIN_HEIGHT,
196 CSS_PROPERTY_MIN_WIDTH,
197 CSS_PROPERTY_OUTLINE_COLOR,
198 CSS_PROPERTY_OUTLINE_STYLE,
199 CSS_PROPERTY_OUTLINE_WIDTH,
200 CSS_PROPERTY_OVERFLOW,
201 CSS_PROPERTY_PADDING_BOTTOM,
202 CSS_PROPERTY_PADDING_LEFT,
203 CSS_PROPERTY_PADDING_RIGHT,
204 CSS_PROPERTY_PADDING_TOP,
205 CSS_PROPERTY_POSITION,
206 CSS_PROPERTY_QUOTES,
207 CSS_PROPERTY_RIGHT,
208 CSS_PROPERTY_TEXT_ALIGN,
209 CSS_PROPERTY_TEXT_DECORATION,
210 CSS_PROPERTY_TEXT_INDENT,
211 CSS_PROPERTY_TEXT_SHADOW,
212 CSS_PROPERTY_TEXT_TRANSFORM,
213 CSS_PROPERTY_TOP,
214 CSS_PROPERTY_UNICODE_BIDI,
215 CSS_PROPERTY_VERTICAL_ALIGN,
216 CSS_PROPERTY_VISIBILITY,
217 CSS_PROPERTY_WHITE_SPACE,
218 CSS_PROPERTY_WIDTH,
219 CSS_PROPERTY_WORD_SPACING,
220 CSS_PROPERTY_Z_INDEX,
221 CSS_PROPERTY_X_LINK,
222 CSS_PROPERTY_X_COLSPAN,
223 CSS_PROPERTY_X_ROWSPAN,
224 PROPERTY_X_LINK,
225 PROPERTY_X_IMG,
226 PROPERTY_X_TOOLTIP,
227 CSS_PROPERTY_LAST
228 } CssPropertyName;
229
230 typedef union {
231 int32_t intVal;
232 char *strVal;
233 } CssPropertyValue;
234
235 typedef enum {
236 CSS_BORDER_WIDTH_THIN,
237 CSS_BORDER_WIDTH_MEDIUM,
238 CSS_BORDER_WIDTH_THICK,
239 } CssBorderWidthExtensions;
240
241 typedef enum {
242 CSS_FONT_WEIGHT_BOLD,
243 CSS_FONT_WEIGHT_BOLDER,
244 CSS_FONT_WEIGHT_LIGHT,
245 CSS_FONT_WEIGHT_LIGHTER,
246 CSS_FONT_WEIGHT_NORMAL,
247 } CssFontWeightExtensions;
248
249 typedef enum {
250 CSS_FONT_SIZE_LARGE,
251 CSS_FONT_SIZE_LARGER,
252 CSS_FONT_SIZE_MEDIUM,
253 CSS_FONT_SIZE_SMALL,
254 CSS_FONT_SIZE_SMALLER,
255 CSS_FONT_SIZE_XX_LARGE,
256 CSS_FONT_SIZE_XX_SMALL,
257 CSS_FONT_SIZE_X_LARGE,
258 CSS_FONT_SIZE_X_SMALL,
259 } CssFontSizeExtensions;
260
261 typedef enum {
262 CSS_LETTER_SPACING_NORMAL
263 } CssLetterSpacingExtensions;
264
265 typedef enum {
266 CSS_WORD_SPACING_NORMAL
267 } CssWordSpacingExtensions;
268
269
270 /**
271 * \brief This class holds a CSS property and value pair.
272 */
273 class CssProperty {
274 public:
275
276 short name;
277 short type;
278 CssPropertyValue value;
279
280 inline void free () {
281 switch (type) {
282 case CSS_TYPE_STRING:
283 case CSS_TYPE_SYMBOL:
284 dFree (value.strVal);
285 break;
286 default:
287 break;
288 }
289 }
290 void print ();
291 };
292
293 /**
294 * \brief A list of CssProperty objects.
295 */
296 class CssPropertyList : public lout::misc::SimpleVector <CssProperty> {
297 int refCount;
298 bool ownerOfStrings;
299
300 public:
301 inline CssPropertyList(bool ownerOfStrings = false) :
302 lout::misc::SimpleVector <CssProperty> (1) {
303 refCount = 0;
304 this->ownerOfStrings = ownerOfStrings;
305 };
306 CssPropertyList(const CssPropertyList &p, bool deep = false);
307 ~CssPropertyList ();
308
309 void set (CssPropertyName name, CssValueType type,
310 CssPropertyValue value);
311 void apply (CssPropertyList *props);
312 void print ();
313 inline void ref () { refCount++; }
314 inline void unref () { if (--refCount == 0) delete this; }
315 };
316
317 class CssSimpleSelector {
318 private:
319 int element;
320 char *pseudo, *id;
321 lout::misc::SimpleVector <char *> *klass;
322
323 public:
324 enum {
325 ELEMENT_NONE = -1,
326 ELEMENT_ANY = -2,
327 };
328
329 typedef enum {
330 SELECT_NONE,
331 SELECT_CLASS,
332 SELECT_PSEUDO_CLASS,
333 SELECT_ID,
334 } SelectType;
335
336 CssSimpleSelector ();
337 ~CssSimpleSelector ();
338 inline void setElement (int e) { element = e; };
339 void setSelect (SelectType t, const char *v);
340 inline lout::misc::SimpleVector <char *> *getClass () { return klass; };
341 inline const char *getPseudoClass () { return pseudo; };
342 inline const char *getId () { return id; };
343 inline int getElement () { return element; };
344 bool match (const DoctreeNode *node);
345 int specificity ();
346 void print ();
347 };
348
349 /**
350 * \brief CSS selector class.
351 *
352 * \todo Implement missing selector options.
353 */
354 class CssSelector {
355 public:
356 typedef enum {
357 DESCENDANT,
358 CHILD,
359 ADJACENT_SIBLING,
360 } Combinator;
361
362 private:
363 struct CombinatorAndSelector {
364 int notMatchingBefore; // used for optimizing CSS selector matching
365 Combinator combinator;
366 CssSimpleSelector *selector;
367 };
368
369 int refCount;
370 lout::misc::SimpleVector <struct CombinatorAndSelector> *selectorList;
371
372 public:
373 CssSelector ();
374 ~CssSelector ();
375 void addSimpleSelector (Combinator c);
376 inline CssSimpleSelector *top () {
377 return selectorList->getRef (selectorList->size () - 1)->selector;
378 };
379 inline int size () { return selectorList->size (); };
380 bool match (Doctree *dt, const DoctreeNode *node);
381 int specificity ();
382 void print ();
383 inline void ref () { refCount++; }
384 inline void unref () { if (--refCount == 0) delete this; }
385 };
386
387 /**
388 * \brief A CssSelector CssPropertyList pair.
389 *
390 * The CssPropertyList is applied if the CssSelector matches.
391 */
392 class CssRule {
393 private:
394 CssPropertyList *props;
395 int spec, pos;
396
397 public:
398 CssSelector *selector;
399
400 CssRule (CssSelector *selector, CssPropertyList *props, int pos);
401 ~CssRule ();
402
403 void apply (CssPropertyList *props,
404 Doctree *docTree, const DoctreeNode *node);
405 inline int specificity () { return spec; };
406 inline int position () { return pos; };
407 void print ();
408 };
409
410 /**
411 * \brief A list of CssRules.
412 *
413 * In apply () all matching rules are applied.
414 */
415 class CssStyleSheet {
416 private:
417 class RuleList : public lout::misc::SimpleVector <CssRule*>,
418 public lout::object::Object {
419 public:
420 RuleList () : lout::misc::SimpleVector <CssRule*> (1) {};
421 ~RuleList () {
422 for (int i = 0; i < size (); i++)
423 delete get (i);
424 };
425
426 void insert (CssRule *rule);
427 inline bool equals (lout::object::Object *other) {
428 return this == other;
429 };
430 inline int hashValue () { return (intptr_t) this; };
431 };
432
433 class RuleMap : public lout::container::typed::HashTable
434 <lout::object::ConstString, RuleList > {
435 public:
436 RuleMap () : lout::container::typed::HashTable
437 <lout::object::ConstString, RuleList > (true, true, 256) {};
438 };
439
440 static const int ntags = 90; // \todo replace 90
441 RuleList *elementTable[ntags];
442
443 RuleMap *idTable;
444 RuleMap *classTable;
445 RuleList *anyTable;
446
447 public:
448 CssStyleSheet();
449 ~CssStyleSheet();
450 void addRule (CssRule *rule);
451 void apply (CssPropertyList *props,
452 Doctree *docTree, const DoctreeNode *node);
453 };
454
455 /**
456 * \brief A set of CssStyleSheets.
457 */
458 class CssContext {
459 private:
460 static CssStyleSheet *userAgentStyle;
461 static CssStyleSheet *userStyle;
462 static CssStyleSheet *userImportantStyle;
463 CssStyleSheet *sheet[CSS_PRIMARY_USER_IMPORTANT + 1];
464 int pos;
465
466 void buildUserAgentStyle ();
467 void buildUserStyle ();
468
469 public:
470 CssContext ();
471 ~CssContext ();
472
473 void addRule (CssSelector *sel, CssPropertyList *props,
474 CssPrimaryOrder order);
475 void apply (CssPropertyList *props,
476 Doctree *docTree, DoctreeNode *node,
477 CssPropertyList *tagStyle, CssPropertyList *tagStyleImportant,
478 CssPropertyList *nonCssHints);
479 };
480
481 #endif
0 /*
1 * File: cssparser.cc
2 *
3 * Copyright 2004 Sebastian Geerken <sgeerken@dillo.org>
4 * Copyright 2008-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * This file is heavily based on the CSS parser of dillo-0.8.0-css-3 -
14 * a dillo1 based CSS prototype written by Sebastian Geerken.
15 */
16
17 #include <ctype.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20
21 #include "msg.h"
22 #include "colors.h"
23 #include "html_common.hh"
24 #include "css.hh"
25 #include "cssparser.hh"
26
27 using namespace dw::core::style;
28
29 #define DEBUG_MSG(A, B, ...) _MSG(B, __VA_ARGS__)
30 #define MSG_CSS(A, ...) MSG(A, __VA_ARGS__)
31 #define DEBUG_TOKEN_LEVEL 0
32 #define DEBUG_PARSE_LEVEL 0
33 #define DEBUG_CREATE_LEVEL 0
34
35 #define DEBUG_LEVEL 10
36
37 /* The last three ones are never parsed. */
38 #define CSS_NUM_INTERNAL_PROPERTIES 3
39 #define CSS_NUM_PARSED_PROPERTIES \
40 (CSS_PROPERTY_LAST - CSS_NUM_INTERNAL_PROPERTIES)
41
42
43 typedef struct {
44 const char *symbol;
45 const CssValueType type[3];
46 const char *const *enum_symbols;
47 } CssPropertyInfo;
48
49 static const char *const Css_border_collapse_enum_vals[] = {
50 "separate", "collapse", NULL
51 };
52
53 static const char *const Css_border_color_enum_vals[] = {
54 "transparent", NULL
55 };
56
57 static const char *const Css_border_style_enum_vals[] = {
58 "none", "hidden", "dotted", "dashed", "solid", "double", "groove",
59 "ridge", "inset", "outset", NULL
60 };
61
62 static const char *const Css_border_width_enum_vals[] = {
63 "thin", "medium", "thick", NULL
64 };
65
66 static const char *const Css_cursor_enum_vals[] = {
67 "crosshair", "default", "pointer", "move", "e-resize", "ne-resize",
68 "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize",
69 "w-resize", "text", "wait", "help", NULL
70 };
71
72 static const char *const Css_display_enum_vals[] = {
73 "block", "inline", "list-item", "none", "table", "table-row-group",
74 "table-header-group", "table-footer-group", "table-row",
75 "table-cell", NULL
76 };
77
78 static const char *const Css_font_size_enum_vals[] = {
79 "large", "larger", "medium", "small", "smaller", "xx-large", "xx-small",
80 "x-large", "x-small", NULL
81 };
82
83 static const char *const Css_font_style_enum_vals[] = {
84 "normal", "italic", "oblique", NULL
85 };
86
87 static const char *const Css_font_variant_enum_vals[] = {
88 "normal", "small-caps", NULL
89 };
90
91 static const char *const Css_font_weight_enum_vals[] = {
92 "bold", "bolder", "light", "lighter", "normal", NULL
93 };
94
95 static const char *const Css_letter_spacing_enum_vals[] = {
96 "normal", NULL
97 };
98
99 static const char *const Css_list_style_position_enum_vals[] = {
100 "inside", "outside", NULL
101 };
102
103 static const char *const Css_line_height_enum_vals[] = {
104 "normal", NULL
105 };
106
107 static const char *const Css_list_style_type_enum_vals[] = {
108 "disc", "circle", "square", "decimal", "decimal-leading-zero",
109 "lower-roman", "upper-roman", "lower-greek", "lower-alpha",
110 "lower-latin", "upper-alpha", "upper-latin", "hebrew", "armenian",
111 "georgian", "cjk-ideographic", "hiragana", "katakana", "hiragana-iroha",
112 "katakana-iroha", "none", NULL
113 };
114
115 static const char *const Css_text_align_enum_vals[] = {
116 "left", "right", "center", "justify", "string", NULL
117 };
118
119 static const char *const Css_text_decoration_enum_vals[] = {
120 "underline", "overline", "line-through", "blink", NULL
121 };
122
123 static const char *const Css_vertical_align_vals[] = {
124 "top", "bottom", "middle", "baseline", "sub", "super", "text-top",
125 "text-bottom", NULL
126 };
127
128 static const char *const Css_white_space_vals[] = {
129 "normal", "pre", "nowrap", "pre-wrap", "pre-line", NULL
130 };
131
132 static const char *const Css_word_spacing_enum_vals[] = {
133 "normal", NULL
134 };
135
136 const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
137 {"background-attachment", {CSS_TYPE_UNUSED}, NULL},
138 {"background-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
139 {"background-image", {CSS_TYPE_UNUSED}, NULL},
140 {"background-position", {CSS_TYPE_UNUSED}, NULL},
141 {"background-repeat", {CSS_TYPE_UNUSED}, NULL},
142 {"border-bottom-color", {CSS_TYPE_ENUM, CSS_TYPE_COLOR, CSS_TYPE_UNUSED},
143 Css_border_color_enum_vals},
144 {"border-bottom-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
145 Css_border_style_enum_vals},
146 {"border-bottom-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
147 Css_border_width_enum_vals},
148 {"border-collapse", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
149 Css_border_collapse_enum_vals},
150 {"border-left-color", {CSS_TYPE_ENUM, CSS_TYPE_COLOR, CSS_TYPE_UNUSED},
151 Css_border_color_enum_vals},
152 {"border-left-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
153 Css_border_style_enum_vals},
154 {"border-left-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
155 Css_border_width_enum_vals},
156 {"border-right-color", {CSS_TYPE_ENUM, CSS_TYPE_COLOR, CSS_TYPE_UNUSED},
157 Css_border_color_enum_vals},
158 {"border-right-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
159 Css_border_style_enum_vals},
160 {"border-rigth-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
161 Css_border_width_enum_vals},
162 {"border-spacing", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
163 {"border-top-color", {CSS_TYPE_ENUM, CSS_TYPE_COLOR, CSS_TYPE_UNUSED},
164 Css_border_color_enum_vals},
165 {"border-top-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
166 Css_border_style_enum_vals},
167 {"border-top-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
168 Css_border_width_enum_vals},
169 {"bottom", {CSS_TYPE_UNUSED}, NULL},
170 {"caption-side", {CSS_TYPE_UNUSED}, NULL},
171 {"clear", {CSS_TYPE_UNUSED}, NULL},
172 {"clip", {CSS_TYPE_UNUSED}, NULL},
173 {"color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
174 {"content", {CSS_TYPE_STRING, CSS_TYPE_UNUSED}, NULL},
175 {"counter-increment", {CSS_TYPE_UNUSED}, NULL},
176 {"counter-reset", {CSS_TYPE_UNUSED}, NULL},
177 {"cursor", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_cursor_enum_vals},
178 {"direction", {CSS_TYPE_UNUSED}, NULL},
179 {"display", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_display_enum_vals},
180 {"empty-cells", {CSS_TYPE_UNUSED}, NULL},
181 {"float", {CSS_TYPE_UNUSED}, NULL},
182 {"font-family", {CSS_TYPE_SYMBOL, CSS_TYPE_UNUSED}, NULL},
183 {"font-size", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED},
184 Css_font_size_enum_vals},
185 {"font-size-adjust", {CSS_TYPE_UNUSED}, NULL},
186 {"font-stretch", {CSS_TYPE_UNUSED}, NULL},
187 {"font-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_font_style_enum_vals},
188 {"font-variant", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
189 Css_font_variant_enum_vals},
190 {"font-weight", {CSS_TYPE_ENUM, CSS_TYPE_FONT_WEIGHT, CSS_TYPE_UNUSED},
191 Css_font_weight_enum_vals},
192 {"height", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
193 {"left", {CSS_TYPE_UNUSED}, NULL},
194 {"letter-spacing", {CSS_TYPE_ENUM, CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED},
195 Css_letter_spacing_enum_vals},
196 {"line-height",
197 {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE_NUMBER, CSS_TYPE_UNUSED},
198 Css_line_height_enum_vals},
199 {"list-style-image", {CSS_TYPE_UNUSED}, NULL},
200 {"list-style-position", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
201 Css_list_style_position_enum_vals},
202 {"list-style-type", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
203 Css_list_style_type_enum_vals},
204 {"margin-bottom", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
205 {"margin-left", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
206 {"margin-right", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
207 {"margin-top", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
208 {"marker-offset", {CSS_TYPE_UNUSED}, NULL},
209 {"marks", {CSS_TYPE_UNUSED}, NULL},
210 {"max-height", {CSS_TYPE_UNUSED}, NULL},
211 {"max-width", {CSS_TYPE_UNUSED}, NULL},
212 {"min-height", {CSS_TYPE_UNUSED}, NULL},
213 {"min-width", {CSS_TYPE_UNUSED}, NULL},
214 {"outline-color", {CSS_TYPE_UNUSED}, NULL},
215 {"outline-style", {CSS_TYPE_UNUSED}, NULL},
216 {"outline-width", {CSS_TYPE_UNUSED}, NULL},
217 {"overflow", {CSS_TYPE_UNUSED}, NULL},
218 {"padding-bottom", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
219 {"padding-left", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
220 {"padding-right", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
221 {"padding-top", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
222 {"position", {CSS_TYPE_UNUSED}, NULL},
223 {"quotes", {CSS_TYPE_UNUSED}, NULL},
224 {"right", {CSS_TYPE_UNUSED}, NULL},
225 {"text-align", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_text_align_enum_vals},
226 {"text-decoration", {CSS_TYPE_MULTI_ENUM, CSS_TYPE_UNUSED},
227 Css_text_decoration_enum_vals},
228 {"text-indent", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
229 {"text-shadow", {CSS_TYPE_UNUSED}, NULL},
230 {"text-transform", {CSS_TYPE_UNUSED}, NULL},
231 {"top", {CSS_TYPE_UNUSED}, NULL},
232 {"unicode-bidi", {CSS_TYPE_UNUSED}, NULL},
233 {"vertical-align",{CSS_TYPE_ENUM, CSS_TYPE_UNUSED},Css_vertical_align_vals},
234 {"visibility", {CSS_TYPE_UNUSED}, NULL},
235 {"white-space", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_white_space_vals},
236 {"width", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
237 {"word-spacing", {CSS_TYPE_ENUM, CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED},
238 Css_word_spacing_enum_vals},
239 {"z-index", {CSS_TYPE_UNUSED}, NULL},
240
241 /* These are extensions, for internal used, and never parsed. */
242 {"x-link", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
243 {"x-colspan", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
244 {"x-rowspan", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
245 {"last", {CSS_TYPE_UNUSED}, NULL},
246 };
247
248 typedef struct {
249 const char *symbol;
250 enum {
251 CSS_SHORTHAND_MULTIPLE, /* [ p1 || p2 || ...], the property pi is
252 * determined by the type */
253 CSS_SHORTHAND_DIRECTIONS, /* <t>{1,4} */
254 CSS_SHORTHAND_BORDER, /* special, used for 'border' */
255 CSS_SHORTHAND_FONT, /* special, used for 'font' */
256 } type;
257 const CssPropertyName * properties;/* CSS_SHORTHAND_MULTIPLE:
258 * must be terminated by -1
259 * CSS_SHORTHAND_DIRECTIONS:
260 * must have length 4
261 * CSS_SHORTHAND_BORDERS:
262 * must have length 12
263 * CSS_SHORTHAND_FONT:
264 * unused */
265 } CssShorthandInfo;
266
267 const CssPropertyName Css_background_properties[] = {
268 CSS_PROPERTY_BACKGROUND_COLOR,
269 CSS_PROPERTY_BACKGROUND_IMAGE,
270 CSS_PROPERTY_BACKGROUND_REPEAT,
271 CSS_PROPERTY_BACKGROUND_ATTACHMENT,
272 CSS_PROPERTY_BACKGROUND_POSITION,
273 (CssPropertyName) - 1
274 };
275
276 const CssPropertyName Css_border_bottom_properties[] = {
277 CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
278 CSS_PROPERTY_BORDER_BOTTOM_STYLE,
279 CSS_PROPERTY_BORDER_BOTTOM_COLOR,
280 (CssPropertyName) - 1
281 };
282
283 const CssPropertyName Css_border_color_properties[4] = {
284 CSS_PROPERTY_BORDER_TOP_COLOR,
285 CSS_PROPERTY_BORDER_BOTTOM_COLOR,
286 CSS_PROPERTY_BORDER_LEFT_COLOR,
287 CSS_PROPERTY_BORDER_RIGHT_COLOR
288 };
289
290 const CssPropertyName Css_border_left_properties[] = {
291 CSS_PROPERTY_BORDER_LEFT_WIDTH,
292 CSS_PROPERTY_BORDER_LEFT_STYLE,
293 CSS_PROPERTY_BORDER_LEFT_COLOR,
294 (CssPropertyName) - 1
295 };
296
297 const CssPropertyName Css_border_right_properties[] = {
298 CSS_PROPERTY_BORDER_RIGHT_WIDTH,
299 CSS_PROPERTY_BORDER_RIGHT_STYLE,
300 CSS_PROPERTY_BORDER_RIGHT_COLOR,
301 (CssPropertyName) - 1
302 };
303
304 const CssPropertyName Css_border_style_properties[] = {
305 CSS_PROPERTY_BORDER_TOP_STYLE,
306 CSS_PROPERTY_BORDER_BOTTOM_STYLE,
307 CSS_PROPERTY_BORDER_LEFT_STYLE,
308 CSS_PROPERTY_BORDER_RIGHT_STYLE
309 };
310
311 const CssPropertyName Css_border_top_properties[] = {
312 CSS_PROPERTY_BORDER_TOP_WIDTH,
313 CSS_PROPERTY_BORDER_TOP_STYLE,
314 CSS_PROPERTY_BORDER_TOP_COLOR,
315 (CssPropertyName) - 1
316 };
317
318 const CssPropertyName Css_border_width_properties[] = {
319 CSS_PROPERTY_BORDER_TOP_WIDTH,
320 CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
321 CSS_PROPERTY_BORDER_LEFT_WIDTH,
322 CSS_PROPERTY_BORDER_RIGHT_WIDTH
323 };
324
325 const CssPropertyName Css_list_style_properties[] = {
326 CSS_PROPERTY_LIST_STYLE_TYPE,
327 CSS_PROPERTY_LIST_STYLE_POSITION,
328 CSS_PROPERTY_LIST_STYLE_IMAGE,
329 (CssPropertyName) - 1
330 };
331
332 const CssPropertyName Css_margin_properties[] = {
333 CSS_PROPERTY_MARGIN_TOP,
334 CSS_PROPERTY_MARGIN_BOTTOM,
335 CSS_PROPERTY_MARGIN_LEFT,
336 CSS_PROPERTY_MARGIN_RIGHT
337 };
338
339 const CssPropertyName Css_outline_properties[] = {
340 CSS_PROPERTY_OUTLINE_COLOR,
341 CSS_PROPERTY_OUTLINE_STYLE,
342 CSS_PROPERTY_OUTLINE_WIDTH,
343 (CssPropertyName) - 1
344 };
345
346 const CssPropertyName Css_padding_properties[] = {
347 CSS_PROPERTY_PADDING_TOP,
348 CSS_PROPERTY_PADDING_BOTTOM,
349 CSS_PROPERTY_PADDING_LEFT,
350 CSS_PROPERTY_PADDING_RIGHT
351 };
352
353 const CssPropertyName Css_border_properties[] = {
354 CSS_PROPERTY_BORDER_TOP_WIDTH,
355 CSS_PROPERTY_BORDER_TOP_STYLE,
356 CSS_PROPERTY_BORDER_TOP_COLOR,
357 CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
358 CSS_PROPERTY_BORDER_BOTTOM_STYLE,
359 CSS_PROPERTY_BORDER_BOTTOM_COLOR,
360 CSS_PROPERTY_BORDER_LEFT_WIDTH,
361 CSS_PROPERTY_BORDER_LEFT_STYLE,
362 CSS_PROPERTY_BORDER_LEFT_COLOR,
363 CSS_PROPERTY_BORDER_RIGHT_WIDTH,
364 CSS_PROPERTY_BORDER_RIGHT_STYLE,
365 CSS_PROPERTY_BORDER_RIGHT_COLOR
366 };
367
368 const CssPropertyName Css_font_properties[] = {
369 CSS_PROPERTY_FONT_SIZE,
370 CSS_PROPERTY_FONT_STYLE,
371 CSS_PROPERTY_FONT_VARIANT,
372 CSS_PROPERTY_FONT_WEIGHT,
373 CSS_PROPERTY_FONT_FAMILY,
374 (CssPropertyName) - 1
375 };
376
377 static const CssShorthandInfo Css_shorthand_info[] = {
378 {"background", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
379 Css_background_properties},
380 {"border", CssShorthandInfo::CSS_SHORTHAND_BORDER,
381 Css_border_properties},
382 {"border-bottom", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
383 Css_border_bottom_properties},
384 {"border-color", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
385 Css_border_color_properties},
386 {"border-left", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
387 Css_border_left_properties},
388 {"border-right", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
389 Css_border_right_properties},
390 {"border-style", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
391 Css_border_style_properties},
392 {"border-top", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
393 Css_border_top_properties},
394 {"border-width", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
395 Css_border_width_properties},
396 {"font", CssShorthandInfo::CSS_SHORTHAND_FONT,
397 Css_font_properties},
398 {"list-style", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
399 Css_list_style_properties},
400 {"margin", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
401 Css_margin_properties},
402 {"outline", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
403 Css_outline_properties},
404 {"padding", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
405 Css_padding_properties},
406 };
407
408 #define CSS_SHORTHAND_NUM \
409 (sizeof(Css_shorthand_info) / sizeof(CssShorthandInfo))
410
411 /* ----------------------------------------------------------------------
412 * Parsing
413 * ---------------------------------------------------------------------- */
414
415 CssParser::CssParser(CssContext *context, CssOrigin origin,
416 const char *buf, int buflen)
417 {
418 this->context = context;
419 this->origin = origin;
420 this->buf = buf;
421 this->buflen = buflen;
422 this->bufptr = 0;
423 this->spaceSeparated = false;
424 this->withinBlock = false;
425
426 nextToken ();
427 }
428
429 /*
430 * Gets the next character from the buffer, or EOF.
431 */
432 int CssParser::getChar()
433 {
434 int c;
435
436 if (bufptr >= buflen)
437 c = EOF;
438 else
439 c = buf[bufptr];
440
441 /* The buffer pointer is increased in any case, so that ungetChar works
442 * correctly at the end of the buffer. */
443 bufptr++;
444 return c;
445 }
446
447 /*
448 * Undoes the last getChar().
449 */
450 void CssParser::ungetChar()
451 {
452 bufptr--;
453 }
454
455 /*
456 * Skip string str if it is found in the input buffer.
457 * If string is found leave bufptr pointing to last matched char.
458 * If not wind back. The first char is passed as parameter c
459 * to avoid unnecessary getChar() / ungetChar() calls.
460 */
461 inline bool CssParser::skipString(int c, const char *str)
462 {
463 for (int n = 0; str[n]; n++) {
464 if (n > 0)
465 c = getChar();
466
467 if (str[n] != c) {
468 while (n--)
469 ungetChar();
470 return false;
471 }
472 }
473
474 return true;
475 }
476
477 void CssParser::nextToken()
478 {
479 int c, c1, d, j;
480 char hexbuf[5];
481 int i = 0;
482
483 ttype = CSS_TK_CHAR; /* init */
484 spaceSeparated = false;
485
486 while (true) {
487 c = getChar();
488 if (isspace(c)) { // ignore whitespace
489 spaceSeparated = true;
490 } else if (skipString(c, "/*")) { // ignore comments
491 do {
492 c = getChar();
493 } while (c != EOF && ! skipString(c, "*/"));
494 } else if (skipString(c, "<!--")) { // ignore XML comment markers
495 } else if (skipString(c, "-->")) {
496 } else {
497 break;
498 }
499 }
500
501 // handle negative numbers
502 if (c == '-') {
503 if (i < maxStrLen - 1)
504 tval[i++] = c;
505 c = getChar();
506 }
507
508 if (isdigit(c)) {
509 ttype = CSS_TK_DECINT;
510 do {
511 if (i < maxStrLen - 1) {
512 tval[i++] = c;
513 }
514 /* else silently truncated */
515 c = getChar();
516 } while (isdigit(c));
517 if (c != '.')
518 ungetChar();
519
520 /* ...but keep going to see whether it's really a float */
521 }
522
523 if (c == '.') {
524 c = getChar();
525 if (isdigit(c)) {
526 ttype = CSS_TK_FLOAT;
527 if (i < maxStrLen - 1)
528 tval[i++] = '.';
529 do {
530 if (i < maxStrLen - 1)
531 tval[i++] = c;
532 /* else silently truncated */
533 c = getChar();
534 } while (isdigit(c));
535
536 ungetChar();
537 tval[i] = 0;
538 DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token number %s\n", tval);
539 return;
540 } else {
541 ungetChar();
542 if (ttype == CSS_TK_DECINT) {
543 ungetChar();
544 } else {
545 c = '.';
546 }
547 }
548 }
549
550 if (ttype == CSS_TK_DECINT) {
551 tval[i] = 0;
552 DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token number %s\n", tval);
553 return;
554 }
555
556 if (i) {
557 ungetChar(); /* ungetChar '-' */
558 i--;
559 c = getChar();
560 }
561
562 if (isalpha(c) || c == '_' || c == '-') {
563 ttype = CSS_TK_SYMBOL;
564
565 tval[0] = c;
566 i = 1;
567 c = getChar();
568 while (isalnum(c) || c == '_' || c == '-') {
569 if (i < maxStrLen - 1) {
570 tval[i] = c;
571 i++;
572 } /* else silently truncated */
573 c = getChar();
574 }
575 tval[i] = 0;
576 ungetChar();
577 DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token symbol '%s'\n", tval);
578 return;
579 }
580
581 if (c == '"' || c == '\'') {
582 c1 = c;
583 ttype = CSS_TK_STRING;
584
585 i = 0;
586 c = getChar();
587
588 while (c != EOF && c != c1) {
589 if (c == '\\') {
590 d = getChar();
591 if (isxdigit(d)) {
592 /* Read hex Unicode char. (Actually, strings are yet only 8
593 * bit.) */
594 hexbuf[0] = d;
595 j = 1;
596 d = getChar();
597 while (j < 4 && isxdigit(d)) {
598 hexbuf[j] = d;
599 j++;
600 d = getChar();
601 }
602 hexbuf[j] = 0;
603 ungetChar();
604 c = strtol(hexbuf, NULL, 16);
605 } else {
606 /* Take character literally. */
607 c = d;
608 }
609 }
610
611 if (i < maxStrLen - 1) {
612 tval[i] = c;
613 i++;
614 } /* else silently truncated */
615 c = getChar();
616 }
617 tval[i] = 0;
618 /* No ungetChar(). */
619 DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token string '%s'\n", tval);
620 return;
621 }
622
623 /*
624 * Within blocks, '#' starts a color, outside, it is used in selectors.
625 */
626 if (c == '#' && withinBlock) {
627 ttype = CSS_TK_COLOR;
628
629 tval[0] = c;
630 i = 1;
631 c = getChar();
632 while (isxdigit(c)) {
633 if (i < maxStrLen - 1) {
634 tval[i] = c;
635 i++;
636 } /* else silently truncated */
637 c = getChar();
638 }
639 tval[i] = 0;
640 ungetChar();
641 DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token color '%s'\n", tval);
642 return;
643 }
644
645 if (c == EOF) {
646 DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token %s\n", "EOF");
647 ttype = CSS_TK_END;
648 return;
649 }
650
651 ttype = CSS_TK_CHAR;
652 tval[0] = c;
653 tval[1] = 0;
654 DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token char '%c'\n", c);
655 }
656
657
658 bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type)
659 {
660 int i, err = 1;
661 CssValueType savedType = *type;
662
663 for (int j = 0; Css_property_info[prop].type[j] != CSS_TYPE_UNUSED; j++) {
664 *type = Css_property_info[prop].type[j];
665
666 switch (Css_property_info[prop].type[j]) {
667
668 case CSS_TYPE_ENUM:
669 if (ttype == CSS_TK_SYMBOL) {
670 for (i = 0; Css_property_info[prop].enum_symbols[i]; i++)
671 if (dStrcasecmp(tval,
672 Css_property_info[prop].enum_symbols[i]) == 0)
673 return true;
674 }
675 break;
676
677 case CSS_TYPE_MULTI_ENUM:
678 if (ttype == CSS_TK_SYMBOL) {
679 if (dStrcasecmp(tval, "none") == 0) {
680 return true;
681 } else {
682 for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) {
683 if (dStrcasecmp(tval,
684 Css_property_info[prop].enum_symbols[i]) == 0)
685 return true;
686 }
687 }
688 }
689 break;
690
691 case CSS_TYPE_LENGTH_PERCENTAGE:
692 case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER:
693 case CSS_TYPE_LENGTH:
694 if (tval[0] == '-')
695 return false;
696 // Fall Through
697 case CSS_TYPE_SIGNED_LENGTH:
698 if (ttype == CSS_TK_DECINT ||
699 ttype == CSS_TK_FLOAT ||
700 (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0))
701 return true;
702 break;
703
704 case CSS_TYPE_COLOR:
705 if ((ttype == CSS_TK_COLOR ||
706 ttype == CSS_TK_SYMBOL) &&
707 (dStrcasecmp(tval, "rgb") == 0 ||
708 a_Color_parse(tval, -1, &err) != -1))
709 return true;
710 break;
711
712 case CSS_TYPE_STRING:
713 if (ttype == CSS_TK_STRING)
714 return true;
715 break;
716
717 case CSS_TYPE_SYMBOL:
718 if (ttype == CSS_TK_SYMBOL ||
719 ttype == CSS_TK_STRING)
720 return true;
721 break;
722
723 case CSS_TYPE_FONT_WEIGHT:
724 if (ttype == CSS_TK_DECINT) {
725 i = strtol(tval, NULL, 10);
726 if (i >= 100 && i <= 900)
727 return true;
728 }
729 break;
730
731 case CSS_TYPE_UNUSED:
732 case CSS_TYPE_INTEGER:
733 /* Not used for parser values. */
734 default:
735 assert(false);
736 break;
737 }
738 }
739
740 *type = savedType;
741 return false;
742 }
743
744 bool CssParser::parseRgbColorComponent(int32_t *cc, int *percentage) {
745 if (ttype != CSS_TK_DECINT) {
746 MSG_CSS("expected integer not found in %s color\n", "rgb");
747 return false;
748 }
749
750 *cc = strtol(tval, NULL, 10);
751
752 nextToken();
753 if (ttype == CSS_TK_CHAR && tval[0] == '%') {
754 if (*percentage == 0) {
755 MSG_CSS("'%s' unexpected in rgb color\n", "%");
756 return false;
757 }
758 *percentage = 1;
759 *cc = *cc * 255 / 100;
760 nextToken();
761 } else {
762 if (*percentage == 1) {
763 MSG_CSS("expected '%s' not found in rgb color\n", "%");
764 return false;
765 }
766 *percentage = 0;
767 }
768
769 if (*cc > 255)
770 *cc = 255;
771 if (*cc < 0)
772 *cc = 0;
773
774 return true;
775 }
776
777 bool CssParser::parseRgbColor(int32_t *c) {
778 int32_t cc;
779 int percentage = -1;
780
781 *c = 0;
782
783 if (ttype != CSS_TK_CHAR || tval[0] != '(') {
784 MSG_CSS("expected '%s' not found in rgb color\n", "(");
785 return false;
786 }
787 nextToken();
788
789 if (!parseRgbColorComponent(&cc, &percentage))
790 return false;
791 *c |= cc << 16;
792
793 if (ttype != CSS_TK_CHAR || tval[0] != ',') {
794 MSG_CSS("expected '%s' not found in rgb color\n", ",");
795 return false;
796 }
797 nextToken();
798
799 if (!parseRgbColorComponent(&cc, &percentage))
800 return false;
801 *c |= cc << 8;
802
803 if (ttype != CSS_TK_CHAR || tval[0] != ',') {
804 MSG_CSS("expected '%s' not found in rgb color\n", ",");
805 return false;
806 }
807 nextToken();
808
809 if (!parseRgbColorComponent(&cc, &percentage))
810 return false;
811 *c |= cc;
812
813 if (ttype != CSS_TK_CHAR || tval[0] != ')') {
814 MSG_CSS("expected '%s' not found in rgb color\n", ")");
815 return false;
816 }
817
818 return true;
819 }
820
821 bool CssParser::parseValue(CssPropertyName prop,
822 CssValueType type,
823 CssPropertyValue * val)
824 {
825 CssLengthType lentype;
826 bool found, ret = false;
827 float fval;
828 int i, ival, err = 1;
829 Dstr *dstr;
830
831 switch (type) {
832 case CSS_TYPE_ENUM:
833 if (ttype == CSS_TK_SYMBOL) {
834 for (i = 0; Css_property_info[prop].enum_symbols[i]; i++)
835 if (dStrcasecmp(tval,
836 Css_property_info[prop].enum_symbols[i]) == 0) {
837 val->intVal = i;
838 ret = true;
839 break;
840 }
841 nextToken();
842 }
843 break;
844
845 case CSS_TYPE_MULTI_ENUM:
846 val->intVal = 0;
847 ret = true;
848
849 while (ttype == CSS_TK_SYMBOL) {
850 if (dStrcasecmp(tval, "none") != 0) {
851 for (i = 0, found = false;
852 !found && Css_property_info[prop].enum_symbols[i]; i++) {
853 if (dStrcasecmp(tval,
854 Css_property_info[prop].enum_symbols[i]) == 0)
855 val->intVal |= (1 << i);
856 }
857 }
858 nextToken();
859 }
860 break;
861
862 case CSS_TYPE_LENGTH_PERCENTAGE:
863 case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER:
864 case CSS_TYPE_LENGTH:
865 case CSS_TYPE_SIGNED_LENGTH:
866 if (ttype == CSS_TK_DECINT || ttype == CSS_TK_FLOAT) {
867 fval = atof(tval);
868 lentype = CSS_LENGTH_TYPE_NONE;
869
870 nextToken();
871 if (!spaceSeparated && ttype == CSS_TK_SYMBOL) {
872 ret = true;
873
874 if (dStrcasecmp(tval, "px") == 0) {
875 lentype = CSS_LENGTH_TYPE_PX;
876 nextToken();
877 } else if (dStrcasecmp(tval, "mm") == 0) {
878 lentype = CSS_LENGTH_TYPE_MM;
879 nextToken();
880 } else if (dStrcasecmp(tval, "cm") == 0) {
881 lentype = CSS_LENGTH_TYPE_MM;
882 fval *= 10;
883 nextToken();
884 } else if (dStrcasecmp(tval, "in") == 0) {
885 lentype = CSS_LENGTH_TYPE_MM;
886 fval *= 25.4;
887 nextToken();
888 } else if (dStrcasecmp(tval, "pt") == 0) {
889 lentype = CSS_LENGTH_TYPE_MM;
890 fval *= (25.4 / 72);
891 nextToken();
892 } else if (dStrcasecmp(tval, "pc") == 0) {
893 lentype = CSS_LENGTH_TYPE_MM;
894 fval *= (25.4 / 6);
895 nextToken();
896 } else if (dStrcasecmp(tval, "em") == 0) {
897 lentype = CSS_LENGTH_TYPE_EM;
898 nextToken();
899 } else if (dStrcasecmp(tval, "ex") == 0) {
900 lentype = CSS_LENGTH_TYPE_EX;
901 nextToken();
902 } else {
903 ret = false;
904 }
905 } else if (!spaceSeparated &&
906 (type == CSS_TYPE_LENGTH_PERCENTAGE ||
907 type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER) &&
908 ttype == CSS_TK_CHAR &&
909 tval[0] == '%') {
910 fval /= 100;
911 lentype = CSS_LENGTH_TYPE_PERCENTAGE;
912 ret = true;
913 nextToken();
914 }
915
916 /* Allow numbers without unit only for 0 or
917 * CSS_TYPE_LENGTH_PERCENTAGE_NUMBER
918 */
919 if (lentype == CSS_LENGTH_TYPE_NONE &&
920 (type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER || fval == 0.0))
921 ret = true;
922
923 val->intVal = CSS_CREATE_LENGTH(fval, lentype);
924 } else if (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0) {
925 ret = true;
926 val->intVal = CSS_LENGTH_TYPE_AUTO;
927 nextToken();
928 }
929 break;
930
931 case CSS_TYPE_COLOR:
932 if (ttype == CSS_TK_COLOR) {
933 val->intVal = a_Color_parse(tval, -1, &err);
934 if (err)
935 MSG_CSS("color is not in \"%s\" format\n", "#RRGGBB");
936 else
937 ret = true;
938 nextToken();
939 } else if (ttype == CSS_TK_SYMBOL) {
940 if (dStrcasecmp(tval, "rgb") == 0) {
941 nextToken();
942 if (parseRgbColor(&val->intVal))
943 ret = true;
944 else
945 MSG_CSS("Failed to parse %s color\n", "rgb(r,g,b)");
946 } else {
947 val->intVal = a_Color_parse(tval, -1, &err);
948 if (err)
949 MSG_CSS("color is not in \"%s\" format\n", "#RRGGBB");
950 else
951 ret = true;
952 }
953 nextToken();
954 }
955 break;
956
957 case CSS_TYPE_STRING:
958 if (ttype == CSS_TK_STRING) {
959 val->strVal = dStrdup(tval);
960 ret = true;
961 nextToken();
962 }
963 break;
964
965 case CSS_TYPE_SYMBOL:
966 /* Read comma separated list of font family names */
967 dstr = dStr_new("");
968 while (ttype == CSS_TK_SYMBOL || ttype == CSS_TK_STRING ||
969 (ttype == CSS_TK_CHAR && tval[0] == ',')) {
970 if (spaceSeparated)
971 dStr_append_c(dstr, ' ');
972 dStr_append(dstr, tval);
973 ret = true;
974 nextToken();
975 }
976
977 if (ret) {
978 val->strVal = dStrstrip(dstr->str);
979 dStr_free(dstr, 0);
980 } else {
981 dStr_free(dstr, 1);
982 }
983 break;
984
985 case CSS_TYPE_FONT_WEIGHT:
986 ival = 0;
987 if (ttype == CSS_TK_DECINT) {
988 ival = strtol(tval, NULL, 10);
989 if (ival < 100 || ival > 900)
990 /* invalid */
991 ival = 0;
992 }
993
994 if (ival != 0) {
995 val->intVal = ival;
996 ret = true;
997 nextToken();
998 }
999 break;
1000
1001 case CSS_TYPE_UNUSED:
1002 /* nothing */
1003 break;
1004
1005 case CSS_TYPE_INTEGER:
1006 /* Not used for parser values. */
1007 default:
1008 assert(false); /* not reached */
1009 }
1010
1011 return ret;
1012 }
1013
1014 bool CssParser::parseWeight()
1015 {
1016 if (ttype == CSS_TK_CHAR && tval[0] == '!') {
1017 nextToken();
1018 if (ttype == CSS_TK_SYMBOL &&
1019 dStrcasecmp(tval, "important") == 0) {
1020 nextToken();
1021 return true;
1022 }
1023 }
1024
1025 return false;
1026 }
1027
1028 /*
1029 * bsearch(3) compare function for searching properties
1030 */
1031 static int Css_property_info_cmp(const void *a, const void *b)
1032 {
1033 return dStrcasecmp(((CssPropertyInfo *) a)->symbol,
1034 ((CssPropertyInfo *) b)->symbol);
1035 }
1036
1037
1038 /*
1039 * bsearch(3) compare function for searching shorthands
1040 */
1041 static int Css_shorthand_info_cmp(const void *a, const void *b)
1042 {
1043 return dStrcasecmp(((CssShorthandInfo *) a)->symbol,
1044 ((CssShorthandInfo *) b)->symbol);
1045 }
1046
1047 void CssParser::parseDeclaration(CssPropertyList * props,
1048 CssPropertyList * importantProps)
1049 {
1050 CssPropertyInfo pi = {NULL, {CSS_TYPE_UNUSED}, NULL}, *pip;
1051 CssShorthandInfo *sip;
1052 CssValueType type = CSS_TYPE_UNUSED;
1053
1054 CssPropertyName prop;
1055 CssPropertyValue val, dir_vals[4];
1056 CssValueType dir_types[4];
1057 bool found, weight;
1058 int sh_index, i, j, n;
1059 int dir_set[4][4] = {
1060 /* 1 value */ {0, 0, 0, 0},
1061 /* 2 values */ {0, 0, 1, 1},
1062 /* 3 values */ {0, 2, 1, 1},
1063 /* 4 values */ {0, 2, 3, 1}
1064 };
1065
1066 if (ttype == CSS_TK_SYMBOL) {
1067 pi.symbol = tval;
1068 pip =
1069 (CssPropertyInfo *) bsearch(&pi, Css_property_info,
1070 CSS_NUM_PARSED_PROPERTIES,
1071 sizeof(CssPropertyInfo),
1072 Css_property_info_cmp);
1073 if (pip) {
1074 prop = (CssPropertyName) (pip - Css_property_info);
1075 nextToken();
1076 if (ttype == CSS_TK_CHAR && tval[0] == ':') {
1077 nextToken();
1078 if (tokenMatchesProperty (prop, &type) &&
1079 parseValue(prop, type, &val)) {
1080 weight = parseWeight();
1081 if (weight && importantProps)
1082 importantProps->set(prop, type, val);
1083 else
1084 props->set(prop, type, val);
1085 }
1086 }
1087 } else {
1088 /* Try shorthands. */
1089 sip =
1090 (CssShorthandInfo *) bsearch(&pi, Css_shorthand_info,
1091 CSS_SHORTHAND_NUM,
1092 sizeof(CssShorthandInfo),
1093 Css_shorthand_info_cmp);
1094 if (sip) {
1095 sh_index = sip - Css_shorthand_info;
1096 nextToken();
1097 if (ttype == CSS_TK_CHAR && tval[0] == ':') {
1098 nextToken();
1099
1100 switch (Css_shorthand_info[sh_index].type) {
1101
1102 case CssShorthandInfo::CSS_SHORTHAND_FONT:
1103 /* \todo Implement details. */
1104 case CssShorthandInfo::CSS_SHORTHAND_MULTIPLE:
1105 do {
1106 for (found = false, i = 0;
1107 !found &&
1108 Css_shorthand_info[sh_index].properties[i] != -1;
1109 i++)
1110 if (tokenMatchesProperty(Css_shorthand_info[sh_index].
1111 properties[i], &type)) {
1112 found = true;
1113 DEBUG_MSG(DEBUG_PARSE_LEVEL,
1114 "will assign to '%s'\n",
1115 Css_property_info
1116 [Css_shorthand_info[sh_index]
1117 .properties[i]].symbol);
1118 if (parseValue(Css_shorthand_info[sh_index]
1119 .properties[i], type, &val)) {
1120 weight = parseWeight();
1121 if (weight && importantProps)
1122 importantProps->
1123 set(Css_shorthand_info[sh_index].
1124 properties[i], type, val);
1125 else
1126 props->set(Css_shorthand_info[sh_index].
1127 properties[i], type, val);
1128 }
1129 }
1130 } while (found);
1131 break;
1132
1133 case CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS:
1134 n = 0;
1135 while (n < 4) {
1136 if (tokenMatchesProperty(Css_shorthand_info[sh_index].
1137 properties[0], &type) &&
1138 parseValue(Css_shorthand_info[sh_index]
1139 .properties[0], type, &val)) {
1140 dir_vals[n] = val;
1141 dir_types[n] = type;
1142 n++;
1143 } else
1144 break;
1145 }
1146
1147 weight = parseWeight();
1148 if (n > 0) {
1149 for (i = 0; i < 4; i++)
1150 if (weight && importantProps)
1151 importantProps->set(Css_shorthand_info[sh_index]
1152 .properties[i],
1153 dir_types[dir_set[n - 1][i]],
1154 dir_vals[dir_set[n - 1][i]]);
1155 else
1156 props->set(Css_shorthand_info[sh_index]
1157 .properties[i],
1158 dir_types[dir_set[n - 1][i]],
1159 dir_vals[dir_set[n - 1][i]]);
1160 } else
1161 MSG_CSS("no values for shorthand property '%s'\n",
1162 Css_shorthand_info[sh_index].symbol);
1163
1164 break;
1165
1166 case CssShorthandInfo::CSS_SHORTHAND_BORDER:
1167 do {
1168 for (found = false, i = 0;
1169 !found && i < 3;
1170 i++)
1171 if (tokenMatchesProperty(Css_shorthand_info[sh_index].
1172 properties[i], &type)) {
1173 found = true;
1174 if (parseValue(Css_shorthand_info[sh_index]
1175 .properties[i], type, &val)) {
1176 weight = parseWeight();
1177 for (j = 0; j < 4; j++)
1178 if (weight && importantProps)
1179 importantProps->
1180 set(Css_shorthand_info[sh_index].
1181 properties[j * 3 + i], type, val);
1182 else
1183 props->set(Css_shorthand_info[sh_index].
1184 properties[j * 3 + i], type, val);
1185 }
1186 }
1187 } while (found);
1188 break;
1189 }
1190 }
1191 }
1192 }
1193 }
1194
1195 /* Skip all tokens until the expected end. */
1196 while (!(ttype == CSS_TK_END ||
1197 (ttype == CSS_TK_CHAR &&
1198 (tval[0] == ';' || tval[0] == '}'))))
1199 nextToken();
1200
1201 if (ttype == CSS_TK_CHAR && tval[0] == ';')
1202 nextToken();
1203 }
1204
1205 bool CssParser::parseSimpleSelector(CssSimpleSelector *selector)
1206 {
1207 CssSimpleSelector::SelectType selectType;
1208
1209 if (ttype == CSS_TK_SYMBOL) {
1210 selector->setElement (a_Html_tag_index(tval));
1211 nextToken();
1212 if (spaceSeparated)
1213 return true;
1214 } else if (ttype == CSS_TK_CHAR && tval[0] == '*') {
1215 selector->setElement (CssSimpleSelector::ELEMENT_ANY);
1216 nextToken();
1217 if (spaceSeparated)
1218 return true;
1219 } else if (ttype == CSS_TK_CHAR &&
1220 (tval[0] == '#' ||
1221 tval[0] == '.' ||
1222 tval[0] == ':')) {
1223 // nothing to be done in this case
1224 } else {
1225 return false;
1226 }
1227
1228 do {
1229 selectType = CssSimpleSelector::SELECT_NONE;
1230 if (ttype == CSS_TK_CHAR) {
1231 switch (tval[0]) {
1232 case '#':
1233 selectType = CssSimpleSelector::SELECT_ID;
1234 break;
1235 case '.':
1236 selectType = CssSimpleSelector::SELECT_CLASS;
1237 break;
1238 case ':':
1239 selectType = CssSimpleSelector::SELECT_PSEUDO_CLASS;
1240 if (selector->getPseudoClass ())
1241 // pseudo class has been set already.
1242 // As dillo currently only supports :link and :visisted, a
1243 // selector with more than one pseudo class will never match.
1244 // By returning false, the whole CssRule will be dropped.
1245 // \todo adapt this when supporting :hover, :active...
1246 return false;
1247 break;
1248 }
1249 }
1250
1251 if (selectType != CssSimpleSelector::SELECT_NONE) {
1252 nextToken();
1253 if (spaceSeparated)
1254 return true;
1255
1256 if (ttype == CSS_TK_SYMBOL) {
1257 selector->setSelect (selectType, tval);
1258 nextToken();
1259 } else {
1260 return false; // don't accept classes or id's starting with integer
1261 }
1262 if (spaceSeparated)
1263 return true;
1264 }
1265 } while (selectType != CssSimpleSelector::SELECT_NONE);
1266
1267 DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of simple selector (%s, %s, %s, %d)\n",
1268 selector->id, selector->klass,
1269 selector->pseudo, selector->element);
1270
1271 return true;
1272 }
1273
1274 CssSelector *CssParser::parseSelector()
1275 {
1276 CssSelector *selector = new CssSelector ();
1277
1278 while (true) {
1279 if (! parseSimpleSelector (selector->top ())) {
1280 delete selector;
1281 selector = NULL;
1282 break;
1283 }
1284
1285 if (ttype == CSS_TK_CHAR &&
1286 (tval[0] == ',' || tval[0] == '{')) {
1287 break;
1288 } else if (ttype == CSS_TK_CHAR && tval[0] == '>') {
1289 selector->addSimpleSelector (CssSelector::CHILD);
1290 nextToken();
1291 } else if (ttype != CSS_TK_END && spaceSeparated) {
1292 selector->addSimpleSelector (CssSelector::DESCENDANT);
1293 } else {
1294 delete selector;
1295 selector = NULL;
1296 break;
1297 }
1298 }
1299
1300 while (ttype != CSS_TK_END &&
1301 (ttype != CSS_TK_CHAR ||
1302 (tval[0] != ',' && tval[0] != '{')))
1303 nextToken();
1304
1305 return selector;
1306 }
1307
1308 void CssParser::parseRuleset()
1309 {
1310 lout::misc::SimpleVector < CssSelector * >*list;
1311 CssPropertyList *props, *importantProps;
1312 CssSelector *selector;
1313
1314 list = new lout::misc::SimpleVector < CssSelector * >(1);
1315
1316 while (true) {
1317 selector = parseSelector();
1318
1319 if (selector) {
1320 selector->ref();
1321 list->increase();
1322 list->set(list->size() - 1, selector);
1323 }
1324
1325 // \todo dump whole ruleset in case of parse error as required by CSS 2.1
1326 // however make sure we don't dump it if only dillo fails to parse
1327 // valid CSS.
1328
1329 if (ttype == CSS_TK_CHAR && tval[0] == ',')
1330 /* To read the next token. */
1331 nextToken();
1332 else
1333 /* No more selectors. */
1334 break;
1335 }
1336
1337 DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of %s\n", "selectors");
1338
1339 props = new CssPropertyList(true);
1340 props->ref();
1341 importantProps = new CssPropertyList(true);
1342 importantProps->ref();
1343
1344 /* Read block. ('{' has already been read.) */
1345 if (ttype != CSS_TK_END) {
1346 withinBlock = true;
1347 nextToken();
1348 do
1349 parseDeclaration(props, importantProps);
1350 while (!(ttype == CSS_TK_END ||
1351 (ttype == CSS_TK_CHAR && tval[0] == '}')));
1352 withinBlock = false;
1353 }
1354
1355 for (int i = 0; i < list->size(); i++) {
1356 CssSelector *s = list->get(i);
1357
1358 if (origin == CSS_ORIGIN_USER_AGENT) {
1359 context->addRule(s, props, CSS_PRIMARY_USER_AGENT);
1360 } else if (origin == CSS_ORIGIN_USER) {
1361 context->addRule(s, props, CSS_PRIMARY_USER);
1362 context->addRule(s, importantProps, CSS_PRIMARY_USER_IMPORTANT);
1363 } else if (origin == CSS_ORIGIN_AUTHOR) {
1364 context->addRule(s, props, CSS_PRIMARY_AUTHOR);
1365 context->addRule(s, importantProps, CSS_PRIMARY_AUTHOR_IMPORTANT);
1366 }
1367
1368 s->unref();
1369 }
1370
1371 props->unref();
1372 importantProps->unref();
1373
1374 delete list;
1375
1376 if (ttype == CSS_TK_CHAR && tval[0] == '}')
1377 nextToken();
1378 }
1379
1380 char * CssParser::parseUrl()
1381 {
1382 Dstr *urlStr = NULL;
1383
1384 if (ttype != CSS_TK_SYMBOL ||
1385 dStrcasecmp(tval, "url") != 0)
1386 return NULL;
1387
1388 nextToken();
1389
1390 if (ttype != CSS_TK_CHAR || tval[0] != '(')
1391 return NULL;
1392
1393 nextToken();
1394
1395 if (ttype == CSS_TK_STRING) {
1396 urlStr = dStr_new(tval);
1397 nextToken();
1398 } else {
1399 urlStr = dStr_new("");
1400 while (ttype != CSS_TK_END &&
1401 (ttype != CSS_TK_CHAR || tval[0] != ')')) {
1402 dStr_append(urlStr, tval);
1403 nextToken();
1404 }
1405 }
1406
1407 if (ttype != CSS_TK_CHAR || tval[0] != ')') {
1408 dStr_free(urlStr, 1);
1409 urlStr = NULL;
1410 }
1411
1412 if (urlStr) {
1413 char *url = urlStr->str;
1414 dStr_free(urlStr, 0);
1415 return url;
1416 } else {
1417 return NULL;
1418 }
1419 }
1420
1421 void CssParser::parseImport(DilloHtml *html, DilloUrl *baseUrl)
1422 {
1423 char *urlStr = NULL;
1424 bool importSyntaxIsOK = false;
1425 bool mediaSyntaxIsOK = true;
1426 bool mediaIsSelected = true;
1427
1428 nextToken();
1429
1430 if (ttype == CSS_TK_SYMBOL &&
1431 dStrcasecmp(tval, "url") == 0)
1432 urlStr = parseUrl();
1433 else if (ttype == CSS_TK_STRING)
1434 urlStr = dStrdup (tval);
1435
1436 nextToken();
1437
1438 /* parse a comma-separated list of media */
1439 if (ttype == CSS_TK_SYMBOL) {
1440 mediaSyntaxIsOK = false;
1441 mediaIsSelected = false;
1442 while (ttype == CSS_TK_SYMBOL) {
1443 if (dStrcasecmp(tval, "all") == 0 ||
1444 dStrcasecmp(tval, "screen") == 0)
1445 mediaIsSelected = true;
1446 nextToken();
1447 if (ttype == CSS_TK_CHAR && tval[0] == ',') {
1448 nextToken();
1449 } else {
1450 mediaSyntaxIsOK = true;
1451 break;
1452 }
1453 }
1454 }
1455
1456 if (mediaSyntaxIsOK &&
1457 ttype == CSS_TK_CHAR &&
1458 tval[0] == ';') {
1459 importSyntaxIsOK = true;
1460 nextToken();
1461 } else
1462 ignoreStatement();
1463
1464 if (urlStr) {
1465 if (importSyntaxIsOK && mediaIsSelected) {
1466 MSG("CssParser::parseImport(): @import %s\n", urlStr);
1467 DilloUrl *url = a_Html_url_new (html, urlStr, a_Url_str(baseUrl),
1468 baseUrl ? 1 : 0);
1469 a_Html_load_stylesheet(html, url);
1470 a_Url_free(url);
1471 }
1472 dFree (urlStr);
1473 }
1474 }
1475
1476 void CssParser::parseMedia()
1477 {
1478 bool mediaSyntaxIsOK = false;
1479 bool mediaIsSelected = false;
1480
1481 nextToken();
1482
1483 /* parse a comma-separated list of media */
1484 while (ttype == CSS_TK_SYMBOL) {
1485 if (dStrcasecmp(tval, "all") == 0 ||
1486 dStrcasecmp(tval, "screen") == 0)
1487 mediaIsSelected = true;
1488 nextToken();
1489 if (ttype == CSS_TK_CHAR && tval[0] == ',') {
1490 nextToken();
1491 } else {
1492 mediaSyntaxIsOK = true;
1493 break;
1494 }
1495 }
1496
1497 /* check that the syntax is OK so far */
1498 if (!(mediaSyntaxIsOK &&
1499 ttype == CSS_TK_CHAR &&
1500 tval[0] == '{')) {
1501 ignoreStatement();
1502 return;
1503 }
1504
1505 /* parse/ignore the block as required */
1506 if (mediaIsSelected) {
1507 nextToken();
1508 while (ttype != CSS_TK_END) {
1509 parseRuleset();
1510 if (ttype == CSS_TK_CHAR && tval[0] == '}') {
1511 nextToken();
1512 break;
1513 }
1514 }
1515 } else
1516 ignoreBlock();
1517 }
1518
1519 const char * CssParser::propertyNameString(CssPropertyName name)
1520 {
1521 return Css_property_info[name].symbol;
1522 }
1523
1524 void CssParser::ignoreBlock()
1525 {
1526 int depth = 0;
1527
1528 while (ttype != CSS_TK_END) {
1529 if (ttype == CSS_TK_CHAR) {
1530 if (tval[0] == '{') {
1531 depth++;
1532 } else if (tval[0] == '}') {
1533 depth--;
1534 if (depth == 0) {
1535 nextToken();
1536 return;
1537 }
1538 }
1539 }
1540 nextToken();
1541 }
1542 }
1543
1544 void CssParser::ignoreStatement()
1545 {
1546 while (ttype != CSS_TK_END) {
1547 if (ttype == CSS_TK_CHAR) {
1548 if (tval[0] == ';') {
1549 nextToken();
1550 return;
1551 } else if (tval[0] =='{') {
1552 ignoreBlock();
1553 return;
1554 }
1555 }
1556 nextToken();
1557 }
1558 }
1559
1560 void CssParser::parse(DilloHtml *html, DilloUrl *url, CssContext * context,
1561 const char *buf,
1562 int buflen, CssOrigin origin)
1563 {
1564 CssParser parser (context, origin, buf, buflen);
1565 bool importsAreAllowed = true;
1566
1567 while (parser.ttype != CSS_TK_END) {
1568 if (parser.ttype == CSS_TK_CHAR &&
1569 parser.tval[0] == '@') {
1570 parser.nextToken();
1571 if (parser.ttype == CSS_TK_SYMBOL) {
1572 if (dStrcasecmp(parser.tval, "import") == 0 &&
1573 html != NULL &&
1574 importsAreAllowed) {
1575 parser.parseImport(html, url);
1576 } else if (dStrcasecmp(parser.tval, "media") == 0) {
1577 parser.parseMedia();
1578 } else {
1579 parser.ignoreStatement();
1580 }
1581 } else {
1582 parser.ignoreStatement();
1583 }
1584 } else {
1585 importsAreAllowed = false;
1586 parser.parseRuleset();
1587 }
1588 }
1589 }
1590
1591 void CssParser::parseDeclarationBlock(const char *buf, int buflen,
1592 CssPropertyList *props,
1593 CssPropertyList *propsImortant)
1594 {
1595 CssParser parser (NULL, CSS_ORIGIN_AUTHOR, buf, buflen);
1596
1597 parser.withinBlock = true;
1598
1599 do
1600 parser.parseDeclaration(props, propsImortant);
1601 while (!(parser.ttype == CSS_TK_END ||
1602 (parser.ttype == CSS_TK_CHAR && parser.tval[0] == '}')));
1603 }
0 #ifndef __CSSPARSER_HH__
1 #define __CSSPARSER_HH__
2
3 #include "css.hh"
4 #include "html_common.hh"
5
6 class CssParser {
7 private:
8 typedef enum {
9 CSS_TK_DECINT, CSS_TK_FLOAT, CSS_TK_COLOR, CSS_TK_SYMBOL,
10 CSS_TK_STRING, CSS_TK_CHAR, CSS_TK_END
11 } CssTokenType;
12
13 static const int maxStrLen = 256;
14 CssContext *context;
15 CssOrigin origin;
16
17 const char *buf;
18 int buflen, bufptr;
19
20 CssTokenType ttype;
21 char tval[maxStrLen];
22 bool withinBlock;
23 bool spaceSeparated; /* used when parsing CSS selectors */
24
25 CssParser(CssContext *context, CssOrigin origin,
26 const char *buf, int buflen);
27 int getChar();
28 void ungetChar();
29 void nextToken();
30 bool skipString(int c, const char *string);
31 bool tokenMatchesProperty(CssPropertyName prop, CssValueType * type);
32 bool parseValue(CssPropertyName prop, CssValueType type,
33 CssPropertyValue * val);
34 bool parseWeight();
35 bool parseRgbColorComponent(int32_t *cc, int *percentage);
36 bool parseRgbColor(int32_t *c);
37 void parseDeclaration(CssPropertyList * props,
38 CssPropertyList * importantProps);
39 bool parseSimpleSelector(CssSimpleSelector *selector);
40 char *parseUrl();
41 void parseImport(DilloHtml *html, DilloUrl *url);
42 void parseMedia();
43 CssSelector *parseSelector();
44 void parseRuleset();
45 void ignoreBlock();
46 void ignoreStatement();
47
48 public:
49 static void parseDeclarationBlock(const char *buf, int buflen,
50 CssPropertyList *props,
51 CssPropertyList *propsImortant);
52 static void parse(DilloHtml *html, DilloUrl *url, CssContext *context,
53 const char *buf, int buflen, CssOrigin origin);
54 static const char *propertyNameString(CssPropertyName name);
55 };
56
57 #endif
+0
-150
src/debug.h less more
0 #ifndef __DEBUG_H__
1 #define __DEBUG_H__
2
3 /*
4 * Simple debug messages. Add:
5 *
6 * #define DEBUG_LEVEL <n>
7 * #include "debug.h"
8 *
9 * to the file you are working on, or let DEBUG_LEVEL undefined to
10 * disable all messages. A higher level denotes a greater importance
11 * of the message.
12 */
13
14 #include <glib.h>
15
16
17 # ifdef DEBUG_LEVEL
18 # define DEBUG_MSG(level, fmt...) \
19 G_STMT_START { \
20 if (DEBUG_LEVEL && (level) >= DEBUG_LEVEL) \
21 g_print(fmt); \
22 } G_STMT_END
23 # else
24 # define DEBUG_MSG(level, fmt...)
25 # endif /* DEBUG_LEVEL */
26
27
28
29 /*
30 * Following is experimental, and will be explained soon.
31 */
32
33 #ifdef DBG_RTFL
34
35 #include <unistd.h>
36 #include <stdio.h>
37
38 #define DBG_MSG(obj, aspect, prio, msg) \
39 G_STMT_START { \
40 printf ("[rtfl]%s:%d:%d:msg:%p:%s:%d:%s\n", \
41 __FILE__, __LINE__, getpid(), obj, aspect, prio, msg); \
42 fflush (stdout); \
43 } G_STMT_END
44
45 #define DBG_MSGF(obj, aspect, prio, fmt, args...) \
46 G_STMT_START { \
47 printf ("[rtfl]%s:%d:%d:msg:%p:%s:%d:" fmt "\n", \
48 __FILE__, __LINE__, getpid(), obj, aspect, prio, args); \
49 fflush (stdout); \
50 } G_STMT_END
51
52 #define DBG_MSG_START(obj) \
53 G_STMT_START { \
54 printf ("[rtfl]%s:%d:%d:msg-start:%p\n", \
55 __FILE__, __LINE__, getpid(), obj); \
56 fflush (stdout); \
57 } G_STMT_END
58
59 #define DBG_MSG_END(obj) \
60 G_STMT_START { \
61 printf ("[rtfl]%s:%d:%d:msg-end:%p\n", \
62 __FILE__, __LINE__, getpid(), obj); \
63 fflush (stdout); \
64 } G_STMT_END
65
66 #define DBG_OBJ_CREATE(obj, klass) \
67 G_STMT_START { \
68 printf ("[rtfl]%s:%d:%d:obj-create:%p:%s\n", \
69 __FILE__, __LINE__, getpid(), obj, klass); \
70 fflush (stdout); \
71 } G_STMT_END
72
73 #define DBG_OBJ_ASSOC(child, parent) \
74 G_STMT_START { \
75 printf ("[rtfl]%s:%d:%d:obj-assoc:%p:%p\n", \
76 __FILE__, __LINE__, getpid(), child, parent); \
77 fflush (stdout); \
78 } G_STMT_END
79
80 #define DBG_OBJ_SET_NUM(obj, var, val) \
81 G_STMT_START { \
82 printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%d\n", \
83 __FILE__, __LINE__, getpid(), obj, var, val); \
84 fflush (stdout); \
85 } G_STMT_END
86
87 #define DBG_OBJ_SET_STR(obj, var, val) \
88 G_STMT_START { \
89 printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%s\n", \
90 __FILE__, __LINE__, getpid(), obj, var, val); \
91 fflush (stdout); \
92 } G_STMT_END
93
94 #define DBG_OBJ_SET_PTR(obj, var, val) \
95 G_STMT_START { \
96 printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%p\n", \
97 __FILE__, __LINE__, getpid(), obj, var, val); \
98 fflush (stdout); \
99 } G_STMT_END
100
101 #define DBG_OBJ_ARRSET_NUM(obj, var, ind, val) \
102 G_STMT_START { \
103 printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%d\n", \
104 __FILE__, __LINE__, getpid(), obj, ind, val); \
105 fflush (stdout); \
106 } G_STMT_END
107
108 #define DBG_OBJ_ARRSET_STR(obj, var, ind, val) \
109 G_STMT_START { \
110 printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%s\n", \
111 __FILE__, __LINE__, getpid(), obj, ind, val); \
112 fflush (stdout); \
113 } G_STMT_END
114
115 #define DBG_OBJ_ARRSET_PTR(obj, var, ind, val) \
116 G_STMT_START { \
117 printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%p\n", \
118 __FILE__, __LINE__, getpid(), obj, ind, val); \
119 fflush (stdout); \
120 } G_STMT_END
121
122 #define DBG_OBJ_COLOR(klass, color) \
123 G_STMT_START { \
124 printf ("[rtfl]%s:%d:%d:obj-color:%s:%s\n", \
125 __FILE__, __LINE__, getpid(), klass, color); \
126 fflush (stdout); \
127 } G_STMT_END
128
129 #else /* DBG_RTFL */
130
131 #define DBG_MSG(obj, aspect, prio, msg)
132 #define DBG_MSGF(obj, aspect, prio, fmt, arg...)
133 #define DBG_MSG_START(obj)
134 #define DBG_MSG_END(obj)
135 #define DBG_OBJ_CREATE(obj, klass)
136 #define DBG_OBJ_ASSOC(child, parent)
137 #define DBG_OBJ_SET_NUM(obj, var, val)
138 #define DBG_OBJ_SET_STR(obj, var, val)
139 #define DBG_OBJ_SET_PTR(obj, var, val)
140 #define DBG_OBJ_ARRSET_NUM(obj, var, ind, val)
141 #define DBG_OBJ_ARRSET_STR(obj, var, ind, val)
142 #define DBG_OBJ_ARRSET_PTR(obj, var, ind, val)
143 #define DBG_OBJ_COLOR(klass, color)
144
145 #endif /* DBG_RTFL */
146
147 #endif /* __DEBUG_H__ */
148
149
0 /*
1 * File: decode.c
2 *
3 * Copyright 2007-2008 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <zlib.h>
12 #include <iconv.h>
13 #include <errno.h>
14 #include <stdlib.h> /* strtol */
15
16 #include "decode.h"
17 #include "utf8.hh"
18 #include "msg.h"
19
20 static const int bufsize = 8*1024;
21
22 /*
23 * Decode chunked data
24 */
25 static Dstr *Decode_chunked(Decode *dc, const char *instr, int inlen)
26 {
27 char *inputPtr, *eol;
28 int inputRemaining;
29 int chunkRemaining = *((int *)dc->state);
30 Dstr *output = dStr_sized_new(inlen);
31
32 dStr_append_l(dc->leftover, instr, inlen);
33 inputPtr = dc->leftover->str;
34 inputRemaining = dc->leftover->len;
35
36 while (inputRemaining > 0) {
37 if (chunkRemaining > 2) {
38 /* chunk body to copy */
39 int copylen = MIN(chunkRemaining - 2, inputRemaining);
40 dStr_append_l(output, inputPtr, copylen);
41 chunkRemaining -= copylen;
42 inputRemaining -= copylen;
43 inputPtr += copylen;
44 }
45
46 if ((chunkRemaining == 2) && (inputRemaining > 0)) {
47 /* CR to discard */
48 chunkRemaining--;
49 inputRemaining--;
50 inputPtr++;
51 }
52 if ((chunkRemaining == 1) && (inputRemaining > 0)) {
53 /* LF to discard */
54 chunkRemaining--;
55 inputRemaining--;
56 inputPtr++;
57 }
58
59 /*
60 * A chunk has a one-line header that begins with the chunk length
61 * in hexadecimal.
62 */
63 if (!(eol = (char *)memchr(inputPtr, '\n', inputRemaining))) {
64 break; /* We don't have the whole line yet. */
65 }
66
67 if (!(chunkRemaining = strtol(inputPtr, NULL, 0x10))) {
68 break; /* A chunk length of 0 means we're done! */
69 }
70 inputRemaining -= (eol - inputPtr) + 1;
71 inputPtr = eol + 1;
72 chunkRemaining += 2; /* CRLF at the end of every chunk */
73 }
74
75 /* If we have a partial chunk header, save it for next time. */
76 dStr_erase(dc->leftover, 0, inputPtr - dc->leftover->str);
77
78 *(int *)dc->state = chunkRemaining;
79 return output;
80 }
81
82 static void Decode_chunked_free(Decode *dc)
83 {
84 dFree(dc->state);
85 dStr_free(dc->leftover, 1);
86 }
87
88 /*
89 * Decode gzipped data
90 */
91 static Dstr *Decode_gzip(Decode *dc, const char *instr, int inlen)
92 {
93 int rc = Z_OK;
94
95 z_stream *zs = (z_stream *)dc->state;
96
97 int inputConsumed = 0;
98 Dstr *output = dStr_new("");
99
100 while ((rc == Z_OK) && (inputConsumed < inlen)) {
101 zs->next_in = (Bytef *)instr + inputConsumed;
102 zs->avail_in = inlen - inputConsumed;
103
104 zs->next_out = (Bytef *)dc->buffer;
105 zs->avail_out = bufsize;
106
107 rc = inflate(zs, Z_SYNC_FLUSH);
108
109 dStr_append_l(output, dc->buffer, zs->total_out);
110
111 if ((rc == Z_OK) || (rc == Z_STREAM_END)) {
112 // Z_STREAM_END at end of file
113
114 inputConsumed += zs->total_in;
115 zs->total_out = 0;
116 zs->total_in = 0;
117 } else if (rc == Z_DATA_ERROR) {
118 MSG_ERR("gzip decompression error\n");
119 }
120 }
121 return output;
122 }
123
124 static void Decode_gzip_free(Decode *dc)
125 {
126 (void)inflateEnd((z_stream *)dc->state);
127
128 dFree(dc->state);
129 dFree(dc->buffer);
130 }
131
132 /*
133 * Translate to desired character set (UTF-8)
134 */
135 static Dstr *Decode_charset(Decode *dc, const char *instr, int inlen)
136 {
137 inbuf_t *inPtr;
138 char *outPtr;
139 size_t inLeft, outRoom;
140
141 Dstr *output = dStr_new("");
142 int rc = 0;
143
144 dStr_append_l(dc->leftover, instr, inlen);
145 inPtr = dc->leftover->str;
146 inLeft = dc->leftover->len;
147
148 while ((rc != EINVAL) && (inLeft > 0)) {
149
150 outPtr = dc->buffer;
151 outRoom = bufsize;
152
153 rc = iconv((iconv_t)dc->state, &inPtr, &inLeft, &outPtr, &outRoom);
154
155 // iconv() on success, number of bytes converted
156 // -1, errno == EILSEQ illegal byte sequence found
157 // EINVAL partial character ends source buffer
158 // E2BIG destination buffer is full
159
160 dStr_append_l(output, dc->buffer, bufsize - outRoom);
161
162 if (rc == -1)
163 rc = errno;
164 if (rc == EILSEQ){
165 inPtr++;
166 inLeft--;
167 dStr_append_l(output, utf8_replacement_char,
168 sizeof(utf8_replacement_char) - 1);
169 }
170 }
171 dStr_erase(dc->leftover, 0, dc->leftover->len - inLeft);
172
173 return output;
174 }
175
176 static void Decode_charset_free(Decode *dc)
177 {
178 /* iconv_close() frees dc->state */
179 (void)iconv_close((iconv_t)(dc->state));
180
181 dFree(dc->buffer);
182 dStr_free(dc->leftover, 1);
183 }
184
185 /*
186 * Initialize transfer decoder. Currently handles "chunked".
187 */
188 Decode *a_Decode_transfer_init(const char *format)
189 {
190 Decode *dc = NULL;
191
192 if (format && !dStrcasecmp(format, "chunked")) {
193 int *chunk_remaining = dNew(int, 1);
194 *chunk_remaining = 0;
195 dc = dNew(Decode, 1);
196 dc->leftover = dStr_new("");
197 dc->state = chunk_remaining;
198 dc->decode = Decode_chunked;
199 dc->free = Decode_chunked_free;
200 dc->buffer = NULL; /* not used */
201 _MSG("chunked!\n");
202 }
203 return dc;
204 }
205
206 /*
207 * Initialize content decoder. Currently handles gzip.
208 *
209 * zlib is also capable of handling "deflate"/zlib-encoded data, but web
210 * servers have not standardized on whether to send such data with a header.
211 */
212 Decode *a_Decode_content_init(const char *format)
213 {
214 Decode *dc = NULL;
215
216 if (format && *format) {
217 if (!dStrcasecmp(format, "gzip") || !dStrcasecmp(format, "x-gzip")) {
218 z_stream *zs;
219 _MSG("gzipped data!\n");
220
221 dc = dNew(Decode, 1);
222 dc->buffer = dNew(char, bufsize);
223 dc->state = zs = dNew(z_stream, 1);
224 zs->zalloc = NULL;
225 zs->zfree = NULL;
226 zs->next_in = NULL;
227 zs->avail_in = 0;
228
229 /* 16 is a magic number for gzip decoding */
230 inflateInit2(zs, MAX_WBITS+16);
231
232 dc->decode = Decode_gzip;
233 dc->free = Decode_gzip_free;
234 dc->leftover = NULL; /* not used */
235 } else {
236 MSG("Content-Encoding '%s' not recognized.\n", format);
237 }
238 }
239 return dc;
240 }
241
242 /*
243 * Legal names for the ASCII character set
244 */
245 static int Decode_is_ascii(const char *str)
246 {
247 return (!(dStrcasecmp(str, "ASCII") &&
248 dStrcasecmp(str, "US-ASCII") &&
249 dStrcasecmp(str, "us") &&
250 dStrcasecmp(str, "IBM367") &&
251 dStrcasecmp(str, "cp367") &&
252 dStrcasecmp(str, "csASCII") &&
253 dStrcasecmp(str, "ANSI_X3.4-1968") &&
254 dStrcasecmp(str, "iso-ir-6") &&
255 dStrcasecmp(str, "ANSI_X3.4-1986") &&
256 dStrcasecmp(str, "ISO_646.irv:1991") &&
257 dStrcasecmp(str, "ISO646-US")));
258 }
259
260 /*
261 * Initialize decoder to translate from any character set known to iconv()
262 * to UTF-8.
263 *
264 * GNU iconv(1) will provide a list of known character sets if invoked with
265 * the "--list" flag.
266 */
267 Decode *a_Decode_charset_init(const char *format)
268 {
269 Decode *dc = NULL;
270
271 if (format &&
272 strlen(format) &&
273 dStrcasecmp(format,"UTF-8") &&
274 !Decode_is_ascii(format)) {
275
276 iconv_t ic = iconv_open("UTF-8", format);
277 if (ic != (iconv_t) -1) {
278 dc = dNew(Decode, 1);
279 dc->state = ic;
280 dc->buffer = dNew(char, bufsize);
281 dc->leftover = dStr_new("");
282
283 dc->decode = Decode_charset;
284 dc->free = Decode_charset_free;
285 } else {
286 MSG_WARN("Unable to convert from character encoding: '%s'\n", format);
287 }
288 }
289 return dc;
290 }
291
292 /*
293 * Decode data.
294 */
295 Dstr *a_Decode_process(Decode *dc, const char *instr, int inlen)
296 {
297 return dc->decode(dc, instr, inlen);
298 }
299
300 /*
301 * Free the decoder.
302 */
303 void a_Decode_free(Decode *dc)
304 {
305 if (dc) {
306 dc->free(dc);
307 dFree(dc);
308 }
309 }
0 #ifndef __DECODE_H__
1 #define __DECODE_H__
2
3 #include "../dlib/dlib.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 typedef struct _Decode Decode;
10
11 struct _Decode {
12 char *buffer;
13 Dstr *leftover;
14 void *state;
15 Dstr *(*decode) (Decode *dc, const char *instr, int inlen);
16 void (*free) (Decode *dc);
17 };
18
19 Decode *a_Decode_transfer_init(const char *format);
20 Decode *a_Decode_content_init(const char *format);
21 Decode *a_Decode_charset_init(const char *format);
22 Dstr *a_Decode_process(Decode *dc, const char *instr, int inlen);
23 void a_Decode_free(Decode *dc);
24
25 #ifdef __cplusplus
26 }
27 #endif /* __cplusplus */
28
29 #endif /* __DECODE_H__ */
0 #ifndef __GIF_H__
1 #define __GIF_H__
2
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7 #include "url.h"
8 #include "image.hh"
9
10
11 void *a_Gif_new(DilloImage *Image, DilloUrl *url, int version);
12 void a_Gif_callback(int Op, void *data);
13
14
15 #ifdef __cplusplus
16 }
17 #endif /* __cplusplus */
18 #endif /* !__GIF_H__ */
0 /*
1 * File: dialog.cc
2 *
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 // UI dialogs
12
13 #include <math.h> // for rint()
14
15 #include <FL/Fl_Window.H>
16 #include <FL/Fl_File_Chooser.H>
17 #include <FL/Fl_Return_Button.H>
18 #include <FL/Fl_Text_Display.H>
19 #include <FL/Fl_Button.H>
20 #include <FL/Fl_Return_Button.H>
21 #include <FL/Fl_Output.H>
22 #include <FL/Fl_Input.H>
23 #include <FL/Fl_Secret_Input.H>
24
25 #include "msg.h"
26 #include "dialog.hh"
27 #include "misc.h"
28 #include "prefs.h"
29
30 /*
31 * Local Data
32 */
33 static int input_answer;
34 static char *input_str = NULL;
35 static int choice5_answer;
36
37
38 /*
39 * Local sub classes
40 */
41
42 //----------------------------------------------------------------------------
43 /*
44 * Used to enable CTRL+{a,e,d,k} in search dialog (for start,end,del,cut)
45 * TODO: bind down arrow to a search engine selection list.
46 */
47 class CustInput3 : public Fl_Input {
48 public:
49 CustInput3 (int x, int y, int w, int h, const char* l=0) :
50 Fl_Input(x,y,w,h,l) {};
51 int handle(int e);
52 };
53
54 int CustInput3::handle(int e)
55 {
56 int k = Fl::event_key();
57
58 _MSG("CustInput3::handle event=%d\n", e);
59
60 // We're only interested in some flags
61 unsigned modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL | FL_ALT);
62
63 if (e == FL_KEYBOARD && modifier == FL_CTRL) {
64 if (k == 'a' || k == 'e') {
65 position(k == 'a' ? 0 : size());
66 return 1;
67 } else if (k == 'k') {
68 cut(position(), size());
69 return 1;
70 } else if (k == 'd') {
71 cut(position(), position()+1);
72 return 1;
73 }
74 }
75 return Fl_Input::handle(e);
76 }
77 //----------------------------------------------------------------------------
78
79
80 /*
81 * Display a message in a popup window.
82 */
83 void a_Dialog_msg(const char *msg)
84 {
85 fl_message("%s", msg);
86 }
87
88
89 /*
90 * Callback for a_Dialog_input()
91 */
92 static void input_cb(Fl_Widget *button, void *number)
93 {
94 input_answer = VOIDP2INT(number);
95 button->window()->hide();
96 }
97
98 /*
99 * Dialog for one line of Input with a message.
100 * avoids the sound bell in fl_input(), and allows customization
101 *
102 * Return value: string on success, NULL upon Cancel or Close window
103 */
104 const char *a_Dialog_input(const char *msg)
105 {
106 int ww = 450, wh = 130, gap = 10, ih = 60, bw = 80, bh = 30;
107
108 input_answer = 0;
109
110 Fl_Window *window = new Fl_Window(ww,wh,"Ask");
111 window->set_modal();
112 window->begin();
113 Fl_Group* ib = new Fl_Group(0,0,window->w(),window->h());
114 ib->begin();
115 window->resizable(ib);
116
117 /* '?' Icon */
118 Fl_Box* o = new Fl_Box(gap, gap, ih, ih);
119 o->box(FL_THIN_UP_BOX);
120 o->labelfont(FL_TIMES_BOLD);
121 o->labelsize(34);
122 o->color(FL_WHITE);
123 o->labelcolor(FL_BLUE);
124 o->label("?");
125 o->show();
126
127 Fl_Box *box = new Fl_Box(ih+2*gap,gap,ww-(ih+3*gap),ih/2, msg);
128 box->labelfont(FL_HELVETICA);
129 box->labelsize(14);
130 box->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CLIP|FL_ALIGN_WRAP);
131
132 CustInput3 *c_inp = new CustInput3(ih+2*gap,gap+ih/2+gap,ww-(ih+3*gap),24);
133 c_inp->labelsize(14);
134 c_inp->textsize(14);
135
136 int xpos = ww-2*(gap+bw), ypos = ih+3*gap;
137 Fl_Return_Button *rb = new Fl_Return_Button(xpos, ypos, bw, bh, "OK");
138 rb->align(FL_ALIGN_INSIDE|FL_ALIGN_CLIP);
139 rb->box(FL_UP_BOX);
140 rb->callback(input_cb, INT2VOIDP(1));
141
142 xpos = ww-(gap+bw);
143 Fl_Button *b = new Fl_Button(xpos, ypos, bw, bh, "Cancel");
144 b->align(FL_ALIGN_INSIDE|FL_ALIGN_CLIP);
145 b->box(FL_UP_BOX);
146 b->callback(input_cb, INT2VOIDP(2));
147
148 window->end();
149
150 window->show();
151 while (window->shown())
152 Fl::wait();
153 _MSG("a_Dialog_input answer = %d\n", input_answer);
154 if (input_answer == 1) {
155 /* we have a string, save it */
156 dFree(input_str);
157 input_str = dStrdup(c_inp->value());
158 }
159 delete window;
160
161 return (input_answer == 1) ? input_str : NULL;
162 }
163
164 /*
165 * Dialog for password
166 */
167 const char *a_Dialog_passwd(const char *msg)
168 {
169 return fl_password("%s", "", msg);
170 }
171
172 /*
173 * Show the save file dialog.
174 *
175 * Return: pointer to chosen filename, or NULL on Cancel.
176 */
177 const char *a_Dialog_save_file(const char *msg,
178 const char *pattern, const char *fname)
179 {
180 return fl_file_chooser(msg, pattern, fname);
181 }
182
183 /*
184 * Show the select file dialog.
185 *
186 * Return: pointer to chosen filename, or NULL on Cancel.
187 */
188 const char *a_Dialog_select_file(const char *msg,
189 const char *pattern, const char *fname)
190 {
191 /*
192 * FileChooser::type(MULTI) appears to allow multiple files to be selected,
193 * but just follow save_file's path for now.
194 */
195 return a_Dialog_save_file(msg, pattern, fname);
196 }
197
198 /*
199 * Show the open file dialog.
200 *
201 * Return: pointer to chosen filename, or NULL on Cancel.
202 */
203 char *a_Dialog_open_file(const char *msg,
204 const char *pattern, const char *fname)
205 {
206 const char *fc_name;
207
208 fc_name = fl_file_chooser(msg, pattern, fname);
209 return (fc_name) ? a_Misc_escape_chars(fc_name, "% ") : NULL;
210 }
211
212 /*
213 * Close text window.
214 */
215 static void text_window_close_cb(Fl_Widget *, void *vtd)
216 {
217 Fl_Text_Display *td = (Fl_Text_Display *)vtd;
218 Fl_Text_Buffer *buf = td->buffer();
219
220 delete (Fl_Window*)td->window();
221 delete buf;
222 }
223
224 /*
225 * Show a new window with the provided text
226 */
227 void a_Dialog_text_window(const char *txt, const char *title)
228 {
229 int wh = prefs.height, ww = prefs.width, bh = 30;
230
231 Fl_Window *window = new Fl_Window(ww, wh, title ? title : "Dillo text");
232 Fl_Group::current(0);
233
234
235 Fl_Text_Buffer *buf = new Fl_Text_Buffer();
236 buf->text(txt);
237 Fl_Text_Display *td = new Fl_Text_Display(0,0,ww, wh-bh);
238 td->buffer(buf);
239 td->textsize((int) rint(14.0 * prefs.font_factor));
240
241 /* enable wrapping lines; text uses entire width of window */
242 td->wrap_mode(true, false);
243 window->add(td);
244
245 Fl_Return_Button *b = new Fl_Return_Button (0, wh-bh, ww, bh, "Close");
246 b->callback(text_window_close_cb, td);
247 window->add(b);
248
249 window->callback(text_window_close_cb, td);
250 window->resizable(td);
251 window->show();
252 }
253
254 /*--------------------------------------------------------------------------*/
255
256 static void choice5_cb(Fl_Widget *button, void *number)
257 {
258 choice5_answer = VOIDP2INT(number);
259 _MSG("choice5_cb: %d\n", choice5_answer);
260 button->window()->hide();
261 }
262
263 /*
264 * Make a question-dialog with a question and up to five alternatives.
265 * (if less alternatives, non used parameters must be NULL).
266 *
267 * Return value: 0 = dialog was cancelled, 1-5 = selected alternative.
268 */
269 int a_Dialog_choice5(const char *QuestionTxt,
270 const char *alt1, const char *alt2, const char *alt3,
271 const char *alt4, const char *alt5)
272 {
273 choice5_answer = 0;
274
275 int ww = 440, wh = 120, bw = 50, bh = 45, ih = 50, nb = 0;
276 const char *txt[7];
277
278 txt[0] = txt[6] = NULL;
279 txt[1] = alt1; txt[2] = alt2; txt[3] = alt3;
280 txt[4] = alt4; txt[5] = alt5;
281 for (int i=1; txt[i]; ++i, ++nb)
282 ;
283
284 if (!nb) {
285 MSG_ERR("a_Dialog_choice5: No choices.\n");
286 return choice5_answer;
287 }
288 ww = 140 + nb*(bw+10);
289
290 Fl_Window *window = new Fl_Window(ww,wh,"Choice5");
291 window->set_modal();
292 window->begin();
293 Fl_Group* ib = new Fl_Group(0,0,window->w(),window->h());
294 ib->begin();
295 window->resizable(ib);
296
297 /* '?' Icon */
298 Fl_Box* o = new Fl_Box(10, (wh-bh-ih)/2, ih, ih);
299 o->box(FL_THIN_UP_BOX);
300 o->labelfont(FL_TIMES_BOLD);
301 o->labelsize(34);
302 o->color(FL_WHITE);
303 o->labelcolor(FL_BLUE);
304 o->label("?");
305 o->show();
306
307 Fl_Box *box = new Fl_Box(60,0,ww-60,wh-bh, QuestionTxt);
308 box->labelfont(FL_HELVETICA);
309 box->labelsize(14);
310 box->align(FL_ALIGN_WRAP);
311
312 Fl_Button *b;
313 int xpos = 0, gap = 8;
314 bw = (ww - gap)/nb - gap;
315 xpos += gap;
316 for (int i=1; i <= nb; ++i) {
317 b = new Fl_Button(xpos, wh-bh, bw, bh, txt[i]);
318 b->align(FL_ALIGN_WRAP|FL_ALIGN_CLIP);
319 b->box(FL_UP_BOX);
320 b->callback(choice5_cb, INT2VOIDP(i));
321 xpos += bw + gap;
322 /* TODO: set focus to the *-prefixed alternative */
323 }
324 window->end();
325
326 window->show();
327 while (window->shown())
328 Fl::wait();
329 _MSG("a_Dialog_choice5 answer = %d\n", choice5_answer);
330 delete window;
331
332 return choice5_answer;
333 }
334
335
336 /*--------------------------------------------------------------------------*/
337 static void Dialog_user_password_cb(Fl_Widget *button, void *)
338 {
339 button->window()->user_data(button);
340 button->window()->hide();
341 }
342
343 /*
344 * Make a user/password dialog.
345 * Call the callback with the result (OK or not) and the given user and
346 * password if OK.
347 */
348 int a_Dialog_user_password(const char *message, UserPasswordCB cb, void *vp)
349 {
350 int ok = 0, window_h = 280, y, msg_w, msg_h;
351 const int window_w = 300, input_x = 80, input_w = 200, input_h = 30,
352 button_h = 30;
353
354 /* window is resized below */
355 Fl_Window *window = new Fl_Window(window_w,window_h,"Dillo User/Password");
356 Fl_Group::current(0);
357 window->user_data(NULL);
358
359 /* message */
360 y = 20;
361 msg_w = window_w - 40;
362 Fl_Box *msg = new Fl_Box(20, y, msg_w, 100); /* resized below */
363 msg->label(message);
364 msg->labelfont(FL_HELVETICA);
365 msg->labelsize(14);
366 msg->align(FL_ALIGN_INSIDE | FL_ALIGN_TOP_LEFT | FL_ALIGN_WRAP);
367
368 fl_font(msg->labelfont(), msg->labelsize());
369 msg_w -= 6; /* The label doesn't fill the entire box. */
370 fl_measure(msg->label(), msg_w, msg_h, 0); /* fl_measure wraps at msg_w */
371 msg->size(msg->w(), msg_h);
372 window->add(msg);
373
374 /* inputs */
375 y += msg_h + 20;
376 Fl_Input *user_input = new Fl_Input(input_x, y, input_w, input_h, "User");
377 user_input->labelsize(14);
378 user_input->textsize(14);
379 window->add(user_input);
380 y += input_h + 10;
381 Fl_Secret_Input *password_input =
382 new Fl_Secret_Input(input_x, y, input_w, input_h, "Password");
383 password_input->labelsize(14);
384 password_input->textsize(14);
385 window->add(password_input);
386
387 /* "OK" button */
388 y += input_h + 20;
389 Fl_Button *ok_button = new Fl_Button(200, y, 50, button_h, "OK");
390 ok_button->labelsize(14);
391 ok_button->callback(Dialog_user_password_cb);
392 window->add(ok_button);
393
394 /* "Cancel" button */
395 Fl_Button *cancel_button =
396 new Fl_Button(50, y, 100, button_h, "Cancel");
397 cancel_button->labelsize(14);
398 cancel_button->callback(Dialog_user_password_cb);
399 window->add(cancel_button);
400
401 y += button_h + 20;
402 window_h = y;
403 window->size(window_w, window_h);
404 window->size_range(window_w, window_h, window_w, window_h);
405 window->resizable(window);
406
407 window->show();
408 while (window->shown())
409 Fl::wait();
410
411 ok = ((Fl_Widget *)window->user_data()) == ok_button ? 1 : 0;
412
413 if (ok) {
414 /* call the callback */
415 const char *user, *password;
416 user = user_input->value();
417 password = password_input->value();
418 _MSG("a_Dialog_user_passwd: ok = %d\n", ok);
419 (*cb)(user, password, vp);
420 }
421 delete window;
422
423 return ok;
424 }
425
0 #ifndef __DIALOG_HH__
1 #define __DIALOG_HH__
2
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7 typedef void (*UserPasswordCB)(const char *user, const char *password,
8 void *vp);
9
10 void a_Dialog_msg(const char *msg);
11 int a_Dialog_choice5(const char *QuestionTxt,
12 const char *alt1, const char *alt2, const char *alt3,
13 const char *alt4, const char *alt5);
14 int a_Dialog_user_password(const char *message, UserPasswordCB cb, void *vp);
15 const char *a_Dialog_input(const char *msg);
16 const char *a_Dialog_passwd(const char *msg);
17 const char *a_Dialog_save_file(const char *msg,
18 const char *pattern, const char *fname);
19 const char *a_Dialog_select_file(const char *msg,
20 const char *pattern, const char *fname);
21 char *a_Dialog_open_file(const char *msg,
22 const char *pattern, const char *fname);
23 void a_Dialog_text_window(const char *txt, const char *title);
24
25 #ifdef __cplusplus
26 }
27 #endif /* __cplusplus */
28
29 #endif // __DIALOG_HH__
00 /*
11 * File: dicache.c
22 *
3 * Copyright 2000 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
11 #include <glib.h> /* GHashTable functions */
12 #include <gtk/gtk.h>
13 #include <sys/time.h> /* for libc5 compatibility */
1411 #include <string.h> /* for memset */
15 #include <stdio.h>
1612 #include <stdlib.h>
1713
18 #include "image.h"
19 #include "web.h"
14 #include "msg.h"
15 #include "image.hh"
16 #include "imgbuf.hh"
17 #include "web.hh"
2018 #include "dicache.h"
21 #include "cache.h"
22 #include "interface.h"
23 #include "prefs.h"
24
25 typedef struct _DICacheHashEntry DICacheHashEntry;
26
27 struct _DICacheHashEntry {
28 gint valid;
29 DilloUrl *url; /* "Key" of this hash entry */
30 DICacheEntry* next; /* pointer to the first dicache entry in this list */
19 #include "dpng.h"
20 #include "dgif.h"
21 #include "djpeg.h"
22
23 typedef struct _DICacheNode DICacheNode;
24
25 enum {
26 DIC_Gif,
27 DIC_Png,
28 DIC_Jpeg
3129 };
3230
33 static GHashTable *dicache_hash;
34
35 static gint dicache_size_total; /* invariant: dicache_size_total is
36 * the sum of the image sizes of all
37 * cache lines in dicache. */
38
39 /*
40 * Determine if two hash entries are equal (used by GHashTable)
41 */
42 static gint Dicache_hash_entry_equal(gconstpointer v1, gconstpointer v2)
43 {
44 return (!a_Url_cmp((DilloUrl *)v1, (DilloUrl *)v2));
45 }
46
47 /*
48 * Determine the hash value given the key (used by GHashTable)
49 */
50 static guint Dicache_url_hash(gconstpointer key)
51 {
52 return g_str_hash(URL_STR(((DilloUrl *)key)));
31 struct _DICacheNode {
32 int valid; /* flag */
33 DilloUrl *url; /* primary "Key" for this dicache entry */
34 DICacheEntry *first; /* pointer to the first dicache entry in this list */
35 };
36
37 /*
38 * List of DICacheNode. One node per URL. Each node may have several
39 * versions of the same image in a linked list.
40 */
41 static Dlist *CachedIMGs = NULL;
42
43 static uint_t dicache_size_total; /* invariant: dicache_size_total is
44 * the sum of the image sizes (3*w*h)
45 * of all the images in the dicache. */
46
47 /*
48 * Compare two dicache nodes
49 */
50 static int Dicache_node_cmp(const void *v1, const void *v2)
51 {
52 const DICacheNode *n1 = v1, *n2 = v2;
53
54 return a_Url_cmp(n1->url, n2->url);
55 }
56
57 /*
58 * Compare function for searching a node by Url
59 */
60 static int Dicache_node_by_url_cmp(const void *v1, const void *v2)
61 {
62 const DICacheNode *node = v1;
63 const DilloUrl *url = v2;
64
65 return a_Url_cmp(node->url, url);
5366 }
5467
5568 /*
5770 */
5871 void a_Dicache_init(void)
5972 {
60 dicache_hash = g_hash_table_new(Dicache_url_hash, Dicache_hash_entry_equal);
73 CachedIMGs = dList_new(256);
6174 dicache_size_total = 0;
6275 }
6376
6679 */
6780 static DICacheEntry *Dicache_entry_new(void)
6881 {
69 DICacheEntry *entry = g_new(DICacheEntry, 1);
82 DICacheEntry *entry = dNew(DICacheEntry, 1);
7083
7184 entry->width = 0;
7285 entry->height = 0;
7386 entry->type = DILLO_IMG_TYPE_NOTSET;
7487 entry->cmap = NULL;
75 entry->ImageBuffer = NULL;
88 entry->v_imgbuf = NULL;
7689 entry->RefCount = 1;
7790 entry->TotalSize = 0;
78 entry->Y = 0;
91 entry->ScanNumber = 0;
7992 entry->BitVec = NULL;
8093 entry->State = DIC_Empty;
81 entry->version = 0;
94 entry->version = 1;
95
96 entry->Decoder = NULL;
97 entry->DecoderData = NULL;
98 entry->DecodedSize = 0;
99
82100 entry->next = NULL;
83101
84102 return entry;
85103 }
86104
87105 /*
88 * Add a new entry to the hash
89 */
90 DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url)
106 * Add a new entry in the dicache
107 * (a single node (URL) may have several entries)
108 */
109 static DICacheEntry *Dicache_add_entry(const DilloUrl *Url)
91110 {
92111 DICacheEntry *entry;
93 DICacheHashEntry *hash_entry;
112 DICacheNode *node;
94113
95114 entry = Dicache_entry_new();
96115
97 if ( (hash_entry = g_hash_table_lookup(dicache_hash, Url))) {
98 /* this URL is already in the hash, add entry at the END of the list */
99 DICacheEntry *ptr = hash_entry->next;
100
101 hash_entry->valid = 1;
116 if ((node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp))) {
117 /* this URL is already in CachedIMGs, add entry at the END of the list */
118 DICacheEntry *ptr = node->first;
119
120 node->valid = 1;
102121 for ( ; ptr->next; ptr = ptr->next);
103122 ptr->next = entry;
104123 entry->version = ptr->version+1;
105 entry->url = hash_entry->url;
106
107 } else { /* no hash entry yet, so create one */
108 DICacheHashEntry *hash_entry = g_new(DICacheHashEntry, 1);
109
110 hash_entry->url = a_Url_dup(Url);
111 entry->url = hash_entry->url;
112 hash_entry->next = entry;
113 hash_entry->valid = 1;
114 g_hash_table_insert(dicache_hash, hash_entry->url, hash_entry);
124 entry->url = node->url;
125
126 } else { /* no node yet, so create one */
127 DICacheNode *node = dNew(DICacheNode, 1);
128
129 node->url = a_Url_dup(Url);
130 entry->url = node->url;
131 node->first = entry;
132 node->valid = 1;
133 dList_insert_sorted(CachedIMGs, node, Dicache_node_cmp);
115134 }
116135
117136 return entry;
118137 }
119138
120139 /*
121 * Search an entry in the dicache (given the Url).
122 * Return value: a pointer to the entry of the _newest_ (i.e. highest)
123 * version if found; NULL otherwise.
124 */
125 DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url)
126 {
127 DICacheHashEntry *hash_entry = g_hash_table_lookup(dicache_hash, Url);
128 DICacheEntry *entry;
129
130 if (!hash_entry || !hash_entry->valid)
131 return NULL;
132
133 for (entry = hash_entry->next; (entry && entry->next); entry = entry->next);
134
140 * Search a particular version of a URL in the Dicache.
141 * Return value: a pointer to the entry if found; NULL otherwise.
142 *
143 * Notes: DIC_Last means last version of the image.
144 * version zero is not allowed.
145 */
146 DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url, int version)
147 {
148 DICacheNode *node;
149 DICacheEntry *entry = NULL;
150
151 dReturn_val_if_fail(version != 0, NULL);
152
153 node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
154 if (node) {
155 if (version == DIC_Last) {
156 if (node->valid) {
157 entry = node->first;
158 for ( ; (entry && entry->next); entry = entry->next);
159 }
160 } else {
161 entry = node->first;
162 for ( ; entry && entry->version != version; entry = entry->next) ;
163 }
164 }
135165 return entry;
136166 }
137167
138168 /*
139 * Search a particular version of an URL in the Dicache.
140 * Return value: a pointer to the entry if found; NULL otherwise.
141 */
142 static DICacheEntry *Dicache_get_entry_version(const DilloUrl *Url,
143 gint version)
144 {
145 DICacheHashEntry *hash_entry = g_hash_table_lookup(dicache_hash, Url);
146 DICacheEntry *entry = hash_entry ? hash_entry->next : NULL;
147
148 while (entry && entry->version != version)
149 entry = entry->next;
150
151 return entry;
152 }
153
154 /*
155169 * Actually free a dicache entry, given the URL and the version number.
156170 */
157 static void Dicache_remove(const DilloUrl *Url, gint version)
158 {
159 DICacheHashEntry *hash_entry = g_hash_table_lookup(dicache_hash, Url);
160 DICacheEntry *entry = hash_entry ? hash_entry->next : NULL, *prev = entry;
171 static void Dicache_remove(const DilloUrl *Url, int version)
172 {
173 DICacheNode *node;
174 DICacheEntry *entry, *prev;
175 _MSG("Dicache_remove url=%s\n", URL_STR(Url));
176 node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
177 prev = entry = (node) ? node->first : NULL;
161178
162179 while (entry && (entry->version != version) ) {
163180 prev = entry;
164181 entry = entry->next;
165182 }
166183
167 if ( entry ) {
184 if (entry) {
185 _MSG("Dicache_remove Decoder=%p DecoderData=%p\n",
186 entry->Decoder, entry->DecoderData);
168187 /* Eliminate this dicache entry */
169 g_free(entry->cmap);
170 if (entry->ImageBuffer) {
171 free(entry->ImageBuffer);
172 dicache_size_total -= entry->TotalSize;
173 }
188 dFree(entry->cmap);
174189 a_Bitvec_free(entry->BitVec);
175
176 if (hash_entry->next == entry) {
190 a_Imgbuf_unref(entry->v_imgbuf);
191 if (entry->Decoder) {
192 entry->Decoder(CA_Abort, entry->DecoderData);
193 }
194 dicache_size_total -= entry->TotalSize;
195
196 if (node->first == entry) {
177197 if (!entry->next) {
178 /* last entry with this URL. Remove the hash entry as well */
179 g_hash_table_remove(dicache_hash, hash_entry->url);
180 a_Url_free(hash_entry->url);
181 g_free(hash_entry);
198 /* last entry with this URL. Remove the node as well */
199 dList_remove(CachedIMGs, node);
200 a_Url_free(node->url);
201 dFree(node);
182202 } else
183 hash_entry->next = entry->next;
184 } else
203 node->first = entry->next;
204 } else {
185205 prev->next = entry->next;
186 g_free(entry);
206 }
207 dFree(entry);
187208 }
188209 }
189210
190211 /*
191212 * Unrefs the counter of a dicache entry, and _if_ no DwImage is acessing
192 * this buffer _and_ there is a higher version of this image, then we call
193 * Dicache_free to do the dirty job.
194 */
195 void a_Dicache_unref(const DilloUrl *Url, gint version)
213 * this buffer, then we call Dicache_remove() to do the job.
214 */
215 void a_Dicache_unref(const DilloUrl *Url, int version)
196216 {
197217 DICacheEntry *entry;
198218
199 if ( (entry = Dicache_get_entry_version(Url, version)) )
200 if (--entry->RefCount == 0 && (entry->next || !prefs.use_dicache) )
219 _MSG("a_Dicache_unref\n");
220 if ((entry = a_Dicache_get_entry(Url, version))) {
221 if (--entry->RefCount == 0) {
201222 Dicache_remove(Url, version);
223 }
224 }
202225 }
203226
204227 /*
205228 * Refs the counter of a dicache entry.
206229 */
207
208 DICacheEntry* a_Dicache_ref(const DilloUrl *Url, gint version)
230 DICacheEntry* a_Dicache_ref(const DilloUrl *Url, int version)
209231 {
210232 DICacheEntry *entry;
211233
212 if ( (entry = Dicache_get_entry_version(Url, version)) )
234 if ((entry = a_Dicache_get_entry(Url, version))) {
213235 ++entry->RefCount;
214
236 }
215237 return entry;
216238 }
217239
218240 /*
219241 * Invalidate this entry. This is used for the reloading mechanism.
220 * Can't erase current versions, but a_Dicache_get_entry must return NULL.
242 * Can't erase current versions, but a_Dicache_get_entry(url, DIC_Last)
243 * must return NULL.
221244 */
222245 void a_Dicache_invalidate_entry(const DilloUrl *Url)
223246 {
224 DICacheHashEntry *hash_entry = g_hash_table_lookup(dicache_hash, Url);
225
226 if (hash_entry) hash_entry->valid = 0;
227 }
228
229
230 /* ------------------------------------------------------------------------- */
231
232 /*
233 * This function is a cache client; (but feeds its clients from dicache)
234 */
235 void a_Dicache_callback(int Op, CacheClient_t *Client)
236 {
237 /* todo: Handle Op = CA_Abort (to show what was got) --Jcid */
238 guint i;
239 DilloWeb *Web = Client->Web;
240 DilloImage *Image = Web->Image;
241 DICacheEntry *DicEntry = a_Dicache_get_entry(Web->url);
242
243 g_return_if_fail ( DicEntry != NULL );
244
245 if ( Op == CA_Send ) {
246 if ( Image->height == 0 && DicEntry->State >= DIC_SetParms ) {
247 /* Set parms */
248 a_Image_set_parms(
249 Image, DicEntry->ImageBuffer, DicEntry->url,
250 DicEntry->version, DicEntry->width, DicEntry->height,
251 DicEntry->type);
252 }
253 if (DicEntry->State == DIC_Write) {
254 for (i = 0; i < DicEntry->height; ++i)
255 if (a_Bitvec_get_bit(DicEntry->BitVec, (gint)i) &&
256 !a_Bitvec_get_bit(Image->BitVec, (gint)i) )
257 a_Image_write(
258 Image, DicEntry->ImageBuffer + i*DicEntry->width*3, i,FALSE);
259
260 }
261 } else if ( Op == CA_Close || Op == CA_Abort ) {
262 a_Image_close(Web->Image);
263 a_Interface_close_client(Web->bw, Client->Key);
264 }
265 }
247 DICacheNode *node;
248
249 node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
250 if (node)
251 node->valid = 0;
252 }
253
266254
267255 /* ------------------------------------------------------------------------- */
268256
270258 * Set image's width, height & type
271259 * (By now, we'll use the image information despite the html tags --Jcid)
272260 */
273 void a_Dicache_set_parms(DilloUrl *url, gint version, DilloImage *Image,
274 guint width, guint height, DilloImgType type)
261 void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
262 uint_t width, uint_t height, DilloImgType type)
275263 {
276264 DICacheEntry *DicEntry;
277 size_t Size = width * height * 3;
278
279 g_return_if_fail ( Image != NULL && width && height );
265
266 _MSG("a_Dicache_set_parms (%s)\n", URL_STR(url));
267 dReturn_if_fail ( Image != NULL && width && height );
280268 /* Find the DicEntry for this Image */
281 DicEntry = Dicache_get_entry_version(url, version);
282 g_return_if_fail ( DicEntry != NULL );
283
284 /* Initialize the DicEntry */
285 /* using malloc() instead of g_new() avoids crashes on oversized images */
286 DicEntry->ImageBuffer = malloc(sizeof(guchar) * Size);
287 g_return_if_fail ( DicEntry->ImageBuffer != NULL );
288 DicEntry->TotalSize = Size;
269 DicEntry = a_Dicache_get_entry(url, version);
270 dReturn_if_fail ( DicEntry != NULL );
271 /* Parameters already set? */
272 dReturn_if_fail ( DicEntry->State < DIC_SetParms );
273
274 _MSG(" RefCount=%d version=%d\n", DicEntry->RefCount, DicEntry->version);
275
276 /* BUG: there's just one image-type now */
277 #define I_RGB 0
278 DicEntry->v_imgbuf = a_Imgbuf_new(Image->dw, I_RGB, width, height);
279
280 DicEntry->TotalSize = width * height * 3;
289281 DicEntry->width = width;
290282 DicEntry->height = height;
291283 DicEntry->type = type;
292 DicEntry->BitVec = a_Bitvec_new((gint)height);
284 DicEntry->BitVec = a_Bitvec_new((int)height);
293285 DicEntry->State = DIC_SetParms;
294286
295 dicache_size_total += Size;
296
297 /* For giggles, make the background of the undrawn parts interesting */
298 memset(DicEntry->ImageBuffer, 0xdd, DicEntry->TotalSize);
299
300 /* Allocate and initialize this image */
301 a_Image_set_parms(Image, DicEntry->ImageBuffer, url, version,
302 width, height, type);
287 dicache_size_total += DicEntry->TotalSize;
303288 }
304289
305290 /*
306291 * Implement the set_cmap method for the Image
307292 */
308 void a_Dicache_set_cmap(DilloUrl *url, gint version, DilloImage *Image,
309 const guchar *cmap, guint num_colors,
310 gint num_colors_max, gint bg_index)
311 {
312 DICacheEntry *DicEntry = Dicache_get_entry_version(url, version);
313
314 g_return_if_fail ( DicEntry != NULL );
315
316 g_free(DicEntry->cmap);
317 DicEntry->cmap = g_new0(guchar, 3 * num_colors_max);
293 void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
294 const uchar_t *cmap, uint_t num_colors,
295 int num_colors_max, int bg_index)
296 {
297 DICacheEntry *DicEntry = a_Dicache_get_entry(url, version);
298
299 _MSG("a_Dicache_set_cmap\n");
300 dReturn_if_fail ( DicEntry != NULL );
301
302 dFree(DicEntry->cmap);
303 DicEntry->cmap = dNew0(uchar_t, 3 * num_colors_max);
318304 memcpy(DicEntry->cmap, cmap, 3 * num_colors);
319 if (bg_index >= 0 && (guint)bg_index < num_colors) {
305 if (bg_index >= 0 && (uint_t)bg_index < num_colors) {
320306 DicEntry->cmap[bg_index * 3] = (Image->bg_color >> 16) & 0xff;
321307 DicEntry->cmap[bg_index * 3 + 1] = (Image->bg_color >> 8) & 0xff;
322308 DicEntry->cmap[bg_index * 3 + 2] = (Image->bg_color) & 0xff;
323309 }
324310
325 a_Image_set_cmap(Image, DicEntry->cmap);
326311 DicEntry->State = DIC_SetCmap;
312 }
313
314 /*
315 * Reset for a new scan from a multiple-scan image.
316 */
317 void a_Dicache_new_scan(const DilloUrl *url, int version)
318 {
319 DICacheEntry *DicEntry;
320
321 _MSG("a_Dicache_new_scan\n");
322 dReturn_if_fail ( url != NULL );
323 DicEntry = a_Dicache_get_entry(url, version);
324 dReturn_if_fail ( DicEntry != NULL );
325 if (DicEntry->State < DIC_SetParms) {
326 MSG("a_Dicache_new_scan before DIC_SetParms\n");
327 exit(1);
328 }
329 a_Bitvec_clear(DicEntry->BitVec);
330 DicEntry->ScanNumber++;
331 a_Imgbuf_new_scan(DicEntry->v_imgbuf);
327332 }
328333
329334 /*
331336 * (Write a scan line into the Dicache entry)
332337 * buf: row buffer
333338 * Y : row number
334 * x : horizontal offset? (always zero)
335 */
336 void a_Dicache_write(DilloImage *Image, DilloUrl *url, gint version,
337 const guchar *buf, gint x, guint Y)
339 */
340 void a_Dicache_write(DilloUrl *url, int version, const uchar_t *buf, uint_t Y)
338341 {
339342 DICacheEntry *DicEntry;
340343
341 g_return_if_fail ( Image != NULL );
342 DicEntry = Dicache_get_entry_version(url, version);
343 g_return_if_fail ( DicEntry != NULL );
344 g_return_if_fail ( DicEntry->width > 0 && DicEntry->height > 0 );
345
346 a_Image_write(Image, buf, Y, TRUE);
347 DicEntry->Y = Y;
348 a_Bitvec_set_bit(DicEntry->BitVec, (gint)Y);
344 _MSG("a_Dicache_write\n");
345 DicEntry = a_Dicache_get_entry(url, version);
346 dReturn_if_fail ( DicEntry != NULL );
347 dReturn_if_fail ( DicEntry->width > 0 && DicEntry->height > 0 );
348
349 /* update the common buffer in the imgbuf */
350 a_Imgbuf_update(DicEntry->v_imgbuf, buf, DicEntry->type,
351 DicEntry->cmap, DicEntry->width, DicEntry->height, Y);
352
353 a_Bitvec_set_bit(DicEntry->BitVec, (int)Y);
349354 DicEntry->State = DIC_Write;
350355 }
351356
352357 /*
353358 * Implement the close method of the decoding process
354359 */
355 void a_Dicache_close(DilloUrl *url, gint version, CacheClient_t *Client)
360 void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client)
356361 {
357362 DilloWeb *Web = Client->Web;
358 DICacheEntry *DicEntry = Dicache_get_entry_version(url, version);
359
360 g_return_if_fail ( DicEntry != NULL );
361
362 DicEntry->State = DIC_Close;
363 if (DicEntry->cmap) {
364 g_free(DicEntry->cmap);
363 DICacheEntry *DicEntry = a_Dicache_get_entry(url, version);
364
365 dReturn_if_fail ( DicEntry != NULL );
366
367 /* a_Dicache_unref() may free DicEntry */
368 _MSG("a_Dicache_close RefCount=%d\n", DicEntry->RefCount - 1);
369
370 if (DicEntry->State < DIC_Close) {
371 DicEntry->State = DIC_Close;
372 dFree(DicEntry->cmap);
365373 DicEntry->cmap = NULL;
366 }
367 a_Image_close(Web->Image);
368 a_Interface_close_client(Web->bw, Client->Key);
374 DicEntry->Decoder = NULL;
375 DicEntry->DecoderData = NULL;
376 }
377 a_Dicache_unref(url, version);
378
379 a_Bw_close_client(Web->bw, Client->Key);
369380 }
370381
371382 /* ------------------------------------------------------------------------- */
372383
373 static gboolean
374 Dicache_remove_hash_entry(gpointer key, gpointer value, gpointer user_data)
375 {
376 DICacheHashEntry *hash_entry = (DICacheHashEntry *)value;
377 DICacheEntry *entry = hash_entry->next;
378
379 /* Eliminate this (last) dicache entry */
380 g_free(entry->cmap);
381 if (entry->ImageBuffer) {
382 free(entry->ImageBuffer);
383 dicache_size_total -= entry->TotalSize;
384 }
385 a_Bitvec_free(entry->BitVec);
386 a_Url_free(hash_entry->url);
387 g_free(hash_entry);
388
389 return TRUE;
390 }
384 /*
385 * Generic MIME handler for GIF, JPEG and PNG.
386 * Sets a_Dicache_callback as the cache-client,
387 * and also sets the image decoder.
388 *
389 * Parameters:
390 * Type: MIME type
391 * Ptr: points to a Web structure
392 * Call: Dillo calls this with more data/eod
393 * Data: Decoding data structure
394 */
395 static void *Dicache_image(int ImgType, const char *MimeType, void *Ptr,
396 CA_Callback_t *Call, void **Data)
397 {
398 DilloWeb *web = Ptr;
399 DICacheEntry *DicEntry;
400
401 dReturn_val_if_fail(MimeType && Ptr, NULL);
402
403 if (!web->Image) {
404 web->Image = a_Image_new(NULL, web->bgColor);
405 a_Image_ref(web->Image);
406 }
407
408 DicEntry = a_Dicache_get_entry(web->url, DIC_Last);
409 if (!DicEntry) {
410 /* Let's create an entry for this image... */
411 DicEntry = Dicache_add_entry(web->url);
412 DicEntry->DecoderData =
413 (ImgType == DIC_Png) ?
414 a_Png_new(web->Image, DicEntry->url, DicEntry->version) :
415 (ImgType == DIC_Gif) ?
416 a_Gif_new(web->Image, DicEntry->url, DicEntry->version) :
417 (ImgType == DIC_Jpeg) ?
418 a_Jpeg_new(web->Image, DicEntry->url, DicEntry->version) :
419 NULL;
420 } else {
421 /* Repeated image */
422 a_Dicache_ref(DicEntry->url, DicEntry->version);
423 }
424 DicEntry->Decoder = (ImgType == DIC_Png) ? (CA_Callback_t)a_Png_callback :
425 (ImgType == DIC_Gif) ? (CA_Callback_t)a_Gif_callback :
426 (ImgType == DIC_Jpeg) ? (CA_Callback_t)a_Jpeg_callback:
427 NULL;
428 *Data = DicEntry->DecoderData;
429 *Call = (CA_Callback_t) a_Dicache_callback;
430
431 return (web->Image->dw);
432 }
433
434 /*
435 * PNG wrapper for Dicache_image()
436 */
437 void *a_Dicache_png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
438 void **Data)
439 {
440 return Dicache_image(DIC_Png, Type, Ptr, Call, Data);
441 }
442
443 /*
444 * GIF wrapper for Dicache_image()
445 */
446 void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
447 void **Data)
448 {
449 return Dicache_image(DIC_Gif, Type, Ptr, Call, Data);
450 }
451
452 /*
453 * JPEG wrapper for Dicache_image()
454 */
455 void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
456 void **Data)
457 {
458 return Dicache_image(DIC_Jpeg, Type, Ptr, Call, Data);
459 }
460
461 /*
462 * This function is a cache client; (but feeds its clients from dicache)
463 */
464 void a_Dicache_callback(int Op, CacheClient_t *Client)
465 {
466 uint_t i;
467 DilloWeb *Web = Client->Web;
468 DilloImage *Image = Web->Image;
469 DICacheEntry *DicEntry = a_Dicache_get_entry(Web->url, DIC_Last);
470
471 dReturn_if_fail ( DicEntry != NULL );
472
473 /* Copy the version number in the Client */
474 if (Client->Version == 0)
475 Client->Version = DicEntry->version;
476
477 /* Only call the decoder when necessary */
478 if (Op == CA_Send && DicEntry->State < DIC_Close &&
479 DicEntry->DecodedSize < Client->BufSize) {
480 DicEntry->Decoder(Op, Client);
481 DicEntry->DecodedSize = Client->BufSize;
482 } else if (Op == CA_Close || Op == CA_Abort) {
483 if (DicEntry->State < DIC_Close) {
484 DicEntry->Decoder(Op, Client);
485 } else {
486 a_Dicache_close(DicEntry->url, DicEntry->version, Client);
487 }
488 }
489
490 /* when the data stream is not an image 'v_imgbuf' remains NULL */
491 if (Op == CA_Send && DicEntry->v_imgbuf) {
492 if (Image->height == 0 && DicEntry->State >= DIC_SetParms) {
493 /* Set parms */
494 a_Image_set_parms(
495 Image, DicEntry->v_imgbuf, DicEntry->url,
496 DicEntry->version, DicEntry->width, DicEntry->height,
497 DicEntry->type);
498 }
499 if (DicEntry->State == DIC_Write) {
500 if (DicEntry->ScanNumber == Image->ScanNumber) {
501 for (i = 0; i < DicEntry->height; ++i)
502 if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) &&
503 !a_Bitvec_get_bit(Image->BitVec, (int)i) )
504 a_Image_write(Image, i);
505 } else {
506 for (i = 0; i < DicEntry->height; ++i) {
507 if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) ||
508 !a_Bitvec_get_bit(Image->BitVec, (int)i) ||
509 DicEntry->ScanNumber > Image->ScanNumber + 1) {
510 a_Image_write(Image, i);
511 }
512 if (!a_Bitvec_get_bit(DicEntry->BitVec, (int)i))
513 a_Bitvec_clear_bit(Image->BitVec, (int)i);
514 }
515 Image->ScanNumber = DicEntry->ScanNumber;
516 }
517 }
518 } else if (Op == CA_Close || Op == CA_Abort) {
519 a_Image_close(Image);
520 a_Bw_close_client(Web->bw, Client->Key);
521 }
522 }
523
524 /* ------------------------------------------------------------------------- */
525
526 /*
527 * Free the imgbuf (RGB data) of unused entries.
528 */
529 void a_Dicache_cleanup(void)
530 {
531 int i;
532 DICacheNode *node;
533 DICacheEntry *entry;
534
535 _MSG("a_Dicache_cleanup\n");
536 for (i = 0; i < dList_length(CachedIMGs); ++i) {
537 node = dList_nth_data(CachedIMGs, i);
538 /* iterate each entry of this node */
539 for (entry = node->first; entry; entry = entry->next) {
540 if (entry->v_imgbuf &&
541 a_Imgbuf_last_reference(entry->v_imgbuf)) {
542 /* free this unused entry */
543 if (entry->next) {
544 Dicache_remove(node->url, entry->version);
545 } else {
546 Dicache_remove(node->url, entry->version);
547 --i;
548 break;
549 }
550 }
551 }
552 }
553 }
554
555 /* ------------------------------------------------------------------------- */
391556
392557 /*
393558 * Deallocate memory used by dicache module
395560 */
396561 void a_Dicache_freeall(void)
397562 {
398 /* Remove every dicache entry */
399 g_hash_table_foreach_remove(dicache_hash,
400 (GHRFunc)Dicache_remove_hash_entry, NULL);
401 /* Remove the dicache hash */
402 g_hash_table_destroy(dicache_hash);
403 }
563 DICacheNode *node;
564 DICacheEntry *entry;
565
566 /* Remove every dicache node and its entries */
567 while ((node = dList_nth_data(CachedIMGs, 0))) {
568 while ((entry = node->first)) {
569 node->first = entry->next;
570 dFree(entry->cmap);
571 a_Bitvec_free(entry->BitVec);
572 a_Imgbuf_unref(entry->v_imgbuf);
573 dicache_size_total -= entry->TotalSize;
574 }
575 dList_remove_fast(CachedIMGs, node);
576 a_Url_free(node->url);
577 dFree(node);
578 }
579 dList_free(CachedIMGs);
580 }
00 #ifndef __DICACHE_H__
11 #define __DICACHE_H__
22
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7
38 #include "bitvec.h"
4 #include "image.h"
9 #include "image.hh"
510 #include "cache.h"
11
12 /* Symbolic name to request the last version of an image */
13 #define DIC_Last -1
14
615
716 /* These will reflect the entry's "state" */
817 typedef enum {
1827
1928 struct _DICacheEntry {
2029 DilloUrl *url; /* Image URL for this entry */
21 guint width, height; /* As taken from image data */
30 uint_t width, height; /* As taken from image data */
2231 DilloImgType type; /* Image type */
23 guchar *cmap; /* Color map */
24 guchar *ImageBuffer; /* Decompressed buffer */
25 size_t TotalSize; /* Amount of memory the image takes up */
26 gint Y; /* Current decoding row */
32 uchar_t *cmap; /* Color map */
33 void *v_imgbuf; /* Void pointer to an Imgbuf object */
34 uint_t TotalSize; /* Amount of memory the image takes up */
35 uint_t ScanNumber; /* Current decoding scan */
2736 bitvec_t *BitVec; /* Bit vector for decoded rows */
2837 DicEntryState State; /* Current status for this entry */
29 gint RefCount; /* Reference Counter */
30 gint version; /* Version number, used for different
38 int RefCount; /* Reference Counter */
39 int version; /* Version number, used for different
3140 versions of the same URL image */
41
42 CA_Callback_t Decoder; /* Client function */
43 void *DecoderData; /* Client function data */
44 uint_t DecodedSize; /* Size of already decoded data */
3245
3346 DICacheEntry *next; /* Link to the next "newer" version */
3447 };
3649
3750 void a_Dicache_init (void);
3851
39 DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url);
40 DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url);
52 DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url, int version);
4153
42 void a_Dicache_callback(gint Op, CacheClient_t *Client);
54 void *a_Dicache_png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
55 void **Data);
56 void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
57 void **Data);
58 void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
59 void **Data);
60 void a_Dicache_callback(int Op, CacheClient_t *Client);
4361
44 void a_Dicache_set_parms(DilloUrl *url, gint version, DilloImage *Image,
45 guint width, guint height, DilloImgType type);
46 void a_Dicache_set_cmap(DilloUrl *url, gint version, DilloImage *Image,
47 const guchar *cmap, guint num_colors,
48 gint num_colors_max, gint bg_index);
49 void a_Dicache_write(DilloImage *Image, DilloUrl *url, gint version,
50 const guchar *buf, gint x, guint Y);
51 void a_Dicache_close(DilloUrl *url, gint version, CacheClient_t *Client);
62 void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
63 uint_t width, uint_t height, DilloImgType type);
64 void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
65 const uchar_t *cmap, uint_t num_colors,
66 int num_colors_max, int bg_index);
67 void a_Dicache_new_scan(const DilloUrl *url, int version);
68 void a_Dicache_write(DilloUrl *url, int version, const uchar_t *buf, uint_t Y);
69 void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client);
5270
5371 void a_Dicache_invalidate_entry(const DilloUrl *Url);
54 DICacheEntry* a_Dicache_ref(const DilloUrl *Url, gint version);
55 void a_Dicache_unref(const DilloUrl *Url, gint version);
72 DICacheEntry* a_Dicache_ref(const DilloUrl *Url, int version);
73 void a_Dicache_unref(const DilloUrl *Url, int version);
74 void a_Dicache_cleanup(void);
5675 void a_Dicache_freeall(void);
5776
77
78 #ifdef __cplusplus
79 }
80 #endif /* __cplusplus */
5881 #endif /* __DICACHE_H__ */
+0
-469
src/dillo.c less more
0 /*
1 * Dillo web browser
2 *
3 * Copyright 1997 Raph Levien <raph@acm.org>
4 * Copyright 1999-2004 Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <stdio.h>
22 #include <gtk/gtk.h>
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <signal.h>
32 #include <sys/wait.h>
33 #include <ctype.h>
34 #include <locale.h>
35
36 #include <config.h>
37 #include "msg.h"
38 #include "dillo.h"
39 #include "misc.h"
40 #include "nav.h"
41 #include "history.h"
42 #include "bookmark.h"
43 #include "dicache.h"
44 #include "dns.h"
45 #include "IO/mime.h"
46 #include "IO/Url.h"
47 #include "prefs.h"
48 #include "interface.h"
49 #include "dw.h"
50 #include "cookies.h"
51
52
53 /*
54 * Command line options structure
55 */
56 typedef struct {
57 char *sh_opt; /* must be non NULL */
58 char *lg_opt; /* must be non NULL */
59 gint opt_argc; /* positive: mandatory, negative: optional */
60 gint opt_flg; /* 1 << n */
61 char *help; /* can be NULL */
62 } CLI_options;
63
64 enum {
65 DILLO_CLI_XID = 1 << 1,
66 DILLO_CLI_FULLWINDOW = 1 << 2,
67 DILLO_CLI_HELP = 1 << 3,
68 DILLO_CLI_VERSION = 1 << 4,
69 DILLO_CLI_LOCAL = 1 << 5,
70 DILLO_CLI_GEOMETRY = 1 << 6,
71 DILLO_CLI_DBG_RENDERING = 1 << 7,
72 DILLO_CLI_ERROR = 1 << 15
73 };
74
75 enum {
76 O_SEARCH, O_FOUND, O_NOTFOUND, O_OK, O_ERROR, O_DONE
77 };
78
79 /*
80 * Global variables from command line options;
81 */
82 gboolean dillo_dbg_rendering = FALSE;
83
84 /*
85 * Forward declarations
86 */
87 static void Dillo_check_home_dir(char *dir);
88 static gint Dillo_get_opt(CLI_options *options, int argc,
89 char **argv, char ***opt_arg, gint *idx);
90 static void Dillo_print_help(CLI_options *options);
91 static void Dillo_print_version(void);
92
93 /*
94 * Local data
95 */
96 static CLI_options Options[] = {
97 {"-x", "--xid", 1, DILLO_CLI_XID,
98 " -x, --xid XID Open first Dillo window in an existing\n"
99 " GtkSocket which window ID is XID (decimal)."},
100 {"-v", "--version", 0, DILLO_CLI_VERSION,
101 " -v, --version Display version info and exit."},
102 {"-h", "--help", 0, DILLO_CLI_HELP,
103 " -h, --help Display this help text and exit."},
104 {"-f", "--fullwindow", 0, DILLO_CLI_FULLWINDOW,
105 " -f, --fullwindow Start in full window mode: hide address bar,\n"
106 " navigation buttons, menu, and status bar."},
107 {"-l", "--local", 0, DILLO_CLI_LOCAL,
108 " -l, --local Don't follow links for this URL(s)."},
109 {"-g", "-geometry", 1, DILLO_CLI_GEOMETRY,
110 " -g, -geometry GEO Set initial window position where GEO is\n"
111 " <width>x<height>{+-}<x>{+-}<y>"},
112 {"-D", "--debug-rendering",
113 0, DILLO_CLI_DBG_RENDERING,
114 " -D, --debug-rendering Draw additionaly several lines in a web page,\n"
115 " representing its structure. For debugging. "},
116 {NULL, NULL, 0, 0, NULL}
117 };
118
119 /*
120 * Return the maximum number of option arguments
121 */
122 static gint Dillo_get_max_opt(CLI_options *options)
123 {
124 gint i, max;
125
126 for (i = 0, max = 0; options[i].sh_opt; i++)
127 if (abs(options[i].opt_argc) > max)
128 max = abs(options[i].opt_argc);
129 return max;
130 }
131
132 /*
133 * Given a command line argument, build a DilloUrl for it.
134 */
135 static DilloUrl *Dillo_make_start_url(gchar *str)
136 {
137 gchar *url_str, *p, *cd;
138 DilloUrl *start_url;
139 gint is_file = FALSE;
140
141 /* This check is for files with non-URL characters */
142 if (access(str, F_OK) == 0) {
143 p = a_Misc_escape_chars(str, "% ");
144 url_str = g_strdup(p ? p : str);
145 is_file = TRUE;
146 } else {
147 /* Filter URL string */
148 url_str = a_Url_string_strip_delimiters(str);
149 }
150
151 if (is_file || access(url_str, F_OK) == 0) {
152 GString *UrlStr = g_string_sized_new(128);
153
154 if (url_str[0] == '/') {
155 /* prepend the "file:" protocol specifier. */
156 g_string_sprintf(UrlStr, "file:%s", url_str);
157 } else {
158 cd = g_get_current_dir();
159 p = a_Misc_escape_chars(cd, "% ");
160 g_string_sprintf(UrlStr, "file:%s", p ? p : cd);
161 g_free(p);
162 g_free(cd);
163 if (UrlStr->str[UrlStr->len - 1] != '/')
164 g_string_append(UrlStr, "/");
165 g_string_append(UrlStr, url_str);
166 }
167 start_url = a_Url_new(UrlStr->str, NULL, 0, 0, 0);
168 g_string_free(UrlStr, TRUE);
169 } else {
170 start_url = a_Url_new(url_str, NULL, 0, 0, 0);
171 }
172
173 g_free(url_str);
174 return start_url;
175 }
176
177 /*
178 * Get status code to avoid zombies!
179 */
180 static void Dillo_sigchld_handler(int signum)
181 {
182 pid_t pid;
183 int status, serrno;
184
185 serrno = errno;
186 while (1) {
187 pid = waitpid (-1, &status, WNOHANG);
188 if (pid < 0) {
189 perror ("waitpid");
190 break;
191 }
192 if (pid == 0)
193 break;
194 /* notice_termination (pid, status); */
195 }
196 errno = serrno;
197 }
198
199
200 /*
201 * ******************************** MAIN *************************************
202 */
203 gint main(int argc, char *argv[])
204 {
205 gchar *dir, *curr_locale;
206 DilloUrl *start_url;
207 BrowserWindow *bw;
208 guint32 xid = 0;
209 guint options_got = 0;
210 gint idx = 0;
211 gint opt_flg;
212 gint i;
213 char **opt_argv = NULL;
214 gint xpos = D_GEOMETRY_DEFAULT_XPOS, ypos = D_GEOMETRY_DEFAULT_YPOS;
215 gint width = D_GEOMETRY_DEFAULT_WIDTH, height = D_GEOMETRY_DEFAULT_HEIGHT;
216
217 /* set locale */
218 curr_locale = g_strdup(setlocale(LC_ALL, NULL));
219 gtk_set_locale();
220 /* Initialize GUI and parse GTK related args */
221 gtk_init(&argc, &argv);
222 gdk_rgb_init();
223 gtk_widget_set_default_colormap (gdk_rgb_get_cmap());
224 gtk_widget_set_default_visual (gdk_rgb_get_visual());
225
226 /* Handle command line options */
227 while ((opt_flg = Dillo_get_opt(Options, argc, argv, &opt_argv, &idx))) {
228 options_got |= opt_flg;
229 switch (opt_flg) {
230 case DILLO_CLI_VERSION:
231 Dillo_print_version();
232 return 0;
233 break;
234 case DILLO_CLI_HELP:
235 Dillo_print_help(Options);
236 return 0;
237 break;
238 case DILLO_CLI_XID:
239 if (opt_argv[0][0] >= '0' && opt_argv[0][0] <= '9') {
240 xid = strtol(opt_argv[0], NULL, 10);
241 } else {
242 g_printerr("Error: the XID must be an unsigned decimal numerical "
243 "value.\nThe offending value was: %s\n", opt_argv[0]);
244 return -1;
245 }
246 break;
247 case DILLO_CLI_FULLWINDOW:
248 case DILLO_CLI_LOCAL:
249 break;
250 case DILLO_CLI_DBG_RENDERING:
251 dillo_dbg_rendering = TRUE;
252 break;
253 case DILLO_CLI_GEOMETRY:
254 if (a_Misc_parse_geometry(opt_argv[0], &xpos, &ypos, &width, &height))
255 break;
256 default:
257 g_printerr("Error in command line options.\n");
258 return -1;
259 break;
260 }
261 }
262
263 /* Send a delayed locale-related message */
264 MSG("Setting locale to %s...\n", curr_locale);
265 g_free(curr_locale);
266
267 /* This lets threads in the file module end peacefully when aborted
268 * todo: implement a cleaner mechanism (in file.c) */
269 signal(SIGPIPE, SIG_IGN);
270
271 /* This avoids making zombies when dpi-programs finish. */
272 signal(SIGCHLD, Dillo_sigchld_handler);
273
274
275 /* check that ~/.dillo exists, create it if it doesn't */
276 dir = a_Misc_prepend_user_home(".dillo");
277 Dillo_check_home_dir(dir);
278 g_free(dir);
279
280 a_Prefs_init();
281 a_Dns_init();
282 a_Http_init();
283 a_Mime_init();
284 a_Cache_init();
285 a_Dicache_init();
286 a_Interface_init();
287 a_Dw_init();
288 a_Cookies_init();
289
290 /* -f overrides dillorc */
291 if (options_got & DILLO_CLI_FULLWINDOW)
292 prefs.fullwindow_start = TRUE;
293
294 /* override dillorc geometry */
295 if (options_got & DILLO_CLI_GEOMETRY) {
296 prefs.width = width;
297 prefs.height = height;
298 prefs.xpos = xpos;
299 prefs.ypos = ypos;
300 }
301 /* a_Nav_init() has been moved into this call because it needs to be
302 * initialized with the new browser_window structure */
303 bw = a_Interface_browser_window_new(prefs.width, prefs.height, xid);
304 if ((prefs.xpos != D_GEOMETRY_DEFAULT_XPOS) ||
305 (prefs.ypos != D_GEOMETRY_DEFAULT_YPOS))
306 gtk_widget_set_uposition(bw->main_window, prefs.xpos, prefs.ypos);
307
308 a_Bookmarks_init();
309
310 /* Send dillo's startup screen */
311 a_Nav_push(bw, prefs.start_page);
312
313 for (i = idx; i < argc; i++) {
314 /* If more than one URL/FILE, open in new window */
315 if (i > idx)
316 bw = a_Interface_browser_window_new(prefs.width, prefs.height, 0);
317
318 start_url = Dillo_make_start_url(argv[i]);
319 if (options_got & DILLO_CLI_LOCAL)
320 a_Url_set_flags(start_url, URL_FLAGS(start_url) | URL_SpamSafe);
321 a_Nav_push(bw, start_url);
322 a_Url_free(start_url);
323 }
324
325 if (prefs.http_proxyuser && !a_Http_proxy_auth())
326 a_Interface_proxy_passwd_dialog(bw);
327
328 /* Start the GTK+ cycle */
329 gtk_main();
330
331 /*
332 * Memory deallocating routines
333 * (This can be left to the OS, but we'll do it, with a view to test
334 * and fix our memory management)
335 */
336 a_Cookies_freeall();
337 a_Cache_freeall();
338 a_Dicache_freeall();
339 a_Http_freeall();
340 a_Dns_freeall();
341 a_Prefs_freeall();
342 a_Dw_freeall();
343 a_History_free();
344
345 /* a_Dpi_bye_dpid(); */
346 MSG("Dillo: normal exit!\n");
347 return 0;
348 }
349
350 /*
351 * Check if '~/.dillo' directory exists.
352 * If not, try to create it.
353 */
354 static void Dillo_check_home_dir(char *dir)
355 {
356 struct stat st;
357
358 if ( stat(dir, &st) == -1 ) {
359 if ( errno == ENOENT && mkdir(dir, 0700) < 0 ) {
360 MSG("Dillo: error creating directory %s: %s\n",
361 dir, g_strerror(errno));
362 } else
363 MSG("Dillo: error reading %s: %s\n", dir, g_strerror(errno));
364 }
365 }
366
367
368 /*
369 * Get next command line option.
370 * Return value:
371 * opt_flg of the option
372 * 0 if no more options found
373 * -1 if an unrecognised option is found or if a mandatory option
374 * argument is not found.
375 */
376 static gint
377 Dillo_get_opt(CLI_options *options, int argc,
378 char **argv, char ***opt_argv, gint *cli_idx)
379 {
380 gint i = 0, opt_flg = 0, n_arg, state;
381 static int idx = 1;
382 gint ret_val = 0;
383
384 /* Allocate opt_argv */
385 if (*opt_argv == NULL)
386 *opt_argv = g_new0(gchar*, Dillo_get_max_opt(options) + 1);
387
388 state = O_SEARCH;
389 if (idx >= argc)
390 state = O_DONE;
391
392 while (1) {
393 switch (state) {
394 case O_SEARCH:
395 for (i = 0; options[i].sh_opt; i++)
396 if (strcmp(options[i].sh_opt, argv[idx]) == 0 ||
397 strcmp(options[i].lg_opt, argv[idx]) == 0)
398 break;
399 state = (options[i].sh_opt) ? O_FOUND : O_NOTFOUND;
400 break;
401 case O_FOUND:
402 ++idx;
403 opt_flg = options[i].opt_flg;
404 n_arg = options[i].opt_argc;
405 /* Find the required/optional arguments of the option */
406 for (i = 0; idx < argc && i < abs(n_arg) && argv[idx][0] != '-'; i++)
407 (*opt_argv)[i] = argv[idx++];
408 (*opt_argv)[i] = NULL;
409
410 /* Optional arguments have opt_argc < 0 */
411 if (i < n_arg) {
412 g_printerr("Option %s requires %d argument(s)\n",
413 argv[idx - i - 1], n_arg);
414 state = O_ERROR;
415 } else
416 state = O_OK;
417 break;
418 case O_NOTFOUND:
419 state = O_DONE;
420 if (strcmp(argv[idx], "--") == 0)
421 idx++;
422 else if (argv[idx][0] == '-')
423 state = O_ERROR;
424 break;
425 case O_OK:
426 *cli_idx = idx;
427 return opt_flg;
428 break;
429 case O_ERROR:
430 ret_val = DILLO_CLI_ERROR;
431 state = O_DONE;
432 break;
433 case O_DONE:
434 g_free(*opt_argv);
435 *cli_idx = idx;
436 return ret_val;
437 break;
438 }
439 }
440 }
441
442 /*
443 * Print a short help text automatically generated from the options structure
444 */
445 static void Dillo_print_help(CLI_options *options)
446 {
447 g_print("\nUsage: dillo [OPTIONS] [URL|FILE]...\n"
448 "Options:\n");
449
450 for (; options && options->sh_opt; options++) {
451 if (options->help)
452 g_print("%s\n", options->help);
453 else
454 g_print(" %s, %s *Undocumented*\n", options->sh_opt,
455 options->lg_opt);
456 }
457 g_print(" URL URL to browse.\n"
458 " FILE Local FILE to view.\n");
459 g_print("\n");
460 }
461
462 /*
463 * Print version (TODO: and maybe a copyright notice)
464 */
465 static void Dillo_print_version(void)
466 {
467 g_print("Dillo %s\n", VERSION);
468 }
0 /*
1 * Dillo web browser
2 *
3 * Copyright 1999-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <time.h>
23 #include <signal.h>
24 #include <locale.h>
25
26 #include <FL/Fl.H>
27 #include <FL/Fl_Window.H>
28 #include <FL/fl_draw.H>
29
30 #include "msg.h"
31 #include "paths.hh"
32 #include "uicmd.hh"
33
34 #include "prefs.h"
35 #include "prefsparser.hh"
36 #include "keys.hh"
37 #include "bw.h"
38 #include "misc.h"
39 #include "history.h"
40
41 #include "dns.h"
42 #include "web.hh"
43 #include "IO/Url.h"
44 #include "IO/mime.h"
45 #include "capi.h"
46 #include "dicache.h"
47 #include "cookies.h"
48 #include "auth.h"
49
50 #include "dw/fltkcore.hh"
51
52 /*
53 * Command line options structure
54 */
55 typedef enum {
56 DILLO_CLI_NONE = 0,
57 DILLO_CLI_XID = 1 << 0,
58 DILLO_CLI_FULLWINDOW = 1 << 1,
59 DILLO_CLI_HELP = 1 << 2,
60 DILLO_CLI_VERSION = 1 << 3,
61 DILLO_CLI_LOCAL = 1 << 4,
62 DILLO_CLI_GEOMETRY = 1 << 5,
63 DILLO_CLI_ERROR = 1 << 15,
64 } OptID;
65
66 typedef struct {
67 const char *shortopt;
68 const char *longopt;
69 int opt_argc; /* positive: mandatory, negative: optional */
70 OptID id;
71 const char *help;
72 } CLI_options;
73
74 static const CLI_options Options[] = {
75 {"-f", "--fullwindow", 0, DILLO_CLI_FULLWINDOW,
76 " -f, --fullwindow Start in full window mode: hide address bar,\n"
77 " navigation buttons, menu, and status bar."},
78 {"-g", "-geometry", 1, DILLO_CLI_GEOMETRY,
79 " -g, -geometry GEO Set initial window position where GEO is\n"
80 " WxH[{+-}X{+-}Y]"},
81 {"-h", "--help", 0, DILLO_CLI_HELP,
82 " -h, --help Display this help text and exit."},
83 {"-l", "--local", 0, DILLO_CLI_LOCAL,
84 " -l, --local Don't load images for these URL(s)."},
85 {"-v", "--version", 0, DILLO_CLI_VERSION,
86 " -v, --version Display version info and exit."},
87 {"-x", "--xid", 1, DILLO_CLI_XID,
88 " -x, --xid XID Open first Dillo window in an existing\n"
89 " window whose window ID is XID."},
90 {NULL, NULL, 0, DILLO_CLI_NONE, NULL}
91 };
92
93 /*
94 * Print help text generated from the options structure
95 */
96 static void printHelp(const char *cmdname, const CLI_options *options)
97 {
98 printf("Usage: %s [OPTION]... [--] [URL|FILE]...\n"
99 "Options:\n", cmdname);
100 while (options && options->help) {
101 printf("%s\n", options->help);
102 options++;
103 }
104 printf(" URL URL to browse.\n"
105 " FILE Local FILE to view.\n"
106 "\n");
107 }
108
109 /*
110 * Return the maximum number of option arguments
111 */
112 static int numOptions(const CLI_options *options)
113 {
114 int i, max;
115
116 for (i = 0, max = 0; options[i].shortopt; i++)
117 if (abs(options[i].opt_argc) > max)
118 max = abs(options[i].opt_argc);
119 return max;
120 }
121
122 /*
123 * Get next command line option.
124 */
125 static OptID getCmdOption(const CLI_options *options, int argc, char **argv,
126 char **opt_argv, int *idx)
127 {
128 typedef enum { O_SEARCH, O_FOUND, O_NOTFOUND, O_DONE } State;
129 OptID opt_id = DILLO_CLI_NONE;
130 int i = 0;
131 State state = O_SEARCH;
132
133 if (*idx >= argc) {
134 state = O_DONE;
135 } else {
136 state = O_NOTFOUND;
137 for (i = 0; options[i].shortopt; i++) {
138 if (strcmp(options[i].shortopt, argv[*idx]) == 0 ||
139 strcmp(options[i].longopt, argv[*idx]) == 0) {
140 state = O_FOUND;
141 ++*idx;
142 break;
143 }
144 }
145 }
146 if (state == O_FOUND) {
147 int n_arg = options[i].opt_argc;
148 opt_id = options[i].id;
149 /* Find the required/optional arguments of the option */
150 for (i = 0; *idx < argc && i < abs(n_arg) && argv[*idx][0] != '-'; i++)
151 opt_argv[i] = argv[(*idx)++];
152 opt_argv[i] = NULL;
153
154 /* Optional arguments have opt_argc < 0 */
155 if (i < n_arg) {
156 fprintf(stderr, "Option %s requires %d argument%s\n",
157 argv[*idx-i-1], n_arg, (n_arg == 1) ? "" : "s");
158 opt_id = DILLO_CLI_ERROR;
159 }
160 }
161 if (state == O_NOTFOUND) {
162 if (strcmp(argv[*idx], "--") == 0)
163 (*idx)++;
164 else if (argv[*idx][0] == '-') {
165 fprintf(stderr, "Command line option \"%s\" not recognized.\n",
166 argv[*idx]);
167 opt_id = DILLO_CLI_ERROR;
168 }
169 }
170 return opt_id;
171 }
172
173 /*
174 * Set FL_NORMAL_LABEL to interpret neither symbols (@) nor shortcuts (&),
175 * and FL_FREE_LABELTYPE to interpret shortcuts.
176 */
177 static void custLabelDraw(const Fl_Label* o, int X, int Y, int W, int H,
178 Fl_Align align)
179 {
180 const int interpret_symbols = 0;
181
182 fl_draw_shortcut = 0;
183 fl_font(o->font, o->size);
184 fl_color((Fl_Color)o->color);
185 fl_draw(o->value, X, Y, W, H, align, o->image, interpret_symbols);
186 }
187
188 static void custLabelMeasure(const Fl_Label* o, int& W, int& H)
189 {
190 const int interpret_symbols = 0;
191
192 fl_draw_shortcut = 0;
193 fl_font(o->font, o->size);
194 fl_measure(o->value, W, H, interpret_symbols);
195 }
196
197 static void custMenuLabelDraw(const Fl_Label* o, int X, int Y, int W, int H,
198 Fl_Align align)
199 {
200 const int interpret_symbols = 0;
201
202 fl_draw_shortcut = 1;
203 fl_font(o->font, o->size);
204 fl_color((Fl_Color)o->color);
205 fl_draw(o->value, X, Y, W, H, align, o->image, interpret_symbols);
206 }
207
208 static void custMenuLabelMeasure(const Fl_Label* o, int& W, int& H)
209 {
210 const int interpret_symbols = 0;
211
212 fl_draw_shortcut = 1;
213 fl_font(o->font, o->size);
214 fl_measure(o->value, W, H, interpret_symbols);
215 }
216
217 /*
218 * Tell the user if default/pref fonts can't be found.
219 */
220 static void checkFont(const char *name, const char *type)
221 {
222 if (! dw::fltk::FltkFont::fontExists(name))
223 MSG_WARN("preferred %s font \"%s\" not found.\n", type, name);
224 }
225
226 static void checkPreferredFonts()
227 {
228 checkFont(prefs.font_sans_serif, "sans-serif");
229 checkFont(prefs.font_serif, "serif");
230 checkFont(prefs.font_monospace, "monospace");
231 checkFont(prefs.font_cursive, "cursive");
232 checkFont(prefs.font_fantasy, "fantasy");
233 }
234
235 /*
236 * Given a command line argument, build a DilloUrl for it.
237 */
238 static DilloUrl *makeStartUrl(char *str, bool local)
239 {
240 char *url_str, *p;
241 DilloUrl *start_url;
242
243 /* Relative path to a local file? */
244 p = (*str == '/') ? dStrdup(str) :
245 dStrconcat(Paths::getOldWorkingDir(), "/", str, NULL);
246
247 if (access(p, F_OK) == 0) {
248 /* absolute path may have non-URL characters */
249 url_str = a_Misc_escape_chars(p, "% ");
250 start_url = a_Url_new(url_str + 1, "file:/");
251 } else {
252 /* Not a file, filter URL string */
253 url_str = a_Url_string_strip_delimiters(str);
254 start_url = a_Url_new(url_str, NULL);
255 }
256 dFree(p);
257 dFree(url_str);
258
259 if (local)
260 a_Url_set_flags(start_url, URL_FLAGS(start_url) | URL_SpamSafe);
261
262 return start_url;
263 }
264
265 /*
266 * MAIN
267 */
268 int main(int argc, char **argv)
269 {
270 uint_t opt_id;
271 uint_t options_got = 0;
272 uint32_t xid = 0;
273 int idx = 1;
274 int xpos = PREFS_GEOMETRY_DEFAULT_XPOS, ypos = PREFS_GEOMETRY_DEFAULT_YPOS,
275 width = PREFS_GEOMETRY_DEFAULT_WIDTH,
276 height = PREFS_GEOMETRY_DEFAULT_HEIGHT;
277 char **opt_argv;
278 FILE *fp;
279
280 srand((uint_t)(time(0) ^ getpid()));
281
282 // Some OSes exit dillo without this (not GNU/Linux).
283 signal(SIGPIPE, SIG_IGN);
284
285 /* Handle command line options */
286 opt_argv = dNew0(char*, numOptions(Options) + 1);
287 while ((opt_id = getCmdOption(Options, argc, argv, opt_argv, &idx))) {
288 options_got |= opt_id;
289 switch (opt_id) {
290 case DILLO_CLI_FULLWINDOW:
291 case DILLO_CLI_LOCAL:
292 break;
293 case DILLO_CLI_XID:
294 {
295 char *end;
296 xid = strtol(opt_argv[0], &end, 0);
297 if (*end) {
298 fprintf(stderr, "XID argument \"%s\" not valid.\n",opt_argv[0]);
299 return 2;
300 }
301 break;
302 }
303 case DILLO_CLI_GEOMETRY:
304 if (!a_Misc_parse_geometry(opt_argv[0],&xpos,&ypos,&width,&height)){
305 fprintf(stderr, "geometry argument \"%s\" not valid. Must be of "
306 "the form WxH[{+-}X{+-}Y].\n", opt_argv[0]);
307 return 2;
308 }
309 break;
310 case DILLO_CLI_VERSION:
311 puts("Dillo version " VERSION);
312 return 0;
313 case DILLO_CLI_HELP:
314 printHelp(argv[0], Options);
315 return 0;
316 default:
317 printHelp(argv[0], Options);
318 return 2;
319 }
320 }
321 dFree(opt_argv);
322
323 // set the default values for the preferences
324 a_Prefs_init();
325
326 // create ~/.dillo if not present
327 Paths::init();
328
329 // initialize default key bindings
330 Keys::init();
331
332 // parse dillorc
333 if ((fp = Paths::getPrefsFP(PATHS_RC_PREFS))) {
334 PrefsParser::parse(fp);
335 }
336 // parse keysrc
337 if ((fp = Paths::getPrefsFP(PATHS_RC_KEYS))) {
338 Keys::parse(fp);
339 }
340 dLib_show_messages(prefs.show_msg);
341
342 // initialize internal modules
343 a_Dpi_init();
344 a_Dns_init();
345 a_Web_init();
346 a_Http_init();
347 a_Mime_init();
348 a_Capi_init();
349 a_Dicache_init();
350 a_Bw_init();
351 a_Cookies_init();
352 a_Auth_init();
353
354 /* command line options override preferences */
355 if (options_got & DILLO_CLI_FULLWINDOW)
356 prefs.fullwindow_start = TRUE;
357 if (options_got & DILLO_CLI_GEOMETRY) {
358 prefs.width = width;
359 prefs.height = height;
360 prefs.xpos = xpos;
361 prefs.ypos = ypos;
362 }
363
364 // Sets WM_CLASS hint on X11
365 Fl_Window::default_xclass("dillo");
366
367 // Disable '@' and '&' interpretation in normal labels.
368 Fl::set_labeltype(FL_NORMAL_LABEL, custLabelDraw, custLabelMeasure);
369
370 // Use to permit '&' interpretation.
371 Fl::set_labeltype(FL_FREE_LABELTYPE,custMenuLabelDraw,custMenuLabelMeasure);
372
373 checkPreferredFonts();
374
375 /* use preferred font for UI */
376 Fl_Font defaultFont = dw::fltk::FltkFont::get (prefs.font_sans_serif, 0);
377 Fl::set_font(FL_HELVETICA, defaultFont); // this seems to be the
378 // only way to set the
379 // default font in fltk1.3
380
381 // Create a new UI/bw pair
382 BrowserWindow *bw = a_UIcmd_browser_window_new(0, 0, xid, NULL);
383
384 /* We need this so that fl_text_extents() in dw/fltkplatform.cc can
385 * work when FLTK is configured without XFT and Dillo is opening
386 * immediately-available URLs from the cmdline (e.g. about:splash).
387 */
388 ((Fl_Widget *)bw->ui)->window()->make_current();
389
390 /* Proxy authentication */
391 if (prefs.http_proxyuser && !a_Http_proxy_auth()) {
392 const char *passwd = a_UIcmd_get_passwd(prefs.http_proxyuser);
393 if (passwd) {
394 a_Http_set_proxy_passwd(passwd);
395 } else {
396 MSG_WARN("Not using proxy authentication.\n");
397 }
398 }
399
400 /* Open URLs/files */
401 const bool local = options_got & DILLO_CLI_LOCAL;
402
403 if (idx == argc) {
404 /* No URLs/files on cmdline. Send startup screen */
405 if (strcmp(URL_STR(prefs.start_page), "about:blank") == 0)
406 a_UIcmd_open_url(bw, NULL);
407 else
408 a_UIcmd_open_url(bw, prefs.start_page);
409 } else {
410 for (int i = idx; i < argc; i++) {
411 DilloUrl *start_url = makeStartUrl(argv[i], local);
412
413 if (i > idx) {
414 if (prefs.middle_click_opens_new_tab) {
415 /* user must prefer tabs */
416 const int focus = 1;
417 a_UIcmd_open_url_nt(bw, start_url, focus);
418 } else {
419 a_UIcmd_open_url_nw(bw, start_url);
420 }
421 } else {
422 a_UIcmd_open_url(bw, start_url);
423 }
424 a_Url_free(start_url);
425 }
426 }
427
428 Fl::run();
429
430 /*
431 * Memory deallocating routines
432 * (This can be left to the OS, but we'll do it, with a view to test
433 * and fix our memory management)
434 */
435 a_Cookies_freeall();
436 a_Cache_freeall();
437 a_Dicache_freeall();
438 a_Http_freeall();
439 a_Dns_freeall();
440 a_History_freeall();
441 a_Prefs_freeall();
442 Keys::free();
443 Paths::free();
444 /* TODO: auth, css */
445
446 //a_Dpi_dillo_exit();
447 MSG("Dillo: normal exit!\n");
448 return 0;
449 }
+0
-9
src/dillo.h less more
0 #ifndef __DILLO_H__
1 #define __DILLO_H__
2
3 #include "browser.h"
4 #include "web.h"
5
6 extern gboolean dillo_dbg_rendering;
7
8 #endif /* __DILLO_H__ */
0 #ifndef __JPEG_H__
1 #define __JPEG_H__
2
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7 #include "url.h"
8 #include "image.hh"
9
10
11 void *a_Jpeg_new(DilloImage *Image, DilloUrl *url, int version);
12 void a_Jpeg_callback(int Op, void *data);
13
14
15 #ifdef __cplusplus
16 }
17 #endif /* __cplusplus */
18 #endif /* !__JPEG_H__ */
00 /*
11 * File: dns.c
22 *
3 * Copyright (C) 1999, 2000, 2001 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 1999-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1212 * Non blocking pthread-handled Dns scheme
1313 */
1414
15 #include <pthread.h>
16 #include <glib.h>
15
16 /*
17 * Uncomment the following line for debugging or gprof profiling.
18 */
19 /* #undef D_DNS_THREADED */
20
21 #ifdef D_DNS_THREADED
22 # include <pthread.h>
23 #endif
24
1725
1826 #include <netdb.h>
1927 #include <sys/types.h>
2028 #include <sys/socket.h>
29 #include <arpa/inet.h>
2130 #include <netinet/in.h>
2231 #include <errno.h>
2332 #include <unistd.h>
2433 #include <stdlib.h>
2534 #include <stdio.h>
26 #include <signal.h>
2735 #include <string.h>
2836
2937 #include "msg.h"
3038 #include "dns.h"
3139 #include "list.h"
32
33 #define DEBUG_LEVEL 5
34 #include "debug.h"
35
36
37 /*
38 * Uncomment the following line for debugging or gprof profiling.
39 */
40 /* #undef D_DNS_THREADED */
41
42 /*
43 * Uncomment the following line for libc5 optimization
44 */
45 /* #define LIBC5 */
40 #include "timeout.hh"
4641
4742
4843 /* Maximum dns resolving threads */
5449
5550
5651 typedef struct {
57 gint channel; /* Index of this channel [0 based] */
58 gboolean in_use; /* boolean to tell if server is doing a lookup */
59 gboolean ip_ready; /* boolean: is IP lookup done? */
60 GSList *addr_list; /* IP address */
61 char *hostname; /* Adress to resolve */
62 guint timeout_id; /* gtk timeout function ID */
63 #ifdef D_DNS_THREADED
64 pthread_t th1; /* Thread id */
52 int channel; /* Index of this channel [0 based] */
53 bool_t in_use; /* boolean to tell if server is doing a lookup */
54 bool_t ip_ready; /* boolean: is IP lookup done? */
55 Dlist *addr_list; /* IP address */
56 char *hostname; /* Adress to resolve */
57 int status; /* errno code for resolving function */
58 #ifdef D_DNS_THREADED
59 pthread_t th1; /* Thread id */
6560 #endif
6661 } DnsServer;
6762
6863 typedef struct {
69 char *hostname; /* host name for cache */
70 GSList *addr_list; /* address of host */
64 char *hostname; /* host name for cache */
65 Dlist *addr_list; /* addresses of host */
7166 } GDnsCache;
7267
7368 typedef struct {
74 gint channel; /* -2 if waiting, otherwise index to dns_server[] */
75 char *hostname; /* The one we're resolving */
76 ChainLink *Info; /* CCC info */
69 int channel; /* -2 if waiting, otherwise index to dns_server[] */
70 char *hostname; /* The one we're resolving */
71 DnsCallback_t cb_func; /* callback function */
72 void *cb_data; /* extra data for the callback function */
7773 } GDnsQueue;
7874
7975
8076 /*
8177 * Forward declarations
8278 */
83 static gboolean Dns_timeout_client(gpointer data);
79 static void Dns_timeout_client(void *data);
8480
8581 /*
8682 * Local Data
8783 */
8884 static DnsServer dns_server[D_DNS_MAX_SERVERS];
89 static gint num_servers;
85 static int num_servers;
9086 static GDnsCache *dns_cache;
91 static gint dns_cache_size, dns_cache_size_max;
87 static int dns_cache_size, dns_cache_size_max;
9288 static GDnsQueue *dns_queue;
93 static gint dns_queue_size, dns_queue_size_max;
94 static gboolean ipv6_enabled;
89 static int dns_queue_size, dns_queue_size_max;
9590
9691
9792 /* ----------------------------------------------------------------------
9893 * Dns queue functions
9994 */
100 static void Dns_queue_add(ChainLink *Info, gint channel, const char *hostname)
95 static void Dns_queue_add(int channel, const char *hostname,
96 DnsCallback_t cb_func, void *cb_data)
10197 {
10298 a_List_add(dns_queue, dns_queue_size, dns_queue_size_max);
103 dns_queue[dns_queue_size].Info = Info;
10499 dns_queue[dns_queue_size].channel = channel;
105 dns_queue[dns_queue_size].hostname = g_strdup(hostname);
100 dns_queue[dns_queue_size].hostname = dStrdup(hostname);
101 dns_queue[dns_queue_size].cb_func = cb_func;
102 dns_queue[dns_queue_size].cb_data = cb_data;
106103 dns_queue_size++;
107104 }
108105
110107 * Find hostname index in dns_queue
111108 * (if found, returns queue index; -1 if not)
112109 */
113 static gint Dns_queue_find(const char *hostname)
114 {
115 gint i;
110 static int Dns_queue_find(const char *hostname)
111 {
112 int i;
116113
117114 for (i = 0; i < dns_queue_size; i++)
118115 if (!strcmp(hostname, dns_queue[i].hostname))
126123 */
127124 static void Dns_queue_remove(int index)
128125 {
129 gint i;
130
131 DEBUG_MSG(2, "Dns_queue_remove: deleting client [%d] [queue_size=%d]\n",
132 index, dns_queue_size);
126 int i;
127
128 _MSG("Dns_queue_remove: deleting client [%d] [queue_size=%d]\n",
129 index, dns_queue_size);
133130
134131 if (index < dns_queue_size) {
135 g_free(dns_queue[index].hostname);
132 dFree(dns_queue[index].hostname);
136133 --dns_queue_size; /* you'll find out why ;-) */
137134 for (i = index; i < dns_queue_size; i++)
138135 dns_queue[i] = dns_queue[i + 1];
144141 *
145142 void Dns_queue_print()
146143 {
147 gint i;
144 int i;
148145
149146 MSG("Queue: [");
150147 for (i = 0; i < dns_queue_size; i++)
154151 */
155152
156153 /*
157 * Given a CCC Info, remove its client
158 */
159 static void Dns_abort_by_ccc(ChainLink *Info)
160 {
161 gint i;
162
163 for (i = 0; i < dns_queue_size; i++)
164 if (dns_queue[i].Info == Info) {
165 Dns_queue_remove(i);
166 break;
167 }
168 }
169
170 /*
171154 * Add an IP/hostname pair to Dns-cache
172155 */
173 static void Dns_cache_add(char *hostname, GSList *addr_list)
156 static void Dns_cache_add(char *hostname, Dlist *addr_list)
174157 {
175158 a_List_add(dns_cache, dns_cache_size, dns_cache_size_max);
176 dns_cache[dns_cache_size].hostname = g_strdup(hostname);
159 dns_cache[dns_cache_size].hostname = dStrdup(hostname);
177160 dns_cache[dns_cache_size].addr_list = addr_list;
178161 ++dns_cache_size;
179 DEBUG_MSG(1, "Cache objects: %d\n", dns_cache_size);
162 _MSG("Cache objects: %d\n", dns_cache_size);
180163 }
181164
182165
185168 */
186169 void a_Dns_init(void)
187170 {
188 gint i;
189
190 #ifdef D_DNS_THREADED
191 DEBUG_MSG(5, "dillo_dns_init: Here we go! (threaded)\n");
171 int i;
172
173 #ifdef D_DNS_THREADED
174 MSG("dillo_dns_init: Here we go! (threaded)\n");
192175 #else
193 DEBUG_MSG(5, "dillo_dns_init: Here we go! (not threaded)\n");
176 MSG("dillo_dns_init: Here we go! (not threaded)\n");
194177 #endif
195178
196179 dns_queue_size = 0;
197180 dns_queue_size_max = 16;
198 dns_queue = g_new(GDnsQueue, dns_queue_size_max);
181 dns_queue = dNew(GDnsQueue, dns_queue_size_max);
199182
200183 dns_cache_size = 0;
201184 dns_cache_size_max = 16;
202 dns_cache = g_new(GDnsCache, dns_cache_size_max);
185 dns_cache = dNew(GDnsCache, dns_cache_size_max);
203186
204187 num_servers = D_DNS_MAX_SERVERS;
205188
210193 dns_server[i].ip_ready = FALSE;
211194 dns_server[i].addr_list = NULL;
212195 dns_server[i].hostname = NULL;
213 dns_server[i].timeout_id = 0;
196 dns_server[i].status = 0;
214197 #ifdef D_DNS_THREADED
215198 dns_server[i].th1 = (pthread_t) -1;
216199 #endif
217200 }
218201
202 #ifdef ENABLE_IPV6
219203 /* IPv6 test */
220 ipv6_enabled = FALSE;
221 #ifdef ENABLE_IPV6
222204 {
223205 /* If the IPv6 address family is not available there is no point
224206 wasting time trying to connect to v6 addresses. */
225207 int fd = socket(AF_INET6, SOCK_STREAM, 0);
226208 if (fd >= 0) {
227209 close(fd);
228 ipv6_enabled = TRUE;
229210 }
230211 }
231212 #endif
234215 /*
235216 * Allocate a host structure and add it to the list
236217 */
237 static GSList *Dns_note_hosts(GSList *list, int af, struct hostent *host)
238 {
239 int i;
240
241 if (host->h_length > DILLO_ADDR_MAX)
242 return list;
243 for (i = 0; host->h_addr_list[i]; i++) {
244 DilloHost *dh = g_new0(DilloHost, 1);
245 dh->af = af;
246 dh->alen = host->h_length;
247 memcpy(&dh->data[0], host->h_addr_list[i], (size_t)host->h_length);
248 list = g_slist_append(list, dh);
249 }
250 return list;
251 }
252
253 #ifdef D_DNS_THREADED
218
219 static void Dns_note_hosts(Dlist *list, struct addrinfo *res0)
220 {
221 struct addrinfo *res;
222 DilloHost *dh;
223
224 for (res = res0; res; res = res->ai_next) {
225
226 if (res->ai_family == AF_INET) {
227 struct sockaddr_in *in_addr;
228
229 if (res->ai_addrlen < sizeof(struct sockaddr_in)) {
230 continue;
231 }
232
233 dh = dNew0(DilloHost, 1);
234 dh->af = AF_INET;
235
236 in_addr = (struct sockaddr_in*) res->ai_addr;
237 dh->alen = sizeof (struct in_addr);
238 memcpy(&dh->data[0], &in_addr->sin_addr.s_addr, dh->alen);
239
240 dList_append(list, dh);
241 #ifdef ENABLE_IPV6
242 } else if (res->ai_family == AF_INET6) {
243 struct sockaddr_in6 *in6_addr;
244
245 if (res->ai_addrlen < sizeof(struct sockaddr_in6)) {
246 continue;
247 }
248
249 dh = dNew0(DilloHost, 1);
250 dh->af = AF_INET6;
251
252 in6_addr = (struct sockaddr_in6*) res->ai_addr;
253 dh->alen = sizeof (struct in6_addr);
254 memcpy(&dh->data[0], &in6_addr->sin6_addr.s6_addr, dh->alen);
255
256 dList_append(list, dh);
257 #endif
258 }
259 }
260 }
261
254262 /*
255263 * Server function (runs on its own thread)
256264 */
257265 static void *Dns_server(void *data)
258266 {
259 struct hostent *host;
260 gint channel = GPOINTER_TO_INT(data);
261 #ifdef LIBC5
262 gint h_err;
263 char buff[1024];
264 struct hostent sh;
265 #endif
266 GSList *hosts = NULL;
267
268 DEBUG_MSG(3, "Dns_server: starting...\n ch: %d host: %s\n",
269 channel, dns_server[channel].hostname);
270
271 #ifdef ENABLE_IPV6
272 if (ipv6_enabled) {
273 host = gethostbyname2(dns_server[channel].hostname, AF_INET6);
274 if (host)
275 hosts = Dns_note_hosts(hosts, AF_INET6, host);
276 }
277 #endif
278
279 #ifdef LIBC5
280 host = gethostbyname_r(dns_server[channel].hostname, &sh, buff,
281 sizeof(buff), &h_err);
282 #else
283 host = gethostbyname(dns_server[channel].hostname);
284 #endif
285
286 if (host)
287 hosts = Dns_note_hosts(hosts, AF_INET, host);
288
289 /* write hostname to client */
290 DEBUG_MSG(5, "Dns_server [%d]: %s is %p\n", channel,
291 dns_server[channel].hostname, hosts);
267 int channel = VOIDP2INT(data);
268 struct addrinfo hints, *res0;
269 int error;
270 Dlist *hosts;
271 size_t length, i;
272 char addr_string[40];
273
274 memset(&hints, 0, sizeof(hints));
275 hints.ai_family = AF_UNSPEC;
276 hints.ai_socktype = SOCK_STREAM;
277
278 hosts = dList_new(2);
279
280 _MSG("Dns_server: starting...\n ch: %d host: %s\n",
281 channel, dns_server[channel].hostname);
282
283 error = getaddrinfo(dns_server[channel].hostname, NULL, &hints, &res0);
284
285 if (error != 0) {
286 dns_server[channel].status = error;
287 if (error == EAI_NONAME)
288 MSG("DNS error: HOST_NOT_FOUND\n");
289 else if (error == EAI_AGAIN)
290 MSG("DNS error: TRY_AGAIN\n");
291 #ifdef EAI_NODATA
292 /* Some FreeBSD don't have this anymore */
293 else if (error == EAI_NODATA)
294 MSG("DNS error: NO_ADDRESS\n");
295 #endif
296 else if (h_errno == EAI_FAIL)
297 MSG("DNS error: NO_RECOVERY\n");
298 } else {
299 Dns_note_hosts(hosts, res0);
300 dns_server[channel].status = 0;
301 freeaddrinfo(res0);
302 }
303
304 if (dList_length(hosts) > 0) {
305 dns_server[channel].status = 0;
306 } else {
307 dList_free(hosts);
308 hosts = NULL;
309 }
310
311 /* tell our findings */
312 MSG("Dns_server [%d]: %s is", channel,
313 dns_server[channel].hostname);
314 if ((length = dList_length(hosts))) {
315 for (i = 0; i < length; i++) {
316 a_Dns_dillohost_to_string(dList_nth_data(hosts, i),
317 addr_string, sizeof(addr_string));
318 MSG(" %s", addr_string);
319 }
320 MSG("\n");
321 } else {
322 MSG(" (nil)\n");
323 }
292324 dns_server[channel].addr_list = hosts;
293325 dns_server[channel].ip_ready = TRUE;
294326
295327 return NULL; /* (avoids a compiler warning) */
296328 }
297 #endif
298
299 #ifndef D_DNS_THREADED
300 /*
301 * Blocking server-function (it doesn't use threads)
302 */
303 static void Dns_blocking_server(void)
304 {
305 gint channel = 0;
306 struct hostent *host = NULL;
307 GSList *hosts = NULL;
308
309 DEBUG_MSG(3, "Dns_blocking_server: starting...\n");
310 DEBUG_MSG(3, "Dns_blocking_server: dns_server[%d].hostname = %s\n",
311 channel, dns_server[channel].hostname);
312
313 #ifdef ENABLE_IPV6
314 if (ipv6_enabled) {
315 host = gethostbyname2(dns_server[channel].hostname, AF_INET6);
316 if (host)
317 hosts = Dns_note_hosts(hosts, AF_INET6, host);
318 }
319 #endif
320
321 if (!host) {
322 host = gethostbyname(dns_server[channel].hostname);
323 if (host == NULL) {
324 DEBUG_MSG(3, "--> Dns_blocking_server: gethostbyname NULL return\n");
325 } else {
326 DEBUG_MSG(3, "--> Dns_blocking_server - good return\n");
327 hosts = Dns_note_hosts(hosts, AF_INET, host);
328 }
329 }
330
331 /* write IP to server data channel */
332 DEBUG_MSG(3, "Dns_blocking_server: IP of %s is %p\n",
333 dns_server[channel].hostname, hosts);
334 dns_server[channel].addr_list = hosts;
335 dns_server[channel].ip_ready = TRUE;
336
337 DEBUG_MSG(3, "Dns_blocking_server: leaving...\n");
338 }
339 #endif
329
340330
341331 /*
342332 * Request function (spawn a server and let it handle the request)
343333 */
344 static void Dns_server_req(gint channel, const char *hostname)
334 static void Dns_server_req(int channel, const char *hostname)
345335 {
346336 #ifdef D_DNS_THREADED
347337 static pthread_attr_t thrATTR;
348 static gint thrATTRInitialized = 0;
338 static int thrATTRInitialized = 0;
349339 #endif
350340
351341 dns_server[channel].in_use = TRUE;
352342 dns_server[channel].ip_ready = FALSE;
353343
354 g_free(dns_server[channel].hostname);
355 dns_server[channel].hostname = g_strdup(hostname);
344 dFree(dns_server[channel].hostname);
345 dns_server[channel].hostname = dStrdup(hostname);
356346
357347 /* Let's set a timeout client to poll the server channel (5 times/sec) */
358 dns_server[channel].timeout_id =
359 gtk_timeout_add(200, (GtkFunction)Dns_timeout_client,
360 GINT_TO_POINTER(dns_server[channel].channel));
348 a_Timeout_add(0.2,Dns_timeout_client,
349 INT2VOIDP(dns_server[channel].channel));
361350
362351 #ifdef D_DNS_THREADED
363352 /* set the thread attribute to the detached state */
368357 }
369358 /* Spawn thread */
370359 pthread_create(&dns_server[channel].th1, &thrATTR, Dns_server,
371 GINT_TO_POINTER(dns_server[channel].channel));
360 INT2VOIDP(dns_server[channel].channel));
372361 #else
373 Dns_blocking_server();
374 #endif
375 }
376
377 /*
378 * Return the IP for the given hostname.
379 * Side effect: a thread can be spawned later.
380 */
381 static void Dns_lookup(ChainLink *Info, const char *hostname)
382 {
383 gint i, channel;
384
385 g_return_if_fail (hostname != NULL);
362 Dns_server(0);
363 #endif
364 }
365
366 /*
367 * Return the IP for the given hostname using a callback.
368 * Side effect: a thread is spawned when hostname is not cached.
369 */
370 void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data)
371 {
372 int i, channel;
373
374 if (!hostname)
375 return;
386376
387377 /* check for cache hit. */
388378 for (i = 0; i < dns_cache_size; i++)
389379 if (!strcmp(hostname, dns_cache[i].hostname))
390380 break;
391381
392 /* if it hits already resolved, call the Callback inmediately. */
393382 if (i < dns_cache_size) {
394 a_Dns_ccc(OpSend, 1, FWD, Info, dns_cache[i].addr_list, NULL);
395 a_Dns_ccc(OpEnd, 1, FWD, Info, NULL, NULL);
396 return;
397 }
398 if ((i = Dns_queue_find(hostname)) != -1) {
383 /* already resolved, call the Callback immediately. */
384 cb_func(0, dns_cache[i].addr_list, cb_data);
385
386 } else if ((i = Dns_queue_find(hostname)) != -1) {
399387 /* hit in queue, but answer hasn't come back yet. */
400 Dns_queue_add(Info, dns_queue[i].channel, hostname);
388 Dns_queue_add(dns_queue[i].channel, hostname, cb_func, cb_data);
389
401390 } else {
402 /* Never requested before -- we must solve it! */
391 /* Never requested before -- we must resolve it! */
403392
404393 /* Find a channel we can send the request to */
405394 for (channel = 0; channel < num_servers; channel++)
407396 break;
408397 if (channel < num_servers) {
409398 /* Found a free channel! */
410 Dns_queue_add(Info, channel, hostname);
399 Dns_queue_add(channel, hostname, cb_func, cb_data);
411400 Dns_server_req(channel, hostname);
412401 } else {
413402 /* We'll have to wait for a thread to finish... */
414 Dns_queue_add(Info, -2, hostname);
403 Dns_queue_add(-2, hostname, cb_func, cb_data);
415404 }
416405 }
417406 }
419408 /*
420409 * Give answer to all queued callbacks on this channel
421410 */
422 static void Dns_serve_channel(gint channel)
423 {
424 gint i;
425 ChainLink *Info;
411 static void Dns_serve_channel(int channel)
412 {
413 int i;
426414 DnsServer *srv = &dns_server[channel];
427415
428416 for (i = 0; i < dns_queue_size; i++) {
429417 if (dns_queue[i].channel == channel) {
430 Info = dns_queue[i].Info;
431 if ( srv->addr_list == NULL ) {
432 a_Dns_ccc(OpAbort, 1, FWD, Info, NULL, NULL);
433 } else {
434 a_Dns_ccc(OpSend, 1, FWD, Info, srv->addr_list, NULL);
435 a_Dns_ccc(OpEnd, 1, FWD, Info, NULL, NULL);
436 }
418 dns_queue[i].cb_func(srv->status, srv->addr_list,
419 dns_queue[i].cb_data);
437420 Dns_queue_remove(i);
438421 --i;
439422 }
447430 */
448431 static void Dns_assign_channels(void)
449432 {
450 gint ch, i, j;
433 int ch, i, j;
451434
452435 for (ch = 0; ch < num_servers; ++ch) {
453436 if (dns_server[ch].in_use == FALSE) {
471454 }
472455
473456 /*
474 * This is a Gtk+ timeout function that
457 * This is a timeout function that
475458 * reads the DNS results and resumes the stopped jobs.
476459 */
477 static gboolean Dns_timeout_client(gpointer data)
478 {
479 gint channel = GPOINTER_TO_INT(data);
460 static void Dns_timeout_client(void *data)
461 {
462 int channel = VOIDP2INT(data);
480463 DnsServer *srv = &dns_server[channel];
481464
482 if ( srv->ip_ready ){
465 if (srv->ip_ready) {
483466 if (srv->addr_list != NULL) {
484467 /* DNS succeeded, let's cache it */
485468 Dns_cache_add(srv->hostname, srv->addr_list);
486469 }
487470 Dns_serve_channel(channel);
488471 Dns_assign_channels();
489 srv->timeout_id = 0;
490 return FALSE; /* Done! */
491 }
492
493 /* IP not already solved, keep on trying... */
494 return TRUE;
495 }
496
497 /*
498 * CCC function for the DNS module
499 * ( Data1 = hostname | IP; Data2 = {Not used here} )
500 */
501 void a_Dns_ccc(int Op, int Branch, int Dir, ChainLink *Info,
502 void *Data1, void *Data2)
503 {
504 gchar *Hostname;
505
506 if ( Branch == 1 ){
507 if (Dir == BCK) {
508 /* Solve hostname */
509 switch (Op) {
510 case OpStart:
511 Hostname = g_strdup(Data1 ? (gchar *)Data1 : "");
512 Info->LocalKey = Hostname;
513 Dns_lookup(Info, Hostname);
514 break;
515 case OpAbort:
516 Dns_abort_by_ccc(Info);
517 g_free(Info->LocalKey);
518 g_free(Info);
519 break;
520 }
521 } else { /* FWD */
522 /* Solve hostname answer */
523 switch (Op) {
524 case OpSend:
525 a_Chain_fcb(OpSend, Info, Data1, NULL);
526 break;
527 case OpEnd:
528 Hostname = Info->LocalKey;
529 a_Chain_fcb(OpEnd, Info, NULL, NULL);
530 g_free(Hostname);
531 break;
532 case OpAbort:
533 Hostname = Info->LocalKey;
534 a_Chain_fcb(OpAbort, Info, NULL, NULL);
535 g_free(Hostname);
536 break;
537 }
538 }
539 }
540 }
472 a_Timeout_remove(); /* Done! */
473
474 } else {
475 /* IP not already resolved, keep on trying... */
476 a_Timeout_repeat(0.2, Dns_timeout_client, data);
477 }
478 }
479
541480
542481 /*
543482 * Dns memory-deallocation
544483 * (Call this one at exit time)
545484 * The Dns_queue is deallocated at execution time (no need to do that here)
546 * 'dns_cache' is the only one that grows dinamically
485 * 'dns_cache' is the only one that grows dynamically
547486 */
548487 void a_Dns_freeall(void)
549488 {
550 gint i;
489 int i, j;
551490
552491 for ( i = 0; i < dns_cache_size; ++i ){
553 g_free(dns_cache[i].hostname);
554 }
555 g_free(dns_cache);
556 }
557
492 dFree(dns_cache[i].hostname);
493 for ( j = 0; j < dList_length(dns_cache[i].addr_list); ++j)
494 dFree(dList_nth_data(dns_cache[i].addr_list, j));
495 dList_free(dns_cache[i].addr_list);
496 }
497 dFree(dns_cache);
498 }
499
500 /*
501 * Writes a string representation of the given DilloHost
502 * into dst. dst will be \0 terminated.
503 * Please note that dst must be at least 40 bytes long for IPv6
504 * addresses.
505 */
506 void a_Dns_dillohost_to_string(DilloHost *host, char *dst, size_t size)
507 {
508 if (!inet_ntop(host->af, host->data, dst, size)) {
509 switch (errno) {
510 case EAFNOSUPPORT:
511 snprintf(dst, size, "Unknown address family");
512 case ENOSPC:
513 snprintf(dst, size, "Buffer too small");
514 }
515 }
516 }
00 #ifndef __DNS_H__
11 #define __DNS_H__
22
3 #include <gtk/gtk.h>
4 #include "chain.h"
3 #include <netinet/in.h>
54
65 #ifdef __cplusplus
76 extern "C" {
87 #endif /* __cplusplus */
98
9
10 typedef void (*DnsCallback_t)(int status, Dlist *addr_list, void *data);
11
1012 void a_Dns_init (void);
1113 void a_Dns_freeall(void);
12 void a_Dns_ccc(int Op, int Dir, int Br, ChainLink *Info,
13 void *Data1, void *Data2);
14 void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data);
1415
15 #define DILLO_ADDR_MAX 16
16 #ifdef ENABLE_IPV6
17 # define DILLO_ADDR_MAX sizeof(struct in6_addr)
18 #else
19 # define DILLO_ADDR_MAX sizeof(struct in_addr)
20 #endif
1621
1722 typedef struct _DilloHost
1823 {
2025 int alen;
2126 char data[DILLO_ADDR_MAX];
2227 } DilloHost;
28 void a_Dns_dillohost_to_string(DilloHost *host, char *dst, size_t size);
2329
2430 #ifdef __cplusplus
2531 }
0 #ifndef __DOCTREE_HH__
1 #define __DOCTREE_HH__
2
3 #include "lout/misc.hh"
4
5 class DoctreeNode {
6 public:
7 DoctreeNode *parent;
8 int num; // unique ascending id
9 int element;
10 lout::misc::SimpleVector<char*> *klass;
11 const char *pseudo;
12 const char *id;
13
14 DoctreeNode () {
15 parent = NULL;
16 klass = NULL;
17 pseudo = NULL;
18 id = NULL;
19 element = 0;
20 };
21 };
22
23 /**
24 * \brief HTML document tree interface.
25 *
26 * The Doctree class defines the interface to the parsed HTML document tree
27 * as it is used for CSS selector matching.
28 * Currently the Doctree can be represented as stack, however to support
29 * CSS adjacent siblings or for future JavaScript support it may have to
30 * be extended to a real tree.
31 */
32 class Doctree {
33 private:
34 DoctreeNode *topNode;
35 int num;
36
37 public:
38 Doctree () {
39 topNode = NULL;
40 num = 0;
41 };
42 ~Doctree () { while (top ()) pop (); };
43 DoctreeNode *push () {
44 DoctreeNode *dn = new DoctreeNode ();
45 dn->parent = topNode;
46 dn->num = num++;
47 topNode = dn;
48 return dn;
49 };
50 void pop () {
51 DoctreeNode *dn = topNode;
52 if (dn) {
53 dFree ((void*) dn->id);
54 if (dn->klass) {
55 for (int i = 0; i < dn->klass->size (); i++)
56 dFree (dn->klass->get(i));
57 delete dn->klass;
58 }
59 topNode = dn->parent;
60 delete dn;
61 }
62 };
63 inline DoctreeNode *top () {
64 return topNode;
65 };
66 inline DoctreeNode *parent (const DoctreeNode *node) {
67 return node->parent;
68 };
69 };
70
71 #endif
00 /*
11 * File: dpiapi.c
22 *
3 * Copyright (C) 2004 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 2004-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1111 /* Support for dpi/dpip from Dillo's side */
1212
1313 #include "msg.h"
14 #include "browser.h"
14 #include "bw.h"
1515 #include "capi.h"
16 #include "interface.h"
1716 #include "dpiapi.h" /* for prototypes */
17 #include "dialog.hh"
1818 #include "../dpip/dpip.h"
1919
2020
21 /*----------------------------------------------------------------------------
22 * Dialog interface
23 */
21 //----------------------------------------------------------------------------
22 // Dialog interface
23 //
2424
2525 /* This variable can be eliminated as a parameter with a cleaner API. */
2626 static char *dialog_server = NULL;
2929 /*
3030 * Generic callback function for dpip dialogs.
3131 */
32 static void Dpiapi_dialog_answer_cb(BrowserWindow *bw)
32 static void Dpiapi_dialog_answer_cb(BrowserWindow *bw, int answer)
3333 {
34 DialogAnswer *answer = bw->question_dialog_answer;
3534 char *cmd, numstr[16];
3635
3736 /* make dpip tag with the answer */
38 g_snprintf(numstr, 16, "%d", answer->alt_num);
37 snprintf(numstr, 16, "%d", answer);
3938 cmd = a_Dpip_build_cmd("cmd=%s to_cmd=%s msg=%s",
4039 "answer", "dialog", numstr);
4140
4241 /* Send answer */
4342 a_Capi_dpi_send_cmd(NULL, bw, cmd, dialog_server, 0);
44
45 /* cleanup */
46 bw->question_dialog_data = NULL;
47 g_free(answer->this);
48 bw->question_dialog_answer = NULL;
43 dFree(cmd);
4944 }
5045
5146 /*
5550 {
5651 char *question, *alt1, *alt2, *alt3, *alt4, *alt5;
5752 size_t dpip_tag_len;
53 int ret;
5854
59 MSG("a_Dpiapi_dialog:\n");
60 MSG(" dpip_tag: %s\n", dpip_tag);
55 _MSG("a_Dpiapi_dialog:\n");
56 _MSG(" dpip_tag: %s\n", dpip_tag);
6157
6258 /* set the module scoped variable */
6359 dialog_server = server;
6460
6561 /* other options can be parsed the same way */
6662 dpip_tag_len = strlen(dpip_tag);
67 question = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "msg");
68 alt1 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt1");
69 alt2 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt2");
70 alt3 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt3");
71 alt4 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt4");
72 alt5 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt5");
63 question = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "msg");
64 alt1 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt1");
65 alt2 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt2");
66 alt3 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt3");
67 alt4 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt4");
68 alt5 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt5");
7369
74 a_Interface_question_dialog(
75 bw, question, TRUE,
76 alt1, alt2, alt3, alt4, alt5,
77 (GtkSignalFunc) Dpiapi_dialog_answer_cb);
70 ret = a_Dialog_choice5(question, alt1, alt2, alt3, alt4, alt5);
71 /* As choice5 is modal, call the callback function directly. */
72 Dpiapi_dialog_answer_cb(bw, ret);
7873
79 g_free(alt1); g_free(alt2); g_free(alt3); g_free(alt4); g_free(alt5);
80 g_free(question);
74 dFree(alt1); dFree(alt2); dFree(alt3); dFree(alt4); dFree(alt5);
75 dFree(question);
8176 }
8277
0 #ifndef __PNG_H__
1 #define __PNG_H__
2
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7 #include "url.h"
8 #include "image.hh"
9
10
11 void *a_Png_new(DilloImage *Image, DilloUrl *url, int version);
12 void a_Png_callback(int Op, CacheClient_t *Client);
13
14
15 #ifdef __cplusplus
16 }
17 #endif /* __cplusplus */
18 #endif /* !__PNG_H__ */
+0
-107
src/dw.c less more
0 /*
1 * Init and freeall, and some general functions.
2 */
3
4 #include "dw.h"
5 #include "dw_style.h"
6 #include <gdk/gdk.h>
7 #include "debug.h"
8
9 GdkCursor *Dw_cursor_hand;
10
11 void a_Dw_init (void)
12 {
13 DBG_OBJ_COLOR ("^Dw", "ffffe0");
14 DBG_OBJ_COLOR ("^Gtk", "d0ffd0");
15
16 a_Dw_style_init ();
17
18 Dw_cursor_hand = gdk_cursor_new(GDK_HAND2);
19 }
20
21
22 void a_Dw_freeall (void)
23 {
24 a_Dw_style_freeall ();
25
26 gdk_cursor_destroy (Dw_cursor_hand);
27 }
28
29 /*
30 * Return whether src1 and src2 intersect. If yes, return the intersection
31 * rectangle in dest.
32 *
33 * The function has been copied from gdktrectangle.c
34 */
35 gint p_Dw_rectangle_intersect (DwRectangle *src1,
36 DwRectangle *src2,
37 DwRectangle *dest)
38 {
39 DwRectangle *temp;
40 gint src1_x2, src1_y2;
41 gint src2_x2, src2_y2;
42 gint return_val;
43
44 g_return_val_if_fail (src1 != NULL, FALSE);
45 g_return_val_if_fail (src2 != NULL, FALSE);
46 g_return_val_if_fail (dest != NULL, FALSE);
47
48 return_val = FALSE;
49
50 if (src2->x < src1->x) {
51 temp = src1;
52 src1 = src2;
53 src2 = temp;
54 }
55 dest->x = src2->x;
56
57 src1_x2 = src1->x + src1->width;
58 src2_x2 = src2->x + src2->width;
59
60 if (src2->x < src1_x2) {
61 if (src1_x2 < src2_x2)
62 dest->width = src1_x2 - dest->x;
63 else
64 dest->width = src2_x2 - dest->x;
65
66 if (src2->y < src1->y) {
67 temp = src1;
68 src1 = src2;
69 src2 = temp;
70 }
71 dest->y = src2->y;
72
73 src1_y2 = src1->y + src1->height;
74 src2_y2 = src2->y + src2->height;
75
76 if (src2->y < src1_y2) {
77 return_val = TRUE;
78
79 if (src1_y2 < src2_y2)
80 dest->height = src1_y2 - dest->y;
81 else
82 dest->height = src2_y2 - dest->y;
83
84 if (dest->height == 0)
85 return_val = FALSE;
86 if (dest->width == 0)
87 return_val = FALSE;
88 }
89 }
90
91 return return_val;
92 }
93
94
95 /*
96 * Return whether rect1 is a subset of rect2.
97 */
98 gboolean p_Dw_rectangle_is_subset (DwRectangle *rect1,
99 DwRectangle *rect2)
100 {
101 return
102 rect1->x >= rect2->x &&
103 rect1->y >= rect2->y &&
104 rect1->x + rect1->width <= rect2->x + rect2->width &&
105 rect1->y + rect1->height <= rect2->y + rect2->height;
106 }
+0
-28
src/dw.h less more
0 #ifndef __DW_H__
1 #define __DW_H__
2
3 #include <gdk/gdktypes.h>
4
5 typedef struct _DwRectangle DwRectangle;
6
7 struct _DwRectangle
8 {
9 gint32 x;
10 gint32 y;
11 gint32 width;
12 gint32 height;
13 };
14
15 void a_Dw_init (void);
16 void a_Dw_freeall (void);
17
18 gboolean p_Dw_rectangle_intersect (DwRectangle *src1,
19 DwRectangle *src2,
20 DwRectangle *dest);
21 gboolean p_Dw_rectangle_is_subset (DwRectangle *rect1,
22 DwRectangle *rect2);
23
24 /* Needed at several points. */
25 extern GdkCursor *Dw_cursor_hand;
26
27 #endif /* __DW_H__ */
+0
-211
src/dw_aligned_page.c less more
0 /*
1 * File: dw_aligned_page.c
2 *
3 * Copyright (C) 2002, 2003 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This is an abstract class for horizontical alignment of an vertical
13 * array of text blocks, e.g. list items (with list item markers of
14 * different width) and (in the future) table cells aligned at
15 * characters (e.g. decimal point/comma).
16 *
17 * The aligned text blocks are represented by instances of sub-classes
18 * of DwAlignedPage, which define a value used for alignment
19 * (e.g. list item marker width), and implement the following two
20 * methods:
21 *
22 * get_value: Returns the value independent of all other
23 * values.
24 *
25 * set_max_value: This method is called to set the actual value,
26 * calculated from the maximum of all values.
27 */
28
29 #include "dw_aligned_page.h"
30 #include "list.h"
31
32 static void Dw_aligned_page_class_init (DwAlignedPageClass *klass);
33 static void Dw_aligned_page_init (DwAlignedPage *aligned_page);
34
35 static void Dw_aligned_page_destroy (GtkObject *object);
36
37 static gint32 Dw_aligned_page_real_get_value (DwAlignedPage *aligned_page);
38 static void Dw_aligned_page_real_set_max_value (DwAlignedPage *aligned_page,
39 gint32 max_value,
40 gint32 value);
41
42 static DwPageClass *parent_class;
43
44 /*
45 * Standard Gtk+ function.
46 */
47 GtkType a_Dw_aligned_page_get_type (void)
48 {
49 static GtkType type = 0;
50
51 if (!type) {
52 GtkTypeInfo info = {
53 "DwAlignedPage",
54 sizeof (DwAlignedPage),
55 sizeof (DwAlignedPageClass),
56 (GtkClassInitFunc) Dw_aligned_page_class_init,
57 (GtkObjectInitFunc) Dw_aligned_page_init,
58 (GtkArgSetFunc) NULL,
59 (GtkArgGetFunc) NULL,
60 (GtkClassInitFunc) NULL
61 };
62
63 type = gtk_type_unique (DW_TYPE_PAGE, &info);
64 }
65
66 return type;
67 }
68
69
70 /*
71 * Standard Gtk+ function.
72 */
73 static void Dw_aligned_page_class_init (DwAlignedPageClass *klass)
74 {
75 parent_class = gtk_type_class (DW_TYPE_PAGE);
76 GTK_OBJECT_CLASS(klass)->destroy = Dw_aligned_page_destroy;
77 klass->get_value = Dw_aligned_page_real_get_value;
78 klass->set_max_value = Dw_aligned_page_real_set_max_value;
79 }
80
81
82 /*
83 * Standard Gtk+ function.
84 */
85 static void Dw_aligned_page_init (DwAlignedPage *aligned_page)
86 {
87 aligned_page->list = NULL;
88 }
89
90
91 /*
92 * Standard Gtk+ function.
93 */
94 static void Dw_aligned_page_destroy (GtkObject *object)
95 {
96 DwAlignedPage *aligned_page = DW_ALIGNED_PAGE (object);
97
98 if (aligned_page->list) {
99 if (aligned_page->list->refcount == 1) {
100 /* This is the last page in the array, so the list is removed. */
101 g_free (aligned_page->list->pages);
102 g_free (aligned_page->list->values);
103 g_free (aligned_page->list);
104 } else {
105 /* Remove this page from the list. The values are simply set to
106 NULL and 0. */
107 aligned_page->list->pages[aligned_page->list_pos] = NULL;
108 aligned_page->list->values[aligned_page->list_pos] = 0;
109 aligned_page->list->refcount--;
110 }
111 }
112
113 GTK_OBJECT_CLASS(parent_class)->destroy (object);
114 }
115
116
117 /*
118 * This function should be called by the sub-class to define the
119 * relations between the aligned pages. ref_page is either NULL (this
120 * will create a new array), or one page in an other array.
121 */
122 void p_Dw_aligned_page_set_ref_page (DwAlignedPage *aligned_page,
123 DwAlignedPage *ref_page)
124 {
125 DwAlignedPageList *list;
126
127 if (ref_page == NULL) {
128 list = g_new (DwAlignedPageList, 1);
129 list->num = 0;
130 list->refcount = 0;
131 list->pages = NULL;
132 list->values = NULL;
133 list->num_pages_max = 4;
134 list->num_values_max = 4;
135 list->max_value = 0;
136 } else
137 list = ref_page->list;
138
139 list->num++;
140 list->refcount++;
141 a_List_add (list->pages, list->num, list->num_pages_max);
142 list->pages[list->num - 1] = aligned_page;
143 a_List_add (list->values, list->num, list->num_values_max);
144 list->values[list->num - 1] = 0;
145 aligned_page->list = list;
146 aligned_page->list_pos = list->num - 1;
147 p_Dw_aligned_page_update_value (aligned_page);
148 }
149
150
151 /*
152 * This function should be called by the sub-class whenever the
153 * alignment value changes.
154 */
155 void p_Dw_aligned_page_update_value (DwAlignedPage *aligned_page)
156 {
157 DwAlignedPageClass *klass;
158 int i;
159
160 if (aligned_page->list) {
161 klass = DW_ALIGNED_PAGE_CLASS (GTK_OBJECT(aligned_page)->klass);
162 aligned_page->list->values[aligned_page->list_pos] =
163 klass->get_value (aligned_page);
164
165 if (aligned_page->list->values[aligned_page->list_pos] >
166 aligned_page->list->max_value) {
167 /* New value greater than current maximum -> apply it to others. */
168 aligned_page->list->max_value =
169 aligned_page->list->values[aligned_page->list_pos];
170
171 for (i = 0; i < aligned_page->list->num; i++)
172 if (aligned_page->list->pages[i]) {
173 klass = DW_ALIGNED_PAGE_CLASS
174 (GTK_OBJECT(aligned_page->list->pages[i])->klass);
175 klass->set_max_value (aligned_page->list->pages[i],
176 aligned_page->list->max_value,
177 aligned_page->list->values[i]);
178 }
179 } else {
180 /* No change, apply old max_value only to this page. */
181 klass = DW_ALIGNED_PAGE_CLASS (GTK_OBJECT(aligned_page)->klass);
182 klass->set_max_value (aligned_page, aligned_page->list->max_value,
183 aligned_page->list->values
184 [aligned_page->list_pos]);
185 }
186 }
187 }
188
189
190 /*
191 * Standard implementation of DwAlignedPage::get_value.
192 */
193 static gint32 Dw_aligned_page_real_get_value (DwAlignedPage *aligned_page)
194 {
195 g_warning ("DwAlignedPage::get_value not implemented for `%s'",
196 gtk_type_name (GTK_OBJECT_TYPE (aligned_page)));
197 return 0;
198 }
199
200
201 /*
202 * Standard implementation of DwAlignedPage::set_max_value.
203 */
204 static void Dw_aligned_page_real_set_max_value (DwAlignedPage *aligned_page,
205 gint32 max_value,
206 gint32 value)
207 {
208 g_warning ("DwAlignedPage::set_max_value not implemented for `%s'",
209 gtk_type_name (GTK_OBJECT_TYPE (aligned_page)));
210 }
+0
-67
src/dw_aligned_page.h less more
0 #ifndef __DW_ALIGNED_PAGE_H__
1 #define __DW_ALIGNED_PAGE_H__
2
3 #include "dw_page.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_ALIGNED_PAGE (a_Dw_aligned_page_get_type ())
10 #define DW_ALIGNED_PAGE(obj) GTK_CHECK_CAST (obj, \
11 DW_TYPE_ALIGNED_PAGE, DwAlignedPage)
12 #define DW_ALIGNED_PAGE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, \
13 DW_TYPE_ALIGNED_PAGE, \
14 DwAlignedPageClass)
15 #define DW_IS_ALIGNED_PAGE(obj) GTK_CHECK_TYPE (obj, \
16 DW_TYPE_ALIGNED_PAGE)
17
18 typedef struct _DwAlignedPage DwAlignedPage;
19 typedef struct _DwAlignedPageClass DwAlignedPageClass;
20 typedef struct _DwAlignedPageList DwAlignedPageList;
21
22 struct _DwAlignedPage
23 {
24 DwPage page;
25
26 DwAlignedPageList *list;
27 gint list_pos;
28 };
29
30 struct _DwAlignedPageClass
31 {
32 DwPageClass parent_class;
33
34 gint32 (*get_value) (DwAlignedPage *aligned_page);
35 void (*set_max_value) (DwAlignedPage *aligned_page,
36 gint32 max_value,
37 gint32 value);
38 };
39
40
41 struct _DwAlignedPageList
42 {
43 gint num; /* The index of the last page added (minus one). */
44 gint refcount; /* The numbers of non-NULL pages, may be smaller than num.
45 * The distinction is necessary, because removing is
46 * simply done by assigning NULL. */
47 DwAlignedPage **pages;
48 gint32 *values;
49 gint num_pages_max;
50 gint num_values_max;
51
52 gint32 max_value;
53 };
54
55
56 GtkType a_Dw_aligned_page_get_type (void);
57
58 void p_Dw_aligned_page_set_ref_page (DwAlignedPage *aligned_page,
59 DwAlignedPage *ref_page);
60 void p_Dw_aligned_page_update_value (DwAlignedPage *aligned_page);
61
62 #ifdef __cplusplus
63 }
64 #endif /* __cplusplus */
65
66 #endif /* __DW_ALIGNED_PAGE_H__ */
+0
-179
src/dw_bullet.c less more
0 /*
1 * File: dw_bullet.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 Luca Rota <drake@freemail.it>
5 * Copyright (C) 2001-2003 Sebastian Geerken <s.geerken@ping.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 /*
14 * Bullets are drawn 1/5 of an x-height above the baseline, and are
15 * 4/5 of an x-height wide and high.
16 */
17
18 #include "dw_bullet.h"
19 #include "dw_gtk_viewport.h"
20
21 static void Dw_bullet_init (DwBullet *bullet);
22 static void Dw_bullet_class_init (DwBulletClass *klass);
23 static void Dw_bullet_size_request (DwWidget *widget,
24 DwRequisition *requisition);
25 static void Dw_bullet_draw (DwWidget *widget,
26 DwRectangle *area,
27 GdkEventExpose *event);
28 static DwIterator* Dw_bullet_iterator (DwWidget *widget,
29 gint32 mask,
30 gboolean at_end);
31 static void Dw_bullet_iterator_highlight (DwIterator *it,
32 gint start,
33 gint end,
34 DwHighlightLayer layer);
35
36
37
38 GtkType a_Dw_bullet_get_type (void)
39 {
40 static GtkType type = 0;
41
42 if (!type) {
43 GtkTypeInfo info = {
44 "DwBullet",
45 sizeof (DwBullet),
46 sizeof (DwBulletClass),
47 (GtkClassInitFunc) Dw_bullet_class_init,
48 (GtkObjectInitFunc) Dw_bullet_init,
49 (GtkArgSetFunc) NULL,
50 (GtkArgGetFunc) NULL,
51 (GtkClassInitFunc) NULL
52 };
53
54 type = gtk_type_unique (DW_TYPE_WIDGET, &info);
55 }
56
57 return type;
58 }
59
60
61 DwWidget* a_Dw_bullet_new (void)
62 {
63 return DW_WIDGET (gtk_object_new (DW_TYPE_BULLET, NULL));
64 }
65
66
67 static void Dw_bullet_init (DwBullet *bullet)
68 {
69 int i;
70 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS; i++)
71 bullet->selected[i] = FALSE;
72 }
73
74
75 static void Dw_bullet_class_init (DwBulletClass *klass)
76 {
77 DwWidgetClass *widget_class;
78
79 widget_class = (DwWidgetClass*)klass;
80 widget_class->size_request = Dw_bullet_size_request;
81 widget_class->draw = Dw_bullet_draw;
82 widget_class->iterator = Dw_bullet_iterator;
83 }
84
85
86 static void Dw_bullet_size_request (DwWidget *widget,
87 DwRequisition *requisition)
88 {
89 requisition->width = MAX (widget->style->font->x_height * 4 / 5, 1);
90 requisition->ascent = MAX (widget->style->font->x_height, 1);
91 requisition->descent = 0;
92 }
93
94
95 static void Dw_bullet_draw (DwWidget *widget,
96 DwRectangle *area,
97 GdkEventExpose *event)
98 {
99 gint32 x0, y0, x, y;
100 GdkGC *gc;
101 DwStyleColor *bg_color;
102 GdkWindow *window;
103 gint32 l;
104 int i;
105 gboolean selected = FALSE;
106
107 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS && !selected; i++)
108 selected = DW_BULLET(widget)->selected[i];
109
110 l = MIN (widget->allocation.width, widget->allocation.ascent);
111 x = x0 = p_Dw_widget_x_world_to_viewport (widget, widget->allocation.x);
112 y0 = p_Dw_widget_y_world_to_viewport (widget, widget->allocation.y);
113 y = y0 + widget->allocation.ascent - widget->style->font->x_height;
114 gc = selected ? widget->style->color->inverse_gc :
115 widget->style->color->gc;
116 window = DW_WIDGET_WINDOW (widget);
117
118 if (selected) {
119 bg_color = p_Dw_widget_get_bg_color (widget);
120 gdk_draw_rectangle (window, bg_color->inverse_gc,
121 TRUE, x0, y0,
122 widget->allocation.width,
123 DW_WIDGET_HEIGHT(widget));
124 }
125
126 switch (widget->style->list_style_type) {
127 case DW_STYLE_LIST_STYLE_TYPE_DISC:
128 gdk_draw_arc (window, gc, TRUE, x, y, l, l, 0, 360 * 64);
129 break;
130 case DW_STYLE_LIST_STYLE_TYPE_CIRCLE:
131 gdk_draw_arc (window, gc, FALSE, x, y, l, l, 0, 360 * 64);
132 break;
133 case DW_STYLE_LIST_STYLE_TYPE_SQUARE:
134 gdk_draw_rectangle (window, gc, FALSE, x, y, l, l);
135 break;
136 default:
137 break;
138 }
139 }
140
141 static DwIterator *Dw_bullet_iterator (DwWidget *widget,
142 gint32 mask,
143 gboolean at_end)
144 {
145 DwIterator *it;
146 gchar *text;
147
148 switch (widget->style->list_style_type) {
149 case DW_STYLE_LIST_STYLE_TYPE_DISC:
150 text = "*";
151 break;
152 case DW_STYLE_LIST_STYLE_TYPE_CIRCLE:
153 text = "o";
154 break;
155 case DW_STYLE_LIST_STYLE_TYPE_SQUARE:
156 text = "-";
157 break;
158 default:
159 text = "?";
160 break;
161 }
162
163 it = p_Dw_widget_text_iterator (widget, mask, at_end, text);
164 if (it)
165 it->highlight = Dw_bullet_iterator_highlight;
166 return it;
167 }
168
169 static void Dw_bullet_iterator_highlight (DwIterator *it,
170 gint start,
171 gint end,
172 DwHighlightLayer layer)
173 {
174 if (it->content.type == DW_CONTENT_TEXT) {
175 DW_BULLET(it->widget)->selected[layer] = (start == 0 && end >= 1);
176 p_Dw_widget_queue_draw (it->widget);
177 }
178 }
+0
-40
src/dw_bullet.h less more
0 #ifndef __DW_BULLET_H__
1 #define __DW_BULLET_H__
2
3 #include "dw_widget.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_BULLET (a_Dw_bullet_get_type ())
10 #define DW_BULLET(obj) GTK_CHECK_CAST (obj,DW_TYPE_BULLET, DwBullet)
11 #define DW_BULLET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, DW_TYPE_BULLET, \
12 DwBulletClass)
13 #define DW_IS_BULLET(obj) GTK_CHECK_TYPE (obj, DW_TYPE_BULLET)
14
15
16 typedef struct _DwBullet DwBullet;
17 typedef struct _DwBulletClass DwBulletClass;
18
19 struct _DwBullet
20 {
21 DwWidget widget;
22
23 gboolean selected[DW_HIGHLIGHT_NUM_LAYERS];
24 };
25
26 struct _DwBulletClass
27 {
28 DwWidgetClass parent_class;
29 };
30
31
32 GtkType a_Dw_bullet_get_type (void);
33 DwWidget* a_Dw_bullet_new (void);
34
35 #ifdef __cplusplus
36 }
37 #endif /* __cplusplus */
38
39 #endif /* __DW_BULLET_H__ */
+0
-631
src/dw_button.c less more
0 /*
1 * File: dw_button.c
2 *
3 * Copyright (C) 2002 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This widget imitates the look and behavior of GtkButton.
13 *
14 * NOTE: Unlike in X, we do not get release events, if the pointer has
15 * been moved out of the widget with mouse button down (implicit
16 * pointer grab, will perhaps be added to Dw). For this reason, the
17 * behavior of DwButton is a bit differernt from GtkButton: If the
18 * user presses the mouse button within the button, then leaves it
19 * (with mouse button pressed), and enters it again, a GtkButton will
20 * still be in the state "pressed", while DwButton resets this state
21 * when the user leaves the button. This is to avoid problems in the
22 * situation when the user leaves the button with mouse button down,
23 * and releases it outside of the button.
24 *
25 * BUG (reminder): If the user releases the button, it gets somehow a
26 * "leave_notify" event. This bug is in the base code, not here,
27 * somehow it is assumed that the mouse pointer is out of the viewport
28 * (Dw_widget_mouse_event is called with widget == NULL). The effect
29 * is that after releasing the mouse button, the DwButton switches
30 * from "active" to "normal", not to "highlighted", as it should.
31 * Should be fixed at the next opportunity.
32 */
33
34 #include "dw_button.h"
35 #include "dw_gtk_viewport.h"
36 #include "debug.h"
37 #include <gtk/gtk.h>
38
39 static void Dw_button_init (DwButton *button);
40 static void Dw_button_class_init (DwButtonClass *klass);
41
42 static void Dw_button_size_request (DwWidget *widget,
43 DwRequisition *requisition);
44 static void Dw_button_get_extremes (DwWidget *widget,
45 DwExtremes *extremes);
46 static void Dw_button_size_allocate (DwWidget *widget,
47 DwAllocation *allocation);
48 static void Dw_button_set_width (DwWidget *widget,
49 gint32 width);
50 static void Dw_button_set_ascent (DwWidget *widget,
51 gint32 ascent);
52 static void Dw_button_set_descent (DwWidget *widget,
53 gint32 descent);
54 static void Dw_button_draw (DwWidget *widget,
55 DwRectangle *area,
56 GdkEventExpose *event);
57 static gboolean Dw_button_button_press (DwWidget *widget,
58 gint32 x,
59 gint32 y,
60 GdkEventButton *event);
61 static gboolean Dw_button_button_release (DwWidget *widget,
62 gint32 x,
63 gint32 y,
64 GdkEventButton *event);
65 static gboolean Dw_button_enter_notify (DwWidget *widget,
66 DwWidget *last_widget,
67 GdkEventMotion *event);
68 static gboolean Dw_button_leave_notify (DwWidget *widget,
69 DwWidget *next_widget,
70 GdkEventMotion *event);
71
72
73 static void Dw_button_add (DwContainer *container,
74 DwWidget *widget);
75 static void Dw_button_remove (DwContainer *container,
76 DwWidget *widget);
77 static void Dw_button_forall (DwContainer *container,
78 DwCallback callback,
79 gpointer callback_data);
80
81 static DwIterator* Dw_button_iterator (DwWidget *widget,
82 gint mask,
83 gboolean at_end);
84 static gboolean Dw_button_iterator_next (DwIterator *it);
85 static gboolean Dw_button_iterator_prev (DwIterator *it);
86 static gint Dw_button_iterator_compare (DwIterator *it1,
87 DwIterator *it2);
88
89
90 enum
91 {
92 CLICKED,
93 CLICKED_AT,
94 LAST_SIGNAL
95 };
96
97 static DwContainerClass *parent_class;
98 static guint button_signals[LAST_SIGNAL] = { 0 };
99
100
101 /*
102 * Return the type of DwButton
103 */
104 GtkType a_Dw_button_get_type (void)
105 {
106 static GtkType type = 0;
107
108 if (!type) {
109 GtkTypeInfo info = {
110 "DwButton",
111 sizeof (DwButton),
112 sizeof (DwButtonClass),
113 (GtkClassInitFunc) Dw_button_class_init,
114 (GtkObjectInitFunc) Dw_button_init,
115 (GtkArgSetFunc) NULL,
116 (GtkArgGetFunc) NULL,
117 (GtkClassInitFunc) NULL
118 };
119
120 type = gtk_type_unique (DW_TYPE_CONTAINER, &info);
121 }
122
123 return type;
124 }
125
126
127 /*
128 * Standard Gtk+ function.
129 *
130 * - "flags" is a mask for the window flags, it should be the mask the
131 * child uses (e.g. DW_USES_HINT for DwPage, 0 for DwImage). (Will
132 * hopefully be removed soon.)
133 * - When passing FALSE for "relief", the button will neither have a
134 * border, nor paint a background.
135 */
136 DwWidget* a_Dw_button_new (gint flags,
137 gboolean relief)
138 {
139 DwButton *button;
140
141 button = DW_BUTTON (gtk_object_new (DW_TYPE_BUTTON, NULL));
142 DBG_OBJ_CREATE (button, "DwButton");
143 DW_WIDGET_SET_FLAGS (button, flags);
144 button->relief = relief;
145
146 return DW_WIDGET (button);
147 }
148
149
150 /*
151 * Standard Gtk+ function.
152 */
153 static void Dw_button_init (DwButton *button)
154 {
155 button->child = NULL;
156 button->in_button = FALSE;
157 button->pressed = FALSE;
158 button->sensitive = FALSE;
159 }
160
161 /*
162 * Standard Gtk+ function.
163 */
164 static void Dw_button_class_init (DwButtonClass *klass)
165 {
166 GtkObjectClass *object_class;
167 DwWidgetClass *widget_class;
168 DwContainerClass *container_class;
169
170 object_class = (GtkObjectClass*) klass;
171 widget_class = (DwWidgetClass*) klass;
172 container_class = (DwContainerClass*) klass;
173 parent_class = gtk_type_class (DW_TYPE_CONTAINER);
174
175 button_signals[CLICKED] =
176 gtk_signal_new ("clicked",
177 GTK_RUN_FIRST | GTK_RUN_ACTION,
178 object_class->type,
179 GTK_SIGNAL_OFFSET (DwButtonClass, clicked),
180 gtk_marshal_NONE__NONE,
181 GTK_TYPE_NONE, 0);
182 button_signals[CLICKED_AT] =
183 gtk_signal_new ("clicked_at",
184 GTK_RUN_FIRST | GTK_RUN_ACTION,
185 object_class->type,
186 GTK_SIGNAL_OFFSET (DwButtonClass, clicked_at),
187 gtk_marshal_NONE__INT_INT,
188 GTK_TYPE_NONE,
189 2, GTK_TYPE_INT, GTK_TYPE_INT);
190 gtk_object_class_add_signals (object_class, button_signals, LAST_SIGNAL);
191
192 widget_class->size_request = Dw_button_size_request;
193 widget_class->get_extremes = Dw_button_get_extremes;
194 widget_class->size_allocate = Dw_button_size_allocate;
195 widget_class->set_width = Dw_button_set_width;
196 widget_class->set_ascent = Dw_button_set_ascent;
197 widget_class->set_descent = Dw_button_set_descent;
198 widget_class->draw = Dw_button_draw;
199 widget_class->button_press_event = Dw_button_button_press;
200 widget_class->button_release_event = Dw_button_button_release;
201 widget_class->enter_notify_event = Dw_button_enter_notify;
202 widget_class->leave_notify_event = Dw_button_leave_notify;
203 widget_class->iterator = Dw_button_iterator;
204
205 container_class->add = Dw_button_add;
206 container_class->remove = Dw_button_remove;
207 container_class->forall = Dw_button_forall;
208
209 klass->clicked = NULL;
210 klass->clicked_at = NULL;
211 }
212
213
214 /*
215 * Standard Dw function.
216 */
217 static void Dw_button_size_request (DwWidget *widget,
218 DwRequisition *requisition)
219 {
220 DwButton *button = DW_BUTTON (widget);
221 DwRequisition child_requisition;
222 GtkStyleClass *styleclass = widget->viewport->style->klass;
223
224 if (button->child) {
225 p_Dw_widget_size_request (button->child, &child_requisition);
226 *requisition = child_requisition;
227 } else {
228 requisition->width = 0;
229 requisition->ascent = 0;
230 requisition->descent = 0;
231 }
232
233 if (button->relief) {
234 requisition->width += 2 * styleclass->xthickness;
235 requisition->ascent += styleclass->ythickness;
236 requisition->descent += styleclass->ythickness;
237 }
238 }
239
240
241 /*
242 * Standard Dw function.
243 */
244 static void Dw_button_get_extremes (DwWidget *widget,
245 DwExtremes *extremes)
246 {
247 DwButton *button = DW_BUTTON (widget);
248 DwExtremes child_extremes;
249 GtkStyleClass *styleclass = widget->viewport->style->klass;
250
251 if (button->child) {
252 p_Dw_widget_get_extremes (button->child, &child_extremes);
253 *extremes = child_extremes;
254 } else {
255 extremes->min_width = 0;
256 extremes->max_width = 0;
257 }
258
259 if (button->relief) {
260 extremes->min_width += 2 * styleclass->xthickness;
261 extremes->max_width += 2 * styleclass->xthickness;
262 }
263 }
264
265
266 /*
267 * Standard Dw function.
268 */
269 static void Dw_button_size_allocate (DwWidget *widget,
270 DwAllocation *allocation)
271 {
272 DwButton *button = DW_BUTTON (widget);
273 DwAllocation child_allocation;
274 GtkStyleClass *styleclass = widget->viewport->style->klass;
275
276 if (button->child) {
277 child_allocation = *allocation;
278
279 if (button->relief) {
280 child_allocation. x += styleclass->xthickness;
281 child_allocation. y += styleclass->ythickness;
282 child_allocation.width -= 2 * styleclass->xthickness;
283 child_allocation.ascent -= styleclass->ythickness;
284 child_allocation.descent -= styleclass->ythickness;
285 }
286
287 p_Dw_widget_size_allocate (button->child, &child_allocation);
288 }
289 }
290
291
292 /*
293 * Standard Dw function.
294 */
295 static void Dw_button_set_width (DwWidget *widget,
296 gint32 width)
297 {
298 DwButton *button = DW_BUTTON (widget);
299
300 if (button->child)
301 p_Dw_widget_set_width
302 (button->child,
303 width - (button->relief ?
304 (2 * widget->viewport->style->klass->xthickness) : 0));
305 }
306
307
308 /*
309 * Standard Dw function.
310 */
311 static void Dw_button_set_ascent (DwWidget *widget,
312 gint32 ascent)
313 {
314 DwButton *button = DW_BUTTON (widget);
315
316 if (button->child)
317 p_Dw_widget_set_ascent
318 (button->child,
319 ascent - (button->relief ?
320 widget->viewport->style->klass->ythickness : 0));
321 }
322
323
324 /*
325 * Standard Dw function.
326 */
327 static void Dw_button_set_descent (DwWidget *widget,
328 gint32 descent)
329 {
330 DwButton *button = DW_BUTTON (widget);
331
332 if (button->child)
333 p_Dw_widget_set_descent
334 (button->child,
335 descent - (button->relief ?
336 widget->viewport->style->klass->ythickness : 0));
337 }
338
339
340 /*
341 * Standard Dw function.
342 */
343 static void Dw_button_draw (DwWidget *widget,
344 DwRectangle *area,
345 GdkEventExpose *event)
346 {
347 DwButton *button = DW_BUTTON (widget);
348 GtkStateType state;
349 GtkShadowType shadow;
350 GdkRectangle gdk_area;
351 DwRectangle child_area;
352
353 if (button->relief) {
354 if (button->sensitive) {
355 if (button->in_button) {
356 if (button->pressed) {
357 state = GTK_STATE_ACTIVE;
358 shadow = GTK_SHADOW_IN;
359 } else {
360 state = GTK_STATE_PRELIGHT;
361 shadow = GTK_SHADOW_OUT;
362 }
363 } else {
364 state = GTK_STATE_NORMAL;
365 shadow = GTK_SHADOW_OUT;
366 }
367 } else {
368 state = GTK_STATE_INSENSITIVE;
369 shadow = GTK_SHADOW_OUT;
370 }
371
372 gdk_area.x =
373 p_Dw_widget_x_world_to_viewport (widget,
374 area->x + widget->allocation.x);
375 gdk_area.y =
376 p_Dw_widget_y_world_to_viewport (widget,
377 area->y + widget->allocation.y);
378 gdk_area.width = area->width;
379 gdk_area.height = area->height;
380
381 gtk_paint_box (widget->viewport->style, DW_WIDGET_WINDOW (widget),
382 state, shadow, &gdk_area, widget->viewport,
383 "buttondefault",
384 p_Dw_widget_x_world_to_viewport (widget,
385 widget->allocation.x),
386 p_Dw_widget_y_world_to_viewport (widget,
387 widget->allocation.y),
388 widget->allocation.width,
389 DW_WIDGET_HEIGHT(widget));
390 }
391
392 if (button->child &&
393 p_Dw_widget_intersect (button->child, area, &child_area))
394 p_Dw_widget_draw (button->child, &child_area, event);
395 }
396
397
398 /*
399 * Standard Dw function.
400 */
401 static gboolean Dw_button_button_press (DwWidget *widget,
402 gint32 x,
403 gint32 y,
404 GdkEventButton *event)
405 {
406 DwButton *button = DW_BUTTON (widget);
407
408 /* assert it was the left mouse button */
409 if (event->button != 1)
410 return FALSE;
411
412 button->pressed = TRUE;
413 if (button->relief)
414 p_Dw_widget_queue_draw (widget);
415 return TRUE;
416 }
417
418
419 /*
420 * Standard Dw function.
421 */
422 static gboolean Dw_button_button_release (DwWidget *widget,
423 gint32 x,
424 gint32 y,
425 GdkEventButton *event)
426 {
427 DwButton *button = DW_BUTTON (widget);
428 gint32 cx, cy;
429
430 /* Notice that button->pressed may have been set to FALSE in
431 * Dw_button_leave_notify. */
432 if (button->pressed && button->in_button && button->sensitive) {
433 /* simple "click" */
434 gtk_signal_emit (GTK_OBJECT (widget), button_signals[CLICKED]);
435
436 /* "clicked_at": like "clicked", but position is passed, relative
437 * to the child widget. */
438 if (button->child) {
439 cx = x + widget->allocation.x - button->child->allocation.x;
440 cy = y + widget->allocation.y - button->child->allocation.y;
441
442 /* If position is outside of the child, clip at the child
443 allocation */
444 if (cx < 0)
445 cx = 0;
446 else if (cx > button->child->allocation.width - 1)
447 cx = button->child->allocation.width - 1;
448
449 if (cy < 0)
450 cy = 0;
451 else if (cy > (DW_WIDGET_HEIGHT(button->child) - 1))
452 cy = DW_WIDGET_HEIGHT(button->child) - 1;
453
454 gtk_signal_emit (GTK_OBJECT (widget), button_signals[CLICKED_AT],
455 cx, cy);
456 }
457 }
458
459 button->pressed = FALSE;
460 if (button->relief)
461 p_Dw_widget_queue_draw (widget);
462
463 return TRUE;
464 }
465
466
467 /*
468 Standard Dw function.
469
470 */
471 static gboolean Dw_button_enter_notify (DwWidget *widget,
472 DwWidget *last_widget,
473 GdkEventMotion *event)
474 {
475 DwButton *button = DW_BUTTON (widget);
476
477 button->in_button = TRUE;
478 if (button->relief)
479 p_Dw_widget_queue_draw (widget);
480 return TRUE;
481 }
482
483
484 /*
485 * Standard Dw function.
486 */
487 static gboolean Dw_button_leave_notify (DwWidget *widget,
488 DwWidget *next_widget,
489 GdkEventMotion *event)
490 {
491 DwButton *button = DW_BUTTON (widget);
492
493 if (button->child == NULL || next_widget != button->child) {
494 button->in_button = FALSE;
495
496 /* See comment at the beginning. */
497 button->pressed = FALSE;
498
499 if (button->relief)
500 p_Dw_widget_queue_draw (widget);
501 }
502 return TRUE;
503 }
504
505
506 /*
507 * Standard Dw function.
508 */
509 static void Dw_button_add (DwContainer *container,
510 DwWidget *widget)
511 {
512 DwButton *button = DW_BUTTON (container);
513
514 g_return_if_fail (button->child == NULL);
515 button->child = widget;
516 p_Dw_widget_set_parent (widget, DW_WIDGET (container));
517 }
518
519
520 /*
521 * Standard Dw function.
522 */
523 static void Dw_button_remove (DwContainer *container,
524 DwWidget *widget)
525 {
526 DwButton *button = DW_BUTTON (container);
527
528 g_return_if_fail (button->child != NULL);
529 g_return_if_fail (widget == button->child);
530 button->child = NULL;
531 }
532
533
534 /*
535 * Standard Dw function.
536 */
537 static void Dw_button_forall (DwContainer *container,
538 DwCallback callback,
539 gpointer callback_data)
540 {
541 DwButton *button = DW_BUTTON (container);
542
543 if (button->child)
544 callback (button->child, callback_data);
545 }
546
547
548
549 /*
550 * An insensitive button does not respond on user interaction. Used
551 * in HTML forms.
552 */
553 void a_Dw_button_set_sensitive (DwButton *button,
554 gboolean sensitive)
555 {
556 button->sensitive = sensitive;
557 if (button->relief)
558 p_Dw_widget_queue_draw (DW_WIDGET (button));
559 }
560
561
562 static DwIterator *Dw_button_iterator (DwWidget *widget,
563 gint mask,
564 gboolean at_end)
565 {
566 DwIterator *it = g_new (DwIterator, 1);
567 it->widget = widget;
568 it->mask = mask;
569 it->content.type = (at_end ? DW_CONTENT_END : DW_CONTENT_START);
570 it->next = Dw_button_iterator_next;
571 it->prev = Dw_button_iterator_prev;
572 it->clone = p_Dw_iterator_clone_std;
573 it->compare = Dw_button_iterator_compare;
574 it->free = p_Dw_iterator_free_std;
575 it->highlight = p_Dw_iterator_highlight_std;
576 it->get_allocation = p_Dw_iterator_get_allocation_std_only_widgets;
577 return it;
578 }
579
580
581 static gboolean Dw_button_iterator_next (DwIterator *it)
582 {
583 if (it->content.type == DW_CONTENT_START &&
584 (it->mask & DW_CONTENT_WIDGET) &&
585 DW_BUTTON(it->widget)->child) {
586 it->content.type = DW_CONTENT_WIDGET;
587 it->content.data.widget = DW_BUTTON(it->widget)->child;
588 return TRUE;
589 } else {
590 it->content.type = DW_CONTENT_END;
591 return FALSE;
592 }
593 }
594
595 static gboolean Dw_button_iterator_prev (DwIterator *it)
596 {
597 if (it->content.type == DW_CONTENT_END &&
598 (it->mask & DW_CONTENT_WIDGET) &&
599 DW_BUTTON(it->widget)->child) {
600 it->content.type = DW_CONTENT_WIDGET;
601 it->content.data.widget = DW_BUTTON(it->widget)->child;
602 return TRUE;
603 } else {
604 it->content.type = DW_CONTENT_START;
605 return FALSE;
606 }
607 }
608
609
610
611 static gint Dw_button_iterator_compare (DwIterator *it1,
612 DwIterator *it2)
613 {
614 if (it1->content.type == it2->content.type)
615 return 0;
616
617 switch (it1->content.type) {
618 case DW_CONTENT_START:
619 return -1;
620 case DW_CONTENT_TEXT:
621 if (it2->content.type == DW_CONTENT_START)
622 return +1;
623 else
624 return -1;
625 case DW_CONTENT_END:
626 return +1;
627 default:
628 return 0;
629 }
630 }
+0
-60
src/dw_button.h less more
0 /* This module contains the dw_button widget, which is the "back end" to
1 Web text widgets including html. */
2
3 #ifndef __DW_BUTTON_H__
4 #define __DW_BUTTON_H__
5
6 #include "dw_container.h"
7
8 #ifdef __cplusplus
9 extern "C" {
10 #endif /* __cplusplus */
11
12 #define DW_TYPE_BUTTON (a_Dw_button_get_type ())
13 #define DW_BUTTON(obj) GTK_CHECK_CAST ((obj), DW_TYPE_BUTTON, \
14 DwButton)
15 #define DW_BUTTON_CLASS(klass) GTK_CHECK_CLASS_CAST ((klass), \
16 DW_TYPE_BUTTON, \
17 DwButtonClass)
18 #define DW_IS_BUTTON(obj) GTK_CHECK_TYPE ((obj), DW_TYPE_BUTTON)
19 #define DW_IS_BUTTON_CLASS(klass) GTK_CHECK_CLASS_TYPE ((klass), \
20 DW_TYPE_BUTTON)
21
22 typedef struct _DwButton DwButton;
23 typedef struct _DwButtonClass DwButtonClass;
24
25
26
27 struct _DwButton
28 {
29 DwContainer container;
30
31 DwWidget *child;
32 gboolean relief, in_button, pressed, sensitive;
33 };
34
35
36 struct _DwButtonClass
37 {
38 DwContainerClass parent_class;
39
40 void (*clicked) (DwButton *button);
41 void (*clicked_at) (DwButton *button,
42 gint32 x,
43 gint32 y);
44 };
45
46
47 GtkType a_Dw_button_get_type (void);
48 DwWidget* a_Dw_button_new (gint flags,
49 gboolean relief);
50 void a_Dw_button_set_sensitive (DwButton *button,
51 gboolean sensitive);
52
53
54 #ifdef __cplusplus
55 }
56 #endif /* __cplusplus */
57
58
59 #endif /* __DW_BUTTON_H__ */
+0
-154
src/dw_container.c less more
0 #include <gtk/gtk.h>
1 #include "dw_container.h"
2
3 static void Dw_container_init (DwContainer *container);
4 static void Dw_container_class_init (DwContainerClass *klass);
5
6 static void Dw_container_destroy (GtkObject *object);
7
8 enum
9 {
10 ADD,
11 REMOVE,
12 LAST_SIGNAL
13 };
14
15 /* static guint container_signals[LAST_SIGNAL] = { 0 }; */
16
17 static DwWidgetClass *parent_class;
18
19
20 /*
21 * Standard Gtk+ function
22 */
23 GtkType a_Dw_container_get_type (void)
24 {
25 static GtkType type = 0;
26
27 if (!type) {
28 GtkTypeInfo info = {
29 "DwContainer",
30 sizeof (DwContainer),
31 sizeof (DwContainerClass),
32 (GtkClassInitFunc) Dw_container_class_init,
33 (GtkObjectInitFunc) Dw_container_init,
34 (GtkArgSetFunc) NULL,
35 (GtkArgGetFunc) NULL,
36 (GtkClassInitFunc) NULL
37 };
38
39 type = gtk_type_unique (DW_TYPE_WIDGET, &info);
40 }
41
42 return type;
43 }
44
45
46 /*
47 * Standard Gtk+ function
48 */
49 static void Dw_container_init (DwContainer *container)
50 {
51 }
52
53
54 /*
55 * Standard Gtk+ function
56 */
57 static void Dw_container_class_init (DwContainerClass *klass)
58 {
59 GtkObjectClass *object_class;
60
61 parent_class = gtk_type_class (DW_TYPE_WIDGET);
62 object_class = GTK_OBJECT_CLASS (klass);
63
64 #if 0
65 container_signals[ADD] =
66 gtk_signal_new ("add",
67 GTK_RUN_FIRST,
68 object_class->type,
69 GTK_SIGNAL_OFFSET (DwContainerClass, add),
70 gtk_marshal_NONE__POINTER,
71 GTK_TYPE_NONE, 1,
72 GTK_TYPE_WIDGET);
73 container_signals[REMOVE] =
74 gtk_signal_new ("remove",
75 GTK_RUN_FIRST,
76 object_class->type,
77 GTK_SIGNAL_OFFSET (DwContainerClass, remove),
78 gtk_marshal_NONE__POINTER,
79 GTK_TYPE_NONE, 1,
80 GTK_TYPE_WIDGET);
81 #endif
82
83 object_class->destroy = Dw_container_destroy;
84 }
85
86
87 /*
88 * Standard Gtk+ function
89 */
90 static void Dw_container_destroy (GtkObject *object)
91 {
92 a_Dw_container_forall (DW_CONTAINER (object),
93 (DwCallback) gtk_object_destroy,
94 NULL);
95
96 GTK_OBJECT_CLASS(parent_class)->destroy (object);
97 }
98
99
100
101 /*
102 * Add a widget in a "standard" way. Currently not used in dillo,
103 * perhaps never needed.
104 */
105 void a_Dw_container_add (DwContainer *container,
106 DwWidget *child)
107 {
108 DwContainerClass *klass;
109
110 klass = DW_CONTAINER_CLASS (GTK_OBJECT (container)->klass);
111 if (klass->add)
112 (* (klass->add)) (container, child);
113 }
114
115
116 /*
117 *
118 */
119 void a_Dw_container_forall (DwContainer *container,
120 DwCallback callback,
121 gpointer callback_data)
122 {
123 DwContainerClass *klass;
124
125 klass = DW_CONTAINER_CLASS (GTK_OBJECT (container)->klass);
126 if (klass->forall)
127 (* (klass->forall)) (container, callback, callback_data);
128
129 /* The following code is handy for testing DwIterator: */
130
131 /*
132 DwIterator *it;
133
134 if ((it = a_Dw_widget_iterator (DW_WIDGET (container),
135 DW_CONTENT_WIDGET))) {
136 while (a_Dw_iterator_next(it))
137 callback (it->content.data.widget, callback_data);
138 a_Dw_iterator_free (it);
139 }
140 */
141 }
142
143 void Dw_container_remove (DwContainer *container,
144 DwWidget *child)
145 {
146 DwContainerClass *klass;
147
148 klass = DW_CONTAINER_CLASS (GTK_OBJECT (container)->klass);
149 if (klass->remove)
150 (* (klass->remove)) (container, child);
151 }
152
153
+0
-58
src/dw_container.h less more
0 #ifndef __DW_CONTAINER_H__
1 #define __DW_CONTAINER_H__
2
3 #include "dw_widget.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_CONTAINER (a_Dw_container_get_type ())
10 #define DW_CONTAINER(obj) GTK_CHECK_CAST (obj, \
11 DW_TYPE_CONTAINER, DwContainer)
12 #define DW_CONTAINER_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, \
13 DW_TYPE_CONTAINER, DwContainerClass)
14 #define DW_IS_CONTAINER(obj) GTK_CHECK_TYPE (obj, DW_TYPE_CONTAINER)
15
16
17 typedef void (*DwCallback) (DwWidget *widget, gpointer data);
18
19 typedef struct _DwContainer DwContainer;
20 typedef struct _DwContainerClass DwContainerClass;
21
22 struct _DwContainer
23 {
24 DwWidget widget;
25 };
26
27
28 struct _DwContainerClass
29 {
30 DwWidgetClass parent_class;
31
32 void (* add) (DwContainer *container,
33 DwWidget *child);
34 void (* remove) (DwContainer *container,
35 DwWidget *widget);
36 void (* forall) (DwContainer *container,
37 DwCallback callback,
38 gpointer callbabck_data);
39 };
40
41
42 GtkType a_Dw_container_get_type (void);
43
44 void a_Dw_container_add (DwContainer *container,
45 DwWidget *child);
46 void a_Dw_container_forall (DwContainer *container,
47 DwCallback callback,
48 gpointer callback_data);
49
50 void Dw_container_remove (DwContainer *container,
51 DwWidget *child);
52
53 #ifdef __cplusplus
54 }
55 #endif /* __cplusplus */
56
57 #endif /* __DW_CONTAINER_H__ */
+0
-328
src/dw_embed_gtk.c less more
0 #include <gtk/gtk.h>
1 #include "dw_embed_gtk.h"
2 #include "dw_gtk_viewport.h"
3 #include "debug.h"
4
5
6 static void Dw_embed_gtk_init (DwEmbedGtk *embed_gtk);
7 static void Dw_embed_gtk_class_init (DwEmbedGtkClass *klass);
8
9 static void Dw_embed_gtk_destroy (GtkObject *object);
10
11 static void Dw_embed_gtk_size_request (DwWidget *widget,
12 DwRequisition *requisition);
13 static gint Dw_embed_gtk_move_idle (void *data);
14 static void Dw_embed_gtk_size_allocate (DwWidget *widget,
15 DwAllocation *allocation);
16 static void Dw_embed_gtk_draw (DwWidget *widget,
17 DwRectangle *area,
18 GdkEventExpose *event);
19
20 static void Dw_embed_gtk_remove_gtk (DwEmbedGtk *embed_gtk);
21
22 static GtkWidgetClass *parent_class;
23
24 /*
25 * Standard Gtk+ function
26 */
27 GtkType a_Dw_embed_gtk_get_type (void)
28 {
29 static GtkType type = 0;
30
31 if (!type) {
32 GtkTypeInfo info = {
33 "DwEmbedGtk",
34 sizeof (DwEmbedGtk),
35 sizeof (DwEmbedGtkClass),
36 (GtkClassInitFunc) Dw_embed_gtk_class_init,
37 (GtkObjectInitFunc) Dw_embed_gtk_init,
38 (GtkArgSetFunc) NULL,
39 (GtkArgGetFunc) NULL,
40 (GtkClassInitFunc) NULL
41 };
42
43 type = gtk_type_unique (DW_TYPE_WIDGET, &info);
44 }
45
46 return type;
47 }
48
49
50 /*
51 * Standard Gtk+ function
52 */
53 DwWidget* a_Dw_embed_gtk_new (void)
54 {
55 GtkObject *object;
56
57 object = gtk_object_new (DW_TYPE_EMBED_GTK, NULL);
58 DBG_OBJ_CREATE (object, "DwEmbedGtk");
59 return DW_WIDGET (object);
60 }
61
62
63 /*
64 * Standard Gtk+ function
65 */
66 static void Dw_embed_gtk_init (DwEmbedGtk *embed_gtk)
67 {
68 embed_gtk->child = NULL;
69 embed_gtk->idle_id = 0;
70 /*embed_gtk->child_size_request_lock = FALSE;*/
71 }
72
73
74 /*
75 * Standard Gtk+ function
76 */
77 static void Dw_embed_gtk_class_init (DwEmbedGtkClass *klass)
78 {
79 GtkObjectClass *object_class;
80 DwWidgetClass *widget_class;
81
82 parent_class = gtk_type_class (DW_TYPE_WIDGET);
83
84 object_class = GTK_OBJECT_CLASS (klass);
85 object_class->destroy = Dw_embed_gtk_destroy;
86
87 widget_class = (DwWidgetClass*) klass;
88 widget_class->size_allocate = Dw_embed_gtk_size_allocate;
89 widget_class->size_request = Dw_embed_gtk_size_request;
90 widget_class->draw = Dw_embed_gtk_draw;
91 }
92
93
94 /*
95 * Standard Gtk+ function
96 */
97 static void Dw_embed_gtk_destroy (GtkObject *object)
98 {
99 DwEmbedGtk *embed_gtk;
100
101 embed_gtk = DW_EMBED_GTK (object);
102 if (embed_gtk->child)
103 gtk_object_destroy (GTK_OBJECT (embed_gtk->child));
104 if (embed_gtk->idle_id != 0)
105 gtk_idle_remove (embed_gtk->idle_id);
106
107 GTK_OBJECT_CLASS(parent_class)->destroy (object);
108 }
109
110
111
112 /*
113 * Standard Gtk+ function
114 */
115 static void Dw_embed_gtk_size_request (DwWidget *widget,
116 DwRequisition *requisition)
117 {
118 DwEmbedGtk *embed_gtk;
119 GtkRequisition child_requisition;
120
121 embed_gtk = DW_EMBED_GTK (widget);
122 if (embed_gtk->child) {
123 /*embed_gtk->child_size_request_lock = TRUE;*/
124 gtk_widget_size_request (embed_gtk->child, &child_requisition);
125 /*embed_gtk->child_size_request_lock = FALSE;*/
126
127 requisition->width = child_requisition.width;
128 requisition->ascent = child_requisition.height;
129 requisition->descent = 0;
130 } else {
131 requisition->width = 0;
132 requisition->ascent = 0;
133 requisition->descent = 0;
134 }
135 }
136
137
138 /*
139 * Standard Gtk+ function
140 */
141 static gint Dw_embed_gtk_move_idle (void *data)
142 {
143 DwWidget *widget;
144 DwEmbedGtk *embed_gtk;
145
146 widget = DW_WIDGET (data);
147 embed_gtk = DW_EMBED_GTK (data);
148
149 if (embed_gtk->child) {
150 if (embed_gtk->child->parent)
151 gtk_layout_move (GTK_LAYOUT (widget->viewport), embed_gtk->child,
152 widget->allocation.x, widget->allocation.y);
153 else
154 gtk_layout_put (GTK_LAYOUT (widget->viewport), embed_gtk->child,
155 widget->allocation.x, widget->allocation.y);
156 }
157
158 embed_gtk->idle_id = 0;
159 return FALSE;
160 }
161
162
163 /*
164 * Standard Gtk+ function
165 */
166 static void Dw_embed_gtk_size_allocate (DwWidget *widget,
167 DwAllocation *allocation)
168 {
169 DwEmbedGtk *embed_gtk;
170
171 embed_gtk = DW_EMBED_GTK (widget);
172
173 if (embed_gtk->child && widget->viewport) {
174 if (allocation->width == 0 ||
175 allocation->ascent + allocation->descent == 0)
176 gtk_widget_hide (embed_gtk->child);
177 else {
178 gtk_widget_show (embed_gtk->child);
179
180 if (allocation->width != widget->allocation.width ||
181 allocation->ascent + allocation->descent
182 != DW_WIDGET_HEIGHT(widget))
183 /* todo: try implementing it by gtk_widget_size_allocate */
184 gtk_widget_set_usize (embed_gtk->child,
185 allocation->width,
186 allocation->ascent + allocation->descent);
187
188 if (allocation->x != widget->allocation.x ||
189 allocation->y != widget->allocation.y) {
190 /* A simple call to gtk_layout_move does not seem to work, so this
191 strange idle function. */
192 if (embed_gtk->idle_id == 0)
193 embed_gtk->idle_id =
194 gtk_idle_add (Dw_embed_gtk_move_idle, (void*) embed_gtk);
195 }
196 }
197 }
198 }
199
200
201 static void Dw_embed_gtk_draw (DwWidget *widget,
202 DwRectangle *area,
203 GdkEventExpose *event)
204 {
205 /* This is the job of GtkDwViewport (resp. the base class GtkLayout). */
206 }
207
208
209 /*
210 * This function is called when the embedded Gtk+ widget is destroyed.
211 * Don't use this function directly!
212 */
213 static void Dw_embed_gtk_remove_gtk (DwEmbedGtk *embed_gtk)
214 {
215 embed_gtk->child = NULL;
216 p_Dw_widget_queue_resize (DW_WIDGET (embed_gtk), 0, TRUE);
217 }
218
219
220 /*
221 * This function is called when the embedded Gtk+ widget is focused and
222 * will set the adjustments of the viewport.
223 * Don't use this function directly!
224 */
225 static int Dw_embed_gtk_child_focus_in (DwEmbedGtk *embed_gtk)
226 {
227 DwWidget *widget;
228 GtkLayout *layout;
229 gint vx, vy, vw, vh, wx, wy, ww, wh;
230
231 widget = DW_WIDGET (embed_gtk);
232 layout = GTK_LAYOUT (widget->viewport);
233
234 vx = layout->xoffset;
235 vy = layout->yoffset;
236 vw = GTK_WIDGET(layout)->allocation.width;
237 vh = GTK_WIDGET(layout)->allocation.height;
238
239 wx = widget->allocation.x;
240 wy = widget->allocation.y;
241 ww = widget->allocation.width;
242 wh = DW_WIDGET_HEIGHT(widget);
243
244 if (vx > wx)
245 gtk_adjustment_set_value (layout->hadjustment, wx);
246 else if (vx < wx + ww - vw)
247 gtk_adjustment_set_value (layout->hadjustment, wx + ww - vw);
248
249 if (vy > wy)
250 gtk_adjustment_set_value (layout->vadjustment, wy);
251 else if (vy < wy + wh - vh)
252 gtk_adjustment_set_value (layout->vadjustment, wy + wh - vh);
253
254 return FALSE;
255 }
256
257 #if 0
258 /*
259 * Called when the embedded Gtk widget emits a "size_request" signal, and used
260 * to resise the embedding Dw widget.
261 *
262 * Currently not used.
263 */
264 static void Dw_embed_gtk_child_size_request (DwEmbedGtk *embed_gtk)
265 {
266 DwWidget *widget;
267 GtkRequisition child_requisition;
268
269 g_return_if_fail (embed_gtk->child);
270
271 if (!embed_gtk->child_size_request_lock) {
272 widget = DW_WIDGET (embed_gtk);
273 embed_gtk->child_size_request_lock = TRUE;
274 gtk_widget_size_request (embed_gtk->child, &child_requisition);
275 embed_gtk->child_size_request_lock = FALSE;
276
277 /* Check wether the size has changed at all. */
278 if (widget->requisition.width != child_requisition.width ||
279 widget->requisition.ascent != child_requisition.height)
280 p_Dw_widget_queue_resize (widget, 0, TRUE);
281 }
282 }
283 #endif
284
285 /*
286 * Add the Gtk+ widget to be embedded.
287 * If there is already one, you have to destroy it before.
288 */
289 void a_Dw_embed_gtk_add_gtk (DwEmbedGtk *embed_gtk,
290 GtkWidget *widget)
291 {
292 /* todo: problems with reparent's? */
293
294 g_return_if_fail (embed_gtk->child == NULL);
295
296 embed_gtk->child = widget;
297
298 if (DW_WIDGET(embed_gtk)->viewport)
299 gtk_layout_put (GTK_LAYOUT (DW_WIDGET (embed_gtk)->viewport),
300 widget,
301 DW_WIDGET (embed_gtk)->allocation.x,
302 DW_WIDGET (embed_gtk)->allocation.y);
303
304 /* todo: We somehow must regognize size changes of the embedded Gtk+
305 * widget. Can this be done with size_request? Anyway, currently
306 * deactivated, since it is not used in dillo. */
307 #if 0
308 gtk_signal_connect_object (GTK_OBJECT (embed_gtk->child),
309 "size_request",
310 GTK_SIGNAL_FUNC(Dw_embed_gtk_child_size_request),
311 GTK_OBJECT (embed_gtk));
312 #endif
313
314 /* for setting the adjustments */
315 gtk_signal_connect_object (GTK_OBJECT (embed_gtk->child),
316 "focus_in_event",
317 GTK_SIGNAL_FUNC (Dw_embed_gtk_child_focus_in),
318 GTK_OBJECT (embed_gtk));
319
320 /* todo: An idea: use "remove" signal of DwGtkContainer instead. */
321 gtk_signal_connect_object (GTK_OBJECT (embed_gtk->child),
322 "destroy",
323 GTK_SIGNAL_FUNC (Dw_embed_gtk_remove_gtk),
324 GTK_OBJECT (embed_gtk));
325
326 p_Dw_widget_queue_resize (DW_WIDGET (embed_gtk), 0, TRUE);
327 }
+0
-45
src/dw_embed_gtk.h less more
0 #ifndef __DW_EMBED_GTK_H__
1 #define __DW_EMBED_GTK_H__
2
3 #include "dw_widget.h"
4 #include <gtk/gtkwidget.h>
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
9
10 #define DW_TYPE_EMBED_GTK (a_Dw_embed_gtk_get_type ())
11 #define DW_EMBED_GTK(obj) GTK_CHECK_CAST (obj, \
12 DW_TYPE_EMBED_GTK, DwEmbedGtk)
13 #define DW_EMBED_GTK_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, \
14 DW_TYPE_EMBED_GTK, DwEmbedGtkClass)
15 #define DW_IS_EMBED_GTK(obj) GTK_CHECK_TYPE (obj, DW_TYPE_EMBED_GTK)
16
17 typedef struct _DwEmbedGtk DwEmbedGtk;
18 typedef struct _DwEmbedGtkClass DwEmbedGtkClass;
19
20 struct _DwEmbedGtk
21 {
22 DwWidget container;
23
24 GtkWidget *child;
25 gint idle_id;
26 /*gboolean child_size_request_lock;*/
27 };
28
29
30 struct _DwEmbedGtkClass
31 {
32 DwWidgetClass parent_class;
33 };
34
35 GtkType a_Dw_embed_gtk_get_type (void);
36 DwWidget* a_Dw_embed_gtk_new (void);
37 void a_Dw_embed_gtk_add_gtk (DwEmbedGtk *embed_gtk,
38 GtkWidget *widget);
39
40 #ifdef __cplusplus
41 }
42 #endif /* __cplusplus */
43
44 #endif /* __DW_EMBED_GTK_H__ */
+0
-544
src/dw_ext_iterator.c less more
0 /*
1 * File: dw_ext_iterator.c
2 *
3 * Copyright (C) 2002 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * DwExtIterator is an extension of DwIterator, which stores
13 * DwIterator's in a stack, for cases where recursions are not
14 * possible.
15 *
16 * DwWordIterator is a simple wrapper for DwExtIterator, which splits
17 * text segments into words, and is used for text search (in
18 * findtext.c)
19 *
20 * todo: In some cases (e.g. for "<i>foo</i>bar"), DwIterator and
21 * DwExtIterator return two words, where there is actually only
22 * one. In this case, iterator->content.space is set to FALSE.
23 * DwWordIterator currently ignores this flag, but instead
24 * returns two words, instead of concaternating them to one.
25 */
26
27 #include "dw_ext_iterator.h"
28 #include "list.h"
29 #include "misc.h"
30
31 /*#define DEBUG_LEVEL 1*/
32 #include "debug.h"
33
34 /*
35 * The following two functions are used by a_Dw_extiterator_new, when the
36 * passed DwIterator points to a widget. Since a DwExtIterator never returns
37 * a widget, the DwIterator has to be corrected, by searching for the next
38 * content downwards (within the widget pointed to), forwards, and backwards
39 * (in the traversed tree).
40 */
41
42 /*
43 * Search downwards. If from_end is TRUE, start search at the end,
44 * otherwise at the beginning.
45 * The pararameter indent is only for debugging purposes.
46 */
47 static DwIterator *Dw_ext_iterator_search_downward (DwIterator *it,
48 gboolean from_end,
49 int indent)
50 {
51 DwIterator *it2, *it3;
52
53 DEBUG_MSG (1, "%*smoving down (%swards) from %s\n",
54 indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
55
56 g_return_val_if_fail (it->content.type == DW_CONTENT_WIDGET, NULL);
57 it2 = a_Dw_widget_iterator (it->content.data.widget, it->mask, from_end);
58
59 if (it2 == NULL) {
60 /* Moving downwards failed. */
61 DEBUG_MSG (1, "%*smoving down failed\n", indent, "");
62 return NULL;
63 }
64
65 while (from_end ? a_Dw_iterator_prev (it2) : a_Dw_iterator_next (it2)) {
66 DEBUG_MSG (1, "%*sexamining %s\n",
67 indent, "", a_Dw_iterator_text (it2));
68
69 if (it2->content.type == DW_CONTENT_WIDGET) {
70 /* Another widget. Search in it downwards. */
71 it3 = Dw_ext_iterator_search_downward (it2, from_end, indent + 3);
72 if (it3 != NULL) {
73 a_Dw_iterator_free (it2);
74 return it3;
75 }
76 /* Else continue in this widget. */
77 } else {
78 /* Success! */
79 DEBUG_MSG (1, "%*smoving down succeeded: %s\n",
80 indent, "", a_Dw_iterator_text (it2));
81 return it2;
82 }
83 }
84
85 /* Nothing found. */
86 a_Dw_iterator_free (it2);
87 DEBUG_MSG (1, "%*smoving down failed (nothing found)\n", indent, "");
88 return NULL;
89 }
90
91 /*
92 * Search sidewards. from_end specifies the direction, FALSE means forwards,
93 * TRUE means backwards.
94 * The pararameter indent is only for debugging purposes.
95 */
96 static DwIterator *Dw_ext_iterator_search_sideward (DwIterator *it,
97 gboolean from_end,
98 int indent)
99 {
100 DwIterator *it2, *it3;
101
102 DEBUG_MSG (1, "%*smoving %swards from %s\n",
103 indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
104
105 g_return_val_if_fail (it->content.type == DW_CONTENT_WIDGET, NULL);
106 it2 = a_Dw_iterator_clone (it);
107
108 while (from_end ? a_Dw_iterator_prev (it2) : a_Dw_iterator_next (it2)) {
109 if (it2->content.type == DW_CONTENT_WIDGET) {
110 /* Search downwards in this widget. */
111 it3 = Dw_ext_iterator_search_downward (it2, from_end, indent + 3);
112 if (it3 != NULL) {
113 a_Dw_iterator_free (it2);
114 DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
115 indent, "", from_end ? "back" : "for",
116 a_Dw_iterator_text (it3));
117 return it3;
118 }
119 /* Else continue in this widget. */
120 } else {
121 /* Success! */
122 DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
123 indent, "", from_end ? "back" : "for",
124 a_Dw_iterator_text (it2));
125 return it2;
126 }
127 }
128
129 /* Nothing found, go upwards in the tree (if possible). */
130 a_Dw_iterator_free (it2);
131 if (it->widget->parent) {
132 it2 = a_Dw_widget_iterator (it->widget->parent, it->mask, FALSE);
133 while (TRUE) {
134 if (!a_Dw_iterator_next(it2)) {
135 g_warning ("BUG in DwExtIterator!");
136 a_Dw_iterator_free (it2);
137 return NULL;
138 }
139 if (it2->content.type == DW_CONTENT_WIDGET &&
140 it2->content.data.widget == it->widget) {
141 it3 = Dw_ext_iterator_search_sideward (it2, from_end, indent + 3);
142 a_Dw_iterator_free (it2);
143 DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
144 indent, "", from_end ? "back" : "for",
145 a_Dw_iterator_text (it3));
146 return it3;
147 }
148 }
149 }
150
151 /* Nothing found at all. */
152 DEBUG_MSG (1, "%*smoving %swards failed (nothing found)\n",
153 indent, "", from_end ? "back" : "for");
154 return NULL;
155 }
156
157
158 /*
159 * Create a new DwExtIterator from an existing DwIterator. The content of the
160 * return value will be the content of "it". If within the widget tree, there
161 * is no non-widget content, NULL is returned.
162 *
163 * NOTES:
164 * (i) If you want to continue using "it", pass a_Dw_iterator_clone (it).
165 * (ii) The mask of "it" must include DW_CONTENT_WIDGET, but
166 * a_Dw_ext_iterator_next will never return widgets.
167 *
168 * TODO: Change in the near future: NULL is by all other functions within this
169 * module interpreted as an "empty" iterator, which makes code outside more
170 * robust.
171 */
172 DwExtIterator* a_Dw_ext_iterator_new (DwIterator *it)
173 {
174 DwExtIterator *eit;
175 DwIterator *it2;
176 DwWidget *w;
177 int sp;
178
179 DEBUG_MSG (1, "a_Dw_ext_iterator_new: %s\n", a_Dw_iterator_text (it));
180
181 /* If "it" points to a widget, find a near non-widget content,
182 * since an DwExtIterator should never return widgets. */
183 if (it->content.type == DW_CONTENT_WIDGET) {
184 /* The second argument of Dw_ext_iterator_search_downward is
185 * actually a matter of taste :-) */
186 if ((it2 = Dw_ext_iterator_search_downward (it, FALSE, 3)) ||
187 (it2 = Dw_ext_iterator_search_sideward (it, FALSE, 3)) ||
188 (it2 = Dw_ext_iterator_search_sideward (it, TRUE, 3))) {
189 a_Dw_iterator_free (it);
190 it = it2;
191 } else {
192 /* This may happen, when a page does not contain any
193 * non-widget content. */
194 DEBUG_MSG (1, "a_Dw_ext_iterator_new got totally helpless!\n");
195 a_Dw_iterator_free (it);
196 return NULL;
197 }
198 }
199
200 DEBUG_MSG (1, " => %s\n", a_Dw_iterator_text (it));
201
202 eit = g_new (DwExtIterator, 1);
203 eit->stack_top = 0;
204 /* If this widget has parents, we must construct appropiate iterators.
205 * todo: There may be a faster way instead of iterating through the
206 * parent widgets. */
207 for (w = it->widget; w->parent != NULL; w = w->parent)
208 eit->stack_top++;
209 eit->stack_max = 4;
210 while (eit->stack_top >= eit->stack_max)
211 eit->stack_max <<= 1;
212 eit->stack = g_new (DwIterator*, eit->stack_max);
213
214 /* Construct the iterators. */
215 for (w = it->widget, sp = eit->stack_top - 1;
216 w->parent != NULL;
217 w = w->parent, sp--) {
218 eit->stack[sp] = a_Dw_widget_iterator (w->parent, it->mask, FALSE);
219 while (TRUE) {
220 if (!a_Dw_iterator_next(eit->stack[sp])) {
221 g_warning ("BUG in DwExtIterator!");
222 return NULL;
223 }
224 if (eit->stack[sp]->content.type == DW_CONTENT_WIDGET &&
225 eit->stack[sp]->content.data.widget == w)
226 break;
227 }
228 }
229
230 eit->stack[eit->stack_top] = it;
231 eit->content = it->content;
232 return eit;
233 }
234
235
236 /*
237 * This function is similar to a_Dw_ext_iterator_new, but is in many
238 * cases faster, since it tries to copy parts of the stack of "base".
239 * Used for selection, where the old and new position (which are
240 * represented by DwExtIterator) are often quite next to each other.
241 * "base" may be NULL.
242 */
243 DwExtIterator* a_Dw_ext_iterator_new_variant (DwExtIterator *base,
244 DwIterator *it)
245 {
246 /* todo: Not yet implemented, and actually not yet needed very much. */
247 return a_Dw_ext_iterator_new (it);
248 }
249
250
251 /*
252 * Move iterator forward and store content in it. Returns TRUE on
253 * success.
254 */
255 gboolean a_Dw_ext_iterator_next (DwExtIterator *eit)
256 {
257 DwIterator *it = eit->stack[eit->stack_top];
258
259 if (a_Dw_iterator_next(it)) {
260 if (it->content.type == DW_CONTENT_WIDGET) {
261 /* Widget: new iterator on stack, to search in this widget. */
262 eit->stack_top++;
263 a_List_add (eit->stack, eit->stack_top, eit->stack_max);
264 eit->stack[eit->stack_top] =
265 a_Dw_widget_iterator (it->content.data.widget, it->mask, FALSE);
266 return a_Dw_ext_iterator_next (eit);
267 } else {
268 /* Simply return the content of the iterartor. */
269 eit->content = it->content;
270 return TRUE;
271 }
272 } else {
273 /* No more data in the top-most widget. */
274 if (eit->stack_top > 0) {
275 /* Pop iterator from stack, and move to next item in the old one. */
276 a_Dw_iterator_free (it);
277 eit->stack_top--;
278 return a_Dw_ext_iterator_next (eit);
279 } else {
280 /* Stack is empty. */
281 eit->content.type = DW_CONTENT_END;
282 return FALSE;
283 }
284 }
285 }
286
287 /*
288 * Move iterator backward and store content in it. Returns TRUE on
289 * success.
290 */
291 gboolean a_Dw_ext_iterator_prev (DwExtIterator *eit)
292 {
293 DwIterator *it = eit->stack[eit->stack_top];
294
295 if (a_Dw_iterator_prev(it)) {
296 if (it->content.type == DW_CONTENT_WIDGET) {
297 /* Widget: new iterator on stack, to search in this widget. */
298 eit->stack_top++;
299 a_List_add (eit->stack, eit->stack_top, eit->stack_max);
300 eit->stack[eit->stack_top] =
301 a_Dw_widget_iterator (it->content.data.widget, it->mask, TRUE);
302 return a_Dw_ext_iterator_prev (eit);
303 } else {
304 /* Simply return the content of the iterartor. */
305 eit->content = it->content;
306 return TRUE;
307 }
308 } else {
309 /* No more data in the top-most widget. */
310 if (eit->stack_top > 0) {
311 /* Pop iterator from stack, and move to previous item in the
312 old one. */
313 a_Dw_iterator_free (it);
314 eit->stack_top--;
315 return a_Dw_ext_iterator_prev (eit);
316 } else {
317 /* Stack is empty. */
318 eit->content.type = DW_CONTENT_START;
319 return FALSE;
320 }
321 }
322 }
323
324 /*
325 * Create an exact copy of the iterator, which then can be used
326 * independantly of the original one.
327 */
328 DwExtIterator* a_Dw_ext_iterator_clone (DwExtIterator *eit)
329 {
330 int i;
331 DwExtIterator *eit2 = g_new (DwExtIterator, 1);
332
333 *eit2 = *eit;
334 eit2->stack = g_new (DwIterator*, eit2->stack_max);
335 for (i = 0; i <= eit2->stack_top; i++)
336 eit2->stack[i] = a_Dw_iterator_clone (eit->stack[i]);
337
338 return eit2;
339 }
340
341 /*
342 * Return a value < 0, if first iterator is smaller, > 0, if it is
343 * greater, or 0 for equal iterators. It is assumed, that both terators
344 * belong to the same widget tree (i.e. they have the same top-level
345 * widget).
346 */
347 gint a_Dw_ext_iterator_compare (DwExtIterator *eit1,
348 DwExtIterator *eit2)
349 {
350 int nea = 0;
351
352 while (eit1->stack[nea]->widget == eit2->stack[nea]->widget) {
353 if (nea == eit1->stack_top || nea == eit2->stack_top)
354 break;
355 nea++;
356 }
357 if (eit1->stack[nea]->widget != eit2->stack[nea]->widget)
358 nea--;
359 return a_Dw_iterator_compare (eit1->stack[nea], eit2->stack[nea]);
360 }
361
362 /*
363 * Free memory of iterator.
364 */
365 void a_Dw_ext_iterator_free (DwExtIterator *eit)
366 {
367 int i;
368 for (i = 0; i <= eit->stack_top; i++)
369 a_Dw_iterator_free (eit->stack[i]);
370 g_free (eit->stack);
371 g_free (eit);
372 }
373
374 /*
375 * Create a new word iterator. <widget> is the top of the widget
376 * tree over which is iterated.
377 */
378 DwWordIterator* a_Dw_word_iterator_new (DwWidget *widget)
379 {
380 DwWordIterator *it;
381 DwIterator *it0;
382
383 it = g_new (DwWordIterator, 1);
384 it->word = NULL;
385 it0 = a_Dw_widget_iterator (widget, DW_CONTENT_TEXT | DW_CONTENT_WIDGET,
386 FALSE);
387 it->iterator = a_Dw_ext_iterator_new (it0);
388 it->word_splitpos = NULL;
389 it->content_hl_start = -1;
390 it->content_hl_end = -1;
391 return it;
392 }
393
394 /*
395 * Move iterator forward and store word in it. Returns TRUE on
396 * success.
397 */
398 gboolean a_Dw_word_iterator_next (DwWordIterator *it)
399 {
400 if (it->word) {
401 g_free (it->word);
402 it->word = NULL;
403 }
404
405 while (it->word_splitpos == NULL ||
406 it->word_splitpos[2 * (it->word_pos + 1)] == -1) {
407 if (it->word_splitpos) {
408 g_free (it->word_splitpos);
409 it->word_splitpos = NULL;
410 it->content_hl_start = -1;
411 it->content_hl_end = -1;
412 }
413 if (!a_Dw_ext_iterator_next (it->iterator))
414 return FALSE;
415 it->word_splitpos =
416 a_Misc_strsplitpos (it->iterator->content.data.text, " \t\n");
417 it->word_pos = -1;
418 }
419
420 it->word_pos++;
421 it->word = a_Misc_strpdup (it->iterator->content.data.text,
422 it->word_splitpos[2 * it->word_pos],
423 it->word_splitpos[2 * it->word_pos + 1]);
424 return TRUE;
425 }
426
427
428 /*
429 * Move iterator backward and store word in it. Returns TRUE on
430 * success.
431 */
432 gboolean a_Dw_word_iterator_prev (DwWordIterator *it)
433 {
434 if (it->word) {
435 g_free (it->word);
436 it->word = NULL;
437 }
438
439 while (it->word_splitpos == NULL || it->word_pos == 0) {
440 if (it->word_splitpos) {
441 g_free (it->word_splitpos);
442 it->word_splitpos = NULL;
443 it->content_hl_start = -1;
444 it->content_hl_end = -1;
445 }
446 if (!a_Dw_ext_iterator_prev (it->iterator))
447 return FALSE;
448 it->word_splitpos =
449 a_Misc_strsplitpos(it->iterator->content.data.text, " \t\n");
450 it->word_pos = 0;
451 while (it->word_splitpos[2 * it->word_pos] != -1)
452 it->word_pos++;
453 }
454
455 it->word_pos--;
456 it->word = a_Misc_strpdup (it->iterator->content.data.text,
457 it->word_splitpos[2 * it->word_pos],
458 it->word_splitpos[2 * it->word_pos + 1]);
459 return TRUE;
460 }
461
462 /*
463 * Highlight a part of the current word. Unhighlight the current word
464 * by passing -1 as start.
465 */
466 void a_Dw_word_iterator_highlight (DwWordIterator *it,
467 gint start,
468 gint end,
469 DwHighlightLayer layer)
470 {
471 gint new_start, new_end;
472
473 if (start == -1) {
474 /* Unhighlight the whole content word.
475 * todo: This works incorrect, by unhighlighting the whole
476 * current content of the DwExtIterator. Anyway, a correct
477 * behavior is not needed. */
478 it->content_hl_start = -1;
479 it->content_hl_end = -1;
480 a_Dw_ext_iterator_unhighlight (it->iterator, layer);
481 } else {
482 new_start = it->word_splitpos[2 * it->word_pos] + start;
483 new_end = it->word_splitpos[2 * it->word_pos] + end;
484
485 if (it->content_hl_start == -1) {
486 /* nothing selected yet */
487 it->content_hl_start = new_start;
488 it->content_hl_end = new_end;
489 } else {
490 it->content_hl_start = MIN (it->content_hl_start, new_start);
491 it->content_hl_end = MAX (it->content_hl_end, new_end);
492 }
493
494 a_Dw_ext_iterator_highlight (it->iterator, it->content_hl_start,
495 it->content_hl_end, layer);
496 }
497 }
498
499 void a_Dw_word_iterator_get_allocation (DwWordIterator *it,
500 gint start,
501 gint end,
502 DwAllocation *allocation)
503 {
504 /* todo: Implement this. (Although it is not used yet.) */
505 g_assert_not_reached ();
506 }
507
508 void a_Dw_word_iterator_scroll_to (DwWordIterator *it1,
509 DwWordIterator *it2,
510 gint start,
511 gint end,
512 DwHPosition hpos,
513 DwVPosition vpos)
514 {
515 gint real_start, real_end;
516
517 real_start = it1->word_splitpos[2 * it1->word_pos] + start;
518 real_end = it2->word_splitpos[2 * it2->word_pos] + end;
519 a_Dw_ext_iterator_scroll_to (it1->iterator, it2->iterator,
520 real_start, real_end, hpos, vpos);
521 }
522
523
524 DwWordIterator* a_Dw_word_iterator_clone (DwWordIterator *it)
525 {
526 DwWordIterator *it2;
527
528 it2 = g_new (DwWordIterator, 1);
529 *it2 = *it;
530 it2->iterator = a_Dw_ext_iterator_clone (it->iterator);
531 it2->word = it->word ? g_strdup (it->word) : NULL;
532 it2->word_splitpos =
533 it->word_splitpos ? a_Misc_strsplitposdup (it->word_splitpos) : NULL;
534 return it2;
535 }
536
537 void a_Dw_word_iterator_free (DwWordIterator *it)
538 {
539 a_Dw_ext_iterator_free (it->iterator);
540 g_free (it->word);
541 g_free (it->word_splitpos);
542 g_free (it);
543 }
+0
-87
src/dw_ext_iterator.h less more
0 #ifndef __DW_EXT_ITERATOR_H__
1 #define __DW_EXT_ITERATOR_H__
2
3 #include "dw_widget.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 typedef struct _DwExtIterator
10 {
11 DwIterator **stack;
12 gint stack_top;
13 gint stack_max;
14
15 DwContent content;
16 } DwExtIterator;
17
18 typedef struct _DwWordIterator
19 {
20 /* The current word. NULL, when at the end or at the beginning */
21 gchar *word;
22
23 /* private stuff */
24 DwExtIterator *iterator;
25 gint *word_splitpos; /* the positions of the words within
26 * iterator->content.data.text, as returned
27 * by a_Misc_strsplitpos */
28 gint word_pos; /* the current position within word_splitpos */
29 gint content_hl_start, content_hl_end;
30 } DwWordIterator;
31
32 DwExtIterator* a_Dw_ext_iterator_new (DwIterator *it);
33 DwExtIterator* a_Dw_ext_iterator_new_variant (DwExtIterator *base,
34 DwIterator *it);
35 gboolean a_Dw_ext_iterator_next (DwExtIterator *eit);
36 gboolean a_Dw_ext_iterator_prev (DwExtIterator *eit);
37 DwExtIterator* a_Dw_ext_iterator_clone (DwExtIterator *eit);
38 gint a_Dw_ext_iterator_compare (DwExtIterator *eit1,
39 DwExtIterator *eit2);
40 void a_Dw_ext_iterator_free (DwExtIterator *eit);
41
42 #define a_Dw_ext_iterator_highlight(eit, s, e, l) \
43 a_Dw_iterator_highlight((eit)->stack[(eit)->stack_top], \
44 s, e, l)
45 #define a_Dw_ext_iterator_unhighlight(eit, l) \
46 a_Dw_iterator_unhighlight((eit)->stack[(eit)->stack_top], l)
47 #define a_Dw_ext_iterator_get_allocation(eit, s, e, a) \
48 a_Dw_iterator_get_allocation((eit)->stack[(eit)->stack_top],\
49 s, e, a)
50 #define a_Dw_ext_iterator_scroll_to(eit1, eit2, s, e, hp, vp) \
51 a_Dw_iterator_scroll_to((eit1)->stack[(eit1)->stack_top], \
52 (eit2)->stack[(eit2)->stack_top], \
53 s, e, hp, vp)
54 #define a_Dw_ext_iterator_text(eit) ((eit) ? \
55 a_Dw_iterator_text((eit)->stack[(eit)->stack_top]) : \
56 "[NULL]")
57
58 DwWordIterator* a_Dw_word_iterator_new (DwWidget *widget);
59 gboolean a_Dw_word_iterator_next (DwWordIterator *it);
60 gboolean a_Dw_word_iterator_prev (DwWordIterator *it);
61 void a_Dw_word_iterator_highlight (DwWordIterator *it,
62 gint start,
63 gint end,
64 DwHighlightLayer layer);
65 void a_Dw_word_iterator_get_allocation (DwWordIterator *it,
66 gint start,
67 gint end,
68 DwAllocation *allocation);
69 void a_Dw_word_iterator_scroll_to (DwWordIterator *it1,
70 DwWordIterator *it2,
71 gint start,
72 gint end,
73 DwHPosition hpos,
74 DwVPosition vpos);
75 DwWordIterator* a_Dw_word_iterator_clone (DwWordIterator *it);
76 void a_Dw_word_iterator_free (DwWordIterator *it);
77
78 #define a_Dw_word_iterator_unhighlight(it, l) \
79 a_Dw_word_iterator_highlight(it, -1, -1, l)
80
81
82 #ifdef __cplusplus
83 }
84 #endif /* __cplusplus */
85
86 #endif /* __DW_EXT_ITERATOR_H__ */
+0
-841
src/dw_gtk_scrolled_frame.c less more
0 /*
1 * File: dw_gtk_scrolled_frame.c
2 *
3 * Copyright (C) 2001 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * GtkDwScrolledFrame is a container for the GtkDwViewport, it adds
13 * a focus around the child and is responsible for focus, keys and the
14 * button 2 dragging.
15 */
16
17 #include <gtk/gtk.h>
18 #include <gdk/gdkkeysyms.h>
19 #include "dw_gtk_scrolled_frame.h"
20
21 static GtkBinClass *parent_class = NULL;
22
23 /* object/class initialisation */
24 static void Dw_gtk_scrolled_frame_init (GtkDwScrolledFrame *frame);
25 static void Dw_gtk_scrolled_frame_class_init (GtkDwScrolledFrameClass
26 *klass);
27
28 /* GtkObject methods */
29 static void Dw_gtk_scrolled_frame_destroy (GtkObject *object);
30
31 /* GtkWidget methods */
32 static void Dw_gtk_scrolled_frame_realize (GtkWidget *widget);
33 static void Dw_gtk_scrolled_frame_unrealize (GtkWidget *widget);
34 static void Dw_gtk_scrolled_frame_size_request (GtkWidget *widget,
35 GtkRequisition *requisition);
36 static void Dw_gtk_scrolled_frame_size_allocate (GtkWidget *widget,
37 GtkAllocation *allocation);
38 static void Dw_gtk_scrolled_frame_draw (GtkWidget *widget,
39 GdkRectangle *area);
40 static void Dw_gtk_scrolled_frame_draw_focus (GtkWidget *widget);
41 static gint Dw_gtk_scrolled_frame_expose (GtkWidget *widget,
42 GdkEventExpose *event);
43 static gint Dw_gtk_scrolled_frame_key_press (GtkWidget *widget,
44 GdkEventKey *event);
45 static gint Dw_gtk_scrolled_frame_focus_in (GtkWidget *widget,
46 GdkEventFocus *event);
47 static gint Dw_gtk_scrolled_frame_focus_out (GtkWidget *widget,
48 GdkEventFocus *event);
49 static gint Dw_gtk_scrolled_frame_button_press (GtkWidget *widget,
50 GdkEventButton *event);
51 static gint Dw_gtk_scrolled_frame_button_release(GtkWidget *widget,
52 GdkEventButton *event);
53 static gint Dw_gtk_scrolled_frame_motion_notify (GtkWidget *widget,
54 GdkEventMotion *event);
55
56 /* GtkContainer methods */
57 static void Dw_gtk_scrolled_frame_add (GtkContainer *container,
58 GtkWidget *widget);
59 static gint Dw_gtk_scrolled_frame_focus (GtkContainer *container,
60 GtkDirectionType direction);
61
62 /* GtkDwScrolledFrame methods */
63 static void Dw_gtk_scrolled_frame_set_scroll_adjustments (GtkDwScrolledFrame
64 *frame,
65 GtkAdjustment
66 *hadjustment,
67 GtkAdjustment
68 *vadjustment);
69
70
71 static void Dw_gtk_scrolled_frame_move_x_to (GtkDwScrolledFrame *frame,
72 gfloat x);
73 static void Dw_gtk_scrolled_frame_move_y_to (GtkDwScrolledFrame *frame,
74 gfloat y);
75 static void Dw_gtk_scrolled_frame_move_by (GtkDwScrolledFrame *frame,
76 gfloat dx,
77 gfloat dy);
78
79 static gfloat Dw_gtk_scrolled_frame_correct_adj (GtkAdjustment *adj,
80 gfloat pos);
81
82
83
84 enum
85 {
86 SET_SCROLL_ADJUSTMENTS,
87 USER_HCHANGED,
88 USER_VCHANGED,
89 LAST_SIGNAL
90 };
91
92 static guint frame_signals[LAST_SIGNAL] = { 0 };
93
94 /*
95 * Standard Gtk+ function
96 */
97 GtkType a_Dw_gtk_scrolled_frame_get_type (void)
98 {
99 static GtkType type = 0;
100
101 if (!type) {
102 GtkTypeInfo info = {
103 "GtkDwScrolledFrame",
104 sizeof (GtkDwScrolledFrame),
105 sizeof (GtkDwScrolledFrameClass),
106 (GtkClassInitFunc) Dw_gtk_scrolled_frame_class_init,
107 (GtkObjectInitFunc) Dw_gtk_scrolled_frame_init,
108 (GtkArgSetFunc) NULL,
109 (GtkArgGetFunc) NULL,
110 (GtkClassInitFunc) NULL
111 };
112
113 type = gtk_type_unique (GTK_TYPE_BIN, &info);
114 }
115
116 return type;
117 }
118
119
120 /*
121 * Standard Gtk+ function
122 */
123 GtkWidget* a_Dw_gtk_scrolled_frame_new (GtkAdjustment *hadjustment,
124 GtkAdjustment *vadjustment)
125 {
126 GtkWidget *widget;
127 GtkDwScrolledFrame *frame;
128
129 widget = gtk_widget_new (GTK_TYPE_DW_SCROLLED_FRAME, NULL);
130
131 frame = GTK_DW_SCROLLED_FRAME (widget);
132 frame->hadjustment = hadjustment;
133 frame->vadjustment = vadjustment;
134
135 return widget;
136 }
137
138 /*********************************
139 * *
140 * object/class initialisation *
141 * *
142 *********************************/
143
144 /*
145 * Standard Gtk+ function
146 */
147 static void Dw_gtk_scrolled_frame_init (GtkDwScrolledFrame *frame)
148 {
149 GTK_WIDGET_SET_FLAGS (frame, GTK_CAN_FOCUS);
150 GTK_WIDGET_UNSET_FLAGS (frame, GTK_NO_WINDOW);
151
152 frame->hadjustment = NULL;
153 frame->vadjustment = NULL;
154 frame->button2_pressed = FALSE;
155 frame->move_idle_id = 0;
156 }
157
158
159 /*
160 * Standard Gtk+ function
161 */
162 static void Dw_gtk_scrolled_frame_class_init (GtkDwScrolledFrameClass *klass)
163 {
164 GtkObjectClass *object_class;
165 GtkWidgetClass *widget_class;
166 GtkContainerClass *container_class;
167
168 parent_class = gtk_type_class (gtk_bin_get_type ());
169
170 object_class = (GtkObjectClass*) klass;
171 widget_class = (GtkWidgetClass*) klass;
172 container_class = (GtkContainerClass*) klass;
173
174 frame_signals[SET_SCROLL_ADJUSTMENTS] =
175 gtk_signal_new ("set_scroll_adjustments",
176 GTK_RUN_LAST,
177 object_class->type,
178 GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
179 set_scroll_adjustments),
180 gtk_marshal_NONE__POINTER_POINTER,
181 GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT,
182 GTK_TYPE_ADJUSTMENT);
183 widget_class->set_scroll_adjustments_signal =
184 frame_signals[SET_SCROLL_ADJUSTMENTS];
185
186 frame_signals[USER_HCHANGED] =
187 gtk_signal_new ("user_hchanged",
188 GTK_RUN_LAST | GTK_RUN_ACTION,
189 object_class->type,
190 GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
191 user_hchanged),
192 gtk_marshal_NONE__NONE,
193 GTK_TYPE_NONE, 0);
194 frame_signals[USER_VCHANGED] =
195 gtk_signal_new ("user_vchanged",
196 GTK_RUN_LAST | GTK_RUN_ACTION,
197 object_class->type,
198 GTK_SIGNAL_OFFSET (GtkDwScrolledFrameClass,
199 user_vchanged),
200 gtk_marshal_NONE__NONE,
201 GTK_TYPE_NONE, 0);
202
203 gtk_object_class_add_signals (object_class, frame_signals, LAST_SIGNAL);
204
205 object_class->destroy = Dw_gtk_scrolled_frame_destroy;
206
207 widget_class->realize = Dw_gtk_scrolled_frame_realize;
208 widget_class->unrealize = Dw_gtk_scrolled_frame_unrealize;
209 widget_class->size_request = Dw_gtk_scrolled_frame_size_request;
210 widget_class->size_allocate = Dw_gtk_scrolled_frame_size_allocate;
211 widget_class->draw_focus = Dw_gtk_scrolled_frame_draw_focus;
212 widget_class->draw = Dw_gtk_scrolled_frame_draw;
213 widget_class->expose_event = Dw_gtk_scrolled_frame_expose;
214 widget_class->key_press_event = Dw_gtk_scrolled_frame_key_press;
215 widget_class->focus_in_event = Dw_gtk_scrolled_frame_focus_in;
216 widget_class->focus_out_event = Dw_gtk_scrolled_frame_focus_out;
217 widget_class->button_press_event = Dw_gtk_scrolled_frame_button_press;
218 widget_class->button_release_event = Dw_gtk_scrolled_frame_button_release;
219 widget_class->motion_notify_event = Dw_gtk_scrolled_frame_motion_notify;
220
221 container_class->add = Dw_gtk_scrolled_frame_add;
222 container_class->focus = Dw_gtk_scrolled_frame_focus;
223
224 klass->set_scroll_adjustments =
225 Dw_gtk_scrolled_frame_set_scroll_adjustments;
226 klass->user_hchanged = NULL;
227 klass->user_vchanged = NULL;
228 }
229
230
231 /***********************
232 * *
233 * GtkObject methods *
234 * *
235 ***********************/
236
237 /*
238 * Standard Gtk+ function
239 */
240 static void Dw_gtk_scrolled_frame_destroy (GtkObject *object)
241 {
242 GtkDwScrolledFrame *frame;
243
244 frame = GTK_DW_SCROLLED_FRAME (object);
245 if (frame->move_idle_id != 0)
246 gtk_idle_remove (frame->move_idle_id);
247
248 GTK_OBJECT_CLASS(parent_class)->destroy (object);
249 }
250
251
252 /***********************
253 * *
254 * GtkWidget methods *
255 * *
256 ***********************/
257
258 /*
259 * Standard Gtk+ function
260 */
261 static void Dw_gtk_scrolled_frame_realize (GtkWidget *widget)
262 {
263 GdkWindowAttr attributes;
264
265 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
266
267 attributes.window_type = GDK_WINDOW_CHILD;
268 attributes.x = widget->allocation.x;
269 attributes.y = widget->allocation.y;
270 attributes.width = widget->allocation.width;
271 attributes.height = widget->allocation.height;
272 attributes.wclass = GDK_INPUT_OUTPUT;
273 attributes.visual = gtk_widget_get_visual (widget);
274 attributes.colormap = gtk_widget_get_colormap (widget);
275 attributes.event_mask = (gtk_widget_get_events (widget)
276 | GDK_EXPOSURE_MASK
277 | GDK_KEY_PRESS_MASK
278 | GDK_BUTTON_PRESS_MASK);
279
280 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
281 &attributes,
282 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL |
283 GDK_WA_COLORMAP);
284 gdk_window_set_user_data (widget->window, widget);
285 widget->style = gtk_style_attach (widget->style, widget->window);
286 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
287
288 GTK_DW_SCROLLED_FRAME(widget)->drag_cursor = gdk_cursor_new (GDK_FLEUR);
289 }
290
291
292 /*
293 * Standard Gtk+ function
294 */
295 static void Dw_gtk_scrolled_frame_unrealize (GtkWidget *widget)
296 {
297 gdk_cursor_destroy (GTK_DW_SCROLLED_FRAME(widget)->drag_cursor);
298 GTK_WIDGET_CLASS(parent_class)->unrealize (widget);
299 }
300
301 /*
302 * Standard Gtk+ function
303 */
304 static void Dw_gtk_scrolled_frame_size_allocate (GtkWidget *widget,
305 GtkAllocation *allocation)
306 {
307 GtkBin *bin;
308 gint border_width;
309 GtkAllocation child_allocation;
310
311 widget->allocation = *allocation;
312 bin = GTK_BIN (widget);
313
314 if (GTK_WIDGET_REALIZED (widget))
315 gdk_window_move_resize (widget->window,
316 allocation->x, allocation->y,
317 allocation->width, allocation->height);
318
319 if (bin->child) {
320 border_width = GTK_CONTAINER(widget)->border_width;
321
322 child_allocation.width =
323 MAX (allocation->width - 2 * (widget->style->klass->xthickness +
324 border_width),
325 1);
326 child_allocation.height =
327 MAX (allocation->height - 2 * (widget->style->klass->ythickness +
328 border_width),
329 1);
330 child_allocation.x = (allocation->width - child_allocation.width) / 2;
331 child_allocation.y = (allocation->height - child_allocation.height) / 2;
332
333 gtk_widget_size_allocate (bin->child, &child_allocation);
334 }
335 }
336
337
338 /*
339 * Standard Gtk+ function
340 */
341 static void Dw_gtk_scrolled_frame_size_request (GtkWidget *widget,
342 GtkRequisition *requisition)
343 {
344 GtkBin *bin;
345 gint border_width;
346 GtkRequisition child_requisition;
347
348 bin = GTK_BIN (widget);
349
350 if (bin->child) {
351 border_width = GTK_CONTAINER(widget)->border_width;
352 gtk_widget_size_request (bin->child, &child_requisition);
353
354 requisition->width =
355 child_requisition.width + 2 * (widget->style->klass->xthickness +
356 border_width);
357 requisition->height =
358 child_requisition.height + 2 * (widget->style->klass->ythickness +
359 border_width);
360 } else {
361 requisition->width = 100;
362 requisition->height = 100;
363 }
364 }
365
366
367 /*
368 * Draw the frame, eventually with a focus border
369 */
370 static void Dw_gtk_scrolled_frame_paint_shadow (GtkWidget *widget,
371 GdkRectangle *area)
372 {
373 gint border_width;
374
375 if (GTK_WIDGET_DRAWABLE (widget)) {
376 border_width = GTK_CONTAINER(widget)->border_width;
377
378 if (GTK_WIDGET_HAS_FOCUS (widget)) {
379 gtk_draw_shadow (widget->style, widget->window,
380 GTK_STATE_NORMAL, GTK_SHADOW_IN,
381 border_width + 1, border_width + 1,
382 widget->allocation.width - 2 * (border_width + 1),
383 widget->allocation.height - 2 * (border_width + 1));
384
385 /* "text" is probably next to what we need */
386 gtk_paint_focus (widget->style, widget->window,
387 area, widget, "text",
388 border_width, border_width,
389 widget->allocation.width - 2 * border_width - 1,
390 widget->allocation.height - 2 * border_width - 1);
391 } else {
392 gtk_draw_shadow (widget->style, widget->window,
393 GTK_STATE_NORMAL, GTK_SHADOW_IN,
394 border_width, border_width,
395 widget->allocation.width - 2 * border_width,
396 widget->allocation.height - 2 * border_width);
397 }
398 }
399 }
400
401
402 /*
403 * Standard Gtk+ function
404 */
405 static void Dw_gtk_scrolled_frame_draw_focus (GtkWidget *widget)
406 {
407 Dw_gtk_scrolled_frame_paint_shadow (widget, NULL);
408 }
409
410
411 /*
412 * Standard Gtk+ function
413 */
414 static void Dw_gtk_scrolled_frame_draw (GtkWidget *widget,
415 GdkRectangle *area)
416 {
417 GTK_WIDGET_CLASS(parent_class)->draw (widget, area);
418 Dw_gtk_scrolled_frame_paint_shadow (widget, area);
419 }
420
421
422 /*
423 * Standard Gtk+ function
424 */
425 static gint Dw_gtk_scrolled_frame_expose (GtkWidget *widget,
426 GdkEventExpose *event)
427 {
428 gint ret_val;
429
430 ret_val = GTK_WIDGET_CLASS(parent_class)->expose_event (widget, event);
431 Dw_gtk_scrolled_frame_paint_shadow (widget, &(event->area));
432
433 return ret_val;
434 }
435
436
437 /*
438 * Standard Gtk+ function
439 */
440 static gint Dw_gtk_scrolled_frame_key_press (GtkWidget *widget,
441 GdkEventKey *event)
442 {
443 GtkContainer *container;
444 GtkDwScrolledFrame *frame;
445
446 container = GTK_CONTAINER (widget);
447 frame = GTK_DW_SCROLLED_FRAME (widget);
448
449 switch (event->keyval) {
450 case GDK_Up:
451 case GDK_KP_Up:
452 Dw_gtk_scrolled_frame_move_by (frame, 0,
453 - frame->vadjustment->step_increment);
454 return TRUE;
455
456 case GDK_Down:
457 case GDK_KP_Down:
458 Dw_gtk_scrolled_frame_move_by (frame, 0,
459 + frame->vadjustment->step_increment);
460 return TRUE;
461
462 case GDK_Left:
463 case GDK_KP_Left:
464 Dw_gtk_scrolled_frame_move_by (frame,
465 - frame->hadjustment->step_increment, 0);
466 return TRUE;
467
468 case GDK_Right:
469 case GDK_KP_Right:
470 Dw_gtk_scrolled_frame_move_by (frame,
471 + frame->hadjustment->step_increment, 0);
472 return TRUE;
473
474 case GDK_Tab:
475 if (event->state & GDK_SHIFT_MASK)
476 return gtk_container_focus (container, GTK_DIR_TAB_BACKWARD);
477 else
478 return gtk_container_focus (container, GTK_DIR_TAB_FORWARD);
479
480 case GDK_b: case GDK_B:
481 if (event->state & GDK_CONTROL_MASK)
482 return FALSE;
483 /* continues */
484 case GDK_Page_Up:
485 case GDK_KP_Page_Up:
486 if (event->state & GDK_CONTROL_MASK)
487 Dw_gtk_scrolled_frame_move_by (frame,
488 - frame->hadjustment->page_increment,
489 0);
490 else
491 Dw_gtk_scrolled_frame_move_by (frame, 0,
492 - frame->vadjustment->page_increment);
493 return TRUE;
494
495 case GDK_Page_Down:
496 case GDK_KP_Page_Down:
497 case GDK_space:
498 if (event->state & GDK_CONTROL_MASK)
499 Dw_gtk_scrolled_frame_move_by (frame,
500 + frame->hadjustment->page_increment,
501 0);
502 else
503 Dw_gtk_scrolled_frame_move_by (frame, 0,
504 + frame->vadjustment->page_increment);
505 return TRUE;
506
507 case GDK_Home:
508 case GDK_KP_Home:
509 if (event->state & GDK_CONTROL_MASK)
510 Dw_gtk_scrolled_frame_move_x_to (frame, 0);
511 else
512 Dw_gtk_scrolled_frame_move_y_to (frame, 0);
513 return TRUE;
514
515 case GDK_End:
516 case GDK_KP_End:
517 if (event->state & GDK_CONTROL_MASK)
518 Dw_gtk_scrolled_frame_move_x_to (frame, G_MAXFLOAT);
519 else
520 Dw_gtk_scrolled_frame_move_y_to (frame, G_MAXFLOAT);
521 return TRUE;
522
523 default:
524 return FALSE;
525 }
526 }
527
528
529 /*
530 * Standard Gtk+ function
531 */
532 static gint Dw_gtk_scrolled_frame_focus_in (GtkWidget *widget,
533 GdkEventFocus *event)
534 {
535 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
536 gtk_widget_draw_focus (widget);
537 return FALSE;
538 }
539
540
541 /*
542 * Standard Gtk+ function
543 */
544 static gint Dw_gtk_scrolled_frame_focus_out (GtkWidget *widget,
545 GdkEventFocus *event)
546 {
547 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
548 gtk_widget_draw_focus (widget);
549 return FALSE;
550 }
551
552
553 /*
554 * Standard Gtk+ function
555 */
556 static gint Dw_gtk_scrolled_frame_button_press (GtkWidget *widget,
557 GdkEventButton *event)
558 {
559 GtkDwScrolledFrame *frame;
560
561 if (!GTK_WIDGET_HAS_FOCUS (widget))
562 gtk_widget_grab_focus (widget);
563
564 if (event->button == 2) {
565 frame = GTK_DW_SCROLLED_FRAME (widget);
566
567 frame->button2_pressed = TRUE;
568 frame->start_lmx = event->x;
569 frame->start_lmy = event->y;
570
571 gdk_pointer_grab (widget->window, FALSE,
572 GDK_BUTTON_RELEASE_MASK | GDK_BUTTON2_MOTION_MASK,
573 NULL, frame->drag_cursor, event->time);
574
575 }
576
577 return TRUE;
578 }
579
580
581 /*
582 * Standard Gtk+ function
583 */
584 static gint Dw_gtk_scrolled_frame_button_release (GtkWidget *widget,
585 GdkEventButton *event)
586 {
587 GTK_DW_SCROLLED_FRAME(widget)->button2_pressed = FALSE;
588 gdk_pointer_ungrab (event->time);
589 return TRUE;
590 }
591
592
593 /*
594 * Standard Gtk+ function
595 */
596 static gint Dw_gtk_scrolled_frame_motion_notify (GtkWidget *widget,
597 GdkEventMotion *event)
598 {
599 GtkDwScrolledFrame *frame = GTK_DW_SCROLLED_FRAME (widget);
600
601 /* frame->button2_pressed makes sure that the button was pressed
602 in the GtkDwScrolledFrame */
603 if ((event->state & GDK_BUTTON2_MASK) && frame->button2_pressed) {
604 Dw_gtk_scrolled_frame_move_by (frame,
605 frame->start_lmx - event->x,
606 frame->start_lmy - event->y);
607
608 frame->start_lmx = event->x;
609 frame->start_lmy = event->y;
610 }
611
612 return TRUE;
613 }
614
615
616 /**************************
617 * *
618 * GtkContainer methods *
619 * *
620 **************************/
621
622 /*
623 * Standard Gtk+ function
624 */
625 static void Dw_gtk_scrolled_frame_add (GtkContainer *container,
626 GtkWidget *widget)
627 {
628 GtkDwScrolledFrame *frame;
629
630 GTK_CONTAINER_CLASS(parent_class)->add (container, widget);
631
632 frame = GTK_DW_SCROLLED_FRAME (container);
633 if ( !gtk_widget_set_scroll_adjustments (
634 GTK_WIDGET (container), frame->hadjustment, frame->vadjustment) )
635 g_warning ("Dw_gtk_scrolled_frame_set_scroll_adjustments(): "
636 "child is non scrollable");
637 }
638
639
640 /*
641 * Standard Gtk+ function
642 */
643 static gint Dw_gtk_scrolled_frame_focus (GtkContainer *container,
644 GtkDirectionType direction)
645 {
646 /* todo:
647 * This crashed if the child is not a container (what will never happen
648 * in dillo, where GtkDwScrolledFrame always contains a GtkDwViewport).
649 */
650 GtkDwScrolledFrame *frame;
651
652 frame = GTK_DW_SCROLLED_FRAME (container);
653
654 if (!GTK_WIDGET_HAS_FOCUS (container) && container->focus_child == NULL){
655 /* no focus on the GtkDwScrolledFrame or any child widget => grab focus!
656 */
657 gtk_widget_grab_focus (GTK_WIDGET (container));
658 return TRUE;
659 }
660
661 switch (direction) {
662 case GTK_DIR_TAB_FORWARD:
663 /* deliver to child */
664 if (!gtk_container_focus (GTK_CONTAINER (GTK_BIN(container)->child),
665 direction)) {
666 /* todo: it would be nice to keep focus on this last child widget,
667 * instead of the container... Anyway this may change with GTK2 */
668 gtk_widget_grab_focus (GTK_WIDGET (container));
669 }
670 return TRUE;
671
672 case GTK_DIR_TAB_BACKWARD:
673 if (GTK_WIDGET_HAS_FOCUS (container)) {
674 /* will focus the widget "before" */
675 return FALSE;
676 } else {
677 if (!gtk_container_focus (GTK_CONTAINER (GTK_BIN(container)->child),
678 GTK_DIR_TAB_BACKWARD))
679 /* first child of the child unfocussed */
680 gtk_widget_grab_focus (GTK_WIDGET (container));
681
682 return TRUE;
683 }
684 break;
685
686 /*
687 case GTK_DIR_LEFT:
688 case GTK_DIR_RIGHT:
689 case GTK_DIR_UP:
690 case GTK_DIR_DOWN:
691 */
692 default:
693 /* focus the GtkDwScrolledFrame */
694 gtk_widget_grab_focus (GTK_WIDGET (container));
695 return TRUE;
696 }
697
698 /* make compiler happy */
699 return FALSE;
700 }
701
702
703 /********************************
704 * *
705 * GtkDwScrolledFrame methods *
706 * *
707 ********************************/
708
709 /*
710 * Standard Gtk+ function
711 */
712 static void
713 Dw_gtk_scrolled_frame_set_scroll_adjustments (GtkDwScrolledFrame *frame,
714 GtkAdjustment *hadjustment,
715 GtkAdjustment *vadjustment)
716 {
717 GtkBin *bin;
718
719 frame->hadjustment = hadjustment;
720 frame->vadjustment = vadjustment;
721
722 bin = GTK_BIN (frame);
723 if (bin->child) {
724 if (!gtk_widget_set_scroll_adjustments (bin->child,
725 hadjustment,
726 vadjustment))
727 g_warning ("Dw_gtk_scrolled_frame_set_scroll_adjustments(): "
728 "child is non scrollable");
729 }
730 }
731
732
733 /*
734 * Functions for moving the viewport. This is queued and done in an
735 * idle function, to prevent locks by too frequent keypresses, or too
736 * fast mouse movements. (How to reproduce: start dillo on a slow,
737 * overloaded system with nice priority.)
738 */
739
740 /*
741 * The idle function.
742 */
743 static gint Dw_gtk_scrolled_frame_move_idle (gpointer data)
744 {
745 GtkDwScrolledFrame *frame;
746
747 frame = GTK_DW_SCROLLED_FRAME (data);
748
749 g_return_val_if_fail (frame->hadjustment != NULL, FALSE);
750 g_return_val_if_fail (frame->vadjustment != NULL, FALSE);
751
752 if (frame->moveto_x != frame->hadjustment->value) {
753 gtk_adjustment_set_value (frame->hadjustment, frame->moveto_x);
754 gtk_signal_emit (GTK_OBJECT (frame), frame_signals[USER_HCHANGED]);
755 }
756
757 if (frame->moveto_y != frame->vadjustment->value) {
758 gtk_adjustment_set_value (frame->vadjustment, frame->moveto_y);
759 gtk_signal_emit (GTK_OBJECT (frame), frame_signals[USER_VCHANGED]);
760 }
761
762 frame->move_idle_id = 0;
763 return FALSE;
764 }
765
766
767 /*
768 * Change horizontall position to x.
769 */
770 static void Dw_gtk_scrolled_frame_move_x_to (GtkDwScrolledFrame *frame,
771 gfloat x)
772 {
773 frame->moveto_x =
774 Dw_gtk_scrolled_frame_correct_adj (frame->hadjustment, x);
775 if (frame->move_idle_id == 0)
776 frame->move_idle_id = gtk_idle_add (Dw_gtk_scrolled_frame_move_idle,
777 (gpointer) frame);
778 }
779
780
781 /*
782 * Change vertical position to y.
783 */
784 static void Dw_gtk_scrolled_frame_move_y_to (GtkDwScrolledFrame *frame,
785 gfloat y)
786 {
787 frame->moveto_y =
788 Dw_gtk_scrolled_frame_correct_adj (frame->vadjustment, y);
789 if (frame->move_idle_id == 0)
790 frame->move_idle_id = gtk_idle_add (Dw_gtk_scrolled_frame_move_idle,
791 (gpointer) frame);
792 }
793
794
795 /*
796 * Move viewport by dx and dy.
797 */
798 static void Dw_gtk_scrolled_frame_move_by (GtkDwScrolledFrame *frame,
799 gfloat dx,
800 gfloat dy)
801 {
802 if (frame->move_idle_id == 0) {
803 /*
804 * Initialization: if the idle function is not active, set
805 * moveto_x and moveto_y to adjustments values, which may
806 * already have been changed by the scrollbars. This code does
807 * not work when a user uses scrollbars and keys at the same
808 * time. ;-)
809 */
810 frame->moveto_x = frame->hadjustment->value;
811 frame->moveto_y = frame->vadjustment->value;
812 }
813
814 frame->moveto_x =
815 Dw_gtk_scrolled_frame_correct_adj (frame->hadjustment,
816 frame->moveto_x + dx);
817 frame->moveto_y =
818 Dw_gtk_scrolled_frame_correct_adj (frame->vadjustment,
819 frame->moveto_y + dy);
820
821 if (frame->move_idle_id == 0)
822 frame->move_idle_id = gtk_idle_add (Dw_gtk_scrolled_frame_move_idle,
823 (gpointer) frame);
824
825 }
826
827
828 /*
829 * Helper function: Corrects pos to fit into the boundaries of adj.
830 */
831 static gfloat Dw_gtk_scrolled_frame_correct_adj (GtkAdjustment *adj,
832 gfloat pos)
833 {
834 if (pos < adj->lower)
835 return adj->lower;
836 else if (pos > adj->upper - adj->page_size)
837 return adj->upper - adj->page_size;
838 else
839 return pos;
840 }
+0
-67
src/dw_gtk_scrolled_frame.h less more
0 #ifndef __DW_GTK_SCROLLED_FRAME_H__
1 #define __DW_GTK_SCROLLED_FRAME_H__
2
3 #include <gdk/gdk.h>
4 #include <gtk/gtkbin.h>
5 #include <gtk/gtkadjustment.h>
6
7 #ifdef __cplusplus
8 extern "C" {
9 #endif /* __cplusplus */
10
11 #define GTK_TYPE_DW_SCROLLED_FRAME (a_Dw_gtk_scrolled_frame_get_type ())
12 #define GTK_DW_SCROLLED_FRAME(obj) (GTK_CHECK_CAST (obj, \
13 GTK_TYPE_DW_SCROLLED_FRAME, \
14 GtkDwScrolledFrame))
15 #define GTK_DW_SCROLLED_FRAME_CLASS(klass) (GTK_CHECK_CLASS_CAST (klass, \
16 GTK_DW_TYPE_SCROLLED_FRAME, \
17 GtkDwScrolledFrameClass))
18 #define GTK_DW_IS_SCROLLED_FRAME(obj) GTK_CHECK_TYPE (obj, \
19 GTK_DW_TYPE_SCROLLED_FRAME)
20
21 typedef struct _GtkDwScrolledFrame GtkDwScrolledFrame;
22 typedef struct _GtkDwScrolledFrameClass GtkDwScrolledFrameClass;
23
24
25 struct _GtkDwScrolledFrame
26 {
27 GtkBin bin;
28
29 GtkAdjustment *hadjustment;
30 GtkAdjustment *vadjustment;
31
32 /* used for button 2 dragging */
33 gboolean button2_pressed; /* TRUE if button 2 has been pressed in
34 the GtkDwScrolledFrame */
35 gfloat start_lmx, start_lmy; /* the last mouse position */
36
37 /* used for fast unblocking moving */
38 gfloat moveto_x, moveto_y; /* the position the idle function should
39 move to */
40 gboolean move_idle_id;
41
42 GdkCursor *drag_cursor;
43 };
44
45
46 struct _GtkDwScrolledFrameClass
47 {
48 GtkBinClass parent_class;
49
50 void (*set_scroll_adjustments) (GtkDwScrolledFrame *scrolled_frame,
51 GtkAdjustment *hadjustment,
52 GtkAdjustment *vadjustment);
53 void (*user_hchanged) (GtkDwScrolledFrame *scrolled_frame);
54 void (*user_vchanged) (GtkDwScrolledFrame *scrolled_frame);
55 };
56
57
58 GtkType a_Dw_gtk_scrolled_frame_get_type (void);
59 GtkWidget* a_Dw_gtk_scrolled_frame_new (GtkAdjustment *hadjustment,
60 GtkAdjustment *vadjustment);
61
62 #ifdef __cplusplus
63 }
64 #endif /* __cplusplus */
65
66 #endif /* __DW_GTK_SCROLLED_FRAME_H__ */
+0
-487
src/dw_gtk_scrolled_window.c less more
0 /*
1 * File: dw_gtk_scrolled_window.c
2 *
3 * Copyright (C) 2001 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <stddef.h>
12
13 #include "dw_gtk_scrolled_window.h"
14 #include "dw_gtk_scrolled_frame.h"
15 #include "dw_gtk_viewport.h"
16 #include "findtext.h"
17 #include <gtk/gtk.h>
18
19 #include "debug.h"
20
21 static GtkScrolledWindowClass *parent_class = NULL;
22
23 static void Dw_gtk_scrolled_window_init (GtkDwScrolledWindow *scrolled);
24 static void Dw_gtk_scrolled_window_class_init(GtkDwScrolledWindowClass *klass);
25
26 static void Dw_gtk_scrolled_window_destroy (GtkObject *object);
27 static void Dw_gtk_scrolled_window_map (GtkWidget *widget);
28 static void Dw_gtk_scrolled_window_unmap (GtkWidget *widget);
29 static void Dw_gtk_scrolled_window_draw (GtkWidget *widget,
30 GdkRectangle *area);
31 static void Dw_gtk_scrolled_window_size_allocate (GtkWidget *widget,
32 GtkAllocation *allocation);
33
34 static void Dw_gtk_scrolled_window_remove (GtkContainer *container,
35 GtkWidget *widget);
36 static void Dw_gtk_scrolled_window_forall (GtkContainer *container,
37 gboolean include_internals,
38 GtkCallback callback,
39 gpointer callback_data);
40
41 static void Dw_gtk_scrolled_window_changed1 (GtkDwScrolledWindow *scrolled);
42 static void Dw_gtk_scrolled_window_changed2 (GtkDwScrolledWindow *scrolled);
43
44
45 /*
46 * Standard Gtk+ function
47 */
48 GtkType a_Dw_gtk_scrolled_window_get_type (void)
49 {
50 static GtkType type = 0;
51
52 if (!type) {
53 GtkTypeInfo info = {
54 "GtkDwScrolledWindow",
55 sizeof (GtkDwScrolledWindow),
56 sizeof (GtkDwScrolledWindowClass),
57 (GtkClassInitFunc) Dw_gtk_scrolled_window_class_init,
58 (GtkObjectInitFunc) Dw_gtk_scrolled_window_init,
59 (GtkArgSetFunc) NULL,
60 (GtkArgGetFunc) NULL,
61 (GtkClassInitFunc) NULL
62 };
63
64 type = gtk_type_unique (GTK_TYPE_SCROLLED_WINDOW, &info);
65 }
66
67 return type;
68 }
69
70
71 /*
72 * Standard Gtk+ function
73 */
74 GtkWidget* a_Dw_gtk_scrolled_window_new (void)
75 {
76 return gtk_widget_new (GTK_TYPE_DW_SCROLLED_WINDOW, NULL);
77 }
78
79
80 /*
81 * Standard Gtk+ function
82 */
83 static void Dw_gtk_scrolled_window_init (GtkDwScrolledWindow *scrolled)
84 {
85 GtkAdjustment *hadjustment, *vadjustment;
86 GtkWidget *frame, *viewport;
87 size_t i;
88 char *signals[] = {
89 "button_press_event",
90 "button_release_event",
91 "motion_notify_event",
92 "key_press_event" /* although the scrollbars are not focused */
93 };
94
95 hadjustment =
96 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 10.0, 0.0, 0.0));
97 vadjustment =
98 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 10.0, 0.0, 0.0));
99
100 gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (scrolled),
101 hadjustment);
102 gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (scrolled),
103 vadjustment);
104
105 frame = a_Dw_gtk_scrolled_frame_new (hadjustment, vadjustment);
106 gtk_container_add (GTK_CONTAINER (scrolled), frame);
107 gtk_widget_show (frame);
108
109 viewport = a_Dw_gtk_viewport_new (hadjustment, vadjustment);
110 gtk_container_add (GTK_CONTAINER (frame), viewport);
111 gtk_widget_show (viewport);
112
113 scrolled->vadjustment = vadjustment;
114 scrolled->old_vadjustment_value = vadjustment->value;
115 scrolled->gadget = NULL;
116
117 /*
118 * For anchors, we need to recognize when the *user* changes the
119 * viewport and distiguish them from changes caused by the program.
120 * Instead of using the "change" signal and checking where it came
121 * from, the following code connects all possible events by which
122 * users could change the scrollbar adjustments. ...
123 */
124 for (i = 0; i < sizeof (signals) / sizeof (signals[0]); i++) {
125 gtk_signal_connect_object
126 (GTK_OBJECT (GTK_SCROLLED_WINDOW(scrolled)->vscrollbar),
127 signals[i], GTK_SIGNAL_FUNC (Dw_gtk_scrolled_window_changed1),
128 GTK_OBJECT (scrolled));
129 gtk_signal_connect_object_after
130 (GTK_OBJECT (GTK_SCROLLED_WINDOW(scrolled)->vscrollbar),
131 signals[i], GTK_SIGNAL_FUNC (Dw_gtk_scrolled_window_changed2),
132 GTK_OBJECT (scrolled));
133 }
134
135 /* ... The GtkDwScrolledFrame has a signal for this. */
136 gtk_signal_connect_object (GTK_OBJECT (frame), "user_vchanged",
137 GTK_SIGNAL_FUNC (Dw_gtk_viewport_remove_anchor),
138 GTK_OBJECT (viewport));
139 #if 0
140 /* This does not seem to work for GtkLayout's (see also dw_embed_gtk.c): */
141 gtk_container_set_focus_hadjustment (GTK_CONTAINER (viewport),
142 hadjustment);
143 gtk_container_set_focus_vadjustment (GTK_CONTAINER (viewport),
144 vadjustment);
145 #endif
146 }
147
148
149 /*
150 * Standard Gtk+ function
151 */
152 static void Dw_gtk_scrolled_window_class_init (GtkDwScrolledWindowClass *klass)
153 {
154 GtkObjectClass *object_class;
155 GtkWidgetClass *widget_class;
156 GtkContainerClass *container_class;
157
158 object_class = (GtkObjectClass*) klass;
159 widget_class = (GtkWidgetClass*) klass;
160 container_class = (GtkContainerClass*) klass;
161 parent_class = gtk_type_class (GTK_TYPE_SCROLLED_WINDOW);
162
163 object_class->destroy = Dw_gtk_scrolled_window_destroy;
164
165 widget_class->map = Dw_gtk_scrolled_window_map;
166 widget_class->unmap = Dw_gtk_scrolled_window_unmap;
167 widget_class->draw = Dw_gtk_scrolled_window_draw;
168 widget_class->size_allocate = Dw_gtk_scrolled_window_size_allocate;
169
170 container_class->remove = Dw_gtk_scrolled_window_remove;
171 container_class->forall = Dw_gtk_scrolled_window_forall;
172 }
173
174 /*
175 * Standard Gtk+ function
176 */
177 static void Dw_gtk_scrolled_window_destroy (GtkObject *object)
178 {
179 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (object);
180
181 if (scrolled->gadget)
182 gtk_widget_destroy (scrolled->gadget);
183
184 GTK_OBJECT_CLASS(parent_class)->destroy (object);
185 }
186
187 /*
188 * Standard Gtk+ function
189 */
190 static void Dw_gtk_scrolled_window_map (GtkWidget *widget)
191 {
192 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (widget);
193
194 GTK_WIDGET_CLASS(parent_class)->map (widget);
195
196 if (scrolled->gadget && GTK_WIDGET_VISIBLE (scrolled->gadget) &&
197 !GTK_WIDGET_MAPPED (scrolled->gadget))
198 gtk_widget_map (scrolled->gadget);
199 }
200
201 /*
202 * Standard Gtk+ function
203 */
204 static void Dw_gtk_scrolled_window_unmap (GtkWidget *widget)
205 {
206 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (widget);
207
208 GTK_WIDGET_CLASS(parent_class)->unmap (widget);
209
210 if (scrolled->gadget && GTK_WIDGET_MAPPED (scrolled->gadget))
211 gtk_widget_unmap (scrolled->gadget);
212 }
213
214 /*
215 * Standard Gtk+ function
216 */
217 static void Dw_gtk_scrolled_window_draw (GtkWidget *widget,
218 GdkRectangle *area)
219 {
220 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (widget);
221 GdkRectangle child_area;
222
223 GTK_WIDGET_CLASS(parent_class)->draw (widget, area);
224
225 if (scrolled->gadget && GTK_WIDGET_VISIBLE (scrolled->gadget) &&
226 gtk_widget_intersect (scrolled->gadget, area, &child_area))
227 gtk_widget_draw (scrolled->gadget, &child_area);
228 }
229
230
231 /*
232 * Standard Gtk+ function
233 */
234 static void Dw_gtk_scrolled_window_size_allocate (GtkWidget *widget,
235 GtkAllocation *allocation)
236 {
237 GtkDwViewport *viewport;
238 GtkAllocation old_allocation = widget->allocation, child_allocation;
239 GtkRequisition child_requisition;
240 GtkDwScrolledWindow *scrolled;
241 GtkWidget *hscrollbar, *vscrollbar;
242 gint gx, gy;
243
244 GTK_WIDGET_CLASS(parent_class)->size_allocate (widget, allocation);
245 widget->allocation = *allocation;
246
247 DEBUG_MSG (2, "Dw_gtk_scrolled_window_size_allocate: %d x %d\n",
248 allocation->width, allocation->height);
249 if (old_allocation.width != allocation->width ||
250 old_allocation.height != allocation->height) {
251 viewport = GTK_DW_VIEWPORT (GTK_BIN(GTK_BIN(widget)->child)->child);
252
253 /* It may be that scrollbars are not needed anymore. See
254 Dw_gtk_viewport_calc_size for more details. */
255 if (allocation->width > old_allocation.width)
256 viewport->hscrollbar_used = FALSE;
257 if (allocation->height > old_allocation.height)
258 viewport->vscrollbar_used = FALSE;
259
260 Dw_gtk_viewport_calc_size (viewport);
261 }
262
263 scrolled = GTK_DW_SCROLLED_WINDOW (widget);
264 if (scrolled->gadget && GTK_WIDGET_VISIBLE (scrolled->gadget)) {
265 /* Raise the gagdet, otherwise it may be hidden. */
266 if (GTK_WIDGET_REALIZED (scrolled->gadget))
267 gdk_window_raise (scrolled->gadget->window);
268
269 /* Allocate the gadget. */
270 gtk_widget_size_request (scrolled->gadget, &child_requisition);
271 gx = allocation->x + allocation->width - child_requisition.width;
272 gy = allocation->y + allocation->height - child_requisition.height;
273 child_allocation.x = MAX (gx, 1);
274 child_allocation.y = MAX (gy, 1);
275 child_allocation.width = child_requisition.width;
276 child_allocation.height = child_requisition.height;
277 gtk_widget_size_allocate (scrolled->gadget, &child_allocation);
278
279 /* Re-allocate the scrollbars, when necessary */
280 hscrollbar = GTK_SCROLLED_WINDOW(scrolled)->hscrollbar;
281 if (GTK_WIDGET_VISIBLE (hscrollbar) &&
282 hscrollbar->allocation.x + hscrollbar->allocation.width > gx) {
283 child_allocation = hscrollbar->allocation;
284 child_allocation.width = MAX (gx - hscrollbar->allocation.x, 1);
285 gtk_widget_size_allocate (hscrollbar, &child_allocation);
286 }
287
288 vscrollbar = GTK_SCROLLED_WINDOW(scrolled)->vscrollbar;
289 if (GTK_WIDGET_VISIBLE (vscrollbar) &&
290 vscrollbar->allocation.y + vscrollbar->allocation.height > gy) {
291 child_allocation = vscrollbar->allocation;
292 child_allocation.height = MAX (gy - vscrollbar->allocation.y, 1);
293 gtk_widget_size_allocate (vscrollbar, &child_allocation);
294 }
295 }
296 }
297
298 /*
299 * Standard Gtk+ function
300 */
301 static void Dw_gtk_scrolled_window_remove (GtkContainer *container,
302 GtkWidget *widget)
303 {
304 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (container);
305 gboolean widget_was_visible;
306
307 if (widget == scrolled->gadget) {
308 widget_was_visible = GTK_WIDGET_VISIBLE (widget);
309 gtk_widget_unparent (widget);
310 scrolled->gadget = NULL;
311 } else
312 GTK_CONTAINER_CLASS(parent_class)->remove (container, widget);
313 }
314
315 /*
316 * Standard Gtk+ function
317 */
318 static void Dw_gtk_scrolled_window_forall (GtkContainer *container,
319 gboolean include_internals,
320 GtkCallback callback,
321 gpointer callback_data)
322 {
323 GtkDwScrolledWindow *scrolled = GTK_DW_SCROLLED_WINDOW (container);
324
325 GTK_CONTAINER_CLASS(parent_class)->forall (container, include_internals,
326 callback, callback_data);
327 if (scrolled->gadget)
328 callback (scrolled->gadget, callback_data);
329 }
330
331
332 /*
333 * Adds the gadget widget.
334 */
335 void a_Dw_gtk_scrolled_window_add_gadget (GtkDwScrolledWindow *scrolled,
336 GtkWidget *gadget)
337 {
338 g_return_if_fail (scrolled->gadget == NULL);
339 scrolled->gadget = gadget;
340 gtk_widget_set_parent (gadget, GTK_WIDGET (scrolled));
341 gtk_widget_queue_resize (GTK_WIDGET (scrolled));
342 }
343
344
345 /*
346 * Sets the top-level DwWidget. The old top-level DwWidget is destroyed.
347 */
348 void a_Dw_gtk_scrolled_window_set_dw (GtkDwScrolledWindow *scrolled,
349 DwWidget *widget)
350 {
351 GtkWidget *viewport;
352 DwWidget *old_child;
353
354 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
355
356 if ((old_child = GTK_DW_VIEWPORT (viewport)->child))
357 gtk_object_destroy (GTK_OBJECT (old_child));
358
359 a_Dw_gtk_viewport_add_dw (GTK_DW_VIEWPORT (viewport), widget);
360 }
361
362 /*
363 * Gets the top-level DwWidget.
364 */
365 DwWidget* a_Dw_gtk_scrolled_window_get_dw (GtkDwScrolledWindow *scrolled)
366 {
367 GtkWidget *viewport;
368
369 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
370 return GTK_DW_VIEWPORT(viewport)->child;
371 }
372
373 /*
374 * See a_Dw_gtk_viewport_set_anchor.
375 */
376 void a_Dw_gtk_scrolled_window_set_anchor (GtkDwScrolledWindow *scrolled,
377 const gchar *anchor)
378 {
379 GtkWidget *viewport;
380
381 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
382 a_Dw_gtk_viewport_set_anchor (GTK_DW_VIEWPORT (viewport), anchor);
383 }
384
385 /*
386 * The scrolling position is remembered setting this value in the history-URL
387 */
388 gint a_Dw_gtk_scrolled_window_get_scrolling_position_x (GtkDwScrolledWindow
389 *scrolled)
390 {
391 GtkLayout *viewport = GTK_LAYOUT(GTK_BIN(GTK_BIN(scrolled)->child)->child);
392
393 return ((int) viewport->hadjustment->value);
394 }
395
396 /*
397 * The scrolling position is remembered setting this value in the history-URL
398 */
399 gint a_Dw_gtk_scrolled_window_get_scrolling_position_y (GtkDwScrolledWindow
400 *scrolled)
401 {
402 GtkLayout *viewport = GTK_LAYOUT(GTK_BIN(GTK_BIN(scrolled)->child)->child);
403
404 return ((int) viewport->vadjustment->value);
405 }
406
407 /*
408 * See a_Dw_gtk_viewport_set_scrolling_position.
409 */
410 void a_Dw_gtk_scrolled_window_set_scrolling_position (GtkDwScrolledWindow
411 *scrolled,
412 gint32 x,
413 gint32 y)
414 {
415 GtkWidget *viewport;
416
417 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
418 a_Dw_gtk_viewport_set_scrolling_position (GTK_DW_VIEWPORT (viewport), x, y);
419 }
420
421
422 /*
423 * See also Dw_gtk_scrolled_window_init.
424 * Called before possible change, save the old value.
425 */
426 static void Dw_gtk_scrolled_window_changed1 (GtkDwScrolledWindow *scrolled)
427 {
428 scrolled->old_vadjustment_value = scrolled->vadjustment->value;
429 }
430
431
432 /*
433 * See also Dw_gtk_scrolled_window_init.
434 * Called after possible change, compare old and new values.
435 */
436 static void Dw_gtk_scrolled_window_changed2 (GtkDwScrolledWindow *scrolled)
437 {
438 GtkWidget *viewport;
439
440 if (scrolled->old_vadjustment_value != scrolled->vadjustment->value) {
441 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
442 Dw_gtk_viewport_remove_anchor (GTK_DW_VIEWPORT (viewport));
443 }
444 }
445
446 /*
447 * Convenience function. See a_Findtext_search.
448 */
449 gboolean a_Dw_gtk_scrolled_window_search (GtkDwScrolledWindow *scrolled,
450 gchar *string,
451 gboolean case_sens)
452 {
453 GtkWidget *viewport;
454
455 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
456 return
457 a_Findtext_search (GTK_DW_VIEWPORT(viewport)->findtext_state, string,
458 case_sens);
459 }
460
461 /*
462 * Convenience function. See a_Findtext_reset_search.
463 */
464 void a_Dw_gtk_scrolled_window_reset_search (GtkDwScrolledWindow *scrolled)
465 {
466 GtkWidget *viewport;
467
468 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
469 a_Findtext_reset_search (GTK_DW_VIEWPORT(viewport)->findtext_state);
470 }
471
472 /*
473 * Convenience function. See a_Dw_gtk_viewport_widget_at_viewport_point.
474 */
475 DwWidget* a_Dw_gtk_scrolled_window_widget_at_viewport_point (
476 GtkDwScrolledWindow *scrolled,
477 gint32 vx,
478 gint32 vy)
479 {
480 GtkWidget *viewport;
481
482 viewport = GTK_BIN(GTK_BIN(scrolled)->child)->child;
483 return
484 a_Dw_gtk_viewport_widget_at_viewport_point (GTK_DW_VIEWPORT(viewport),
485 vx, vy);
486 }
+0
-78
src/dw_gtk_scrolled_window.h less more
0 #ifndef __DW_GTK_SCROLLED_WINDOW_H__
1 #define __DW_GTK_SCROLLED_WINDOW_H__
2
3 #include <gtk/gtkscrolledwindow.h>
4 #include "dw_widget.h"
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
9
10
11 #define GTK_TYPE_DW_SCROLLED_WINDOW (a_Dw_gtk_scrolled_window_get_type ())
12 #define GTK_DW_SCROLLED_WINDOW(obj) (GTK_CHECK_CAST (obj, \
13 GTK_TYPE_DW_SCROLLED_WINDOW, \
14 GtkDwScrolledWindow))
15 #define GTK_DW_SCROLLED_WINDOW_CLASS(klass) (GTK_CHECK_CLASS_CAST (klass, \
16 GTK_TYPE_DW_SCROLLED_WINDOW, \
17 GtkDwScrolledWindowClass))
18 #define GTK_IS_DW_SCROLLED_WINDOW(obj) GTK_CHECK_TYPE (obj, \
19 GTK_TYPE_DW_SCROLLED_WINDOW)
20
21
22 typedef struct _GtkDwScrolledWindow GtkDwScrolledWindow;
23 typedef struct _GtkDwScrolledWindowClass GtkDwScrolledWindowClass;
24
25
26 struct _GtkDwScrolledWindow
27 {
28 GtkScrolledWindow scrolled_window;
29
30 GtkWidget *gadget; /* This is a widget shown in the upper right
31 * corner, and in dillo used for the "full
32 * screen off" button. */
33 GtkAdjustment *vadjustment;
34 gfloat old_vadjustment_value;
35 };
36
37
38 struct _GtkDwScrolledWindowClass
39 {
40 GtkScrolledWindowClass parent_class;
41 };
42
43
44 GtkType a_Dw_gtk_scrolled_window_get_type (void);
45 GtkWidget* a_Dw_gtk_scrolled_window_new (void);
46 void a_Dw_gtk_scrolled_window_add_gadget (
47 GtkDwScrolledWindow *scrolled, GtkWidget *gadget);
48
49 void a_Dw_gtk_scrolled_window_set_dw (
50 GtkDwScrolledWindow *scrolled, DwWidget *widget);
51 DwWidget* a_Dw_gtk_scrolled_window_get_dw (GtkDwScrolledWindow *scrolled);
52
53 void a_Dw_gtk_scrolled_window_set_anchor (
54 GtkDwScrolledWindow *scrolled, const gchar *anchor);
55 gint a_Dw_gtk_scrolled_window_get_scrolling_position_x (
56 GtkDwScrolledWindow *scrolled);
57 gint a_Dw_gtk_scrolled_window_get_scrolling_position_y (
58 GtkDwScrolledWindow *scrolled);
59 void a_Dw_gtk_scrolled_window_set_scrolling_position (
60 GtkDwScrolledWindow *scrolled, gint32 x, gint32 y);
61
62 gboolean a_Dw_gtk_scrolled_window_search (
63 GtkDwScrolledWindow *scrolled, gchar *string,
64 gboolean case_sens);
65 void a_Dw_gtk_scrolled_window_reset_search (
66 GtkDwScrolledWindow *scrolled);
67
68 DwWidget* a_Dw_gtk_scrolled_window_widget_at_viewport_point (
69 GtkDwScrolledWindow *scrolled,
70 gint32 vx,
71 gint32 vy);
72
73 #ifdef __cplusplus
74 }
75 #endif /* __cplusplus */
76
77 #endif /* __DW_GTK_SCROLLED_WINDOW_H__ */
+0
-222
src/dw_gtk_statuslabel.c less more
0 /*
1 * GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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
19 #include <string.h>
20 #include <gtk/gtklabel.h>
21
22 #include "msg.h"
23 #include "dw_gtk_statuslabel.h"
24
25
26 static void Dw_gtk_statuslabel_class_init(GtkLabelClass * klass);
27 static void Dw_gtk_statuslabel_size_request(GtkWidget * widget,
28 GtkRequisition * requisition);
29 static gint Dw_gtk_statuslabel_expose(GtkWidget * widget,
30 GdkEventExpose * event);
31
32
33 static GtkLabelClass *parent_class = NULL;
34
35 /* todo: provide a Dw_gtk_statuslabel_set function that doesn't do a
36 * check_resize. */
37
38 /*
39 * ?
40 */
41 guint Dw_gtk_statuslabel_get_type()
42 {
43 static guint statuslabel_type = 0;
44
45 if (!statuslabel_type) {
46 GtkTypeInfo statuslabel_info =
47 {
48 "GtkStatusLabel",
49 sizeof(GtkStatusLabel),
50 sizeof(GtkStatusLabelClass),
51 (GtkClassInitFunc) Dw_gtk_statuslabel_class_init,
52 (GtkObjectInitFunc) NULL,
53 (GtkArgSetFunc) NULL,
54 (GtkArgGetFunc) NULL,
55 (GtkClassInitFunc) NULL
56 };
57
58 statuslabel_type = gtk_type_unique(gtk_label_get_type(),
59 &statuslabel_info);
60 }
61 return statuslabel_type;
62 }
63
64 /*
65 * ?
66 */
67 static void Dw_gtk_statuslabel_class_init(GtkLabelClass * class)
68 {
69 GtkWidgetClass *widget_class;
70
71 widget_class = (GtkWidgetClass *) class;
72
73 parent_class = gtk_type_class(gtk_label_get_type());
74
75 widget_class->size_request = Dw_gtk_statuslabel_size_request;
76 widget_class->expose_event = Dw_gtk_statuslabel_expose;
77 }
78
79 /*
80 * ?
81 */
82 GtkWidget *a_Dw_gtk_statuslabel_new(char *str)
83 {
84 GtkLabel *label;
85
86 g_return_val_if_fail(str != NULL, NULL);
87
88 label = gtk_type_new(Dw_gtk_statuslabel_get_type());
89
90 gtk_label_set(label, str);
91
92 return GTK_WIDGET(label);
93 }
94
95 /*
96 * ?
97 */
98 static void Dw_gtk_statuslabel_size_request(GtkWidget * widget,
99 GtkRequisition * requisition)
100 {
101 GtkLabel *label;
102
103 g_return_if_fail(widget != NULL);
104 g_return_if_fail(GTK_IS_LABEL(widget));
105 g_return_if_fail(requisition != NULL);
106
107 label = GTK_LABEL(widget);
108
109 requisition->width = (100 +
110 label->misc.xpad * 2);
111 requisition->height = (GTK_WIDGET(label)->style->font->ascent +
112 GTK_WIDGET(label)->style->font->descent +
113 label->misc.ypad * 2 + 2);
114 }
115
116 /*
117 * ?
118 */
119 static void Dw_gtk_statuslabel_expose_string(GtkWidget * widget,
120 gint state,
121 char *string,
122 gint width)
123 {
124 gint x, y;
125 GtkMisc *misc;
126
127 misc = GTK_MISC(widget);
128 x = (widget->allocation.x * (1.0 - misc->xalign) +
129 (widget->allocation.x + widget->allocation.width - (width -
130 misc->xpad * 2)) *
131 misc->xalign) + 0.5;
132 y = (widget->allocation.y * (1.0 - misc->yalign) +
133 (widget->allocation.y + widget->allocation.height -
134 (widget->requisition.height - misc->ypad * 2)) * misc->yalign +
135 widget->style->font->ascent) + 1.5;
136
137 if (state == GTK_STATE_INSENSITIVE)
138 gdk_draw_string(widget->window,
139 widget->style->font,
140 widget->style->white_gc,
141 x + 1, y + 1, string);
142
143 /*
144 * gdk_draw_rectangle (widget->window,
145 * widget->style->bg_gc[GTK_STATE_SELECTED], FALSE,
146 * widget->allocation.x, widget->allocation.y,
147 * widget->allocation.width - 1, widget->allocation.height - 1);
148 */
149
150 gdk_draw_string(widget->window,
151 widget->style->font,
152 widget->style->fg_gc[state],
153 x, y, string);
154 }
155
156 /*
157 * ?
158 */
159 static gint Dw_gtk_statuslabel_expose(GtkWidget * widget,
160 GdkEventExpose * event)
161 {
162 GtkLabel *label;
163 GtkMisc *misc;
164 gint state;
165 gint width;
166 char *scratch_string;
167 gint len, num_elide, num_prefix;
168
169 g_return_val_if_fail(widget != NULL, FALSE);
170 g_return_val_if_fail(GTK_IS_LABEL(widget), FALSE);
171 g_return_val_if_fail(event != NULL, FALSE);
172
173 if (GTK_WIDGET_VISIBLE(widget) && GTK_WIDGET_MAPPED(widget)) {
174 label = GTK_LABEL(widget);
175 misc = GTK_MISC(widget);
176
177 state = widget->state;
178 if (!GTK_WIDGET_IS_SENSITIVE(widget))
179 state = GTK_STATE_INSENSITIVE;
180
181 width = gdk_string_width(widget->style->font,
182 label->label) +
183 misc->xpad * 2;
184 /* If the label fits in the space allocated, we draw it. If not,
185 * we (somehow) truncate. */
186 if ((widget->allocation.width >= width) &&
187 (widget->allocation.height >= widget->requisition.height)) {
188 Dw_gtk_statuslabel_expose_string(widget, state, label->label, width);
189 } else if (widget->allocation.height >= widget->requisition.height) {
190 /* enough height, but not enough width - truncate */
191 /* I know this algorithm is a bit inefficient, but I seriously
192 * doubt it matters. */
193 len = strlen(label->label);
194 scratch_string = g_new(char, len + 3);
195
196 scratch_string[0] = '\0';
197 for (num_elide = 1; num_elide < len; num_elide++) {
198 num_prefix = (len - num_elide) / 2;
199 memcpy(scratch_string, label->label, num_prefix);
200 scratch_string[num_prefix] = '.';
201 scratch_string[num_prefix + 1] = '.';
202 scratch_string[num_prefix + 2] = '.';
203 memcpy(scratch_string + num_prefix + 3,
204 label->label + num_elide + num_prefix,
205 len + 1 - (num_elide + num_prefix));
206 width = gdk_string_width(widget->style->font, scratch_string) +
207 misc->xpad * 2;
208 if (widget->allocation.width >= width)
209 break;
210 }
211 Dw_gtk_statuslabel_expose_string(widget, state, scratch_string,width);
212 g_free(scratch_string);
213 } else {
214 _MSG("Dw_gtk_statuslabel_expose:"
215 " allocation too small: %d %d ( %d %d )\n",
216 widget->allocation.width, widget->allocation.height,
217 widget->requisition.width, widget->requisition.height);
218 }
219 }
220 return TRUE;
221 }
+0
-64
src/dw_gtk_statuslabel.h less more
0 /* GTK - The GIMP Toolkit
1 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Library General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Library General Public License for more details.
12 *
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library; if not, write to the Free
15 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17 #ifndef __GTK_STATUSLABEL_H__
18 #define __GTK_STATUSLABEL_H__
19
20
21 #include <gdk/gdk.h>
22 #include <gtk/gtkmisc.h>
23
24
25 #ifdef __cplusplus
26 extern "C" {
27 #endif /* __cplusplus */
28
29
30 #define GTK_STATUSLABEL(obj) \
31 GTK_CHECK_CAST (obj, Dw_gtk_statuslabel_get_type (), GtkLabel)
32
33 #define GTK_STATUSLABEL_CLASS(klass) \
34 GTK_CHECK_CLASS_CAST (klass, Dw_gtk_statuslabel_get_type (), GtkLabelClass)
35
36 #define GTK_IS_STATUSLABEL(obj) \
37 GTK_CHECK_TYPE (obj, Dw_gtk_statuslabel_get_type ())
38
39
40 typedef struct _GtkStatusLabel GtkStatusLabel;
41 typedef struct _GtkStatusLabelClass GtkStatusLabelClass;
42
43 struct _GtkStatusLabel
44 {
45 GtkLabel label;
46 };
47
48 struct _GtkStatusLabelClass
49 {
50 GtkMiscClass parent_class;
51 };
52
53
54 guint Dw_gtk_statuslabel_get_type (void);
55 GtkWidget* a_Dw_gtk_statuslabel_new (char *str);
56
57
58 #ifdef __cplusplus
59 }
60 #endif /* __cplusplus */
61
62
63 #endif /* __GTK_STATUSLABEL_H__ */
+0
-1282
src/dw_gtk_viewport.c less more
0 /*
1 * File: dw_gtk_viewport.c
2 *
3 * Copyright (C) 2001-2003 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <gtk/gtk.h>
12 #include "msg.h"
13 #include "dw_gtk_viewport.h"
14 #include "dw_container.h"
15 #include "list.h"
16
17 /*#define DEBUG_LEVEL 1*/
18 #include "debug.h"
19 #include "msg.h"
20
21 typedef struct
22 {
23 gchar *name;
24 DwWidget *widget;
25 gint32 y;
26 } GtkDwViewportAnchor;
27
28 static GtkLayoutClass *parent_class = NULL;
29
30 /* object/class initialisation */
31 static void Dw_gtk_viewport_init (GtkDwViewport *viewport);
32 static void Dw_gtk_viewport_class_init (GtkDwViewportClass *klass);
33
34 /* GtkObject methods */
35 static void Dw_gtk_viewport_destroy (GtkObject *object);
36
37 /* GtkWidget methods */
38 static void Dw_gtk_viewport_size_allocate (GtkWidget *widget,
39 GtkAllocation *allocation);
40 static void Dw_gtk_viewport_realize (GtkWidget *widget);
41 static void Dw_gtk_viewport_draw (GtkWidget *widget,
42 GdkRectangle *area);
43 static gint Dw_gtk_viewport_expose (GtkWidget *widget,
44 GdkEventExpose *event);
45 static gint Dw_gtk_viewport_button_press (GtkWidget *widget,
46 GdkEventButton *event);
47 static gint Dw_gtk_viewport_button_release(GtkWidget *widget,
48 GdkEventButton *event);
49 static gint Dw_gtk_viewport_motion_notify (GtkWidget *widget,
50 GdkEventMotion *event);
51 static gint Dw_gtk_viewport_enter_notify (GtkWidget *widget,
52 GdkEventCrossing *event);
53 static gint Dw_gtk_viewport_leave_notify (GtkWidget *widget,
54 GdkEventCrossing *event);
55 static void Dw_gtk_viewport_adj_changed (GtkAdjustment *adj,
56 GtkDwViewport *viewport);
57
58 /*
59 * Standard Gtk+ function
60 */
61 GtkType a_Dw_gtk_viewport_get_type (void)
62 {
63 static GtkType type = 0;
64
65 if (!type) {
66 GtkTypeInfo info = {
67 "GtkDwViewport",
68 sizeof (GtkDwViewport),
69 sizeof (GtkDwViewportClass),
70 (GtkClassInitFunc) Dw_gtk_viewport_class_init,
71 (GtkObjectInitFunc) Dw_gtk_viewport_init,
72 (GtkArgSetFunc) NULL,
73 (GtkArgGetFunc) NULL,
74 (GtkClassInitFunc) NULL
75 };
76
77 type = gtk_type_unique (GTK_TYPE_LAYOUT, &info);
78 }
79
80 return type;
81 }
82
83
84 /*
85 * Standard Gtk+ function
86 */
87 GtkWidget* a_Dw_gtk_viewport_new (GtkAdjustment *hadjustment,
88 GtkAdjustment *vadjustment)
89 {
90 GtkWidget *widget;
91
92 widget = gtk_widget_new (GTK_TYPE_DW_VIEWPORT, NULL);
93 gtk_layout_set_hadjustment (GTK_LAYOUT (widget), hadjustment);
94 gtk_layout_set_vadjustment (GTK_LAYOUT (widget), vadjustment);
95
96 /* Following two statements expect that the adjustments are passed as
97 * arguments (!= NULL), and don't change. This is the case in dillo,
98 * however, for more general perposes, the signal function
99 * "set_scroll_adjustments" had to be redefined.
100 */
101 gtk_signal_connect (GTK_OBJECT (hadjustment), "value_changed",
102 GTK_SIGNAL_FUNC (Dw_gtk_viewport_adj_changed),
103 (gpointer) widget);
104 gtk_signal_connect (GTK_OBJECT (vadjustment), "value_changed",
105 GTK_SIGNAL_FUNC (Dw_gtk_viewport_adj_changed),
106 (gpointer) widget);
107
108 return widget;
109 }
110
111 /*********************************
112 * *
113 * object/class initialisation *
114 * *
115 *********************************/
116
117 /*
118 * Standard Gtk+ function
119 */
120 static void Dw_gtk_viewport_init (GtkDwViewport *viewport)
121 {
122 DBG_OBJ_CREATE (viewport, "GtkDwViewport");
123
124 GTK_WIDGET_UNSET_FLAGS (viewport, GTK_NO_WINDOW);
125 GTK_WIDGET_UNSET_FLAGS (viewport, GTK_CAN_FOCUS);
126
127 /* Without this, gtk_layout_{draw|expose} will clear the window.
128 Look at gtklayout.c */
129 GTK_WIDGET_SET_FLAGS (viewport, GTK_APP_PAINTABLE);
130
131 viewport->back_pixmap = NULL;
132 viewport->child = NULL;
133 viewport->last_entered = NULL;
134 viewport->draw_resize_idle_id = 0;
135 viewport->anchor = NULL;
136 viewport->anchor_idle_id = 0;
137 viewport->findtext_state = a_Findtext_state_new ();
138 viewport->selection = a_Selection_new ();
139 viewport->anchors_table = g_hash_table_new (g_str_hash, g_str_equal);
140 viewport->draw_areas = NULL;
141 viewport->num_draw_areas = 0;
142 viewport->num_draw_areas_max = 4;
143
144 DBG_OBJ_ASSOC (viewport->findtext_state, viewport);
145 DBG_OBJ_ASSOC (viewport->selection, viewport);
146 }
147
148
149 /*
150 * Standard Gtk+ function
151 */
152 static void Dw_gtk_viewport_class_init (GtkDwViewportClass *klass)
153 {
154 GtkObjectClass *object_class;
155 GtkWidgetClass *widget_class;
156
157 parent_class = gtk_type_class (gtk_layout_get_type ());
158
159 object_class = (GtkObjectClass*) klass;
160 widget_class = (GtkWidgetClass*) klass;
161
162 object_class->destroy = Dw_gtk_viewport_destroy;
163
164 widget_class->size_allocate = Dw_gtk_viewport_size_allocate;
165 widget_class->realize = Dw_gtk_viewport_realize;
166 widget_class->draw = Dw_gtk_viewport_draw;
167 widget_class->expose_event = Dw_gtk_viewport_expose;
168 widget_class->button_press_event = Dw_gtk_viewport_button_press;
169 widget_class->button_release_event = Dw_gtk_viewport_button_release;
170 widget_class->motion_notify_event = Dw_gtk_viewport_motion_notify;
171 widget_class->enter_notify_event = Dw_gtk_viewport_enter_notify;
172 widget_class->leave_notify_event = Dw_gtk_viewport_leave_notify;
173 }
174
175
176 /***********************
177 * *
178 * GtkObject methods *
179 * *
180 ***********************/
181
182 static gboolean Dw_gtk_viewport_destroy_anchor (gpointer key,
183 gpointer value,
184 gpointer user_data)
185 {
186 g_free (value);
187 return TRUE;
188 }
189
190 /*
191 * Standard Gtk+ function
192 */
193 static void Dw_gtk_viewport_destroy (GtkObject *object)
194 {
195 GtkDwViewport *viewport;
196
197 viewport = GTK_DW_VIEWPORT (object);
198
199 if (viewport->back_pixmap)
200 gdk_pixmap_unref (viewport->back_pixmap);
201 if (viewport->child)
202 gtk_object_destroy (GTK_OBJECT (viewport->child));
203 if (viewport->draw_resize_idle_id != 0)
204 gtk_idle_remove (viewport->draw_resize_idle_id);
205 if (viewport->anchor_idle_id != 0)
206 gtk_idle_remove (viewport->anchor_idle_id);
207 g_free (viewport->anchor);
208
209 g_hash_table_foreach_remove (viewport->anchors_table,
210 Dw_gtk_viewport_destroy_anchor, NULL);
211 g_hash_table_destroy (viewport->anchors_table);
212
213 g_free (viewport->draw_areas);
214
215 a_Findtext_state_destroy (viewport->findtext_state);
216 a_Selection_free (viewport->selection);
217
218 GTK_OBJECT_CLASS(parent_class)->destroy (object);
219 }
220
221
222 /***********************
223 * *
224 * GtkWidget methods *
225 * *
226 ***********************/
227
228 /*
229 * Standard Gtk+ function
230 */
231 static void Dw_gtk_viewport_size_allocate (GtkWidget *widget,
232 GtkAllocation *allocation)
233 {
234 GTK_WIDGET_CLASS(parent_class)->size_allocate (widget, allocation);
235
236 /* gtk_layout_size_allocate() sets them to different values. */
237 GTK_LAYOUT(widget)->hadjustment->page_increment = allocation->width;
238 GTK_LAYOUT(widget)->vadjustment->page_increment = 0.96 * allocation->height;
239 }
240
241 /*
242 * Standard Gtk+ function
243 */
244 static void Dw_gtk_viewport_realize (GtkWidget *widget)
245 {
246 GtkDwViewport *viewport;
247
248 GTK_WIDGET_CLASS(parent_class)->realize (widget);
249
250 gdk_window_set_events (widget->window,
251 gdk_window_get_events (widget->window)
252 | GDK_BUTTON_PRESS_MASK
253 | GDK_BUTTON_RELEASE_MASK
254 | GDK_POINTER_MOTION_MASK
255 | GDK_ENTER_NOTIFY_MASK
256 | GDK_LEAVE_NOTIFY_MASK);
257
258 viewport = GTK_DW_VIEWPORT (widget);
259 gdk_window_get_geometry (widget->window, NULL, NULL, NULL, NULL,
260 &viewport->depth);
261
262 Dw_gtk_viewport_update_background (viewport);
263 if (viewport->child)
264 Dw_widget_update_cursor (viewport->child);
265 }
266
267
268 /*
269 * (Nearly) standard Gtk+ function
270 */
271 static void Dw_gtk_viewport_paint (GtkWidget *widget,
272 GdkRectangle *area,
273 GdkEventExpose *event)
274 {
275 GtkLayout *layout;
276 DwRectangle parent_area, child_area, intersection;
277 GtkDwViewport *viewport;
278 gboolean new_back_pixmap;
279
280 if (GTK_WIDGET_DRAWABLE (widget)) {
281 layout = GTK_LAYOUT (widget);
282 viewport = GTK_DW_VIEWPORT (widget);
283
284 DEBUG_MSG (2, "Drawing (%d, %d), %d x %d\n",
285 area->x, area->y, area->width, area->height);
286
287 /* Make sure the backing pixmap is large enough. */
288 if (viewport->child) {
289 if (viewport->back_pixmap)
290 new_back_pixmap =
291 (widget->allocation.width > viewport->back_width ||
292 widget->allocation.height > viewport->back_height);
293 else
294 new_back_pixmap = TRUE;
295
296 if (new_back_pixmap) {
297 if (viewport->back_pixmap)
298 gdk_pixmap_unref (viewport->back_pixmap);
299 viewport->back_pixmap = gdk_pixmap_new (widget->window,
300 widget->allocation.width,
301 widget->allocation.height,
302 viewport->depth);
303 viewport->back_width = widget->allocation.width;
304 viewport->back_height = widget->allocation.height;
305 DEBUG_MSG (1, " Creating new pixmap, size = %d x %d\n",
306 widget->allocation.width, widget->allocation.height);
307 }
308
309 /* Draw top-level Dw widget. */
310 parent_area.x =
311 p_Dw_widget_x_viewport_to_world (viewport->child, area->x);
312 parent_area.y =
313 p_Dw_widget_y_viewport_to_world (viewport->child, area->y);
314 parent_area.width = area->width;
315 parent_area.height = area->height;
316
317 child_area.x = viewport->child->allocation.x;
318 child_area.y = viewport->child->allocation.y;
319 child_area.width = viewport->child->allocation.width;
320 child_area.height = DW_WIDGET_HEIGHT(viewport->child);
321
322 if (p_Dw_rectangle_intersect (&parent_area, &child_area,
323 &intersection)) {
324 intersection.x -= viewport->child->allocation.x;
325 intersection.y -= viewport->child->allocation.y;
326
327 /* "Clear" backing pixmap. */
328 gdk_draw_rectangle (viewport->back_pixmap,
329 viewport->child->style->background_color->gc,
330 TRUE, area->x, area->y,
331 area->width, area->height);
332 /* Widgets draw in backing pixmap. */
333 p_Dw_widget_draw (viewport->child, &intersection, event);
334 /* Copy backing pixmap into window. */
335 gdk_draw_pixmap (layout->bin_window, widget->style->black_gc,
336 viewport->back_pixmap, area->x, area->y,
337 area->x, area->y, area->width, area->height);
338 }
339 } else
340 gdk_window_clear_area (layout->bin_window,
341 area->x, area->y, area->width, area->height);
342 }
343 }
344
345
346 /*
347 * Standard Gtk+ function
348 */
349 static void Dw_gtk_viewport_draw (GtkWidget *widget,
350 GdkRectangle *area)
351 {
352 Dw_gtk_viewport_paint (widget, area, NULL);
353 GTK_WIDGET_CLASS(parent_class)->draw (widget, area);
354 }
355
356
357 /*
358 * Standard Gtk+ function
359 */
360 static gint Dw_gtk_viewport_expose (GtkWidget *widget,
361 GdkEventExpose *event)
362 {
363 Dw_gtk_viewport_paint (widget, &(event->area), event);
364 return GTK_WIDGET_CLASS(parent_class)->expose_event (widget, event);
365 }
366
367
368 /*
369 * Handle the mouse event and deliver it to the Dw widget.
370 * Most is done in a_Dw_widget_mouse_event.
371 */
372 static gint Dw_gtk_viewport_mouse_event (GtkWidget *widget,
373 gint32 x,
374 gint32 y,
375 GdkEvent *event)
376 {
377 GtkDwViewport *viewport;
378 DwWidget *dw_widget;
379 gint32 world_x, world_y;
380
381 if (event == NULL || event->any.window == widget->window) {
382 viewport = GTK_DW_VIEWPORT (widget);
383 if (viewport->child) {
384 world_x = x + gtk_layout_get_hadjustment(GTK_LAYOUT(viewport))->value;
385 world_y = y + gtk_layout_get_vadjustment(GTK_LAYOUT(viewport))->value;
386 dw_widget =
387 Dw_gtk_viewport_widget_at_point (viewport, world_x, world_y);
388
389 return Dw_widget_mouse_event (dw_widget, widget,
390 world_x, world_y, event);
391 }
392 }
393
394 return FALSE;
395 }
396
397
398 /*
399 * Standard Gtk+ function
400 */
401 static gint Dw_gtk_viewport_button_press (GtkWidget *widget,
402 GdkEventButton *event)
403 {
404 /* We focus always the viewport window (i.e., more precisely, the
405 * GktDwScrolledFrame). */
406 if (widget->parent)
407 gtk_widget_grab_focus(widget->parent);
408
409 return Dw_gtk_viewport_mouse_event (widget, event->x, event->y,
410 (GdkEvent*) event);
411 }
412
413
414 /*
415 * Standard Gtk+ function
416 */
417 static gint Dw_gtk_viewport_button_release (GtkWidget *widget,
418 GdkEventButton *event)
419 {
420 return Dw_gtk_viewport_mouse_event (widget, event->x, event->y,
421 (GdkEvent*) event);
422 }
423
424
425 /*
426 * Standard Gtk+ function
427 */
428 static gint Dw_gtk_viewport_motion_notify (GtkWidget *widget,
429 GdkEventMotion *event)
430 {
431 GtkDwViewport *viewport = GTK_DW_VIEWPORT (widget);
432
433 viewport->mouse_x = event->x;
434 viewport->mouse_y = event->y;
435 return Dw_gtk_viewport_mouse_event (widget, event->x, event->y,
436 (GdkEvent*) event);
437 }
438
439
440 /*
441 * Standard Gtk+ function
442 */
443 static gint Dw_gtk_viewport_enter_notify (GtkWidget *widget,
444 GdkEventCrossing *event)
445 {
446 return Dw_gtk_viewport_mouse_event (widget, event->x, event->y, NULL);
447 }
448
449
450 /*
451 * Standard Gtk+ function
452 */
453 static gint Dw_gtk_viewport_leave_notify (GtkWidget *widget,
454 GdkEventCrossing *event)
455 {
456 /* There will anyway be no Dw widget, thus this simple call */
457 return Dw_widget_mouse_event (NULL, widget, 0, 0, NULL);
458 }
459
460
461 /*
462 * This function is called when the viewport changes, and causes
463 * motion_notify events to be simulated.
464 */
465 static void Dw_gtk_viewport_adj_changed (GtkAdjustment *adj,
466 GtkDwViewport *viewport)
467 {
468 Dw_gtk_viewport_mouse_event (GTK_WIDGET (viewport),
469 viewport->mouse_x, viewport->mouse_y, NULL);
470 }
471
472 /*
473 * This function sets the background and the viewport.
474 */
475 void Dw_gtk_viewport_update_background (GtkDwViewport *viewport)
476 {
477 /* The toplevel widget should always have a defined background color,
478 * except at the beginning. Searching a defined background is not
479 * necessary. */
480 if (viewport->child && viewport->child->style &&
481 viewport->child->style->background_color)
482 gdk_window_set_background (GTK_LAYOUT(viewport)->bin_window,
483 &viewport->child->style->background_color
484 ->color);
485 }
486
487 /**********************
488 * *
489 * public functions *
490 * *
491 **********************/
492
493 /*
494 * Set the top-level Dw widget.
495 * If there is already one, you must destroy it before, otherwise the
496 * function will fail.
497 */
498 void a_Dw_gtk_viewport_add_dw (GtkDwViewport *viewport,
499 DwWidget *widget)
500 {
501 g_return_if_fail (viewport->child == NULL);
502
503 viewport->child = widget;
504 DBG_OBJ_ASSOC(widget, viewport);
505
506 widget->parent = NULL;
507 widget->viewport = GTK_WIDGET (viewport);
508
509 Dw_gtk_viewport_update_background (viewport);
510 Dw_widget_update_cursor (viewport->child);
511
512 Dw_gtk_viewport_calc_size (viewport);
513 Dw_gtk_viewport_remove_anchor (viewport);
514
515 a_Findtext_state_set_widget (viewport->findtext_state, widget);
516 a_Selection_reset (viewport->selection);
517 }
518
519 /**************************************************
520 * *
521 * Functions used by GtkDwViewport and DwWidget *
522 * *
523 **************************************************/
524
525 /*
526 * This function only *recognizes* that the top-level Dw widget has
527 * been destroyed, and only removes it from the viewport. It is called
528 * by Dw_widget_destroy. Don't use this function directly!
529 *
530 * N.b.: a widget, which stores anchors, should already have removed
531 * them from the widget, by calling a_Dw_gtk_viewport_remove_anchor().
532 * A good place for this is the implementation of GtkObject::destroy,
533 * the call to the parent destroy method should then be done at the
534 * end of the function, since Dw_widget_destroy calls this function.
535 */
536 void Dw_gtk_viewport_remove_dw (GtkDwViewport *viewport)
537 {
538 /* Test, that all anchors have been removed properly. */
539 gint num_anchors_left = g_hash_table_size (viewport->anchors_table);
540 /* g_assert (num_anchors_left == 0); */
541 if (num_anchors_left != 0)
542 g_warning ("%d anchors left", num_anchors_left);
543
544 a_Findtext_state_set_widget (viewport->findtext_state, NULL);
545 viewport->child = NULL;
546 Dw_gtk_viewport_remove_anchor (viewport);
547 Dw_gtk_viewport_calc_size (viewport);
548 }
549
550
551 /*
552 * Used by Dw_gtk_viewport_calc_size.
553 */
554 static void Dw_gtk_viewport_calc_child_size (GtkDwViewport *viewport,
555 gint32 child_width,
556 gint32 child_height,
557 DwRequisition *child_requisition)
558 {
559 if (child_width < 0) child_width = 0;
560 if (child_height < 0) child_height = 0;
561
562 DEBUG_MSG (2, " width = %d, height = %d ...\n",
563 child_width, child_height);
564
565 p_Dw_widget_set_width (viewport->child, child_width);
566 p_Dw_widget_set_ascent (viewport->child, child_height);
567 p_Dw_widget_set_descent (viewport->child, 0);
568
569 p_Dw_widget_size_request (viewport->child, child_requisition);
570 }
571
572
573 /*
574 * Calculate the size of the scrolled area and allocate the top-level
575 * widget. This function is called when the top-level Dw widget has
576 * changed its size etc.
577 */
578 void Dw_gtk_viewport_calc_size (GtkDwViewport *viewport)
579 {
580 GtkWidget *widget;
581 GtkScrolledWindow *scrolled;
582
583 DwRequisition child_requisition;
584 DwAllocation child_allocation;
585 gint border_width, space;
586
587 GtkRequisition bar_requisition;
588 gint max_width, max_height, bar_width_diff, bar_height_diff, child_height;
589
590 if (viewport->calc_size_blocked)
591 return;
592
593 viewport->calc_size_blocked = TRUE;
594
595 if (viewport->child) {
596 /*
597 * Determine the size hints for the Dw widget. This is a bit
598 * tricky, because you must know if scrollbars are visible or
599 * not, which depends on the size of the Dw widget, which then
600 * depends on the hints. The idea is to test several
601 * configurations, there are four of them, from combining the
602 * cases horizontal/vertical scrollbar visible/invisible.
603 *
604 * For optimization, the horizontal scrollbar is currently not
605 * regarded, the height hint is always the same, as if the
606 * scrollbar was allways visible. In future, this may be
607 * implemented correctly, by using the minimal width to optimize
608 * most cases. (Minimal widths will also be used by tables.)
609 *
610 * Furthermore, the last result (vertical scrollbar visible or
611 * not) is stored in the viewport, and tested first. This will
612 * make a second test only necessary when the visibility
613 * switches, which normally happens only once when filling the
614 * page with text. (Actually, this assumes that the page size is
615 * always *growing*, but this is nevertheless true in dillo.)
616 */
617
618 widget = GTK_WIDGET (viewport);
619 scrolled = GTK_SCROLLED_WINDOW (widget->parent->parent);
620 space = GTK_SCROLLED_WINDOW_CLASS(GTK_OBJECT(scrolled)->klass)
621 ->scrollbar_spacing;
622 border_width = GTK_CONTAINER(viewport)->border_width;
623
624 gtk_widget_size_request (scrolled->vscrollbar, &bar_requisition);
625 bar_width_diff = bar_requisition.width + space;
626 max_width = widget->allocation.width - 2 * border_width;
627 if (scrolled->vscrollbar_visible)
628 max_width += bar_width_diff;
629
630 gtk_widget_size_request (scrolled->hscrollbar, &bar_requisition);
631 bar_height_diff = bar_requisition.height + space;
632 max_height = widget->allocation.height - 2 * border_width;
633 if (scrolled->hscrollbar_visible)
634 max_height += bar_height_diff;
635
636 DEBUG_MSG (2, "------------------------------------------------->\n");
637 DEBUG_MSG (2, "Dw_gtk_viewport_calc_size: %d x %d (%c/%c) -> %d x %d\n",
638 widget->allocation.width, widget->allocation.height,
639 scrolled->vscrollbar_visible ? 't' : 'f',
640 scrolled->hscrollbar_visible ? 't' : 'f',
641 max_width, max_height);
642
643 if (scrolled->vscrollbar_policy == GTK_POLICY_NEVER)
644 child_height = max_height;
645 else
646 child_height = max_height - bar_height_diff;
647
648 switch (scrolled->vscrollbar_policy) {
649 case GTK_POLICY_ALWAYS:
650 Dw_gtk_viewport_calc_child_size (viewport, max_width - bar_width_diff,
651 child_height,
652 &child_requisition);
653 break;
654
655 case GTK_POLICY_AUTOMATIC:
656 if (viewport->vscrollbar_used) {
657 DEBUG_MSG (2, "Testing with vertical scrollbar ...\n");
658 Dw_gtk_viewport_calc_child_size (viewport,
659 max_width - bar_width_diff,
660 child_height,
661 &child_requisition);
662
663 if (child_requisition.ascent
664 + child_requisition.descent <= child_height) {
665 DEBUG_MSG (2, " failed!\n");
666 Dw_gtk_viewport_calc_child_size (viewport, max_width,
667 child_height,
668 &child_requisition);
669 viewport->vscrollbar_used = TRUE;
670 }
671
672 } else {
673 DEBUG_MSG (2, "Testing without vertical scrollbar ...\n");
674 Dw_gtk_viewport_calc_child_size (viewport, max_width,
675 child_height,
676 &child_requisition);
677
678 /* todo: see above */
679 if (child_requisition.ascent
680 + child_requisition.descent > child_height) {
681 DEBUG_MSG (2, " failed!\n");
682 Dw_gtk_viewport_calc_child_size (viewport,
683 max_width - bar_width_diff,
684 child_height,
685 &child_requisition);
686 viewport->vscrollbar_used = TRUE;
687 }
688 }
689 break;
690
691 case GTK_POLICY_NEVER:
692 Dw_gtk_viewport_calc_child_size (viewport, max_width,
693 child_height,
694 &child_requisition);
695 }
696
697 child_allocation.x = border_width;
698 child_allocation.y = border_width;
699 child_allocation.width = child_requisition.width;
700 child_allocation.ascent = child_requisition.ascent;
701 child_allocation.descent = child_requisition.descent;
702 p_Dw_widget_size_allocate (viewport->child, &child_allocation);
703
704 gtk_layout_set_size (GTK_LAYOUT (viewport),
705 child_requisition.width + 2 * border_width,
706 child_requisition.ascent + child_requisition.descent
707 + 2 * border_width);
708
709 DEBUG_MSG (1, "Setting size to %d x %d\n",
710 child_requisition.width + 2 * border_width,
711 child_requisition.ascent + child_requisition.descent
712 + 2 * border_width);
713
714 DEBUG_MSG (2, "<-------------------------------------------------\n");
715 } else {
716 gtk_layout_set_size (GTK_LAYOUT (viewport), 1, 1);
717 viewport->hscrollbar_used = FALSE;
718 viewport->vscrollbar_used = FALSE;
719 }
720
721 Dw_gtk_viewport_update_anchor (viewport);
722 gtk_widget_queue_draw (GTK_WIDGET (viewport));
723
724 viewport->calc_size_blocked = FALSE;
725 }
726
727
728 /* used by Dw_gtk_viewport_widget_at_point */
729 typedef struct
730 {
731 gint32 x;
732 gint32 y;
733 DwWidget *widget;
734 } WidgetAtPointData;
735
736 /* used by Dw_gtk_viewport_widget_at_point */
737 static void Dw_gtk_viewport_widget_at_point_callback (DwWidget *widget,
738 gpointer data)
739 {
740 WidgetAtPointData *callback_data;
741
742 callback_data = (WidgetAtPointData*) data;
743 DEBUG_MSG (1, " Checking %p ...\n", widget);
744
745 if (/* As a special exception, for the top-level widget, not the
746 * allocation is regarded, but the whole viewport. This makes
747 * selections more useful, since so the user can start the
748 * selection outside of the allocation. */
749 widget->parent == NULL ||
750 /* Otherwise, check whether pointer is in the allocation. */
751 (callback_data->x >= widget->allocation.x &&
752 callback_data->y >= widget->allocation.y &&
753 callback_data->x < widget->allocation.x + widget->allocation.width &&
754 callback_data->y < widget->allocation.y + DW_WIDGET_HEIGHT(widget))) {
755 DEBUG_MSG (1, " yes\n");
756 if (DW_IS_CONTAINER (widget))
757 a_Dw_container_forall (DW_CONTAINER (widget),
758 Dw_gtk_viewport_widget_at_point_callback,
759 data);
760
761 if (callback_data->widget == NULL)
762 callback_data->widget = widget;
763 }
764 }
765
766 /*
767 * Return the widget at point (x, y) (world coordinates).
768 */
769 DwWidget* Dw_gtk_viewport_widget_at_point (GtkDwViewport *viewport,
770 gint32 x,
771 gint32 y)
772 {
773 WidgetAtPointData callback_data;
774
775 callback_data.x = x;
776 callback_data.y = y;
777 callback_data.widget = NULL;
778
779 if (viewport->child)
780 Dw_gtk_viewport_widget_at_point_callback (viewport->child,
781 &callback_data);
782
783 return callback_data.widget;
784 }
785
786
787 /*************
788 * *
789 * Anchors *
790 * *
791 *************/
792
793 /*
794 * todo: Currently, no horizontal scrolling is done. This is generally
795 * possible, DW_HPOS_INTO_VIEW should be used for this, but it is
796 * rather complicated to determine the width of an anchor. This would
797 * be the lenght of the region between <a> and </a>, the page widget
798 * would have to have two kinds of content types (opening and closing
799 * anchor), and some changes in the HTML parser are necessary.
800 */
801
802 /*
803 * Add an anchor, and assign a position for it. For all widgets
804 * directly or indirectly assigned to a viewports, anchors must be
805 * unique, this is tested. "name" is copied, so no strdup is
806 * neccessary for the caller.
807 *
808 * Return the copy on success, or NULL, when this anchor had already
809 * been added to the widget tree.
810 *
811 * The viewport gets the responsibility to free "name".
812 */
813 gchar* p_Dw_gtk_viewport_add_anchor (DwWidget *widget,
814 const gchar *name,
815 gint32 y)
816 {
817 GtkDwViewport *viewport;
818 GtkDwViewportAnchor *anchor;
819
820 _MSG("new anchor %p/'%s' -> %d\n", widget, name, y);
821
822 g_return_val_if_fail (widget->viewport != NULL, NULL);
823 viewport = GTK_DW_VIEWPORT (widget->viewport);
824
825 if (g_hash_table_lookup_extended (viewport->anchors_table, name, NULL,NULL))
826 /* Anchor does already exist. */
827 return NULL;
828 else {
829 anchor = g_new (GtkDwViewportAnchor, 1);
830 anchor->name = g_strdup (name);
831 anchor->widget = widget;
832 anchor->y = y;
833
834 g_hash_table_insert (viewport->anchors_table, anchor->name, anchor);
835 Dw_gtk_viewport_update_anchor (viewport);
836
837 return anchor->name;
838 }
839 }
840
841
842 /*
843 * Assign a position for an already existing anchor.
844 */
845 void p_Dw_gtk_viewport_change_anchor (DwWidget *widget,
846 gchar *name,
847 gint32 y)
848 {
849 GtkDwViewport *viewport;
850 GtkDwViewportAnchor *anchor;
851 gpointer tmp_anchor;
852 gboolean exists;
853
854 _MSG("changing anchor %p/'%s' -> %d\n", widget, name, y);
855
856 g_return_if_fail (widget->viewport != NULL);
857 viewport = GTK_DW_VIEWPORT (widget->viewport);
858
859 exists =
860 g_hash_table_lookup_extended (viewport->anchors_table, name, NULL,
861 &tmp_anchor);
862 g_return_if_fail(exists);
863
864 anchor = tmp_anchor;
865 g_return_if_fail(anchor->widget == widget);
866
867 anchor->y = y;
868
869 Dw_gtk_viewport_update_anchor (viewport);
870 }
871
872
873 /*
874 * Remove an anchor from the table in the viewport. Notice that "name"
875 * is freed here.
876 */
877 void p_Dw_gtk_viewport_remove_anchor (DwWidget *widget,
878 gchar *name)
879 {
880 GtkDwViewport *viewport;
881 GtkDwViewportAnchor *anchor;
882 gpointer tmp_anchor;
883 gboolean exists;
884
885 _MSG("removing anchor %p/'%s'\n", widget, name);
886
887 g_return_if_fail (widget->viewport != NULL);
888 viewport = GTK_DW_VIEWPORT (widget->viewport);
889
890 exists =
891 g_hash_table_lookup_extended (viewport->anchors_table, name, NULL,
892 &tmp_anchor);
893 g_return_if_fail(exists);
894
895 anchor = tmp_anchor;
896 g_return_if_fail(anchor->widget == widget);
897
898 g_hash_table_remove (viewport->anchors_table, name);
899 g_free (anchor->name);
900 g_free (anchor);
901 }
902
903
904 /*
905 * Used by Dw_gtk_viewport_update_anchor_idle.
906 */
907 static gboolean Dw_gtk_viewport_calc_into (gint32 requested_value,
908 gint32 requested_size,
909 gint32 current_value,
910 gint32 size,
911 gint32 *return_value)
912 {
913 if (requested_size > size) {
914 /* The viewport size is smaller than the size of the region which will
915 * be shown. If the region is already visible, do not change the
916 * position. Otherwise, show the left/upper border, this is most likely
917 * what is needed. */
918 if (current_value >= requested_value &&
919 current_value + size < requested_value + requested_size)
920 return FALSE;
921 else
922 requested_size = size;
923 }
924
925 if (requested_value < current_value) {
926 *return_value = requested_value;
927 return TRUE;
928 } else if (requested_value + requested_size > current_value + size) {
929 *return_value = requested_value - size + requested_size;
930 return TRUE;
931 } else
932 return FALSE;
933 }
934
935 /*
936 * See Dw_gtk_viewport_scroll_to.
937 */
938 static gint Dw_gtk_viewport_update_anchor_idle (gpointer data)
939 {
940 gint32 vp_width, vp_height, x = 0, y = 0;
941 GtkScrolledWindow *scrolled;
942 GtkDwViewport *viewport;
943 GtkWidget *vwidget;
944 GtkAdjustment *vadj, *hadj;
945 gboolean change_x, change_y;
946
947 DBG_MSG (data, "scrolling", 0, "Dw_gtk_viewport_update_anchor_idle");
948 DBG_MSG_START (data);
949
950 vwidget = GTK_WIDGET (data);
951 viewport = GTK_DW_VIEWPORT (vwidget);
952 scrolled = GTK_SCROLLED_WINDOW (vwidget->parent->parent);
953 hadj = GTK_LAYOUT(viewport)->hadjustment;
954 vadj = GTK_LAYOUT(viewport)->vadjustment;
955
956 vp_width =
957 vwidget->allocation.width - GTK_CONTAINER(viewport)->border_width;
958 vp_height =
959 vwidget->allocation.height - GTK_CONTAINER(viewport)->border_width;
960 DBG_MSGF (viewport, "scrolling", 0, "vp_width = %d", vp_width);
961 DBG_MSGF (viewport, "scrolling", 0, "vp_height = %d", vp_height);
962
963 change_x = TRUE;
964 switch (viewport->anchor_pos.hpos) {
965 case DW_HPOS_LEFT:
966 DBG_MSG (viewport, "scrolling", 0, "DW_HPOS_LEFT");
967 x = viewport->anchor_pos.x;
968 break;
969 case DW_HPOS_CENTER:
970 DBG_MSG (viewport, "scrolling", 0, "DW_HPOS_CENTER");
971 x = viewport->anchor_pos.x - (vp_width - viewport->anchor_pos.width) / 2;
972 break;
973 case DW_HPOS_RIGHT:
974 DBG_MSG (viewport, "scrolling", 0, "DW_HPOS_RIGHT");
975 x = viewport->anchor_pos.x - (vp_width - viewport->anchor_pos.width);
976 break;
977 case DW_HPOS_INTO_VIEW:
978 DBG_MSG (viewport, "scrolling", 0, "DW_HPOS_INTO_VIEW");
979 change_x = Dw_gtk_viewport_calc_into (viewport->anchor_pos.x,
980 viewport->anchor_pos.width,
981 hadj->value, vp_width, &x);
982 break;
983 case DW_HPOS_NO_CHANGE:
984 DBG_MSG (viewport, "scrolling", 0, "DW_HPOS_NO_CHANGE");
985 change_x = FALSE;
986 break;
987 }
988
989 change_y = TRUE;
990 switch (viewport->anchor_pos.vpos) {
991 case DW_VPOS_TOP:
992 DBG_MSG (viewport, "scrolling", 0, "DW_VPOS_TOP");
993 y = viewport->anchor_pos.y;
994 break;
995 case DW_VPOS_CENTER:
996 DBG_MSG (viewport, "scrolling", 0, "DW_VPOS_CENTER");
997 y = viewport->anchor_pos.y -
998 (vp_height - viewport->anchor_pos.height) / 2;
999 break;
1000 case DW_VPOS_BOTTOM:
1001 DBG_MSG (viewport, "scrolling", 0, "DW_VPOS_BOTTOM");
1002 y = viewport->anchor_pos.y - (vp_height - viewport->anchor_pos.height);
1003 break;
1004 case DW_VPOS_INTO_VIEW:
1005 DBG_MSG (viewport, "scrolling", 0, "DW_VPOS_INTO_VIEW");
1006 change_y = Dw_gtk_viewport_calc_into (viewport->anchor_pos.y,
1007 viewport->anchor_pos.height,
1008 vadj->value, vp_height, &y);
1009 case DW_VPOS_NO_CHANGE:
1010 DBG_MSG (viewport, "scrolling", 0, "DW_VPOS_NO_CHANGE");
1011 change_y = FALSE;
1012 break;
1013 }
1014
1015 DBG_MSGF (viewport, "scrolling", 0, "scrolling to (%d, %d)\n", x, y);
1016 DBG_MSGF (viewport, "scrolling", 0,
1017 "hadj->upper = %g, hadj->page_size = %g",
1018 hadj->upper, hadj->page_size);
1019 DBG_MSGF (viewport, "scrolling", 0,
1020 "vadj->upper = %g, vadj->page_size = %g",
1021 vadj->upper, vadj->page_size);
1022
1023 if (change_x) {
1024 if (x > hadj->upper - hadj->page_size)
1025 gtk_adjustment_set_value (hadj, hadj->upper - hadj->page_size);
1026 else
1027 gtk_adjustment_set_value (hadj, x);
1028 }
1029
1030 if (change_y) {
1031 if (y > vadj->upper - vadj->page_size)
1032 gtk_adjustment_set_value (vadj, vadj->upper - vadj->page_size);
1033 else
1034 gtk_adjustment_set_value (vadj, y);
1035 }
1036
1037 viewport->anchor_idle_id = 0;
1038
1039 DBG_MSG_END (viewport);
1040 return FALSE;
1041 }
1042
1043
1044 /*
1045 * Called when possibly the scroll position has to be changed because
1046 * of anchors.
1047 */
1048 void Dw_gtk_viewport_update_anchor (GtkDwViewport *viewport)
1049 {
1050 GtkDwViewportAnchor *anchor;
1051 gpointer tmp_anchor;
1052
1053 if (viewport->anchor &&
1054 g_hash_table_lookup_extended (viewport->anchors_table, viewport->anchor,
1055 NULL, &tmp_anchor)) {
1056 anchor = tmp_anchor;
1057 Dw_gtk_viewport_scroll_to (viewport, DW_HPOS_NO_CHANGE, DW_VPOS_TOP,
1058 0, anchor->y + anchor->widget->allocation.y,
1059 0, 0);
1060 }
1061 }
1062
1063
1064 /*
1065 * Sets the anchor to scroll to.
1066 */
1067 void a_Dw_gtk_viewport_set_anchor (GtkDwViewport *viewport,
1068 const gchar *anchor)
1069 {
1070 Dw_gtk_viewport_remove_anchor (viewport);
1071
1072 if (anchor) {
1073 viewport->anchor = g_strdup (anchor);
1074 Dw_gtk_viewport_update_anchor (viewport);
1075 } else {
1076 viewport->anchor = NULL;
1077 gtk_adjustment_set_value (GTK_LAYOUT(viewport)->vadjustment, 0);
1078 }
1079 }
1080
1081
1082 /*
1083 * Sets the position to scroll to. The current anchor will be removed.
1084 */
1085 void a_Dw_gtk_viewport_set_scrolling_position (GtkDwViewport *viewport,
1086 gint32 x,
1087 gint32 y)
1088 {
1089 Dw_gtk_viewport_remove_anchor (viewport);
1090 Dw_gtk_viewport_scroll_to (viewport, DW_HPOS_LEFT, DW_VPOS_TOP, x, y, 0, 0);
1091 }
1092
1093 /*
1094 * Scrolls the viewport, so that the region [x, y, width, height] (world
1095 * coordinates) is seen, according to hpos and vpos.
1096 *
1097 * The actual scrolling is done in an idle function.
1098 */
1099 void Dw_gtk_viewport_scroll_to (GtkDwViewport *viewport,
1100 DwHPosition hpos,
1101 DwVPosition vpos,
1102 gint32 x,
1103 gint32 y,
1104 gint32 width,
1105 gint32 height)
1106 {
1107 viewport->anchor_pos.hpos = hpos;
1108 viewport->anchor_pos.vpos = vpos;
1109 viewport->anchor_pos.x = x;
1110 viewport->anchor_pos.y = y;
1111 viewport->anchor_pos.width = width;
1112 viewport->anchor_pos.height = height;
1113
1114 DBG_OBJ_SET_NUM (viewport, "anchor_pos.hpos", viewport->anchor_pos.hpos);
1115 DBG_OBJ_SET_NUM (viewport, "anchor_pos.vpos", viewport->anchor_pos.vpos);
1116 DBG_OBJ_SET_NUM (viewport, "anchor_pos.x", viewport->anchor_pos.x);
1117 DBG_OBJ_SET_NUM (viewport, "anchor_pos.y", viewport->anchor_pos.y);
1118 DBG_OBJ_SET_NUM (viewport, "anchor_pos.width", viewport->anchor_pos.width);
1119 DBG_OBJ_SET_NUM (viewport, "anchor_pos.height",
1120 viewport->anchor_pos.height);
1121
1122 if (viewport->anchor_idle_id == 0)
1123 viewport->anchor_idle_id = gtk_idle_add
1124 (Dw_gtk_viewport_update_anchor_idle, (gpointer)viewport);
1125 }
1126
1127
1128 /*
1129 * Remove anchor and idle function.
1130 */
1131 void Dw_gtk_viewport_remove_anchor (GtkDwViewport *viewport)
1132 {
1133 if (viewport->anchor) {
1134 g_free (viewport->anchor);
1135 viewport->anchor = NULL;
1136 }
1137
1138 if (viewport->anchor_idle_id != 0) {
1139 gtk_idle_remove (viewport->anchor_idle_id);
1140 viewport->anchor_idle_id = 0;
1141 }
1142 }
1143
1144 /*
1145 * Drawing and resizing is done in this idle function.
1146 */
1147 static gint Dw_gtk_viewport_draw_resize_idle (gpointer data)
1148 {
1149 GtkDwViewport *viewport;
1150 GtkLayout *layout;
1151 GtkWidget *widget;
1152 DwRectangle viewport_area, world_area;
1153 GdkRectangle gtk_area;
1154 int i;
1155
1156 viewport = GTK_DW_VIEWPORT (data);
1157
1158 switch (viewport->draw_resize_action) {
1159 case DW_GTK_VIEWPORT_DRAW:
1160 for (i = 0; i < viewport->num_draw_areas; i++) {
1161 widget = GTK_WIDGET (viewport);
1162 layout = GTK_LAYOUT (viewport);
1163
1164 viewport_area.x = gtk_layout_get_hadjustment(layout)->value;
1165 viewport_area.y = gtk_layout_get_vadjustment(layout)->value;;
1166 viewport_area.width = widget->allocation.width;
1167 viewport_area.height = widget->allocation.height;
1168
1169 if (p_Dw_rectangle_intersect (&viewport->draw_areas[i],
1170 &viewport_area, &world_area)) {
1171 gtk_area.x = world_area.x - viewport_area.x;
1172 gtk_area.y = world_area.y - viewport_area.y;
1173 gtk_area.width = world_area.width;
1174 gtk_area.height = world_area.height;
1175 gtk_widget_draw (widget, &gtk_area);
1176 }
1177 }
1178
1179 /* No more areas to be drawn. */
1180 viewport->num_draw_areas = 0;
1181 break;
1182
1183 case DW_GTK_VIEWPORT_RESIZE:
1184 Dw_gtk_viewport_calc_size (viewport);
1185 break;
1186 }
1187
1188 viewport->draw_resize_idle_id = 0;
1189 return FALSE;
1190 }
1191
1192 /*
1193 * Queue an area for drawing. This function is called by
1194 * p_Dw_widget_queue_draw_area. x and y are passed in world coordinates.
1195 */
1196 void Dw_gtk_viewport_queue_draw (GtkDwViewport *viewport,
1197 gint32 x,
1198 gint32 y,
1199 gint32 width,
1200 gint32 height)
1201 {
1202 DwRectangle area;
1203 int i;
1204
1205 if (viewport->draw_resize_idle_id == 0) {
1206 viewport->draw_resize_action = DW_GTK_VIEWPORT_DRAW;
1207 viewport->draw_resize_idle_id =
1208 gtk_idle_add (Dw_gtk_viewport_draw_resize_idle, (gpointer)viewport);
1209 } else if (viewport->draw_resize_action == DW_GTK_VIEWPORT_RESIZE)
1210 /* Drawing is always overridden by resizing. */
1211 return;
1212
1213 area.x = x;
1214 area.y = y;
1215 area.width = width;
1216 area.height = height;
1217
1218 /* First, try to keep the list as clean as possible. Check whether other
1219 * rectangles interfer with this one in some way. */
1220 /* An idea for optimization: The list could be sorted, and so the part of
1221 * the list we have to consider here, may be reduced, the start may be
1222 * found via linear search. However, this probably makes balanced binary
1223 * trees necessary, since moving elements within the array may be quite
1224 * time-consuming.
1225 */
1226 _MSG(" num_draw_areas = %d\n", viewport->num_draw_areas);
1227 for (i = 0; i < viewport->num_draw_areas; i++) {
1228 if (p_Dw_rectangle_is_subset (&area, &viewport->draw_areas[i]))
1229 /* First case: area is a subset of an already queued rectangle
1230 * -> nothing to do. */
1231 return;
1232 else if (p_Dw_rectangle_is_subset (&viewport->draw_areas[i], &area)) {
1233 /* Second case: area is a subset of an already queued rectangle
1234 * -> replace the other one with area. */
1235 viewport->draw_areas[i] = area;
1236 return;
1237 }
1238 /* Maybe some more tests: if both areas may exactly be combined to a
1239 * rectangle? Very unlikely case ... */
1240 }
1241
1242 /* No interference: add the new area to the list. */
1243 viewport->num_draw_areas++;
1244 a_List_add (viewport->draw_areas, viewport->num_draw_areas,
1245 viewport->num_draw_areas_max);
1246 viewport->draw_areas[viewport->num_draw_areas - 1] = area;
1247 }
1248
1249 /*
1250 * Start the resizing idle. This function is called by
1251 * p_Dw_widget_queue_resize, after the appropriate attributes have been set in
1252 * the widgets, where necessary.
1253 */
1254 void Dw_gtk_viewport_queue_resize (GtkDwViewport *viewport)
1255 {
1256 /* Resizing always overrides drawing. */
1257 viewport->draw_resize_action = DW_GTK_VIEWPORT_RESIZE;
1258 viewport->num_draw_areas = 0;
1259
1260 if (viewport->draw_resize_idle_id == 0)
1261 viewport->draw_resize_idle_id =
1262 gtk_idle_add (Dw_gtk_viewport_draw_resize_idle, (gpointer)viewport);
1263 }
1264
1265
1266 /*
1267 * Return the DwWidget which is at position (vx, vy) in viewport coordinates.
1268 */
1269 DwWidget* a_Dw_gtk_viewport_widget_at_viewport_point (GtkDwViewport *viewport,
1270 gint32 vx,
1271 gint32 vy)
1272 {
1273 gint32 world_x, world_y;
1274
1275 if (viewport->child) {
1276 world_x = vx + gtk_layout_get_hadjustment(GTK_LAYOUT(viewport))->value;
1277 world_y = vy + gtk_layout_get_vadjustment(GTK_LAYOUT(viewport))->value;
1278 return Dw_gtk_viewport_widget_at_point (viewport, world_x, world_y);
1279 } else
1280 return NULL;
1281 }
+0
-131
src/dw_gtk_viewport.h less more
0 #ifndef __DW_GTK_VIEWPORT_H__
1 #define __DW_GTK_VIEWPORT_H__
2
3 #include <gtk/gtklayout.h>
4 #include "dw_widget.h"
5 #include "findtext.h"
6 #include "selection.h"
7
8 #ifdef __cplusplus
9 extern "C" {
10 #endif /* __cplusplus */
11
12 #define GTK_TYPE_DW_VIEWPORT (a_Dw_gtk_viewport_get_type ())
13 #define GTK_DW_VIEWPORT(obj) (GTK_CHECK_CAST (obj, \
14 GTK_TYPE_DW_VIEWPORT,GtkDwViewport))
15 #define GTK_DW_VIEWPORT_CLASS(klass) (GTK_CHECK_CLASS_CAST (klass, \
16 GTK_TYPE_DW_VIEWPORT, \
17 GtkDwViewportClass)
18 #define GTK_IS_DW_VIEWPORT(obj) GTK_CHECK_TYPE (obj, \
19 GTK_TYPE_DW_VIEWPORT)
20
21 typedef enum {
22 DW_GTK_VIEWPORT_DRAW,
23 DW_GTK_VIEWPORT_RESIZE
24 } DwGtkViewportDrawResizeAction;
25
26 typedef struct _GtkDwViewport GtkDwViewport;
27 typedef struct _GtkDwViewportClass GtkDwViewportClass;
28
29
30 struct _GtkDwViewport
31 {
32 GtkLayout layout;
33
34 GdkPixmap *back_pixmap; /* backing pixmap for buffering */
35 gint back_width, back_height;
36 gint depth;
37
38 DwWidget *child;
39 DwWidget *last_entered;
40 gboolean hscrollbar_used, vscrollbar_used, calc_size_blocked;
41
42 /* updated by Dw_gtk_viewport_motion_notify */
43 gdouble mouse_x, mouse_y;
44
45 gchar *anchor;
46 DwRectPosition anchor_pos;
47 gint anchor_idle_id;
48
49 FindtextState *findtext_state;
50 Selection *selection;
51
52 /* Anchors of the widget tree.
53 * Key: gchar*, has to be stored elsewhere
54 * Value: an instance of GtkDwViewportAnchor (in .c file) */
55 GHashTable *anchors_table;
56
57 /* Queue of draw and resize requests. */
58 gint draw_resize_idle_id;
59 DwGtkViewportDrawResizeAction draw_resize_action;
60
61 /* What has to be redrawn. DwRectangle's are in world coordinates. */
62 DwRectangle *draw_areas;
63 gint num_draw_areas;
64 gint num_draw_areas_max; /* number allocated */
65 };
66
67
68 struct _GtkDwViewportClass
69 {
70 GtkLayoutClass parent_class;
71 };
72
73
74 GtkType a_Dw_gtk_viewport_get_type (void);
75 GtkWidget* a_Dw_gtk_viewport_new (GtkAdjustment *hadjustment,
76 GtkAdjustment *vadjustment);
77 void a_Dw_gtk_viewport_add_dw (GtkDwViewport *viewport,
78 DwWidget *widget);
79
80 void a_Dw_gtk_viewport_set_anchor (GtkDwViewport *viewport,
81 const gchar *anchor);
82 void a_Dw_gtk_viewport_set_scrolling_position (GtkDwViewport
83 *viewport,
84 gint32 x,
85 gint32 y);
86
87 DwWidget* a_Dw_gtk_viewport_widget_at_viewport_point (GtkDwViewport
88 *viewport,
89 gint32 vx,
90 gint32 vy);
91
92 gchar* p_Dw_gtk_viewport_add_anchor (DwWidget *widget,
93 const gchar* name,
94 gint32 y);
95 void p_Dw_gtk_viewport_change_anchor (DwWidget *widget,
96 gchar *name,
97 gint32 y);
98 void p_Dw_gtk_viewport_remove_anchor (DwWidget *widget,
99 gchar *name);
100
101 void Dw_gtk_viewport_remove_dw (GtkDwViewport *viewport);
102 void Dw_gtk_viewport_calc_size (GtkDwViewport *viewport);
103
104 DwWidget* Dw_gtk_viewport_widget_at_point (GtkDwViewport *viewport,
105 gint32 x,
106 gint32 y);
107
108 void Dw_gtk_viewport_update_anchor (GtkDwViewport *viewport);
109 void Dw_gtk_viewport_scroll_to (GtkDwViewport *viewport,
110 DwHPosition hpos,
111 DwVPosition vpos,
112 gint32 x,
113 gint32 y,
114 gint32 width,
115 gint32 height);
116 void Dw_gtk_viewport_remove_anchor (GtkDwViewport *viewport);
117 void Dw_gtk_viewport_queue_draw (GtkDwViewport *viewport,
118 gint32 x,
119 gint32 y,
120 gint32 width,
121 gint32 height);
122 void Dw_gtk_viewport_queue_resize (GtkDwViewport *viewport);
123
124 void Dw_gtk_viewport_update_background (GtkDwViewport *viewport);
125
126 #ifdef __cplusplus
127 }
128 #endif /* __cplusplus */
129
130 #endif /* __DW_GTK_VIEWPORT_H__ */
+0
-141
src/dw_hruler.c less more
0 /*
1 * File: dw_hruler.c
2 *
3 * Copyright (C) 2000 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This is really an empty widget, the HTML parser puts a border
13 * around it, and drawing is done in Dw_widget_draw_widget_box. The
14 * only remarkable point is that the DW_HAS_CONTENT flag is
15 * cleared.
16 */
17
18 #include "dw_hruler.h"
19 #include "dw_gtk_viewport.h"
20 #include "debug.h"
21
22 static void Dw_hruler_init (DwHruler *hruler);
23 static void Dw_hruler_class_init (DwHrulerClass *klass);
24
25 static void Dw_hruler_size_request (DwWidget *widget,
26 DwRequisition *requisition);
27 static void Dw_hruler_draw (DwWidget *widget,
28 DwRectangle *area,
29 GdkEventExpose *event);
30 static DwIterator* Dw_hruler_iterator (DwWidget *widget,
31 gint32 mask,
32 gboolean at_end);
33 static void Dw_hruler_iterator_highlight (DwIterator *it,
34 gint start,
35 gint end,
36 DwHighlightLayer layer);
37
38
39 GtkType a_Dw_hruler_get_type (void)
40 {
41 static GtkType type = 0;
42
43 if (!type) {
44 GtkTypeInfo info = {
45 "DwHruler",
46 sizeof (DwHruler),
47 sizeof (DwHrulerClass),
48 (GtkClassInitFunc) Dw_hruler_class_init,
49 (GtkObjectInitFunc) Dw_hruler_init,
50 (GtkArgSetFunc) NULL,
51 (GtkArgGetFunc) NULL,
52 (GtkClassInitFunc) NULL
53 };
54
55 type = gtk_type_unique (DW_TYPE_WIDGET, &info);
56 }
57
58 return type;
59 }
60
61
62 DwWidget* a_Dw_hruler_new (void)
63 {
64 GtkObject *object = gtk_object_new (DW_TYPE_HRULER, NULL);
65 DBG_OBJ_CREATE (object, "DwHRuler");
66 return DW_WIDGET (object);
67 }
68
69
70 static void Dw_hruler_init (DwHruler *hruler)
71 {
72 int i;
73 DW_WIDGET_UNSET_FLAGS (hruler, DW_HAS_CONTENT);
74 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS; i++)
75 hruler->selected[i] = FALSE;
76 }
77
78
79 static void Dw_hruler_class_init (DwHrulerClass *klass)
80 {
81 GtkObjectClass *object_class;
82 DwWidgetClass *widget_class;
83
84 object_class = GTK_OBJECT_CLASS (klass);
85
86 widget_class = (DwWidgetClass*)klass;
87 widget_class->size_request = Dw_hruler_size_request;
88 widget_class->draw = Dw_hruler_draw;
89 widget_class->iterator = Dw_hruler_iterator;
90 }
91
92
93 static void Dw_hruler_size_request (DwWidget *widget,
94 DwRequisition *requisition)
95 {
96 requisition->width = p_Dw_style_box_diff_width (widget->style);
97 requisition->ascent = p_Dw_style_box_diff_height (widget->style);
98 requisition->descent = 0;
99 }
100
101
102 static void Dw_hruler_draw (DwWidget *widget,
103 DwRectangle *area,
104 GdkEventExpose *event)
105 {
106 int i;
107 gboolean selected = FALSE;
108
109 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS && !selected; i++)
110 selected = DW_HRULER(widget)->selected[i];
111 p_Dw_widget_draw_widget_box (widget, area, selected);
112 if (selected)
113 p_Dw_widget_draw_selected (widget, area);
114 }
115
116
117 static DwIterator *Dw_hruler_iterator (DwWidget *widget,
118 gint32 mask,
119 gboolean at_end)
120 {
121 DwIterator *it;
122
123 it = p_Dw_widget_text_iterator (widget, mask, at_end,
124 "-----------------------------------"
125 "-----------------------------------");
126 if (it)
127 it->highlight = Dw_hruler_iterator_highlight;
128 return it;
129 }
130
131 static void Dw_hruler_iterator_highlight (DwIterator *it,
132 gint start,
133 gint end,
134 DwHighlightLayer layer)
135 {
136 if (it->content.type == DW_CONTENT_TEXT) {
137 DW_HRULER(it->widget)->selected[layer] = (start == 0 && end >= 1);
138 p_Dw_widget_queue_draw (it->widget);
139 }
140 }
+0
-40
src/dw_hruler.h less more
0 #ifndef __DW_HRULER_H__
1 #define __DW_HRULER_H__
2
3 #include "dw_widget.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_HRULER (a_Dw_hruler_get_type ())
10 #define DW_HRULER(obj) GTK_CHECK_CAST (obj,DW_TYPE_HRULER, DwHruler)
11 #define DW_HRULER_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, DW_TYPE_HRULER, \
12 DwHrulerClass)
13 #define DW_IS_HRULER(obj) GTK_CHECK_TYPE (obj, DW_TYPE_HRULER)
14
15 typedef struct _DwHruler DwHruler;
16 typedef struct _DwHrulerClass DwHrulerClass;
17
18
19 struct _DwHruler
20 {
21 DwWidget widget;
22 gboolean selected[DW_HIGHLIGHT_NUM_LAYERS];
23 };
24
25
26 struct _DwHrulerClass
27 {
28 DwWidgetClass parent_class;
29 };
30
31
32 GtkType a_Dw_hruler_get_type (void);
33 DwWidget* a_Dw_hruler_new (void);
34
35 #ifdef __cplusplus
36 }
37 #endif /* __cplusplus */
38
39 #endif /* __DW_HRULER_H__ */
+0
-882
src/dw_image.c less more
0 /*
1 * File: dw_image.c
2 *
3 * Copyright (C) 2001 Sebastian Geerken <S.Geerken@ping.de>,
4 * Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #include "msg.h"
13 #include "dw_image.h"
14 #include "dw_gtk_viewport.h"
15 #include "prefs.h"
16 #include "dw_marshal.h"
17 #include "list.h"
18 #include "dicache.h"
19 #include "debug.h"
20 #include <gdk/gdk.h>
21 #include <gtk/gtk.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 static void Dw_image_init (DwImage *image);
26 static void Dw_image_class_init (DwImageClass *klass);
27
28 static void Dw_image_destroy (GtkObject *object);
29
30 static void Dw_image_size_request (DwWidget *widget,
31 DwRequisition *requisition);
32 static void Dw_image_size_allocate (DwWidget *widget,
33 DwAllocation *allocation);
34 static void Dw_image_draw (DwWidget *widget,
35 DwRectangle *area,
36 GdkEventExpose *event);
37 static gboolean Dw_image_button_press (DwWidget *widget,
38 gint32 x,
39 gint32 y,
40 GdkEventButton *event);
41 static gboolean Dw_image_button_release (DwWidget *widget,
42 gint32 x,
43 gint32 y,
44 GdkEventButton *event);
45 static gboolean Dw_image_motion_notify (DwWidget *widget,
46 gint32 x,
47 gint32 y,
48 GdkEventMotion *event);
49 static gboolean Dw_image_enter_notify (DwWidget *widget,
50 DwWidget *last_widget,
51 GdkEventMotion *event);
52 static gboolean Dw_image_leave_notify (DwWidget *widget,
53 DwWidget *next_widget,
54 GdkEventMotion *event);
55 static DwIterator* Dw_image_iterator (DwWidget *widget,
56 gint32 mask,
57 gboolean at_end);
58 static void Dw_image_iterator_highlight (DwIterator *it,
59 gint start,
60 gint end,
61 DwHighlightLayer layer);
62
63 static void Dw_image_find_link (DwImage *image,
64 gint x, gint y,
65 gint *link,
66 gint *link_x, gint *link_y);
67 static void Dw_image_scale_row (DwImage *image, gint y_dest);
68 static void Dw_image_scale (DwImage *image);
69
70 static gint Dw_image_map_list_find_link (DwImageMapList *list,
71 DilloUrl *url,
72 gint x,
73 gint y);
74
75
76 #define Dw_image_scaled_y(image, y_src) \
77 ( (y_src) * ( ((DwWidget*)(image))->allocation.ascent + \
78 ((DwWidget*)(image))->allocation.descent - \
79 p_Dw_style_box_diff_height ( ((DwWidget*)(image))->style ) ) \
80 / ((DwImage*)(image))->height )
81
82
83 enum
84 {
85 LINK_ENTERED,
86 LINK_PRESSED,
87 LINK_RELEASED,
88 LINK_CLICKED,
89 LAST_SIGNAL
90 };
91
92 static guint image_signals[LAST_SIGNAL] = { 0 };
93 static DwWidgetClass *parent_class;
94
95
96 /*
97 * Standard Gtk+ function.
98 */
99 GtkType a_Dw_image_get_type (void)
100 {
101 static GtkType type = 0;
102
103 if (!type) {
104 GtkTypeInfo info = {
105 "DwImage",
106 sizeof (DwImage),
107 sizeof (DwImageClass),
108 (GtkClassInitFunc) Dw_image_class_init,
109 (GtkObjectInitFunc) Dw_image_init,
110 (GtkArgSetFunc) NULL,
111 (GtkArgGetFunc) NULL,
112 (GtkClassInitFunc) NULL
113 };
114
115 type = gtk_type_unique (DW_TYPE_WIDGET, &info);
116 }
117
118 return type;
119 }
120
121
122 /*
123 * Standard Gtk+ function.
124 */
125 DwWidget* a_Dw_image_new (DwImageType type, const gchar *alt_text)
126 {
127 GtkObject *object;
128 DwImage *image;
129
130 object = gtk_object_new (DW_TYPE_IMAGE, NULL);
131 DBG_OBJ_CREATE (object, "DwImage");
132 image = DW_IMAGE(object);
133 image->alt_text = g_strdup (alt_text);
134 return DW_WIDGET (object);
135 }
136
137
138 /*
139 * Standard Gtk+ function.
140 */
141 static void Dw_image_init (DwImage *image)
142 {
143 int i;
144
145 image->url = NULL;
146 image->width = 0;
147 image->height = 0;
148 image->alt_text_width = -1; /* not yet calculated */
149 image->buffer = NULL;
150 image->scaled_buffer = NULL;
151 image->alt_text = NULL;
152 image->usemap_url = NULL;
153 image->ismap = FALSE;
154 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS; i++)
155 image->selected[i] = FALSE;
156 }
157
158
159 /*
160 * Standard Gtk+ function.
161 */
162 static void Dw_image_class_init (DwImageClass *klass)
163 {
164 GtkObjectClass *object_class;
165 DwWidgetClass *widget_class;
166
167 parent_class = gtk_type_class (DW_TYPE_WIDGET);
168
169 object_class = (GtkObjectClass*)klass;
170 object_class->destroy = Dw_image_destroy;
171
172 image_signals[LINK_ENTERED] =
173 gtk_signal_new ("link_entered",
174 GTK_RUN_LAST,
175 object_class->type,
176 GTK_SIGNAL_OFFSET (DwImageClass, link_entered),
177 p_Dw_marshal_link_enter,
178 GTK_TYPE_BOOL,
179 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT);
180 image_signals[LINK_PRESSED] =
181 gtk_signal_new ("link_pressed",
182 GTK_RUN_LAST,
183 object_class->type,
184 GTK_SIGNAL_OFFSET (DwImageClass, link_pressed),
185 p_Dw_marshal_link_button,
186 GTK_TYPE_BOOL,
187 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
188 GTK_TYPE_GDK_EVENT);
189 image_signals[LINK_RELEASED] =
190 gtk_signal_new ("link_released",
191 GTK_RUN_LAST,
192 object_class->type,
193 GTK_SIGNAL_OFFSET (DwImageClass, link_released),
194 p_Dw_marshal_link_button,
195 GTK_TYPE_BOOL,
196 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
197 GTK_TYPE_GDK_EVENT);
198 image_signals[LINK_CLICKED] =
199 gtk_signal_new ("link_clicked",
200 GTK_RUN_LAST,
201 object_class->type,
202 GTK_SIGNAL_OFFSET (DwImageClass, link_clicked),
203 p_Dw_marshal_link_button,
204 GTK_TYPE_BOOL,
205 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
206 GTK_TYPE_GDK_EVENT);
207 gtk_object_class_add_signals (object_class, image_signals, LAST_SIGNAL);
208
209 widget_class = (DwWidgetClass*)klass;
210 widget_class->size_request = Dw_image_size_request;
211 widget_class->size_allocate = Dw_image_size_allocate;
212 widget_class->draw = Dw_image_draw;
213 widget_class->button_press_event = Dw_image_button_press;
214 widget_class->button_release_event = Dw_image_button_release;
215 widget_class->motion_notify_event = Dw_image_motion_notify;
216 widget_class->enter_notify_event = Dw_image_enter_notify;
217 widget_class->leave_notify_event = Dw_image_leave_notify;
218 widget_class->iterator = Dw_image_iterator;
219 }
220
221
222 /*
223 * Standard Gtk+ function.
224 */
225 static void Dw_image_destroy (GtkObject *object)
226 {
227 DwImage *image = DW_IMAGE (object);
228
229 if (image->usemap_url)
230 a_Url_free (image->usemap_url);
231 if (image->url)
232 a_Dicache_unref (image->url, image->version);
233 g_free (image->alt_text);
234 g_free (image->scaled_buffer);
235
236 GTK_OBJECT_CLASS(parent_class)->destroy (object);
237 }
238
239
240 /*
241 * Standard Dw function.
242 */
243 static void Dw_image_size_request (DwWidget *widget,
244 DwRequisition *requisition)
245 {
246 DwImage *image;
247
248 image = DW_IMAGE (widget);
249
250 if (image->buffer != NULL ||
251 (image->alt_text == NULL || image->alt_text[0] == '\0')) {
252 requisition->width = image->width;
253 requisition->ascent = image->height;
254 requisition->descent = 0;
255 } else {
256 if (image->alt_text_width == -1)
257 image->alt_text_width =
258 gdk_string_width (widget->style->font->font, image->alt_text);
259
260 requisition->width = image->alt_text_width;
261 requisition->ascent = widget->style->font->font->ascent;
262 requisition->descent = widget->style->font->font->descent;
263 }
264
265 requisition->width += p_Dw_style_box_diff_width (widget->style);
266 requisition->ascent += p_Dw_style_box_offset_y (widget->style);
267 requisition->descent += p_Dw_style_box_rest_height (widget->style);
268 }
269
270
271 /*
272 * Standard Dw function.
273 */
274 static void Dw_image_size_allocate (DwWidget *widget,
275 DwAllocation *allocation)
276 {
277 DwImage *image;
278
279 /* if image is moved only */
280 if (allocation->width == widget->allocation.width &&
281 allocation->ascent + allocation->descent == DW_WIDGET_HEIGHT(widget))
282 return;
283
284 /* this is also done in a_Dw_widget_size_allocate, but
285 Dw_image_scale needs this. */
286 widget->allocation = *allocation;
287
288 image = DW_IMAGE (widget);
289 if (image->buffer != NULL && image->width > 0 && image->height > 0)
290 Dw_image_scale (image);
291 }
292
293
294 /*
295 * Standard Dw function.
296 */
297 static void Dw_image_draw (DwWidget *widget,
298 DwRectangle *area,
299 GdkEventExpose *event)
300 {
301 gint32 vx, vy;
302 DwRectangle content, intersection;
303 GdkGC *gc;
304 DwImage *image = DW_IMAGE (widget);
305 guchar *buffer, *bstart;
306 int i;
307 gboolean selected = FALSE;
308
309 if (image->buffer) {
310 p_Dw_widget_draw_widget_box (widget, area, FALSE);
311
312 content.x = p_Dw_style_box_offset_x (widget->style);
313 content.y = p_Dw_style_box_offset_y (widget->style);
314 content.width = DW_WIDGET_CONTENT_WIDTH(widget);
315 content.height = DW_WIDGET_CONTENT_HEIGHT(widget);
316
317 if (p_Dw_rectangle_intersect (area, &content, &intersection)) {
318 vx = p_Dw_widget_x_world_to_viewport (widget, widget->allocation.x);
319 vy = p_Dw_widget_y_world_to_viewport (widget, widget->allocation.y);
320 gc = widget->style->color->gc;
321
322 if (image->scaled_buffer)
323 buffer = image->scaled_buffer;
324 else
325 buffer = image->buffer;
326
327 bstart = buffer +
328 3 * ( intersection.x - p_Dw_style_box_offset_x (widget->style)+
329 content.width * (intersection.y -
330 p_Dw_style_box_offset_y (widget->style)) );
331
332 gdk_draw_rgb_image (
333 DW_WIDGET_WINDOW (widget), gc,
334 vx + intersection.x, vy + intersection.y,
335 intersection.width, intersection.height, GDK_RGB_DITHER_MAX,
336 bstart, content.width * 3);
337 }
338 } else {
339 if (image->alt_text && image->alt_text[0]) {
340 if (image->alt_text_width == -1)
341 image->alt_text_width =
342 gdk_string_width (widget->style->font->font, image->alt_text);
343
344 if (widget->allocation.width < image->alt_text_width ||
345 widget->allocation.ascent + widget->allocation.descent
346 < widget->style->font->font->ascent
347 + widget->style->font->font->descent)
348 p_Dw_widget_will_clip (widget);
349
350 p_Dw_widget_draw_widget_box (widget, area, FALSE);
351 gdk_draw_string
352 (DW_WIDGET_WINDOW (widget), widget->style->font->font,
353 widget->style->color->gc,
354 p_Dw_widget_x_world_to_viewport (widget, widget->allocation.x),
355 p_Dw_widget_y_world_to_viewport (widget, widget->allocation.y)
356 + widget->style->font->font->ascent,
357 image->alt_text);
358 }
359 }
360
361 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS && !selected; i++)
362 selected = image->selected[i];
363 if (selected)
364 p_Dw_widget_draw_selected (widget, area);
365 }
366
367
368 /*
369 * Standard Dw function.
370 */
371 static gboolean Dw_image_enter_notify (DwWidget *widget,
372 DwWidget *last_widget,
373 GdkEventMotion *event)
374 {
375 return FALSE;
376 }
377
378
379 /*
380 * Standard Dw function.
381 */
382 static gboolean Dw_image_leave_notify (DwWidget *widget,
383 DwWidget *next_widget,
384 GdkEventMotion *event)
385 {
386 DwImage *image = DW_IMAGE (widget);
387 gboolean return_val = FALSE;
388
389 if (image->hover_link != -1) {
390 image->hover_link = -1;
391 gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_ENTERED],
392 -1, -1, -1, &return_val);
393 return return_val;
394 }
395
396 return FALSE;
397 }
398
399
400 /*
401 * Standard Dw function.
402 */
403 static gboolean Dw_image_button_press (DwWidget *widget,
404 gint32 x,
405 gint32 y,
406 GdkEventButton *event)
407 {
408 DwImage *image = DW_IMAGE (widget);
409 gint link_x, link_y;
410 gboolean return_val = FALSE;
411
412 Dw_image_find_link (image, x, y, &image->pressed_link, &link_x, &link_y);
413
414 if (image->pressed_link >= 0)
415 gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_PRESSED],
416 image->pressed_link, link_x, link_y, event,
417 &return_val);
418
419 return return_val;
420 }
421
422
423 /*
424 * Standard Dw function.
425 */
426 static gboolean Dw_image_button_release (DwWidget *widget,
427 gint32 x,
428 gint32 y,
429 GdkEventButton *event)
430 {
431 DwImage *image = DW_IMAGE (widget);
432 gint link_pressed, link_released, link_x, link_y;
433 gboolean return_val1 = FALSE, return_val2 = FALSE;
434
435 link_pressed = image->pressed_link;
436 Dw_image_find_link (image, x, y, &link_released, &link_x, &link_y);
437 image->pressed_link = -1;
438
439 if (link_released >= 0) {
440 gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_RELEASED],
441 link_released, link_x, link_y, event, &return_val1);
442 if (link_pressed == link_released) {
443 gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_CLICKED],
444 link_released, link_x, link_y, event, &return_val2);
445 return return_val1 || return_val2;
446 }
447
448 return return_val1;
449 }
450
451 return FALSE;
452 }
453
454
455 /*
456 * Standard Dw function.
457 */
458 static gboolean Dw_image_motion_notify (DwWidget *widget,
459 gint32 x, gint32 y,
460 GdkEventMotion *event)
461 {
462 DwImage *image = DW_IMAGE (widget);
463 gint link, link_old, link_x, link_y;
464 gboolean return_val = FALSE;
465
466 link_old = image->hover_link;
467 Dw_image_find_link (image, x, y, &link, &link_x, &link_y);
468 image->hover_link = link;
469
470 /* In case of a ISMAP image, the signal has to be emitted always */
471 if (link_x != -1 || link != link_old) {
472 gtk_signal_emit (GTK_OBJECT (widget), image_signals[LINK_ENTERED],
473 link, link_x, link_y, &return_val);
474 return return_val;
475 } else
476 return (link != -1);
477 }
478
479
480 /*
481 * Find a link given a coordinate location relative to the window.
482 */
483 static void Dw_image_find_link (DwImage *image,
484 gint x, gint y,
485 gint *link,
486 gint *link_x, gint *link_y)
487 {
488 DwStyle *style;
489 DwWidget *widget = DW_WIDGET (image);
490
491 *link_x = -1;
492 *link_y = -1;
493
494 if (image->map_list && image->usemap_url) {
495 *link = Dw_image_map_list_find_link
496 (image->map_list, image->usemap_url,
497 x - p_Dw_style_box_offset_x (widget->style),
498 y - p_Dw_style_box_offset_y (widget->style));
499 if (*link != -1)
500 return;
501 }
502
503 if (image->ismap &&
504 (style = DW_WIDGET(image)->style) && style->x_link != -1) {
505 *link = style->x_link;
506 *link_x = x - p_Dw_style_box_offset_x (widget->style);
507 *link_y = y - p_Dw_style_box_offset_y (widget->style);
508 return;
509 }
510
511 *link = -1;
512 }
513
514
515 /*
516 * Set or resize a image.
517 */
518 void a_Dw_image_size (DwImage *image, gint width, gint height)
519 {
520 gint Resize = (image->width != width || image->height != height);
521
522 image->width = width;
523 image->height = height;
524 if (Resize)
525 p_Dw_widget_queue_resize (DW_WIDGET (image), 0, TRUE);
526
527 if (image->buffer)
528 /* if a_Dw_image_set_buffer has been called before */
529 Dw_image_scale (image);
530 }
531
532 /*
533 * Called after the RGB line buffer has been copied into the full
534 * image buffer. Uses for drawing and scaling.
535 */
536 void a_Dw_image_draw_row (DwImage *image,
537 gint Width, gint Height, gint x, gint y)
538 {
539 DwWidget *widget = DW_WIDGET (image);
540 gint dy1, dy2;
541
542 _MSG("a_Dw_image_draw_row: x=%d y=%d\n", x, y);
543 g_return_if_fail (image->buffer != NULL);
544
545 if (image->scaled_buffer) {
546 Dw_image_scale_row (image, y);
547
548 dy1 = Dw_image_scaled_y (image, y);
549 dy2 = Dw_image_scaled_y (image, y + 1);
550 p_Dw_widget_queue_draw_area(widget,
551 x,
552 dy1 + p_Dw_style_box_offset_y(widget->style),
553 widget->allocation.width, dy2 - dy1);
554 } else
555 p_Dw_widget_queue_draw_area (widget,
556 x,
557 y + p_Dw_style_box_offset_y (widget->style),
558 widget->allocation.width, 1);
559 }
560
561 /*
562 * Set the widget buffer to reference the dicache entry buffer
563 */
564 void a_Dw_image_set_buffer (DwImage *image, guchar *ImageBuffer,
565 DilloUrl *url, gint version)
566 {
567 image->buffer = ImageBuffer;
568 image->url = url;
569 image->version = version;
570
571 if (image->width > 0 && image->height > 0)
572 /* if a_Dw_image_set_size has been called before */
573 Dw_image_scale (image);
574 }
575
576 /*
577 * Sets image as server side image map.
578 */
579 void a_Dw_image_set_ismap (DwImage *image)
580 {
581 image->ismap = TRUE;
582 }
583
584 /*
585 * Sets image as client side image map.
586 */
587 void a_Dw_image_set_usemap (DwImage *image, DwImageMapList *map_list,
588 DilloUrl *usemap_url)
589 {
590 image->map_list = map_list;
591
592 if (image->usemap_url)
593 a_Url_free (image->usemap_url);
594 image->usemap_url = usemap_url ? a_Url_dup (usemap_url) : NULL;
595 }
596
597
598 /*
599 * Scale the whole image: Compare buffer size with allocation, and, if
600 * necessary, allocate a second buffer and scale all rows.
601 */
602 static void Dw_image_scale (DwImage *image)
603 {
604 int w, h, y;
605 DwWidget *widget;
606
607 if (image->scaled_buffer) {
608 g_free (image->scaled_buffer);
609 image->scaled_buffer = NULL;
610 }
611
612 widget = DW_WIDGET (image);
613 w = DW_WIDGET_CONTENT_WIDTH(widget);
614 h = DW_WIDGET_CONTENT_HEIGHT(widget);
615
616 /* Zero or negative sizes? Ignore. */
617 if (w <= 0 || h <= 0)
618 return;
619
620 if (image->width != w || image->height != h) {
621 /* scaled image */
622 image->scaled_buffer = g_malloc ((gulong)3 * w * h);
623
624 for (y = 0; y < image->height; y++)
625 Dw_image_scale_row (image, y);
626 }
627 }
628
629
630 /*
631 * Scale one row. y_src is the row in the dicache buffer.
632 */
633 static void Dw_image_scale_row (DwImage *image, gint y_src)
634 {
635 DwWidget *widget;
636 guchar *src, *dest, *dest1;
637 gint w_src, w_dest, x_src, x_dest, y_dest1, y_dest2, y_dest, delta;
638
639 widget = DW_WIDGET (image);
640 w_src = image->width;
641 w_dest = DW_WIDGET_CONTENT_WIDTH(widget);
642 y_dest1 = Dw_image_scaled_y (image, y_src);
643 y_dest2 = Dw_image_scaled_y (image, y_src + 1);
644
645 src = image->buffer + 3 * y_src * w_src;
646
647 if (y_dest1 != y_dest2) {
648 dest1 = image->scaled_buffer + 3 * y_dest1 * w_dest;
649
650 if (w_src == w_dest)
651 memcpy (dest1, src, 3 * (size_t)w_src);
652 else if (w_dest > w_src) {
653 delta = w_src / 2;
654 x_src = 0;
655 x_dest = 0;
656
657 while (x_dest < w_dest) {
658 memcpy (dest1 + 3 * x_dest, src + 3 * x_src, 3);
659 x_dest++;
660 delta += w_src;
661 while (delta > w_dest) {
662 delta -= w_dest;
663 x_src++;
664 }
665 }
666 } else {
667 delta = w_dest / 2;
668 x_src = 0;
669 x_dest = 0;
670
671 while (x_src < w_src) {
672 memcpy (dest1 + 3 * x_dest, src + 3 * x_src, 3);
673 x_src++;
674 delta += w_dest;
675 while (delta > w_src) {
676 delta -= w_src;
677 x_dest++;
678 }
679 }
680 }
681
682 /* The other lines are simply copied. */
683 for (y_dest = y_dest1 + 1; y_dest < y_dest2; y_dest++) {
684 dest = image->scaled_buffer + 3 * y_dest * w_dest;
685 memcpy (dest, dest1, 3 * (size_t)w_dest);
686 }
687 }
688 }
689
690
691 /*
692 * Image Maps
693 */
694
695 /*
696 * Initialize a DwImageMapList. The memory has to be allocated before.
697 */
698 void a_Dw_image_map_list_init (DwImageMapList *list)
699 {
700 list->num_maps = 0;
701 list->num_maps_max = 8;
702 list->maps = g_new (DwImageMap, list->num_maps_max);
703
704 list->num_shapes = 0;
705 list->num_shapes_max = 8;
706 list->shapes = g_new (DwImageMapShape, list->num_shapes_max);
707 }
708
709
710 /*
711 * Free the content of a DwImageMapList. The memory for the list is
712 * not freed.
713 */
714 void a_Dw_image_map_list_free (DwImageMapList *list)
715 {
716 gint i;
717
718 for (i = 0; i < list->num_maps; i++)
719 a_Url_free (list->maps[i].url);
720 g_free (list->maps);
721
722 for (i = 0; i < list->num_shapes; i++)
723 if (list->shapes[i].type == DW_IMAGE_MAP_SHAPE_POLY)
724 gdk_region_destroy (list->shapes[i].data.poly);
725 g_free (list->shapes);
726 }
727
728
729 /*
730 * Add a new map to the list. Next added shapes will belong to this
731 * map.
732 */
733 void a_Dw_image_map_list_add_map (DwImageMapList *list,
734 DilloUrl *url)
735 {
736 list->num_maps++;
737 a_List_add (list->maps, list->num_maps, list->num_maps_max);
738 list->maps[list->num_maps - 1].url = a_Url_dup (url);
739 list->maps[list->num_maps - 1].start_shape = list->num_shapes;
740 }
741
742
743 void a_Dw_image_map_list_add_shape (DwImageMapList *list,
744 gint type,
745 gint link,
746 GdkPoint *points,
747 gint num_points)
748 {
749 gboolean correct_args;
750 DwImageMapShape *shape;
751
752 switch (type) {
753 case DW_IMAGE_MAP_SHAPE_RECT:
754 correct_args = (num_points >= 2);
755 break;
756
757 case DW_IMAGE_MAP_SHAPE_CIRCLE:
758 correct_args = (num_points >= 1 && points[1].x != 0);
759 break;
760
761 case DW_IMAGE_MAP_SHAPE_POLY:
762 correct_args = (num_points >= 3);
763 break;
764
765 default:
766 correct_args = FALSE;
767 }
768
769 if (correct_args) {
770 list->num_shapes++;
771 a_List_add (list->shapes, list->num_shapes, list->num_shapes_max);
772 shape = &list->shapes[list->num_shapes - 1];
773 shape->type = type;
774 shape->link = link;
775
776 switch (type) {
777 case DW_IMAGE_MAP_SHAPE_RECT:
778 shape->data.rect.left = points[0].x;
779 shape->data.rect.top = points[0].y;
780 shape->data.rect.right = points[1].x;
781 shape->data.rect.bottom = points[1].y;
782 break;
783
784 case DW_IMAGE_MAP_SHAPE_CIRCLE:
785 shape->data.circle.x = points[0].x;
786 shape->data.circle.y = points[0].y;
787 shape->data.circle.r2 = points[1].x * points[1].x;
788 break;
789
790 case DW_IMAGE_MAP_SHAPE_POLY:
791 shape->data.poly = gdk_region_polygon (points, num_points,
792 GDK_WINDING_RULE);
793 break;
794 }
795 }
796 }
797
798
799 /*
800 * Find a link in an image map.
801 */
802 static gint Dw_image_map_list_find_link (DwImageMapList *list,
803 DilloUrl *url,
804 gint x,
805 gint y)
806 {
807 gint i, j, start, end, dx, dy;
808 DwImageMapShape *shape;
809
810 for (i = 0; i < list->num_maps; i++) {
811 if (a_Url_cmp (list->maps[i].url, url) == 0 &&
812 URL_STRCAMP_EQ(URL_FRAGMENT_(list->maps[i].url),URL_FRAGMENT_(url))){
813 /* map found */
814 start = list->maps[i].start_shape;
815 if (i == list->num_maps - 1)
816 end = list->num_shapes;
817 else
818 end = list->maps[i + 1].start_shape;
819
820 for (j = start; j < end; j++) {
821 shape = &list->shapes[j];
822 switch (shape->type) {
823 case DW_IMAGE_MAP_SHAPE_CIRCLE:
824 dx = shape->data.circle.x - x;
825 dy = shape->data.circle.y - y;
826 if (shape->data.circle.r2 >= (dx*dx + dy*dy))
827 return shape->link;
828 break;
829
830 case DW_IMAGE_MAP_SHAPE_RECT:
831 if (x > shape->data.rect.left &&
832 x < shape->data.rect.right &&
833 y > shape->data.rect.top &&
834 y < shape->data.rect.bottom)
835 return shape->link;
836 break;
837
838 case DW_IMAGE_MAP_SHAPE_POLY:
839 if (gdk_region_point_in (shape->data.poly, x, y))
840 return shape->link;
841 break;
842 }
843 }
844
845 /* no shape found */
846 return -1;
847 }
848 }
849
850 /* no map found */
851 return -1;
852 }
853
854 static DwIterator *Dw_image_iterator (DwWidget *widget,
855 gint32 mask,
856 gboolean at_end)
857 {
858 DwIterator *it;
859 DwImage *image = DW_IMAGE (widget);
860
861 if (image->alt_text) {
862 it = p_Dw_widget_text_iterator (widget, mask, at_end, image->alt_text);
863 if (it)
864 it->highlight = Dw_image_iterator_highlight;
865 return it;
866 } else
867 return NULL;
868 }
869
870 static void Dw_image_iterator_highlight (DwIterator *it,
871 gint start,
872 gint end,
873 DwHighlightLayer layer)
874 {
875 if (it->content.type == DW_CONTENT_TEXT) {
876 /* The whole image is highlighted, as soon as something is selected at
877 * all. */
878 DW_IMAGE(it->widget)->selected[layer] = (start != -1 && start != end);
879 p_Dw_widget_queue_draw (it->widget);
880 }
881 }
+0
-153
src/dw_image.h less more
0 #ifndef __DW_IMAGE_H__
1 #define __DW_IMAGE_H__
2
3 #include <stdio.h>
4 #include "dw_widget.h"
5 #include "url.h" /* for DilloUrl */
6
7 #ifdef __cplusplus
8 extern "C" {
9 #endif /* __cplusplus */
10
11 #define DW_TYPE_IMAGE (a_Dw_image_get_type ())
12 #define DW_IMAGE(obj) GTK_CHECK_CAST (obj,DW_TYPE_IMAGE, DwImage)
13 #define DW_IMAGE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, DW_TYPE_IMAGE, \
14 DwImageClass)
15 #define DW_IS_IMAGE(obj) GTK_CHECK_TYPE (obj, DW_TYPE_IMAGE)
16
17 typedef enum {
18 DW_IMAGE_RGB
19 } DwImageType;
20
21 typedef struct _DwImage DwImage;
22 typedef struct _DwImageClass DwImageClass;
23
24 typedef struct _DwImageMap DwImageMap;
25 typedef struct _DwImageMapList DwImageMapList;
26 typedef struct _DwImageMapShape DwImageMapShape;
27
28
29 struct _DwImage
30 {
31 DwWidget widget;
32
33 DilloUrl *url;
34 gint version;
35 DwImageType type;
36 guchar *buffer;
37 gint width;
38 gint height;
39 gint alt_text_width;
40
41 /* non NULL if image is scaled */
42 guchar *scaled_buffer;
43
44 /* ALT text (for selection) */
45 gchar *alt_text;
46
47 DwImageMapList *map_list;
48 DilloUrl *usemap_url;
49 gboolean ismap;
50
51 gint hover_link;
52 gint pressed_link;
53 gboolean selected[DW_HIGHLIGHT_NUM_LAYERS];
54 };
55
56 struct _DwImageClass
57 {
58 DwWidgetClass parent_class;
59
60 gboolean (*link_entered) (DwImage *page,
61 gint link, gint x, gint y);
62 gboolean (*link_pressed) (DwImage *page,
63 gint link, gint x, gint y,
64 GdkEventButton *event);
65 gboolean (*link_released) (DwImage *page,
66 gint link, gint x, gint y,
67 GdkEventButton *event);
68 gboolean (*link_clicked) (DwImage *page,
69 gint link, gint x, gint y,
70 GdkEventButton *event);
71 };
72
73
74 /*
75 * Image Maps
76 */
77
78 #define DW_IMAGE_MAP_SHAPE_RECT 0
79 #define DW_IMAGE_MAP_SHAPE_CIRCLE 1
80 #define DW_IMAGE_MAP_SHAPE_POLY 2
81
82 struct _DwImageMapList
83 {
84 DwImageMap *maps;
85 gint num_maps;
86 gint num_maps_max;
87
88 DwImageMapShape *shapes;
89 gint num_shapes;
90 gint num_shapes_max;
91 };
92
93 struct _DwImageMap
94 {
95 DilloUrl *url;
96 gint start_shape;
97 };
98
99 struct _DwImageMapShape
100 {
101 gint type;
102 gint link;
103
104 union {
105 GdkRegion *poly;
106 struct {
107 gint32 x;
108 gint32 y;
109 gint32 r2;
110 } circle;
111 struct {
112 gint32 top;
113 gint32 bottom;
114 gint32 left;
115 gint32 right;
116 } rect;
117 } data;
118 };
119
120
121 /*
122 * Function prototypes
123 */
124 GtkType a_Dw_image_get_type (void);
125 DwWidget* a_Dw_image_new (DwImageType type, const gchar *alt_text);
126 void a_Dw_image_size (DwImage *image, gint width, gint height);
127 void a_Dw_image_draw_row(DwImage *image,
128 gint Width, gint Height, gint x, gint y);
129 void a_Dw_image_set_buffer(DwImage *image, guchar *ImageBuffer,
130 DilloUrl *url, gint version);
131
132 void a_Dw_image_set_ismap (DwImage *image);
133 void a_Dw_image_set_usemap (DwImage *image, DwImageMapList *map_list,
134 DilloUrl *usemap_url);
135
136 /* Image maps */
137 void a_Dw_image_map_list_init (DwImageMapList *list);
138 void a_Dw_image_map_list_free (DwImageMapList *list);
139
140 void a_Dw_image_map_list_add_map (DwImageMapList *list,
141 DilloUrl *url);
142 void a_Dw_image_map_list_add_shape (DwImageMapList *list,
143 gint type,
144 gint link,
145 GdkPoint *points,
146 gint num_points);
147
148 #ifdef __cplusplus
149 }
150 #endif /* __cplusplus */
151
152 #endif /* __DW_IMAGE_H__ */
+0
-149
src/dw_list_item.c less more
0 /*
1 * File: dw_aligned_page.c
2 *
3 * Copyright (C) 2002 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "dw_list_item.h"
12 #include "debug.h"
13
14 static void Dw_list_item_class_init (DwListItemClass *klass);
15 static void Dw_list_item_init (DwListItem *list_item);
16
17 static gint32 Dw_list_item_get_value (DwAlignedPage *aligned_page);
18 static void Dw_list_item_set_max_value (DwAlignedPage *aligned_page,
19 gint32 max_value,
20 gint32 value);
21
22 /*
23 * Standard Gtk+ function.
24 */
25 GtkType a_Dw_list_item_get_type (void)
26 {
27 static GtkType type = 0;
28
29 if (!type) {
30 GtkTypeInfo info = {
31 "DwListItem",
32 sizeof (DwListItem),
33 sizeof (DwListItemClass),
34 (GtkClassInitFunc) Dw_list_item_class_init,
35 (GtkObjectInitFunc) Dw_list_item_init,
36 (GtkArgSetFunc) NULL,
37 (GtkArgGetFunc) NULL,
38 (GtkClassInitFunc) NULL
39 };
40
41 type = gtk_type_unique (DW_TYPE_ALIGNED_PAGE, &info);
42 }
43
44 return type;
45 }
46
47
48 /*
49 * Standard Gtk+ function: Create a new list item.
50 * ref_list_item is either another item in the same list, or NULL for
51 * the first item in the list.
52 */
53 DwWidget* a_Dw_list_item_new (DwListItem *ref_list_item)
54 {
55 GtkObject *object;
56
57 object = gtk_object_new (DW_TYPE_LIST_ITEM, NULL);
58 DBG_OBJ_CREATE (object, "DwListItem");
59 p_Dw_aligned_page_set_ref_page (DW_ALIGNED_PAGE (object),
60 (DwAlignedPage*)ref_list_item);
61 return DW_WIDGET (object);
62 }
63
64
65 /*
66 * Standard Gtk+ function.
67 */
68 static void Dw_list_item_class_init (DwListItemClass *klass)
69 {
70 DwAlignedPageClass *aligned_page_class = DW_ALIGNED_PAGE_CLASS (klass);
71
72 aligned_page_class->get_value = Dw_list_item_get_value;
73 aligned_page_class->set_max_value = Dw_list_item_set_max_value;
74 }
75
76
77 /*
78 * Standard Gtk+ function.
79 */
80 static void Dw_list_item_init (DwListItem *list_item)
81 {
82 DW_PAGE(list_item)->list_item = TRUE;
83 }
84
85
86 /*
87 * Implementation of DwAlignedPage::get_value.
88 */
89 static gint32 Dw_list_item_get_value (DwAlignedPage *aligned_page)
90 {
91 DwPage *page = DW_PAGE (aligned_page);
92
93 if (page->num_words == 0)
94 return 0;
95 else
96 return page->words[0].size.width + page->words[0].orig_space;
97 }
98
99
100 /*
101 * Implementation of DwAlignedPage::set_max_value.
102 */
103 static void Dw_list_item_set_max_value (DwAlignedPage *aligned_page,
104 gint32 max_value,
105 gint32 value)
106 {
107 DwPage *page = DW_PAGE (aligned_page);
108
109 page->inner_padding = max_value;
110 page->line1_offset = - value;
111 p_Dw_widget_queue_resize (DW_WIDGET (aligned_page), 0, TRUE);
112 }
113
114
115 /*
116 * This function sets a widget as the list item marker (typically
117 * DwBullet).
118 * (Note: Currently, the following order should be used:
119 * 1. a_Dw_list_item_new,
120 * 2. add the list item to the parent widget, and
121 * 3. a_Dw_list_item_init_with_widget or a_Dw_list_item_init_with_text.)
122 */
123 void a_Dw_list_item_init_with_widget (DwListItem *list_item,
124 DwWidget *widget,
125 DwStyle *style)
126 {
127 DwPage *page = DW_PAGE (list_item);
128
129 a_Dw_page_add_widget (page, widget, style);
130 a_Dw_page_add_space (page, style);
131 p_Dw_aligned_page_update_value (DW_ALIGNED_PAGE (list_item));
132 }
133
134
135 /*
136 * This function sets a text word as the list item marker (typically a
137 * number).
138 */
139 void a_Dw_list_item_init_with_text (DwListItem *list_item,
140 gchar *text,
141 DwStyle *style)
142 {
143 DwPage *page = DW_PAGE (list_item);
144
145 a_Dw_page_add_text (page, text, style);
146 a_Dw_page_add_space (page, style);
147 p_Dw_aligned_page_update_value (DW_ALIGNED_PAGE (list_item));
148 }
+0
-45
src/dw_list_item.h less more
0 #ifndef __DW_LIST_ITEM_H__
1 #define __DW_LIST_ITEM_H__
2
3 #include "dw_aligned_page.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_LIST_ITEM (a_Dw_list_item_get_type ())
10 #define DW_LIST_ITEM(obj) GTK_CHECK_CAST (obj,DW_TYPE_LIST_ITEM, \
11 DwListItem)
12 #define DW_LIST_ITEM_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, \
13 DW_TYPE_LIST_ITEM, DwListItemClass)
14 #define DW_IS_LIST_ITEM(obj) GTK_CHECK_TYPE (obj, DW_TYPE_LIST_ITEM)
15
16
17 typedef struct _DwListItem DwListItem;
18 typedef struct _DwListItemClass DwListItemClass;
19
20 struct _DwListItem
21 {
22 DwAlignedPage aligned_page;
23 };
24
25 struct _DwListItemClass
26 {
27 DwAlignedPageClass parent_class;
28 };
29
30
31 GtkType a_Dw_list_item_get_type (void);
32 DwWidget* a_Dw_list_item_new (DwListItem *ref_list_item);
33 void a_Dw_list_item_init_with_widget (DwListItem *list_item,
34 DwWidget *widget,
35 DwStyle *style);
36 void a_Dw_list_item_init_with_text (DwListItem *list_item,
37 gchar *text,
38 DwStyle *style);
39
40 #ifdef __cplusplus
41 }
42 #endif /* __cplusplus */
43
44 #endif /* __DW_LIST_ITEM_H__ */
+0
-77
src/dw_marshal.c less more
0 #include "dw_marshal.h"
1
2
3 typedef gboolean (*GtkSignal_BOOL__INT_INT_INT) (GtkObject *object,
4 gint arg1, gint arg2,
5 gint arg3,
6 gpointer user_data);
7
8 void p_Dw_marshal_BOOL__INT_INT_INT (GtkObject *object,
9 GtkSignalFunc Func,
10 gpointer FuncData, GtkArg *args)
11 {
12 GtkSignal_BOOL__INT_INT_INT rfunc;
13 gboolean *return_val;
14 return_val = GTK_RETLOC_BOOL (args[3]);
15 rfunc = (GtkSignal_BOOL__INT_INT_INT) Func;
16 *return_val = rfunc (object,
17 GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]),
18 GTK_VALUE_INT (args[2]), FuncData);
19 }
20
21
22 typedef gboolean (*GtkSignal_BOOL__INT_INT_INT_POINTER) (GtkObject *object,
23 gint arg1, gint arg2,
24 gint arg3,
25 gpointer arg4,
26 gpointer user_data);
27
28 void p_Dw_marshal_BOOL__INT_INT_INT_POINTER (GtkObject *object,
29 GtkSignalFunc Func,
30 gpointer FuncData, GtkArg *args)
31 {
32 GtkSignal_BOOL__INT_INT_INT_POINTER rfunc;
33 gboolean *return_val;
34 return_val = GTK_RETLOC_BOOL (args[4]);
35 rfunc = (GtkSignal_BOOL__INT_INT_INT_POINTER) Func;
36 *return_val = rfunc (object,
37 GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]),
38 GTK_VALUE_INT (args[2]), GTK_VALUE_POINTER (args[3]),
39 FuncData);
40 }
41
42 typedef gboolean (*GtkSignal_BOOL__INT_INT_POINTER) (GtkObject *object,
43 gint arg1, gint arg2,
44 gpointer arg3,
45 gpointer user_data);
46
47 void p_Dw_marshal_BOOL__INT_INT_POINTER (GtkObject *object,
48 GtkSignalFunc Func,
49 gpointer FuncData, GtkArg *args)
50 {
51 GtkSignal_BOOL__INT_INT_POINTER rfunc;
52 gboolean *return_val;
53 return_val = GTK_RETLOC_BOOL (args[3]);
54 rfunc = (GtkSignal_BOOL__INT_INT_POINTER) Func;
55 *return_val = rfunc (object,
56 GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]),
57 GTK_VALUE_POINTER (args[2]), FuncData);
58 }
59
60 typedef gboolean (*GtkSignal_BOOL__POINTER_POINTER) (GtkObject *object,
61 gpointer arg1,
62 gpointer arg2,
63 gpointer user_data);
64
65 void p_Dw_marshal_BOOL__POINTER_POINTER (GtkObject *object,
66 GtkSignalFunc Func,
67 gpointer FuncData, GtkArg *args)
68 {
69 GtkSignal_BOOL__POINTER_POINTER rfunc;
70 gboolean *return_val;
71 return_val = GTK_RETLOC_BOOL (args[2]);
72 rfunc = (GtkSignal_BOOL__POINTER_POINTER) Func;
73 *return_val = rfunc (object,
74 GTK_VALUE_POINTER (args[0]),
75 GTK_VALUE_POINTER (args[1]), FuncData);
76 }
+0
-27
src/dw_marshal.h less more
0 #ifndef __DW_MARSHAL_H__
1 #define __DW_MARSHAL_H__
2
3 #include <gtk/gtktypeutils.h>
4 #include <gtk/gtkobject.h>
5
6 void p_Dw_marshal_BOOL__INT_INT_INT (GtkObject *object,
7 GtkSignalFunc Func,
8 gpointer FuncData, GtkArg *args);
9 void p_Dw_marshal_BOOL__INT_INT_INT_POINTER (GtkObject *object,
10 GtkSignalFunc Func,
11 gpointer FuncData, GtkArg *args);
12 void p_Dw_marshal_BOOL__INT_INT_POINTER (GtkObject *object,
13 GtkSignalFunc Func,
14 gpointer FuncData, GtkArg *args);
15 void p_Dw_marshal_BOOL__POINTER_POINTER (GtkObject *object,
16 GtkSignalFunc Func,
17 gpointer FuncData, GtkArg *args);
18
19 /*
20 * Marshal fuctions for standard link signals.
21 */
22 #define p_Dw_marshal_link_enter p_Dw_marshal_BOOL__INT_INT_INT
23 #define p_Dw_marshal_link_button p_Dw_marshal_BOOL__INT_INT_INT_POINTER
24
25
26 #endif /* __DW_MARSHAL_H__ */
+0
-2544
src/dw_page.c less more
0 /*
1 * File: dw_page.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 Luca Rota <drake@freemail.it>
5 * Copyright (C) 2001-2003 Sebastian Geerken <s.geerken@ping.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 /*
14 * This module contains the Dw_page widget, which is the "back end" to
15 * Web text widgets including html.
16 */
17
18 #include <ctype.h>
19 #include <string.h>
20 #include <stdio.h>
21
22 #include <gtk/gtk.h>
23
24 #include "msg.h"
25 #include "list.h"
26 #include "dw_page.h"
27 #include "dw_marshal.h"
28 #include "dw_gtk_viewport.h"
29
30 #include "prefs.h"
31
32 #define DEBUG_REWRAP_LEVEL 0
33 #define DEBUG_SIZE_LEVEL 10
34 #define DEBUG_EVENT_LEVEL 0
35 #define DEBUG_ITERATOR_LEVEL 0
36
37 /*#define DEBUG_LEVEL 10*/
38 #include "debug.h"
39
40 #define MAX3(a, b, c) (MAX (a, MAX (b, c)))
41
42 static void Dw_page_init (DwPage *page);
43 static void Dw_page_class_init (DwPageClass *klass);
44
45 static void Dw_page_destroy (GtkObject *object);
46
47 static void Dw_page_size_request (DwWidget *widget,
48 DwRequisition *requisition);
49 static void Dw_page_get_extremes (DwWidget *widget,
50 DwExtremes *extremes);
51 static void Dw_page_size_allocate (DwWidget *widget,
52 DwAllocation *allocation);
53 static void Dw_page_mark_change (DwWidget *widget,
54 gint ref);
55 static void Dw_page_set_width (DwWidget *widget,
56 gint32 width);
57 static void Dw_page_set_ascent (DwWidget *widget,
58 gint32 ascent);
59 static void Dw_page_set_descent (DwWidget *widget,
60 gint32 descent);
61 static void Dw_page_draw (DwWidget *page,
62 DwRectangle *area,
63 GdkEventExpose *event);
64 static gboolean Dw_page_button_press (DwWidget *widget,
65 gint32 x,
66 gint32 y,
67 GdkEventButton *event);
68 static gboolean Dw_page_button_release(DwWidget *widget,
69 gint32 x,
70 gint32 y,
71 GdkEventButton *event);
72 static gboolean Dw_page_motion_notify (DwWidget *widget,
73 gint32 x,
74 gint32 y,
75 GdkEventMotion *event);
76 static gboolean Dw_page_leave_notify (DwWidget *widget,
77 DwWidget *next_widget,
78 GdkEventMotion *event);
79
80
81 static void Dw_page_add (DwContainer *container,
82 DwWidget *widget);
83 static void Dw_page_remove (DwContainer *container,
84 DwWidget *widget);
85 static void Dw_page_forall (DwContainer *container,
86 DwCallback callback,
87 gpointer callback_data);
88 static void Dw_page_real_word_wrap (DwPage *page,
89 gint word_ind);
90
91 static DwIterator* Dw_page_iterator (DwWidget *widget,
92 gint mask,
93 gboolean at_end);
94 static gboolean Dw_page_iterator_next (DwIterator *it);
95 static gboolean Dw_page_iterator_prev (DwIterator *it);
96 static void Dw_page_iterator_highlight (DwIterator *it,
97 gint start,
98 gint end,
99 DwHighlightLayer layer);
100 static void Dw_page_iterator_get_allocation (DwIterator *it,
101 gint start,
102 gint end,
103 DwAllocation *allocation);
104
105 static void Dw_page_rewrap (DwPage *page);
106
107 /*
108 * Returns the x offset (the indentation plus any offset needed for
109 * centering or right justification) for the line. The offset returned
110 * is relative to the page *content* (i.e. without border etc.).
111 */
112 #define Dw_page_line_x_offset(page, line) \
113 ( (page)->inner_padding + (line)->left_offset + \
114 ((line) == (page)->lines ? (page)->line1_offset_eff : 0) )
115
116 /*
117 * Like Dw_style_box_offset_x, but relative to the allocation (i.e.
118 * including border etc.).
119 */
120 #define Dw_page_line_total_x_offset(page, line) \
121 ( Dw_page_line_x_offset (page, line) + \
122 p_Dw_style_box_offset_x (((DwWidget*)(page))->style) )
123
124 /*
125 * Returns the y offset (within the widget) of a line.
126 */
127 #define Dw_page_line_total_y_offset(page, line) \
128 Dw_page_line_total_y_offset_allocation(page, line, \
129 &((DwWidget*)(page))->allocation)
130
131 /*
132 * Like Dw_page_line_total_y_offset, but with the allocation as parameter.
133 */
134 #define Dw_page_line_total_y_offset_allocation(page, line, allocation) \
135 ( (allocation)->ascent - (page)->lines[0].ascent + (line)->top )
136
137 /*
138 * Similar to Dw_page_line_total_y_offset.
139 */
140 #define Dw_page_line_total_y_offset_i(page, line_index) \
141 Dw_page_line_total_y_offset(page, &(page)->lines[line_index])
142
143 #define Dw_page_word_wrap(page, word_ind) \
144 (((DwPageClass*)(((GtkObject*)(page))->klass))->word_wrap (page, word_ind))
145
146 enum
147 {
148 LINK_ENTERED,
149 LINK_PRESSED,
150 LINK_RELEASED,
151 LINK_CLICKED,
152 LAST_SIGNAL
153 };
154
155 static DwContainerClass *parent_class;
156 static guint page_signals[LAST_SIGNAL] = { 0 };
157
158
159 /*
160 * Return the type of DwPage
161 */
162 GtkType a_Dw_page_get_type (void)
163 {
164 static GtkType type = 0;
165
166 if (!type) {
167 GtkTypeInfo info = {
168 "DwPage",
169 sizeof (DwPage),
170 sizeof (DwPageClass),
171 (GtkClassInitFunc) Dw_page_class_init,
172 (GtkObjectInitFunc) Dw_page_init,
173 (GtkArgSetFunc) NULL,
174 (GtkArgGetFunc) NULL,
175 (GtkClassInitFunc) NULL
176 };
177
178 type = gtk_type_unique (DW_TYPE_CONTAINER, &info);
179 }
180
181 return type;
182 }
183
184
185 /*
186 * Create a new DwPage
187 */
188 DwWidget* a_Dw_page_new (void)
189 {
190 GtkObject *object;
191
192 object = gtk_object_new (DW_TYPE_PAGE, NULL);
193 DBG_OBJ_CREATE (object, "DwPage");
194 return DW_WIDGET (object);
195 }
196
197
198 /*
199 * Initialize a DwPage
200 */
201 static void Dw_page_init (DwPage *page)
202 {
203 DW_WIDGET_SET_FLAGS (page, DW_USES_HINTS);
204
205 page->list_item = FALSE;
206 page->inner_padding = 0;
207 page->line1_offset = 0;
208 page->line1_offset_eff = 0;
209 page->ignore_line1_offset_sometimes = FALSE;
210
211 /*
212 * The initial sizes of page->lines and page->words should not be
213 * too high, since this will waste much memory with tables
214 * containing many small cells. The few more calls to realloc
215 * should not decrease the speed considerably.
216 * (Current setting is for minimal memory usage. An interesting fact
217 * is that high values decrease speed due to memory handling overhead!)
218 * todo: Some tests would be useful.
219 */
220 page->num_lines_max = 1; /* 2 */
221 page->num_lines = 0;
222 page->lines = NULL; /* g_new(DwPageLine, page->num_lines_max); */
223
224 DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
225
226 page->num_words_max = 1; /* 8 */
227 page->num_words = 0;
228 page->words = NULL; /* g_new(DwPageWord, page->num_words_max); */
229
230 page->last_line_width = 0;
231 page->last_line_par_min = 0;
232 page->last_line_par_max = 0;
233 page->wrap_ref = -1;
234
235 DBG_OBJ_SET_NUM(page, "last_line_width", page->last_line_width);
236 DBG_OBJ_SET_NUM(page, "last_line_par_min", page->last_line_par_min);
237 DBG_OBJ_SET_NUM(page, "last_line_par_max", page->last_line_par_max);
238 DBG_OBJ_SET_NUM(page, "wrap_ref", page->wrap_ref);
239
240 page->hover_link = -1;
241
242 /* random values */
243 page->avail_width = 100;
244 page->avail_ascent = 100;
245 page->avail_descent = 0;
246
247 page->hover_tooltip = NULL;
248 }
249
250 /*
251 * Initialize the DwPage's class
252 */
253 static void Dw_page_class_init (DwPageClass *klass)
254 {
255 GtkObjectClass *object_class;
256 DwWidgetClass *widget_class;
257 DwContainerClass *container_class;
258
259 object_class = (GtkObjectClass*) klass;
260 widget_class = (DwWidgetClass*) klass;
261 container_class = (DwContainerClass*) klass;
262 parent_class = gtk_type_class (DW_TYPE_CONTAINER);
263
264 page_signals[LINK_ENTERED] =
265 gtk_signal_new ("link_entered",
266 GTK_RUN_LAST,
267 object_class->type,
268 GTK_SIGNAL_OFFSET (DwPageClass, link_entered),
269 p_Dw_marshal_link_enter,
270 GTK_TYPE_BOOL,
271 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT);
272 page_signals[LINK_PRESSED] =
273 gtk_signal_new ("link_pressed",
274 GTK_RUN_LAST,
275 object_class->type,
276 GTK_SIGNAL_OFFSET (DwPageClass, link_pressed),
277 p_Dw_marshal_link_button,
278 GTK_TYPE_BOOL,
279 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
280 GTK_TYPE_GDK_EVENT);
281 page_signals[LINK_RELEASED] =
282 gtk_signal_new ("link_released",
283 GTK_RUN_LAST,
284 object_class->type,
285 GTK_SIGNAL_OFFSET (DwPageClass, link_released),
286 p_Dw_marshal_link_button,
287 GTK_TYPE_BOOL,
288 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
289 GTK_TYPE_GDK_EVENT);
290 page_signals[LINK_CLICKED] =
291 gtk_signal_new ("link_clicked",
292 GTK_RUN_LAST,
293 object_class->type,
294 GTK_SIGNAL_OFFSET (DwPageClass, link_clicked),
295 p_Dw_marshal_link_button,
296 GTK_TYPE_BOOL,
297 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT,
298 GTK_TYPE_GDK_EVENT);
299 gtk_object_class_add_signals (object_class, page_signals, LAST_SIGNAL);
300
301 object_class->destroy = Dw_page_destroy;
302
303 widget_class->size_request = Dw_page_size_request;
304 widget_class->get_extremes = Dw_page_get_extremes;
305 widget_class->size_allocate = Dw_page_size_allocate;
306 widget_class->mark_size_change = Dw_page_mark_change;
307 widget_class->mark_extremes_change = Dw_page_mark_change;
308 widget_class->set_width = Dw_page_set_width;
309 widget_class->set_ascent = Dw_page_set_ascent;
310 widget_class->set_descent = Dw_page_set_descent;
311 widget_class->draw = Dw_page_draw;
312 widget_class->button_press_event = Dw_page_button_press;
313 widget_class->button_release_event = Dw_page_button_release;
314 widget_class->motion_notify_event = Dw_page_motion_notify;
315 widget_class->leave_notify_event = Dw_page_leave_notify;
316 widget_class->iterator = Dw_page_iterator;
317
318 container_class->add = Dw_page_add;
319 container_class->remove = Dw_page_remove;
320 container_class->forall = Dw_page_forall;
321
322 klass->link_entered = NULL;
323 klass->link_pressed = NULL;
324 klass->link_released = NULL;
325 klass->link_clicked = NULL;
326 klass->word_wrap = Dw_page_real_word_wrap;
327 }
328
329 /*
330 * Destroy the page (standard Gtk+ function)
331 */
332 static void Dw_page_destroy (GtkObject *object)
333 {
334 DwWidget *widget = DW_WIDGET (object);
335 DwPage *page = DW_PAGE (object);
336 DwPageWord *word;
337 gint i;
338
339 _MSG ("Dw_page_destroy\n");
340
341 for (i = 0; i < page->num_words; i++) {
342 word = &page->words[i];
343 if (word->content.type == DW_CONTENT_WIDGET)
344 gtk_object_unref (GTK_OBJECT(word->content.data.widget));
345 else if (word->content.type == DW_CONTENT_TEXT)
346 g_free (word->content.data.text);
347 else if (word->content.type == DW_CONTENT_ANCHOR)
348 /* This also frees the names (see p_Dw_gtk_viewport_remove_anchor()
349 * and related). */
350 p_Dw_gtk_viewport_remove_anchor(widget, word->content.data.anchor);
351
352 a_Dw_style_unref (word->style);
353 a_Dw_style_unref (word->space_style);
354 }
355
356 g_free (page->lines);
357 g_free (page->words);
358
359 /* Make sure we don't own widgets anymore. Necessary before call of
360 parent_class->destroy. */
361 page->num_words = 0;
362 page->num_lines = 0;
363
364 DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
365
366 GTK_OBJECT_CLASS(parent_class)->destroy (object);
367 }
368
369
370 /*
371 * Standard Dw function
372 *
373 * The ascent of a page is the ascent of the first line, plus
374 * padding/border/margin. This can be used to align the first lines
375 * of several pages in a horizontal line.
376 */
377 static void Dw_page_size_request (DwWidget *widget,
378 DwRequisition *requisition)
379 {
380 DwPage *page = DW_PAGE (widget);
381 DwPageLine *last_line;
382
383 Dw_page_rewrap (page);
384
385 if (page->num_lines > 0) {
386 last_line = &page->lines[page->num_lines - 1];
387 requisition->width =
388 MAX (last_line->max_line_width, page->last_line_width);
389 /* Note: the break_space of the last line is ignored, so breaks
390 at the end of a page are not visible. */
391 requisition->ascent = page->lines[0].ascent;
392 requisition->descent = last_line->top
393 + last_line->ascent + last_line->descent - page->lines[0].ascent;
394 } else {
395 requisition->width = page->last_line_width;
396 requisition->ascent = 0;
397 requisition->descent = 0;
398 }
399
400 requisition->width +=
401 page->inner_padding + p_Dw_style_box_diff_width (widget->style);
402 requisition->ascent += p_Dw_style_box_offset_y (widget->style);
403 requisition->descent += p_Dw_style_box_rest_height (widget->style);
404
405 if (requisition->width < page->avail_width)
406 requisition->width = page->avail_width;
407 }
408
409
410 /*
411 * Get the extremes of a word within a page.
412 */
413 static void Dw_page_get_word_extremes (DwPageWord *word,
414 DwExtremes *extremes)
415 {
416 if (word->content.type == DW_CONTENT_WIDGET) {
417 if (DW_WIDGET_USES_HINTS (word->content.data.widget))
418 p_Dw_widget_get_extremes (word->content.data.widget, extremes);
419 else {
420 if (DW_STYLE_IS_PER_LENGTH(word->content.data.widget->style->width)) {
421 extremes->min_width = 0;
422 if (DW_WIDGET_HAS_CONTENT (word->content.data.widget))
423 extremes->max_width = DW_INFINITY;
424 else
425 extremes->max_width = 0;
426 } else if (DW_STYLE_IS_ABS_LENGTH
427 (word->content.data.widget->style->width)) {
428 /* Fixed lengths are only applied to the content, so we have to
429 * add padding, border and margin. */
430 extremes->min_width = extremes->max_width =
431 DW_STYLE_ABS_LENGTH_VAL(word->content.data.widget->style->width)
432 + p_Dw_style_box_diff_width(word->style);
433 } else
434 p_Dw_widget_get_extremes (word->content.data.widget, extremes);
435 }
436 } else {
437 extremes->min_width = word->size.width;
438 extremes->max_width = word->size.width;
439 }
440 }
441
442 /*
443 * Standard Dw function
444 */
445 static void Dw_page_get_extremes (DwWidget *widget,
446 DwExtremes *extremes)
447 {
448 DwPage *page = DW_PAGE (widget);
449 DwExtremes word_extremes;
450 DwPageLine *line;
451 DwPageWord *word;
452 gint word_index, line_index;
453 gint32 par_min, par_max;
454 gboolean nowrap;
455
456 DBG_MSG (widget, "extremes", 0, "Dw_page_get_extremes");
457 DBG_MSG_START (widget);
458
459 if (page->num_lines == 0) {
460 /* empty page */
461 extremes->min_width = 0;
462 extremes->max_width = 0;
463 } else if (page->wrap_ref == -1) {
464 /* no rewrap necessary -> values in lines are up to date */
465 line = &page->lines[page->num_lines - 1];
466 /* Historical note: The former distinction between lines with and without
467 * words[first_word]->nowrap set is no longer necessary, since
468 * Dw_page_real_word_wrap sets max_word_min to the correct value in any
469 * case. */
470 extremes->min_width = line->max_word_min;
471 extremes->max_width = MAX (line->max_par_max, page->last_line_par_max);
472 DBG_MSG (widget, "extremes", 0, "simple case");
473 } else {
474 /* Calculate the extremes, based on the values in the line from
475 where a rewrap is necessary. */
476 DBG_MSG (widget, "extremes", 0, "complex case");
477
478 if (page->wrap_ref == 0) {
479 extremes->min_width = 0;
480 extremes->max_width = 0;
481 par_min = 0;
482 par_max = 0;
483 } else {
484 line = &page->lines[page->wrap_ref];
485 extremes->min_width = line->max_word_min;
486 extremes->max_width = line->max_par_max;
487 par_min = line->par_min;
488 par_max = line->par_max;
489
490 DBG_MSGF (widget, "extremes", 0, "par_min = %d", par_min);
491 }
492
493 _MSG ("*** par_min = %d\n", par_min);
494
495 for (line_index = page->wrap_ref; line_index < page->num_lines;
496 line_index++) {
497
498 DBG_MSGF (widget, "extremes", 0, "line %d", line_index);
499 DBG_MSG_START (widget);
500
501 line = &page->lines[line_index];
502 nowrap = page->words[line->first_word].style->white_space
503 != DW_STYLE_WHITE_SPACE_NORMAL;
504
505 DEBUG_MSG (DEBUG_SIZE_LEVEL, " line %d (of %d), nowrap = %d\n",
506 line_index, page->num_lines, nowrap);
507
508 for (word_index = line->first_word; word_index < line->last_word;
509 word_index++) {
510 word = &page->words[word_index];
511 Dw_page_get_word_extremes (word, &word_extremes);
512
513 /* For the first word, we simply add the line1_offset. */
514 if (!page->ignore_line1_offset_sometimes && word_index == 0) {
515 word_extremes.min_width += page->line1_offset;
516 DEBUG_MSG (DEBUG_SIZE_LEVEL + 1,
517 " (next plus %d)\n", page->line1_offset);
518 }
519
520 if (nowrap) {
521 par_min += word_extremes.min_width + word->orig_space;
522 DBG_MSGF (widget, "extremes", 0, "par_min = %d", par_min);
523 } else
524 if (extremes->min_width < word_extremes.min_width)
525 extremes->min_width = word_extremes.min_width;
526
527 par_max += word_extremes.max_width + word->orig_space;
528
529 DEBUG_MSG (DEBUG_SIZE_LEVEL + 1,
530 " word %s: max_width = %d\n",
531 a_Dw_content_text (&word->content),
532 word_extremes.max_width);
533 }
534
535 if ( ( line->last_word > line->first_word &&
536 page->words[line->last_word - 1].content.type
537 == DW_CONTENT_BREAK ) ||
538 line_index == page->num_lines - 1 ) {
539 word = &page->words[line->last_word - 1];
540 par_max -= word->orig_space;
541
542 DEBUG_MSG (DEBUG_SIZE_LEVEL + 2,
543 " par_max = %d, after word %d (%s)\n",
544 par_max, line->last_word - 1,
545 a_Dw_content_text (&word->content));
546
547 if (extremes->max_width < par_max)
548 extremes->max_width = par_max;
549
550 if (nowrap) {
551 par_min -= word->orig_space;
552 DBG_MSGF (widget, "extremes", 0, "par_min = %d", par_min);
553 if (extremes->min_width < par_min)
554 extremes->min_width = par_min;
555
556 DEBUG_MSG (DEBUG_SIZE_LEVEL + 2,
557 " par_min = %d, after word %d (%s)\n",
558 par_min, line->last_word - 1,
559 a_Dw_content_text (&word->content));
560 }
561
562 par_min = 0;
563 par_max = 0;
564 }
565
566 DBG_MSG_END (widget);
567 }
568
569 DEBUG_MSG (DEBUG_SIZE_LEVEL + 3, " Result: %d, %d\n",
570 extremes->min_width, extremes->max_width);
571 }
572
573 DBG_MSGF (widget, "extremes", 0, "width difference: %d + %d",
574 page->inner_padding, p_Dw_style_box_diff_width (widget->style));
575
576 extremes->min_width +=
577 page->inner_padding + p_Dw_style_box_diff_width (widget->style);
578 extremes->max_width +=
579 page->inner_padding + p_Dw_style_box_diff_width (widget->style);
580
581 DBG_MSG_END (widget);
582 }
583
584
585 /*
586 * Standard Dw function
587 */
588 static void Dw_page_size_allocate (DwWidget *widget,
589 DwAllocation *allocation)
590 {
591 DwPage *page;
592 int line_index, word_index;
593 DwPageLine *line;
594 DwPageWord *word;
595 int x_cursor;
596 DwAllocation child_allocation;
597
598 page = DW_PAGE (widget);
599
600 for (line_index = 0; line_index < page->num_lines; line_index++) {
601 line = &(page->lines[line_index]);
602 x_cursor = Dw_page_line_total_x_offset (page, line);
603
604 for (word_index = line->first_word; word_index < line->last_word;
605 word_index++) {
606 word = &(page->words[word_index]);
607
608 switch (word->content.type) {
609 case DW_CONTENT_WIDGET:
610 /* todo: justification within the line is done here! */
611 child_allocation.x = x_cursor + allocation->x;
612 /* align=top:
613 child_allocation.y = line->top + allocation->y;
614 */
615 /* align=bottom (base line) */
616 child_allocation.y = allocation->y +
617 Dw_page_line_total_y_offset_allocation (page, line, allocation)
618 + (line->ascent - word->size.ascent)
619 - word->content.data.widget->style->margin.top;
620 child_allocation.width = word->size.width;
621 child_allocation.ascent = word->size.ascent +
622 word->content.data.widget->style->margin.top;
623 child_allocation.descent = word->size.descent +
624 word->content.data.widget->style->margin.bottom;
625 p_Dw_widget_size_allocate (word->content.data.widget,
626 &child_allocation);
627 break;
628
629 case DW_CONTENT_ANCHOR:
630 p_Dw_gtk_viewport_change_anchor
631 (widget, word->content.data.anchor,
632 Dw_page_line_total_y_offset_allocation (page, line,
633 allocation));
634 break;
635
636 default:
637 /* make compiler happy */
638 break;
639 }
640
641 x_cursor += (word->size.width + word->eff_space);
642 }
643 }
644 }
645
646
647 /*
648 * Implementation for both mark_size_change and mark_extremes_change.
649 */
650 static void Dw_page_mark_change (DwWidget *widget,
651 gint ref)
652 {
653 DwPage *page;
654
655 if (ref != -1) {
656 page = DW_PAGE (widget);
657
658 DBG_MSGF (page, "wrap", 0, "Dw_page_mark_size_change (ref = %d)", ref);
659
660 if (page->wrap_ref == -1)
661 page->wrap_ref = ref;
662 else
663 page->wrap_ref = MIN (page->wrap_ref, ref);
664
665 DBG_OBJ_SET_NUM (page, "wrap_ref", page->wrap_ref);
666 }
667 }
668
669 /*
670 * Standard Dw function
671 */
672 static void Dw_page_set_width (DwWidget *widget,
673 gint32 width)
674 {
675 DwPage *page;
676
677 page = DW_PAGE (widget);
678
679 /* If limit_text_width is set to YES, a queue_resize may also be
680 necessary. */
681 if (page->avail_width != width || prefs.limit_text_width) {
682 DEBUG_MSG(DEBUG_REWRAP_LEVEL,
683 "Dw_page_set_width: Calling p_Dw_widget_queue_resize, "
684 "in page with %d word(s)\n",
685 page->num_words);
686
687 page->avail_width = width;
688 p_Dw_widget_queue_resize (widget, 0, FALSE);
689 page->must_queue_resize = FALSE;
690 }
691 }
692
693
694 /*
695 * Standard Dw function
696 */
697 static void Dw_page_set_ascent (DwWidget *widget,
698 gint32 ascent)
699 {
700 DwPage *page;
701
702 page = DW_PAGE (widget);
703
704 if (page->avail_ascent != ascent) {
705 DEBUG_MSG(DEBUG_REWRAP_LEVEL,
706 "Dw_page_set_ascent: Calling p_Dw_widget_queue_resize, "
707 "in page with %d word(s)\n",
708 page->num_words);
709
710 page->avail_ascent = ascent;
711 p_Dw_widget_queue_resize (widget, 0, FALSE);
712 page->must_queue_resize = FALSE;
713 }
714 }
715
716
717 /*
718 * Standard Dw function
719 */
720 static void Dw_page_set_descent (DwWidget *widget,
721 gint32 descent)
722 {
723 DwPage *page;
724
725 page = DW_PAGE (widget);
726
727 if (page->avail_descent != descent) {
728 DEBUG_MSG(DEBUG_REWRAP_LEVEL,
729 "Dw_page_set_descent: Calling p_Dw_widget_queue_resize, "
730 "in page with %d word(s)\n",
731 page->num_words);
732
733 page->avail_descent = descent;
734 p_Dw_widget_queue_resize (widget, 0, FALSE);
735 page->must_queue_resize = FALSE;
736 }
737 }
738
739
740 /*
741 * Standard Dw function
742 */
743 static void Dw_page_add (DwContainer *container,
744 DwWidget *widget)
745 {
746 /* todo */
747 }
748
749
750 /*
751 * Standard Dw function
752 */
753 static void Dw_page_remove (DwContainer *container,
754 DwWidget *widget)
755 {
756 /* todo */
757 }
758
759
760 /*
761 * Standard Dw function
762 */
763 static void Dw_page_forall (DwContainer *container,
764 DwCallback callback,
765 gpointer callback_data)
766 {
767 DwPage *page;
768 int word_index;
769 DwPageWord *word;
770
771 page = DW_PAGE (container);
772
773 for (word_index = 0; word_index < page->num_words; word_index++) {
774 word = &page->words[word_index];
775
776 if (word->content.type == DW_CONTENT_WIDGET)
777 (*callback) (word->content.data.widget, callback_data);
778 }
779 }
780
781
782 /*
783 * ...
784 *
785 * avail_line is passed from Dw_page_real_word_wrap, to avoid calculating
786 * it twice.
787 */
788 static void Dw_page_justify_line (DwPage *page, DwPageLine *line,
789 gint32 avail_width)
790 {
791 /* To avoid rounding errors, the calculation is based on accumulated
792 * values (*_cum). */
793 gint i;
794 gint32 orig_space_sum, orig_space_cum;
795 gint32 eff_space_diff_cum, last_eff_space_diff_cum;
796 gint32 diff;
797
798 diff = avail_width - page->last_line_width;
799 if (diff > 0) {
800 orig_space_sum = 0;
801 for (i = line->first_word; i < line->last_word - 1; i++)
802 orig_space_sum += page->words[i].orig_space;
803
804 orig_space_cum = 0;
805 last_eff_space_diff_cum = 0;
806 for (i = line->first_word; i < line->last_word - 1; i++) {
807 orig_space_cum += page->words[i].orig_space;
808
809 if (orig_space_cum == 0)
810 eff_space_diff_cum = last_eff_space_diff_cum;
811 else
812 eff_space_diff_cum = diff * orig_space_cum / orig_space_sum;
813
814 page->words[i].eff_space = page->words[i].orig_space +
815 (eff_space_diff_cum - last_eff_space_diff_cum);
816 DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", i,
817 page->words[i].eff_space);
818
819 last_eff_space_diff_cum = eff_space_diff_cum;
820 }
821 }
822 }
823
824 /*
825 * ...
826 */
827 static void Dw_page_add_line (DwPage *page, gint word_ind, gboolean new_par)
828 {
829 DwPageLine *last_line, *plast_line;
830
831 DBG_MSG (page, "wrap", 0, "Dw_page_add_line");
832 DBG_MSG_START (page);
833
834 page->num_lines++;
835 a_List_add (page->lines, page->num_lines, page->num_lines_max);
836 DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
837
838 DEBUG_MSG (DEBUG_REWRAP_LEVEL, "--- new line %d in %p, with word %d of %d"
839 "\n", page->num_lines - 1, page, word_ind, page->num_words);
840
841 last_line = &page->lines[page->num_lines - 1];
842
843 if (page->num_lines == 1)
844 plast_line = NULL;
845 else
846 plast_line = &page->lines[page->num_lines - 2];
847
848 if (plast_line) {
849 /* second or more lines: copy values of last line */
850 last_line->top =
851 plast_line->top + plast_line->ascent +
852 plast_line->descent + plast_line->break_space;
853 last_line->max_line_width = plast_line->max_line_width;
854 last_line->max_word_min = plast_line->max_word_min;
855 last_line->max_par_max = plast_line->max_par_max;
856 last_line->par_min = plast_line->par_min;
857 last_line->par_max = plast_line->par_max;
858 } else {
859 /* first line: initialize values */
860 last_line->top = 0;
861 last_line->max_line_width = page->line1_offset_eff;
862 last_line->max_word_min = 0;
863 last_line->max_par_max = 0;
864 last_line->par_min = page->line1_offset_eff;
865 last_line->par_max = page->line1_offset_eff;
866 }
867
868 DBG_OBJ_ARRSET_NUM (page, "lines.%d.top", page->num_lines - 1,
869 last_line->top);
870 DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_line_width", page->num_lines - 1,
871 last_line->max_line_width);
872 DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_word_min", page->num_lines - 1,
873 last_line->max_word_min);
874 DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_par_max", page->num_lines - 1,
875 last_line->max_par_max);
876 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_min", page->num_lines - 1,
877 last_line->par_min);
878 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_max", page->num_lines - 1,
879 last_line->par_max);
880
881 last_line->first_word = word_ind;
882 last_line->ascent = 0;
883 last_line->descent = 0;
884 last_line->margin_descent = 0;
885 last_line->break_space = 0;
886 last_line->left_offset = 0;
887
888 DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
889 last_line->ascent);
890 DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
891 last_line->descent);
892
893 /* update values in line */
894 last_line->max_line_width =
895 MAX (last_line->max_line_width, page->last_line_width);
896
897 if (page->num_lines > 1)
898 page->last_line_width = 0;
899 else
900 page->last_line_width = page->line1_offset_eff;
901
902 if (new_par) {
903 last_line->max_par_max =
904 MAX (last_line->max_par_max, page->last_line_par_max);
905 DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_par_max", page->num_lines - 1,
906 last_line->max_par_max);
907
908 if (page->num_lines > 1) {
909 last_line->par_min = 0;
910 last_line->par_max = 0;
911 } else {
912 last_line->par_min = page->line1_offset_eff;
913 last_line->par_max = page->line1_offset_eff;
914 }
915 page->last_line_par_min = 0;
916 page->last_line_par_max = 0;
917
918 DBG_OBJ_SET_NUM(page, "last_line_par_min", page->last_line_par_min);
919 DBG_OBJ_SET_NUM(page, "last_line_par_max", page->last_line_par_max);
920 }
921
922 last_line->par_min = page->last_line_par_min;
923 last_line->par_max = page->last_line_par_max;
924
925 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_min", page->num_lines - 1,
926 last_line->par_min);
927 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_max", page->num_lines - 1,
928 last_line->par_max);
929
930 DBG_MSG_END (page);
931 }
932
933
934 /*
935 * This function is called in two cases: (i) when a word is added (by
936 * Dw_page_add_word), and (ii) when a page has to be (partially)
937 * rewrapped. It does word wrap, and adds new lines, if necesary.
938 */
939 static void Dw_page_real_word_wrap (DwPage *page, gint word_ind)
940 {
941 DwPageLine *last_line;
942 DwPageWord *word, *prev_word;
943 gint32 avail_width, last_space, left_offset;
944 gboolean new_line = FALSE, new_par = FALSE;
945 DwWidget *widget = DW_WIDGET (page);
946 DwExtremes word_extremes;
947
948 DBG_MSGF (page, "wrap", 0, "Dw_page_real_word_wrap (%d): %s, width = %d",
949 word_ind, a_Dw_content_html (&page->words[word_ind].content),
950 page->words[word_ind].size.width);
951 DBG_MSG_START (page);
952
953 avail_width =
954 page->avail_width - p_Dw_style_box_diff_width (widget->style)
955 - page->inner_padding;
956 if (prefs.limit_text_width &&
957 avail_width > widget->viewport->allocation.width - 10)
958 avail_width = widget->viewport->allocation.width - 10;
959
960 word = &page->words[word_ind];
961
962 if (page->num_lines == 0) {
963 DBG_MSG (page, "wrap", 0, "first line");
964 new_line = TRUE;
965 new_par = TRUE;
966 last_line = NULL;
967 } else {
968 last_line = &page->lines[page->num_lines - 1];
969
970 if (page->num_words > 0) {
971 prev_word = &page->words[word_ind - 1];
972 if (prev_word->content.type == DW_CONTENT_BREAK) {
973 DBG_MSG (page, "wrap", 0, "after a break");
974 /* previous word is a break */
975 new_line = TRUE;
976 new_par = TRUE;
977 } else if (word->style->white_space != DW_STYLE_WHITE_SPACE_NORMAL) {
978 DBG_MSGF (page, "wrap", 0, "no wrap (white_space = %d)",
979 word->style->white_space);
980 new_line = FALSE;
981 new_par = FALSE;
982 } else {
983 if (last_line->first_word != word_ind) {
984 /* Does new word fit into the last line? */
985 DBG_MSGF (page, "wrap", 0,
986 "word %d (%s) fits? (%d + %d + %d &lt;= %d)...",
987 word_ind, a_Dw_content_html (&word->content),
988 page->last_line_width, prev_word->orig_space,
989 word->size.width, avail_width);
990 new_line = (page->last_line_width + prev_word->orig_space
991 + word->size.width > avail_width);
992 DBG_MSGF (page, "wrap", 0, "... %s.",
993 new_line ? "No" : "Yes");
994 }
995 }
996 }
997 }
998
999 /* Has sometimes the wrong value. */
1000 word->eff_space = word->orig_space;
1001 DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", word_ind, word->eff_space);
1002
1003 /* Test, whether line1_offset can be used. */
1004 if (word_ind == 0) {
1005 if (page->ignore_line1_offset_sometimes) {
1006 if (page->line1_offset + word->size.width > avail_width)
1007 page->line1_offset_eff = 0;
1008 else
1009 page->line1_offset_eff = page->line1_offset;
1010 } else
1011 page->line1_offset_eff = page->line1_offset;
1012 }
1013
1014 if (last_line != NULL && new_line && !new_par &&
1015 word->style->text_align == DW_STYLE_TEXT_ALIGN_JUSTIFY)
1016 Dw_page_justify_line (page, last_line, avail_width);
1017
1018 if (new_line) {
1019 Dw_page_add_line (page, word_ind, new_par);
1020 last_line = &page->lines[page->num_lines - 1];
1021 }
1022
1023 last_line->last_word = word_ind + 1;
1024 last_line->ascent = MAX (last_line->ascent, word->size.ascent);
1025 last_line->descent = MAX (last_line->descent, word->size.descent);
1026
1027 DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
1028 last_line->ascent);
1029 DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
1030 last_line->descent);
1031
1032 if (word->content.type == DW_CONTENT_WIDGET) {
1033 last_line->margin_descent =
1034 MAX (last_line->margin_descent,
1035 word->size.descent +
1036 word->content.data.widget->style->margin.bottom);
1037
1038 DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
1039 last_line->descent);
1040
1041 /* If the widget is not in the first line of the paragraph, its top
1042 * margin may make the line higher.
1043 */
1044 if (page->num_lines > 1) {
1045 /* Here, we know already what the break and the bottom margin
1046 * contributed to the space before this line.
1047 */
1048 last_line->ascent =
1049 MAX (last_line->ascent,
1050 word->size.ascent
1051 + word->content.data.widget->style->margin.top);
1052
1053 DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
1054 last_line->ascent);
1055 }
1056 } else
1057 last_line->margin_descent =
1058 MAX (last_line->margin_descent, last_line->descent);
1059
1060 Dw_page_get_word_extremes (word, &word_extremes);
1061 last_space = (word_ind > 0) ? page->words[word_ind - 1].orig_space : 0;
1062
1063 if (word->content.type == DW_CONTENT_BREAK)
1064 last_line->break_space =
1065 MAX3 (word->content.data.break_space,
1066 last_line->margin_descent - last_line->descent,
1067 last_line->break_space);
1068
1069 page->last_line_width += word->size.width;
1070 if (!new_line)
1071 page->last_line_width += last_space;
1072
1073 page->last_line_par_min += word_extremes.max_width;
1074 page->last_line_par_max += word_extremes.max_width;
1075 if (!new_par) {
1076 page->last_line_par_min += last_space;
1077 page->last_line_par_max += last_space;
1078 }
1079
1080 if (word->style->white_space != DW_STYLE_WHITE_SPACE_NORMAL) {
1081 last_line->par_min += word_extremes.min_width + last_space;
1082 /* This may also increase the accumulated minimum word width. */
1083 last_line->max_word_min =
1084 MAX (last_line->max_word_min, last_line->par_min);
1085 /* NOTE: Most code relies on that all values of nowrap are equal for all
1086 * words within one line. */
1087 } else
1088 /* Simple case. */
1089 last_line->max_word_min =
1090 MAX (last_line->max_word_min, word_extremes.min_width);
1091
1092 DBG_OBJ_SET_NUM(page, "last_line_par_min", page->last_line_par_min);
1093 DBG_OBJ_SET_NUM(page, "last_line_par_max", page->last_line_par_max);
1094 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_min", page->num_lines - 1,
1095 last_line->par_min);
1096 DBG_OBJ_ARRSET_NUM (page, "lines.%d.par_max", page->num_lines - 1,
1097 last_line->par_max);
1098 DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_word_min", page->num_lines - 1,
1099 last_line->max_word_min);
1100
1101 /* Finally, justify the line. Breaks are ignored, since the HTML
1102 * parser sometimes assignes the wrong style to them. (todo: ) */
1103 if (word->content.type != DW_CONTENT_BREAK) {
1104 switch (word->style->text_align) {
1105 case DW_STYLE_TEXT_ALIGN_LEFT:
1106 case DW_STYLE_TEXT_ALIGN_JUSTIFY: /* see some lines above */
1107 case DW_STYLE_TEXT_ALIGN_STRING: /* handled elsewhere (in future) */
1108 left_offset = 0;
1109 break;
1110
1111 case DW_STYLE_TEXT_ALIGN_RIGHT:
1112 left_offset = avail_width - page->last_line_width;
1113 break;
1114
1115 case DW_STYLE_TEXT_ALIGN_CENTER:
1116 left_offset = (avail_width - page->last_line_width) / 2;
1117 break;
1118
1119 default:
1120 /* compiler happiness */
1121 left_offset = 0;
1122 }
1123
1124 /* For large lines (images etc), which do not fit into the viewport: */
1125 if (left_offset < 0)
1126 left_offset = 0;
1127
1128 if (page->list_item && last_line == page->lines) {
1129 /* List item markers are always on the left. */
1130 last_line->left_offset = 0;
1131 page->words[0].eff_space = page->words[0].orig_space + left_offset;
1132 DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", 0,
1133 page->words[0].eff_space);
1134 } else
1135 last_line->left_offset = left_offset;
1136 }
1137
1138 page->must_queue_resize = TRUE;
1139
1140 DBG_MSG_END (page);
1141 }
1142
1143
1144 /*
1145 * Calculate the size of a widget within the page.
1146 * (Subject of change in the near future!)
1147 */
1148 static void Dw_page_calc_widget_size (DwPage *page,
1149 DwWidget *widget,
1150 DwRequisition *size)
1151 {
1152 DwRequisition requisition;
1153 gint32 avail_width, avail_ascent, avail_descent;
1154
1155 /* We ignore line1_offset[_eff]. */
1156 avail_width =
1157 page->avail_width - p_Dw_style_box_diff_width (DW_WIDGET(page)->style)
1158 - page->inner_padding;
1159 avail_ascent =
1160 page->avail_ascent - p_Dw_style_box_diff_height (DW_WIDGET(page)->style);
1161 avail_descent = page->avail_descent;
1162
1163 if (DW_WIDGET_USES_HINTS (widget)) {
1164 p_Dw_widget_set_width (widget, avail_width);
1165 p_Dw_widget_set_ascent (widget, avail_ascent);
1166 p_Dw_widget_set_descent (widget, avail_descent);
1167 p_Dw_widget_size_request (widget, size);
1168 size->ascent -= widget->style->margin.top;
1169 size->descent -= widget->style->margin.bottom;
1170 } else {
1171 /* TODO: Use margin.{top|bottom} here, like above.
1172 * (No harm for the next future.) */
1173 if (widget->style->width == DW_STYLE_LENGTH_AUTO ||
1174 widget->style->height == DW_STYLE_LENGTH_AUTO)
1175 p_Dw_widget_size_request (widget, &requisition);
1176
1177 if (widget->style->width == DW_STYLE_LENGTH_AUTO)
1178 size->width = requisition.width;
1179 else if (DW_STYLE_IS_ABS_LENGTH (widget->style->width))
1180 /* Fixed lengths are only applied to the content, so we have to
1181 * add padding, border and margin. */
1182 size->width = DW_STYLE_ABS_LENGTH_VAL (widget->style->width)
1183 + p_Dw_style_box_diff_width (widget->style);
1184 else
1185 size->width =
1186 DW_STYLE_PER_LENGTH_VAL (widget->style->width) * avail_width;
1187
1188 if (widget->style->height == DW_STYLE_LENGTH_AUTO) {
1189 size->ascent = requisition.ascent;
1190 size->descent = requisition.descent;
1191 } else if (DW_STYLE_IS_ABS_LENGTH (widget->style->height)) {
1192 /* Fixed lengths are only applied to the content, so we have to
1193 * add padding, border and margin. */
1194 size->ascent =
1195 DW_STYLE_ABS_LENGTH_VAL (widget->style->height)
1196 + p_Dw_style_box_diff_height (widget->style);
1197 size->descent = 0;
1198 } else {
1199 size->ascent =
1200 DW_STYLE_PER_LENGTH_VAL (widget->style->height) * avail_ascent;
1201 size->descent =
1202 DW_STYLE_PER_LENGTH_VAL (widget->style->height) * avail_descent;
1203 }
1204 }
1205 }
1206
1207
1208 /*
1209 * Rewrap the page from the line from which this is necessary.
1210 * There are basically two times we'll want to do this:
1211 * either when the viewport is resized, or when the size changes on one
1212 * of the child widgets.
1213 */
1214 static void Dw_page_rewrap (DwPage *page)
1215 {
1216 DwWidget *widget;
1217 gint i, word_index;
1218 DwPageWord *word;
1219 DwPageLine *last_line;
1220
1221 if (page->wrap_ref == -1)
1222 /* page does not have to be rewrapped */
1223 return;
1224
1225 widget = DW_WIDGET (page);
1226
1227 DBG_MSGF (page, "wrap", 0,
1228 "Dw_page_rewrap: page->wrap_ref = %d, in page with %d word(s)",
1229 page->wrap_ref, page->num_words);
1230 DBG_MSG_START (page);
1231
1232 /* All lines up from page->wrap_ref will be rebuild from the word list,
1233 * the line list up from this position is rebuild. */
1234 page->num_lines = page->wrap_ref;
1235 page->last_line_width = 0;
1236 DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
1237 DBG_OBJ_SET_NUM(page, "last_line_width", page->last_line_width);
1238
1239 /* In the word list, we start at the last word, plus one (see definition
1240 * of last_word), in the line before. */
1241 if (page->wrap_ref > 0) {
1242 /* Note: In this case, Dw_page_real_word_wrap will immediately find
1243 * the need to rewrap the line, since we start with the last one (plus
1244 * one). This is also the reason, why page->last_line_width is set
1245 * to the length of the line. */
1246 last_line = &page->lines[page->num_lines - 1];
1247
1248 page->last_line_par_min = last_line->par_min;
1249 page->last_line_par_max = last_line->par_max;
1250
1251 word_index = last_line->last_word;
1252 for (i = last_line->first_word; i < last_line->last_word - 1; i++)
1253 page->last_line_width += (page->words[i].size.width +
1254 page->words[i].orig_space);
1255 page->last_line_width +=
1256 page->words[last_line->last_word - 1].size.width;
1257 } else {
1258 page->last_line_par_min = 0;
1259 page->last_line_par_max = 0;
1260
1261 word_index = 0;
1262 }
1263
1264 for (; word_index < page->num_words; word_index++) {
1265 word = &page->words[word_index];
1266
1267 if (word->content.type == DW_CONTENT_WIDGET)
1268 Dw_page_calc_widget_size (page, word->content.data.widget,
1269 &word->size);
1270 Dw_page_word_wrap (page, word_index);
1271
1272 if (word->content.type == DW_CONTENT_WIDGET) {
1273 word->content.data.widget->parent_ref = page->num_lines - 1;
1274 DBG_OBJ_SET_NUM (word->content.data.widget, "parent_ref",
1275 word->content.data.widget->parent_ref);
1276 }
1277
1278 DEBUG_MSG(DEBUG_REWRAP_LEVEL,
1279 "Assigning parent_ref = %d to rewrapped word %d, "
1280 "in page with %d word(s)\n",
1281 page->num_lines - 1, word_index, page->num_words);
1282
1283 if ( word->content.type == DW_CONTENT_ANCHOR )
1284 p_Dw_gtk_viewport_change_anchor
1285 (widget, word->content.data.anchor,
1286 Dw_page_line_total_y_offset (page,
1287 &page->lines[page->num_lines - 1]));
1288 }
1289
1290 /* Next time, the page will not have to be rewrapped. */
1291 page->wrap_ref = -1;
1292
1293 DBG_MSG_END (page);
1294 }
1295
1296 /*
1297 * Paint a line
1298 * - x and y are toplevel dw coordinates (Question: what Dw? Changed. Test!)
1299 * - area is used always (ev. set it to event->area)
1300 * - event is only used when is_expose
1301 */
1302 static void Dw_page_draw_line (DwPage *page,
1303 DwPageLine *line,
1304 DwRectangle *area,
1305 GdkEventExpose *event)
1306 {
1307 DwWidget *widget;
1308 DwPageWord *word;
1309 gint word_index;
1310 gint32 x_widget, y_widget, x_viewport, y_viewport, y_viewport_base;
1311 gint32 start_hl, width_hl;
1312 gint32 diff, word_len, eff_hl_end, layer;
1313 DwWidget *child;
1314 DwRectangle child_area;
1315 GdkWindow *window;
1316 GdkGC *gc, *hl_gc;
1317 DwStyleColor *page_bg_color, *word_bg_color;
1318
1319 /* Here's an idea on how to optimize this routine to minimize the number
1320 * of calls to gdk_draw_string:
1321 *
1322 * Copy the text from the words into a buffer, adding a new word
1323 * only if: the attributes match, and the spacing is either zero or
1324 * equal to the width of ' '. In the latter case, copy a " " into
1325 * the buffer. Then draw the buffer. */
1326
1327 widget = DW_WIDGET (page);
1328 window = DW_WIDGET_WINDOW (widget);
1329 page_bg_color = p_Dw_widget_get_bg_color (widget);
1330
1331 x_widget = Dw_page_line_total_x_offset(page,line);
1332 x_viewport =
1333 p_Dw_widget_x_world_to_viewport (widget,
1334 widget->allocation.x + x_widget);
1335 y_widget = Dw_page_line_total_y_offset(page,line);
1336 y_viewport =
1337 p_Dw_widget_y_world_to_viewport (widget,
1338 widget->allocation.y + y_widget);
1339 y_viewport_base = y_viewport + line->ascent;
1340
1341 for (word_index = line->first_word; word_index < line->last_word;
1342 word_index++) {
1343 word = &page->words[word_index];
1344 diff = 0;
1345 gc = word->style->color->gc;
1346
1347
1348 DBG_OBJ_ARRSET_NUM (page, "words.%d.<i>drawn at</i>.x", word_index,
1349 x_widget);
1350 DBG_OBJ_ARRSET_NUM (page, "words.%d.<i>drawn at</i>.y", word_index,
1351 y_widget);
1352
1353 switch (word->content.type) {
1354 case DW_CONTENT_TEXT:
1355 if (word->style->background_color)
1356 word_bg_color = word->style->background_color;
1357 else
1358 word_bg_color = page_bg_color;
1359
1360 /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */
1361 if (word->style->valign == DW_STYLE_VALIGN_SUB)
1362 diff = word->size.ascent / 2;
1363 else if (word->style->valign == DW_STYLE_VALIGN_SUPER)
1364 diff -= word->size.ascent / 3;
1365
1366 /* Draw background (color, image), when given. */
1367 /* todo: Test (word->style->background_color) is incomplete, and
1368 * should in future include background images. */
1369 if (word->style->background_color &&
1370 word->size.width > 0)
1371 p_Dw_widget_draw_box (widget, word->style, area,
1372 x_widget,
1373 y_widget + line->ascent - word->size.ascent,
1374 word->size.width,
1375 word->size.ascent + word->size.descent,
1376 FALSE);
1377
1378 /* Draw space background (color, image), when given. */
1379 /* todo: Test (word->space_style->background_color) is incomplete, and
1380 * should in future include background images. */
1381 if (word->space_style->background_color &&
1382 word->eff_space > 0)
1383 p_Dw_widget_draw_box (widget, word->space_style, area,
1384 x_widget + word->size.width,
1385 y_widget + line->ascent - word->size.ascent,
1386 word->eff_space,
1387 word->size.ascent + word->size.descent,
1388 FALSE);
1389
1390 gdk_draw_string (window, word->style->font->font, gc,
1391 x_viewport, y_viewport_base + diff,
1392 word->content.data.text);
1393
1394 /* underline */
1395 if (word->style->text_decoration & DW_STYLE_TEXT_DECORATION_UNDERLINE)
1396 gdk_draw_line (window, gc,
1397 x_viewport, y_viewport_base + 1 + diff,
1398 x_viewport + word->size.width - 1,
1399 y_viewport_base + 1 + diff);
1400 if (word_index + 1 < line->last_word &&
1401 (word->space_style->text_decoration
1402 & DW_STYLE_TEXT_DECORATION_UNDERLINE))
1403 gdk_draw_line (window, gc,
1404 x_viewport + word->size.width,
1405 y_viewport_base + 1 + diff,
1406 x_viewport + word->size.width + word->eff_space - 1,
1407 y_viewport_base + 1 + diff);
1408
1409 /* strike-through */
1410 if (word->style->text_decoration
1411 & DW_STYLE_TEXT_DECORATION_LINE_THROUGH)
1412 gdk_draw_line (window, gc,
1413 x_viewport,
1414 y_viewport_base - word->size.ascent / 2 + diff,
1415 x_viewport + word->size.width - 1,
1416 y_viewport_base - word->size.ascent / 2 + diff);
1417 if (word_index + 1 < line->last_word &&
1418 (word->space_style->text_decoration
1419 & DW_STYLE_TEXT_DECORATION_LINE_THROUGH))
1420 gdk_draw_line (window, gc,
1421 x_viewport + word->size.width,
1422 y_viewport_base - word->size.ascent / 2 + diff,
1423 x_viewport + word->size.width + word->eff_space - 1,
1424 y_viewport_base - word->size.ascent / 2 + diff);
1425
1426 for (layer = 0; layer < DW_HIGHLIGHT_NUM_LAYERS; layer++) {
1427 if (word->hl_start[layer] != -1) {
1428 word_len = strlen (word->content.data.text);
1429 eff_hl_end = MIN (word_len, word->hl_end[layer]);
1430 start_hl = x_viewport +
1431 gdk_text_width (word->style->font->font,
1432 word->content.data.text,
1433 word->hl_start[layer]);
1434 width_hl =
1435 gdk_text_width (word->style->font->font,
1436 word->content.data.text
1437 + word->hl_start[layer],
1438 eff_hl_end - word->hl_start[layer]);
1439
1440 /* If the space after this word highlighted, and this word
1441 * is not the last one in this line, highlight also the
1442 * space. */
1443 /* todo: This should also be done with spaces after non-text
1444 * words, but this is not yet defined very well. */
1445 if (word->hl_end[layer] > eff_hl_end &&
1446 word_index < page->num_words &&
1447 word_index != line->last_word - 1)
1448 width_hl += word->eff_space;
1449
1450
1451 if (width_hl != 0) {
1452 /* Draw background for highlighted text. */
1453 gdk_draw_rectangle (window, word_bg_color->inverse_gc,
1454 TRUE, start_hl,
1455 y_viewport_base - word->size.ascent,
1456 width_hl,
1457 word->size.ascent + word->size.descent);
1458
1459 /* Highlight the text. */
1460 hl_gc = word->style->color->inverse_gc;
1461 gdk_draw_text (window, word->style->font->font, hl_gc,
1462 start_hl, y_viewport_base + diff,
1463 word->content.data.text
1464 + word->hl_start[layer],
1465 eff_hl_end - word->hl_start[layer]);
1466
1467 /* underline and strike-through */
1468 if (word->style->text_decoration
1469 & DW_STYLE_TEXT_DECORATION_UNDERLINE)
1470 gdk_draw_line (window, hl_gc,
1471 start_hl, y_viewport_base + 1 + diff,
1472 start_hl + width_hl - 1,
1473 y_viewport_base + 1 + diff);
1474 if (word->style->text_decoration
1475 & DW_STYLE_TEXT_DECORATION_LINE_THROUGH)
1476 gdk_draw_line (window, hl_gc,
1477 start_hl,
1478 y_viewport_base
1479 - word->size.ascent / 2 + diff,
1480 start_hl + width_hl - 1,
1481 y_viewport_base
1482 - word->size.ascent / 2 + diff);
1483 }
1484 }
1485 }
1486 break;
1487
1488 case DW_CONTENT_WIDGET:
1489 child = word->content.data.widget;
1490 if (p_Dw_widget_intersect (child, area, &child_area))
1491 p_Dw_widget_draw (child, &child_area, event);
1492 break;
1493
1494 case DW_CONTENT_ANCHOR: case DW_CONTENT_BREAK:
1495 /* nothing - an anchor/break isn't seen */
1496 /*
1497 * Historical note:
1498 * > BUG: sometimes anchors have x_space;
1499 * > we subtract that just in case --EG
1500 * This is inconsistent with other parts of the code, so it should
1501 * be tried to prevent this earlier.--SG
1502 */
1503 /*
1504 * x_viewport -= word->size.width + word->eff_space;
1505 * x_widget -= word->size.width + word->eff_space;
1506 */
1507 #if 0
1508 /* Useful for testing: draw breaks. */
1509 if (word->content.type == DW_CONTENT_BREAK)
1510 gdk_draw_rectangle (window, gc, TRUE,
1511 p_Dw_widget_x_world_to_viewport (widget,
1512 widget->allocation.x +
1513 Dw_page_line_total_x_offset(page, line)),
1514 y_viewport_base + line->descent,
1515 DW_WIDGET_CONTENT_WIDTH(widget),
1516 word->content.data.break_space);
1517 #endif
1518 break;
1519
1520 default:
1521 g_warning ("BUG!!! at (%d, %d).", x_viewport, y_viewport_base + diff);
1522 break;
1523 }
1524
1525 x_viewport += word->size.width + word->eff_space;
1526 x_widget += word->size.width + word->eff_space;
1527 }
1528 }
1529
1530 /*
1531 * Find the first line index that includes y, relative to top of widget.
1532 */
1533 static gint Dw_page_find_line_index (DwPage *page, gint y)
1534 {
1535 gint max_index = page->num_lines - 1;
1536 gint step, index, low = 0;
1537
1538 step = (page->num_lines + 1) >> 1;
1539 while ( step > 1 ) {
1540 index = low + step;
1541 if (index <= max_index &&
1542 Dw_page_line_total_y_offset_i (page, index) < y)
1543 low = index;
1544 step = (step + 1) >> 1;
1545 }
1546
1547 if (low < max_index && Dw_page_line_total_y_offset_i (page, low + 1) < y)
1548 low++;
1549
1550 /*
1551 * This new routine returns the line number between (top) and
1552 * (top + size.ascent + size.descent + break_space): the space
1553 * _below_ the line is considered part of the line. Old routine
1554 * returned line number between (top - previous_line->break_space)
1555 * and (top + size.ascent + size.descent): the space _above_ the
1556 * line was considered part of the line. This is important for
1557 * Dw_page_find_link() --EG
1558 * That function has now been inlined into Dw_page_motion_notify() --JV
1559 */
1560 return low;
1561 }
1562
1563 /*
1564 * Find the line of word <word_index>.
1565 */
1566 static gint Dw_page_find_line_of_word (DwPage *page, gint word_index)
1567 {
1568 gint high = page->num_lines - 1, index, low = 0;
1569
1570 g_return_val_if_fail (word_index >= 0, -1);
1571 g_return_val_if_fail (word_index < page->num_words, -1);
1572
1573 while (TRUE) {
1574 index = (low + high) / 2;
1575 if (word_index >= page->lines[index].first_word) {
1576 if (word_index < page->lines[index].last_word)
1577 return index;
1578 else
1579 low = index + 1;
1580 } else
1581 high = index - 1;
1582 }
1583 }
1584
1585 /*
1586 * Draw the actual lines, starting at (x, y) in toplevel Dw coords.
1587 * (former Dw_page_expose_lines)
1588 */
1589 static void Dw_page_draw (DwWidget *widget,
1590 DwRectangle *area,
1591 GdkEventExpose *event)
1592 {
1593 DwPage *page;
1594 gint line_index;
1595 DwPageLine *line;
1596
1597 p_Dw_widget_draw_widget_box (widget, area, FALSE);
1598
1599 page = DW_PAGE (widget);
1600 line_index = Dw_page_find_line_index (page, area->y);
1601
1602 for (; line_index < page->num_lines; line_index++) {
1603 line = &(page->lines[line_index]);
1604 if (Dw_page_line_total_y_offset(page, line) >= area->y + area->height)
1605 break;
1606
1607 Dw_page_draw_line (page, line, area, event);
1608 }
1609 }
1610
1611
1612 /*
1613 * Find the index of the word, or -1.
1614 */
1615 static gint Dw_page_find_word (DwPage *page, gint x, gint y)
1616 {
1617 gint line_index, word_index;
1618 gint x_cursor, last_x_cursor;
1619 DwPageLine *line;
1620 DwPageWord *word;
1621
1622 if ( (line_index = Dw_page_find_line_index (page, y)) >= page->num_lines )
1623 return -1;
1624 line = &page->lines[line_index];
1625 if (Dw_page_line_total_y_offset(page, line) + line->ascent + line->descent
1626 <= y)
1627 return -1;
1628
1629 x_cursor = Dw_page_line_total_x_offset (page, line);
1630 for (word_index = line->first_word; word_index < line->last_word;
1631 word_index++) {
1632 word = &page->words[word_index];
1633 last_x_cursor = x_cursor;
1634 x_cursor += word->size.width + word->eff_space;
1635 if (last_x_cursor <= x && x_cursor > x)
1636 return word_index;
1637 }
1638 return -1;
1639 }
1640
1641 /*
1642 * Construct an iterator for a word.
1643 */
1644 static DwIterator *Dw_page_construct_iterator (DwPage *page, gint word_index)
1645 {
1646 DwIterator *it;
1647
1648 it = Dw_page_iterator (DW_WIDGET (page), DW_CONTENT_ALL, FALSE);
1649 /* DW_CONTENT_TEXT | DW_CONTENT_WIDGET, FALSE); */
1650 ((DwIteratorInt*)it)->pos = word_index;
1651 it->content = page->words[word_index].content;
1652 return it;
1653 }
1654
1655 /*
1656 * Send event to selection.
1657 */
1658 static gboolean Dw_page_send_selection_event (DwPage *page,
1659 gint (*fn) (Selection*,
1660 DwIterator *,
1661 gint, gint,
1662 GdkEventButton*,
1663 gboolean
1664 within_content),
1665 gint32 x, gint32 y,
1666 GdkEventButton *event)
1667 {
1668 DwIterator *it;
1669 DwPageLine *line, *last_line;
1670 gint32 next_word_start_x, word_start_x, word_x, next_word_x, yfirst, ylast;
1671 gint char_pos = 0, word_index, line_index, link;
1672 DwPageWord *word;
1673 gboolean found;
1674 gboolean within_content;
1675
1676 DEBUG_MSG (DEBUG_EVENT_LEVEL,
1677 "Dw_page_send_selection_event: x = %d, y = %d\n", x, y);
1678
1679 if (page->num_words == 0) {
1680 DEBUG_MSG (DEBUG_EVENT_LEVEL, " no words\n");
1681 return FALSE;
1682 }
1683
1684 /* In most cases true, so set here: */
1685 link = -1;
1686 within_content = TRUE;
1687
1688 last_line = &page->lines[page->num_lines - 1];
1689 yfirst = Dw_page_line_total_y_offset_i (page, 0);
1690 ylast =
1691 Dw_page_line_total_y_offset (page, last_line) +
1692 last_line->ascent + last_line->descent;
1693 if (y < yfirst) {
1694 /* Above the first line: take the first word. */
1695 DEBUG_MSG (DEBUG_EVENT_LEVEL, " above first line (at %d)\n", yfirst);
1696 within_content = FALSE;
1697 word_index = 0;
1698 char_pos = 0;
1699 } else if (y >= ylast) {
1700 /* Below the last line: take the last word. */
1701 DEBUG_MSG (DEBUG_EVENT_LEVEL, " below last line (at %d)\n", ylast);
1702 within_content = FALSE;
1703 word_index = page->num_words - 1;
1704 word = &page->words[word_index];
1705 char_pos = word->content.type == DW_CONTENT_TEXT ?
1706 strlen (word->content.data.text) : 0;
1707 } else {
1708 line_index = Dw_page_find_line_index (page, y);
1709 line = &page->lines[line_index];
1710 DEBUG_MSG (DEBUG_EVENT_LEVEL, " in line %d\n", line_index);
1711
1712 /* Pointer within the break space? */
1713 if (y > (Dw_page_line_total_y_offset(page, line) + line->ascent +
1714 line->descent)) {
1715 /* Choose this break. */
1716 DEBUG_MSG (DEBUG_EVENT_LEVEL, " break\n");
1717 within_content = FALSE;
1718 word_index = line->last_word - 1;
1719 char_pos = 0;
1720 } else if (x < Dw_page_line_total_x_offset (page, line)) {
1721 /* Left of the first word in the line. */
1722 DEBUG_MSG (DEBUG_EVENT_LEVEL, " left of this line\n");
1723 word_index = line->first_word;
1724 within_content = FALSE;
1725 char_pos = 0;
1726 } else {
1727 next_word_start_x = Dw_page_line_total_x_offset (page, line);
1728 found = FALSE;
1729 for (word_index = line->first_word;
1730 !found && word_index < line->last_word;
1731 word_index++) {
1732 word = &page->words[word_index];
1733 word_start_x = next_word_start_x;
1734 next_word_start_x += word->size.width + word->eff_space;
1735 DEBUG_MSG (DEBUG_EVENT_LEVEL,
1736 " word %d (%s) from %d to %d, delta = %d + %d\n",
1737 word_index, a_Dw_content_text (&word->content),
1738 word_start_x, next_word_start_x,
1739 word->size.width, word->eff_space);
1740 if (x >= word_start_x && x < next_word_start_x) {
1741 DEBUG_MSG (DEBUG_EVENT_LEVEL,
1742 " found word %d (%s)\n",
1743 word_index, a_Dw_content_text (&word->content));
1744
1745 /* We have found the word. */
1746 if (word->content.type == DW_CONTENT_TEXT) {
1747 /* Search the character the mouse pointer is in.
1748 * next_word_x is the right side of this character. */
1749 char_pos = 0;
1750 while ((next_word_x = word_start_x +
1751 gdk_text_width(word->style->font->font,
1752 word->content.data.text,
1753 char_pos))
1754 <= x)
1755 char_pos++;
1756
1757 /* The left side of this character. */
1758 word_x =
1759 word_start_x + gdk_text_width (word->style->font->font,
1760 word->content.data.text,
1761 char_pos - 1);
1762
1763 /* If the mouse pointer is left from the middle, use the left
1764 * position, otherwise, use the right one. */
1765 if (x <= (word_x + next_word_x) / 2)
1766 char_pos--;
1767 } else {
1768 /* Depends on whether the pointer is within the left or
1769 * right half of the (non-text) word. */
1770 if (x >= (word_start_x + next_word_start_x) / 2)
1771 char_pos = SELECTION_EOW;
1772 else
1773 char_pos = 0;
1774 }
1775
1776 found = TRUE;
1777 link = word->style ? word->style->x_link : -1;
1778 break;
1779 }
1780 }
1781
1782 if (!found) {
1783 /* No word found in this line (i.e. we are on the right side),
1784 * take the last of this line. */
1785 DEBUG_MSG (DEBUG_EVENT_LEVEL, " not found\n");
1786 within_content = FALSE;
1787 word_index = line->last_word - 1;
1788 if (word_index >= page->num_words)
1789 word_index--;
1790 word = &page->words[word_index];
1791 char_pos = word->content.type == DW_CONTENT_TEXT ?
1792 strlen (word->content.data.text) : SELECTION_EOW;
1793 }
1794 }
1795 }
1796
1797 word = &page->words[word_index];
1798 it = Dw_page_construct_iterator (page, word_index);
1799 DEBUG_MSG (DEBUG_EVENT_LEVEL, "-> word %d (of %d), char %d, link %d: %s\n",
1800 word_index, page->num_words, char_pos, link,
1801 a_Dw_iterator_text (it));
1802 return fn (GTK_DW_VIEWPORT(DW_WIDGET(page)->viewport)->selection,
1803 it, char_pos, link, event, within_content);
1804 }
1805
1806
1807 /*
1808 * Standard Dw function.
1809 */
1810 static gboolean Dw_page_button_press (DwWidget *widget,
1811 gint32 x,
1812 gint32 y,
1813 GdkEventButton *event)
1814 {
1815 return Dw_page_send_selection_event (DW_PAGE (widget),
1816 a_Selection_button_press, x, y,
1817 event);
1818 }
1819
1820
1821 /*
1822 * Standard Dw function.
1823 */
1824 static gboolean Dw_page_button_release (DwWidget *widget,
1825 gint32 x,
1826 gint32 y,
1827 GdkEventButton *event)
1828 {
1829 return Dw_page_send_selection_event (DW_PAGE (widget),
1830 a_Selection_button_release, x, y,
1831 event);
1832 }
1833
1834
1835 /*
1836 * Standard Dw function.
1837 */
1838 static gboolean Dw_page_motion_notify (DwWidget *widget,
1839 gint32 x,
1840 gint32 y,
1841 GdkEventMotion *event)
1842 {
1843 DwPage *page = DW_PAGE (widget);
1844 gint link_old, word_index;
1845 DwTooltip *tooltip_old;
1846 gboolean return_val = FALSE;
1847
1848 if (event && (event->state & GDK_BUTTON1_MASK))
1849 return Dw_page_send_selection_event (page,
1850 a_Selection_button_motion, x, y,
1851 NULL);
1852 else {
1853 /*DEBUG_MSG (DEBUG_EVENT_LEVEL, "Dw_page_motion_notify\n");*/
1854 word_index = Dw_page_find_word (page, x, y);
1855
1856 link_old = page->hover_link;
1857 tooltip_old = page->hover_tooltip;
1858
1859 if (word_index == -1) {
1860 page->hover_link = -1;
1861 page->hover_tooltip = NULL;
1862 } else {
1863 page->hover_link = page->words[word_index].style->x_link;
1864 page->hover_tooltip = page->words[word_index].style->x_tooltip;
1865 }
1866
1867 /* Show/hide tooltip */
1868 if (tooltip_old != page->hover_tooltip) {
1869 if (tooltip_old)
1870 a_Dw_tooltip_on_leave (tooltip_old);
1871 if (page->hover_tooltip)
1872 a_Dw_tooltip_on_enter(page->hover_tooltip);
1873 } else if (page->hover_tooltip)
1874 a_Dw_tooltip_on_motion(page->hover_tooltip);
1875
1876 if (page->hover_link != link_old) {
1877 gtk_signal_emit (GTK_OBJECT (widget), page_signals[LINK_ENTERED],
1878 page->hover_link, -1, -1, &return_val);
1879 return return_val;
1880 } else
1881 return (page->hover_link != -1);
1882 }
1883 }
1884
1885
1886 /*
1887 * Standard Dw function.
1888 */
1889 static gboolean Dw_page_leave_notify (DwWidget *widget,
1890 DwWidget *next_widget,
1891 GdkEventMotion *event)
1892 {
1893 DwPage *page = DW_PAGE (widget);
1894 gboolean return_val = FALSE;
1895
1896 if (page->hover_tooltip) {
1897 a_Dw_tooltip_on_leave(page->hover_tooltip);
1898 page->hover_tooltip = NULL;
1899 }
1900
1901 if (page->hover_link != -1) {
1902 page->hover_link = -1;
1903 gtk_signal_emit (GTK_OBJECT (widget), page_signals[LINK_ENTERED],
1904 -1, -1, -1, &return_val);
1905 return return_val;
1906 }
1907
1908 return FALSE;
1909 }
1910
1911
1912 /*
1913 * Add a new word (text, widget etc.) to a page.
1914 */
1915 static DwPageWord *Dw_page_add_word (DwPage *page,
1916 gint width,
1917 gint ascent,
1918 gint descent,
1919 DwStyle *style)
1920 {
1921 DwPageWord *word;
1922 int i;
1923
1924 page->num_words++;
1925 a_List_add (page->words, page->num_words, page->num_words_max);
1926
1927 word = &page->words[page->num_words - 1];
1928 word->size.width = width;
1929 word->size.ascent = ascent;
1930 word->size.descent = descent;
1931 word->orig_space = 0;
1932 word->eff_space = 0;
1933 word->content.space = FALSE;
1934
1935 DBG_OBJ_ARRSET_NUM (page, "words.%d.size.width", page->num_words - 1,
1936 word->size.width);
1937 DBG_OBJ_ARRSET_NUM (page, "words.%d.size.descent", page->num_words - 1,
1938 word->size.descent);
1939 DBG_OBJ_ARRSET_NUM (page, "words.%d.size.ascent", page->num_words - 1,
1940 word->size.ascent);
1941 DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", page->num_words - 1,
1942 word->orig_space);
1943 DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", page->num_words - 1,
1944 word->eff_space);
1945 DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", page->num_words - 1,
1946 word->content.space);
1947
1948 for (i = 0; i < DW_HIGHLIGHT_NUM_LAYERS; i++) {
1949 word->hl_start[i] = -1;
1950 word->hl_end[i] = -1;
1951 }
1952
1953 word->style = style;
1954 word->space_style = style;
1955 a_Dw_style_ref (style);
1956 a_Dw_style_ref (style);
1957
1958 return word;
1959 }
1960
1961 /*
1962 * Calculate the size of a text word.
1963 */
1964 static void Dw_page_calc_text_size (DwPage *page, char *text, DwStyle *style,
1965 DwRequisition *size)
1966 {
1967 size->width = gdk_string_width (style->font->font, text);
1968 size->ascent = style->font->font->ascent;
1969 size->descent = style->font->font->descent;
1970
1971 /* In case of a sub or super script we increase the word's height and
1972 * potentially the line's height.
1973 */
1974 if (style->valign == DW_STYLE_VALIGN_SUB)
1975 size->descent += (size->ascent / 2);
1976 else if (style->valign == DW_STYLE_VALIGN_SUPER)
1977 size->ascent += (size->ascent / 3);
1978 }
1979
1980
1981 /*
1982 * Add a word to the page structure. Stashes the argument pointer in
1983 * the page data structure so that it will be deallocated on destroy.
1984 */
1985 void a_Dw_page_add_text (DwPage *page, char *text, DwStyle *style)
1986 {
1987 DwPageWord *word;
1988 DwRequisition size;
1989
1990 Dw_page_calc_text_size (page, text, style, &size);
1991 word = Dw_page_add_word (page, size.width, size.ascent, size.descent,
1992 style);
1993 word->content.type = DW_CONTENT_TEXT;
1994 word->content.data.text = text;
1995
1996 DBG_OBJ_ARRSET_STR (page, "words.%d.content.text", page->num_words - 1,
1997 word->content.data.text);
1998
1999 Dw_page_word_wrap (page, page->num_words - 1);
2000 }
2001
2002 /*
2003 * Add a widget (word type) to the page.
2004 */
2005 void a_Dw_page_add_widget (DwPage *page,
2006 DwWidget *widget,
2007 DwStyle *style)
2008 {
2009 DwPageWord *word;
2010 DwRequisition size;
2011
2012 /* We first assign -1 as parent_ref, since the call of widget->size_request
2013 * will otherwise let this DwPage be rewrapped from the beginning.
2014 * (parent_ref is actually undefined, but likely has the value 0.) At the,
2015 * end of this function, the correct value is assigned. */
2016 widget->parent_ref = -1;
2017
2018 p_Dw_widget_set_parent (widget, DW_WIDGET (page));
2019 a_Dw_widget_set_style (widget, style);
2020
2021 Dw_page_calc_widget_size (page, widget, &size);
2022 word =
2023 Dw_page_add_word (page, size.width, size.ascent, size.descent, style);
2024
2025 word->content.type = DW_CONTENT_WIDGET;
2026 word->content.data.widget = widget;
2027
2028 DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", page->num_words - 1,
2029 word->content.data.widget);
2030
2031 Dw_page_word_wrap (page, page->num_words - 1);
2032 word->content.data.widget->parent_ref = page->num_lines - 1;
2033 DBG_OBJ_SET_NUM (word->content.data.widget, "parent_ref",
2034 word->content.data.widget->parent_ref);
2035
2036 DEBUG_MSG(DEBUG_REWRAP_LEVEL,
2037 "Assigning parent_ref = %d to added word %d, "
2038 "in page with %d word(s)\n",
2039 page->num_lines - 1, page->num_words - 1, page->num_words);
2040 }
2041
2042
2043 /*
2044 * Add an anchor to the page. "name" is copied, so no strdup is neccessary for
2045 * the caller.
2046 *
2047 * Return TRUE on success, and FALSE, when this anchor had already been
2048 * added to the widget tree.
2049 */
2050 gboolean a_Dw_page_add_anchor (DwPage *page, const gchar *name, DwStyle *style)
2051 {
2052 DwPageWord *word;
2053 char *copy;
2054 gint32 y;
2055
2056 if (page->num_lines == 0)
2057 y = 0;
2058 else
2059 y = Dw_page_line_total_y_offset_i (page, page->num_lines - 1);
2060
2061 /*
2062 * Since an anchor does not take any space, it is safe to call
2063 * p_Dw_gtk_viewport_add_anchor already here.
2064 */
2065 if ((copy = p_Dw_gtk_viewport_add_anchor(DW_WIDGET(page), name, y)) == NULL)
2066 return FALSE;
2067 else {
2068 word = Dw_page_add_word (page, 0, 0, 0, style);
2069 word->content.type = DW_CONTENT_ANCHOR;
2070 word->content.data.anchor = copy;
2071 Dw_page_word_wrap (page, page->num_words - 1);
2072
2073 DBG_OBJ_ARRSET_STR (page, "words.%d.content.anchor", page->num_words - 1,
2074 word->content.data.anchor);
2075
2076 return TRUE;
2077 }
2078 }
2079
2080
2081 /*
2082 * ?
2083 */
2084 void a_Dw_page_add_space (DwPage *page, DwStyle *style)
2085 {
2086 gint nl, nw;
2087 gint space;
2088
2089 nl = page->num_lines - 1;
2090 if (nl >= 0) {
2091 nw = page->num_words - 1;
2092 if (nw >= 0) {
2093 /* todo: remove this test case */
2094 if (page->words[nw].orig_space != 0) {
2095 _MSG(" a_Dw_page_add_space:: already existing space!!!\n");
2096 }
2097 space = style->font->space_width;
2098 page->words[nw].orig_space = space;
2099 page->words[nw].eff_space = space;
2100 page->words[nw].content.space = TRUE;
2101
2102 DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", nw,
2103 page->words[nw].orig_space);
2104 DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", nw,
2105 page->words[nw].eff_space);
2106 DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", nw,
2107 page->words[nw].content.space);
2108
2109 a_Dw_style_unref (page->words[nw].space_style);
2110 page->words[nw].space_style = style;
2111 a_Dw_style_ref (style);
2112 }
2113 }
2114 }
2115
2116
2117 /*
2118 * Cause a paragraph break
2119 */
2120 void a_Dw_page_add_parbreak (DwPage *page, gint space, DwStyle *style)
2121 {
2122 DwPageWord *word, *word2;
2123 DwWidget *widget;
2124 DwPage *page2;
2125 gboolean isfirst;
2126 gint lineno;
2127
2128 /* A break may not be the first word of a page, or directly after
2129 the bullet/number (which is the first word) in a list item. (See
2130 also comment in Dw_page_size_request.) */
2131 if (page->num_words == 0 ||
2132 (page->list_item && page->num_words == 1)) {
2133 /* This is a bit hackish: If a break is added as the
2134 first/second word of a page, and the parent widget is also a
2135 DwPage, and there is a break before -- this is the case when
2136 a widget is used as a text box (lists, blockquotes, list
2137 items etc) -- then we simply adjust the break before, in a
2138 way that the space is in any case visible. */
2139
2140 /* Find the widget where to adjust the break_space. */
2141 for (widget = DW_WIDGET (page);
2142 widget->parent && DW_IS_PAGE (widget->parent);
2143 widget = widget->parent) {
2144 page2 = DW_PAGE (widget->parent);
2145 if (page2->list_item)
2146 isfirst = (page2->words[1].content.type == DW_CONTENT_WIDGET
2147 && page2->words[1].content.data.widget == widget);
2148 else
2149 isfirst = (page2->words[0].content.type == DW_CONTENT_WIDGET
2150 && page2->words[0].content.data.widget == widget);
2151 if (!isfirst) {
2152 /* The page we searched for has been found. */
2153 lineno = widget->parent_ref;
2154 if (lineno > 0 &&
2155 (word2 = &page2->words[page2->lines[lineno - 1].first_word]) &&
2156 word2->content.type == DW_CONTENT_BREAK) {
2157 if (word2->content.data.break_space < space) {
2158 word2->content.data.break_space = space;
2159 p_Dw_widget_queue_resize (DW_WIDGET (page2), lineno, FALSE);
2160 page2->must_queue_resize = FALSE;
2161 }
2162 }
2163 return;
2164 }
2165 /* Otherwise continue to examine parents. */
2166 }
2167 /* Return in any case. */
2168 return;
2169 }
2170
2171 /* Another break before? */
2172 if ((word = &page->words[page->num_words - 1]) &&
2173 word->content.type == DW_CONTENT_BREAK) {
2174 word->content.data.break_space =
2175 MAX (word->content.data.break_space, space);
2176 return;
2177 }
2178
2179 word = Dw_page_add_word (page, 0, 0, 0, style);
2180 word->content.type = DW_CONTENT_BREAK;
2181 word->content.data.break_space = space;
2182 Dw_page_word_wrap (page, page->num_words - 1);
2183 }
2184
2185 /*
2186 * Cause a line break.
2187 */
2188 void a_Dw_page_add_linebreak (DwPage *page, DwStyle *style)
2189 {
2190 DwPageWord *word;
2191
2192 if (page->num_words == 0 ||
2193 page->words[page->num_words - 1].content.type == DW_CONTENT_BREAK)
2194 /* An <BR> in an empty line gets the height of the current font
2195 * (why would someone else place it here?), ... */
2196 word = Dw_page_add_word (page, 0, style->font->font->ascent,
2197 style->font->font->descent, style);
2198 else
2199 /* ... otherwise, it has no size (and does not enlarge the line). */
2200 word = Dw_page_add_word (page, 0, 0, 0, style);
2201 word->content.type = DW_CONTENT_BREAK;
2202 word->content.data.break_space = 0;
2203 word->style = style;
2204 Dw_page_word_wrap (page, page->num_words - 1);
2205 }
2206
2207 /*
2208 * This function "hands" the last break of a page "over" to a parent
2209 * page. This is used for "collapsing spaces".
2210 */
2211 void a_Dw_page_hand_over_break (DwPage *page, DwStyle *style)
2212 {
2213 DwPageLine *last_line;
2214 DwWidget *parent;
2215
2216 if (page->num_lines == 0)
2217 return;
2218
2219 last_line = &page->lines[page->num_lines - 1];
2220 if (last_line->break_space != 0 &&
2221 (parent = DW_WIDGET(page)->parent) && DW_IS_PAGE (parent))
2222 a_Dw_page_add_parbreak (DW_PAGE (parent), last_line->break_space, style);
2223 }
2224
2225 /*
2226 * Any words added by a_Dw_page_add_... are not immediately (queued to
2227 * be) drawn, instead, this function must be called. This saves some
2228 * calls to p_Dw_widget_queue_resize.
2229 *
2230 */
2231 void a_Dw_page_flush (DwPage *page)
2232 {
2233 if (page->must_queue_resize) {
2234 p_Dw_widget_queue_resize (DW_WIDGET (page), -1, TRUE);
2235 page->must_queue_resize = FALSE;
2236 }
2237 }
2238
2239 /*
2240 * Change the color of all words with a specific link. Used to
2241 * visualize links opened in a new window.
2242 */
2243 void a_Dw_page_change_link_color (DwPage *page,
2244 gint link,
2245 gint32 new_color)
2246 {
2247 DwStyle *old_style, style_attrs;
2248 gint i, j, changed;
2249 DwWidget *widget = DW_WIDGET (page);
2250
2251 for (i = 0; i < page->num_lines; i++) {
2252 changed = FALSE;
2253
2254 for (j = page->lines[i].first_word; j < page->lines[i].last_word; j++)
2255 if (page->words[j].style->x_link == link) {
2256 old_style = page->words[j].style;
2257
2258 style_attrs = *old_style;
2259 style_attrs.color =
2260 a_Dw_style_color_new (new_color, widget->viewport->window);
2261 page->words[j].style =
2262 a_Dw_style_new (&style_attrs, widget->viewport->window);
2263 /* unref'ing it before may crash dillo! */
2264 a_Dw_style_unref (old_style);
2265 changed = TRUE;
2266 }
2267
2268 if (changed)
2269 p_Dw_widget_queue_draw_area (widget, 0,
2270 Dw_page_line_total_y_offset_i (page, i),
2271 widget->allocation.width,
2272 page->lines[i].ascent
2273 + page->lines[i].descent);
2274 }
2275 }
2276
2277 /*
2278 * Assign a new style to some text words. Other words than text are not
2279 * affected.
2280 */
2281 void a_Dw_page_change_word_style (DwPage *page, gint from, gint to,
2282 DwStyle *style, gboolean include_first_space,
2283 gboolean include_last_space)
2284 {
2285 int i;
2286 DwStyle *old_style = NULL, *old_space_style;
2287 gint line, break_line = DW_INFINITY;
2288 DwWidget *widget = DW_WIDGET (page);
2289 gboolean diffs = FALSE, must_queue_resize = FALSE;
2290 DwPageWord *word;
2291 DwRequisition size;
2292
2293 if (from == to)
2294 return;
2295
2296 line = Dw_page_find_line_of_word (page, MAX (from - 1, 0));
2297
2298 /* We also have to include the space before the first word. */
2299 for (i = MAX (from - 1, 0); i < to; i++) {
2300 word = &page->words[i];
2301
2302 if (word->content.type == DW_CONTENT_TEXT) {
2303 if (i >= from) {
2304 old_style = word->style;
2305 word->style = style;
2306 a_Dw_style_ref (style);
2307
2308 if ((diffs = a_Dw_style_size_diffs (old_style, style))) {
2309 must_queue_resize = TRUE;
2310 break_line = MIN (break_line, line);
2311 Dw_page_calc_text_size (page, word->content.data.text,
2312 style, &size);
2313 word->size = size;
2314 }
2315 }
2316
2317
2318 if ((i >= from || include_first_space) &&
2319 (i < to -1 || include_last_space)) {
2320 old_space_style = word->space_style;
2321 word->space_style = style;
2322 a_Dw_style_ref (style);
2323
2324 /* In most cases, old_space_style will be old_style, so
2325 * that a_Dw_style_size_diffs does not have to be called.
2326 * Note: For the space before the first word, old_style is
2327 * NULL.
2328 */
2329 if (old_space_style == old_style ?
2330 diffs : a_Dw_style_size_diffs (old_space_style, style)) {
2331 must_queue_resize = TRUE;
2332 break_line = MIN (break_line, line);
2333 if (word->content.space)
2334 word->orig_space = style->font->space_width;
2335 }
2336
2337 a_Dw_style_unref (old_space_style);
2338 }
2339
2340 if (old_style)
2341 a_Dw_style_unref (old_style);
2342 }
2343
2344 /* Resizing will also imply redrawing, so if we resize anyway,
2345 * redrawing can be skipped. Otherwise, whole lines are redrawn,
2346 * i.e. when we have completed the whole task, or if this word
2347 * is the last one in the line.
2348 */
2349 if (!must_queue_resize &&
2350 (i == to - 1 || i == page->lines[line].last_word - 1)) {
2351 p_Dw_widget_queue_draw_area
2352 (widget, 0, Dw_page_line_total_y_offset_i (page, line),
2353 widget->allocation.width,
2354 page->lines[line].ascent + page->lines[line].descent);
2355
2356 if (i < to - 1)
2357 line = Dw_page_find_line_of_word (page, i + 1);
2358 }
2359 }
2360
2361 if (must_queue_resize)
2362 p_Dw_widget_queue_resize (widget, break_line, TRUE);
2363 }
2364
2365
2366 /*
2367 * Standard Dw function.
2368 */
2369 static DwIterator *Dw_page_iterator (DwWidget *widget,
2370 gint mask,
2371 gboolean at_end)
2372 {
2373 DwIteratorInt *it = g_new (DwIteratorInt, 1);
2374 it->it.widget = widget;
2375 it->it.mask = mask;
2376 it->it.next = Dw_page_iterator_next;
2377 it->it.prev = Dw_page_iterator_prev;
2378 it->it.clone = p_Dw_iterator_clone_std_int;
2379 it->it.compare = p_Dw_iterator_compare_std_int;
2380 it->it.free = p_Dw_iterator_free_std;
2381 it->it.highlight = Dw_page_iterator_highlight;
2382 it->it.get_allocation = Dw_page_iterator_get_allocation;
2383
2384 if (at_end) {
2385 it->it.content.type = DW_CONTENT_END;
2386 it->pos = DW_PAGE(widget)->num_words;
2387 } else {
2388 it->it.content.type = DW_CONTENT_START;
2389 it->pos = -1;
2390 }
2391
2392 DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_page_iterator -> %p, at %s\n",
2393 it, at_end ? "end" : "start");
2394
2395 return (DwIterator*)it;
2396 }
2397
2398 /*
2399 * Standard Dw function.
2400 */
2401 static gboolean Dw_page_iterator_next (DwIterator *it)
2402 {
2403 DwPage *page = DW_PAGE (it->widget);
2404 DwIteratorInt *ii = (DwIteratorInt*)it;
2405
2406 DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_page_iterator_next (%p): %d of %d\n",
2407 it, ii->pos, page->num_words);
2408
2409 if (it->content.type == DW_CONTENT_END)
2410 return FALSE;
2411
2412 do {
2413 ii->pos++;
2414 if (ii->pos >= page->num_words) {
2415 it->content.type = DW_CONTENT_END;
2416 return FALSE;
2417 }
2418 } while ((page->words[ii->pos].content.type & it->mask) == 0);
2419
2420 it->content = page->words[ii->pos].content;
2421 return TRUE;
2422 }
2423
2424 /*
2425 * Standard Dw function.
2426 */
2427 static gboolean Dw_page_iterator_prev (DwIterator *it)
2428 {
2429 DwPage *page = DW_PAGE (it->widget);
2430 DwIteratorInt *ii = (DwIteratorInt*)it;
2431
2432 DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_page_iterator_prev (%p): %d of %d\n",
2433 it, ii->pos, page->num_words);
2434
2435 if (it->content.type == DW_CONTENT_START)
2436 return FALSE;
2437
2438 do {
2439 ii->pos--;
2440 if (ii->pos < 0) {
2441 it->content.type = DW_CONTENT_START;
2442 return FALSE;
2443 }
2444 } while ((page->words[ii->pos].content.type & it->mask) == 0);
2445
2446 it->content = page->words[ii->pos].content;
2447 return TRUE;
2448 }
2449
2450 /*
2451 * Standard Dw function.
2452 */
2453 void Dw_page_iterator_highlight (DwIterator *it,
2454 gint start,
2455 gint end,
2456 DwHighlightLayer layer)
2457 {
2458 DwIteratorInt *ii = (DwIteratorInt*)it;
2459 DwPage *page = DW_PAGE(it->widget);
2460 gint line;
2461
2462 page->words[ii->pos].hl_start[layer] = start;
2463 page->words[ii->pos].hl_end[layer] = end;
2464
2465 line = Dw_page_find_line_of_word (page, ii->pos);
2466 p_Dw_widget_queue_draw_area (it->widget, 0,
2467 Dw_page_line_total_y_offset_i (page, line),
2468 it->widget->allocation.width,
2469 page->lines[line].ascent
2470 + page->lines[line].descent);
2471 }
2472
2473 /*
2474 * Standard Dw function.
2475 */
2476 static void Dw_page_iterator_get_allocation (DwIterator *it,
2477 gint start,
2478 gint end,
2479 DwAllocation *allocation)
2480 {
2481 DwIteratorInt *ii = (DwIteratorInt*)it;
2482 DwPage *page = DW_PAGE(it->widget);
2483 DwPageWord *word;
2484 DwPageLine *line;
2485 gint wi, l;
2486 gint32 diff;
2487
2488 line = &page->lines[Dw_page_find_line_of_word (page, ii->pos)];
2489 word = &page->words[ii->pos];
2490
2491 /* the difference at the start */
2492 if (start == 0)
2493 diff = 0;
2494 else {
2495 if (word->content.type == DW_CONTENT_TEXT)
2496 diff = gdk_text_width (word->style->font->font,
2497 word->content.data.text, start);
2498 else
2499 diff = word->size.width;
2500 }
2501
2502 allocation->x =
2503 Dw_page_line_total_x_offset (page, line) + it->widget->allocation.x
2504 + diff;
2505 wi = line->first_word;
2506 while (wi < ii->pos) {
2507 word = &page->words[wi];
2508 allocation->x += word->size.width + word->eff_space;
2509 wi++;
2510 }
2511
2512 allocation->y =
2513 Dw_page_line_total_y_offset (page, line) + it->widget->allocation.y;
2514
2515 word = &page->words[wi];
2516 if (word->content.type == DW_CONTENT_TEXT) {
2517 l = strlen (word->content.data.text);
2518 if (end == l)
2519 allocation->width = word->size.width;
2520 else if (end >= l)
2521 allocation->width = word->size.width + word->eff_space;
2522 else
2523 allocation->width = gdk_text_width (word->style->font->font,
2524 word->content.data.text, end);
2525 } else {
2526 if (end == 1)
2527 allocation->width = word->size.width;
2528 else if (end >= 1)
2529 allocation->width = word->size.width + word->eff_space;
2530 else
2531 allocation->width = 0;
2532 }
2533
2534 allocation->width -= diff;
2535
2536 allocation->ascent = page->words[wi].size.ascent;
2537 allocation->descent = page->words[wi].size.descent;
2538
2539 DBG_MSGF (page, "scrolling", 0,
2540 "Dw_page_iterator_get_allocation: (%d, %d), (%d x %d x %d)",
2541 allocation->x, allocation->y,
2542 allocation->width, allocation->ascent, allocation->descent);
2543 }
+0
-177
src/dw_page.h less more
0 /* This module contains the dw_page widget, which is the "back end" to
1 Web text widgets including html. */
2
3 #ifndef __DW_PAGE_H__
4 #define __DW_PAGE_H__
5
6 #include <gdk/gdk.h>
7 #include "dw_container.h"
8 #include "url.h"
9
10 #ifdef __cplusplus
11 extern "C" {
12 #endif /* __cplusplus */
13
14 #define DW_TYPE_PAGE (a_Dw_page_get_type ())
15 #define DW_PAGE(obj) GTK_CHECK_CAST ((obj), DW_TYPE_PAGE, DwPage)
16 #define DW_PAGE_CLASS(klass) GTK_CHECK_CLASS_CAST ((klass), DW_TYPE_PAGE, \
17 DwPageClass)
18 #define DW_IS_PAGE(obj) GTK_CHECK_TYPE ((obj), DW_TYPE_PAGE)
19 #define DW_IS_PAGE_CLASS(klass) GTK_CHECK_CLASS_TYPE ((klass), DW_TYPE_PAGE)
20
21 typedef struct _DwPage DwPage;
22 typedef struct _DwPageClass DwPageClass;
23
24 /* Internal data structures (maybe shouldn't be in public .h file? */
25 typedef struct _DwPageLine DwPageLine;
26 typedef struct _DwPageWord DwPageWord;
27
28 struct _DwPageLine
29 {
30 gint first_word; /* first-word's position in DwPageWord [0 based] */
31 gint last_word; /* last-word's position in DwPageWord [1 based] */
32
33 /* "top" is always relative to the top of the first line, i.e.
34 * page->lines[0].top is always 0. */
35 gint32 top, ascent, descent, break_space, left_offset;
36
37 /* This is similar to descent, but includes the bottom margins of the
38 * widgets within this line. */
39 gint32 margin_descent;
40
41 /* The following members contain accumulated values, from the top
42 * down to the line before. */
43 gint32 max_line_width; /* maximum of all line widths */
44 gint32 max_word_min; /* maximum of all word minima */
45 gint32 max_par_max; /* maximum of all paragraph maxima */
46 gint32 par_min; /* the minimal total width down from the last
47 paragraph start, to the *beginning* of the
48 line */
49 gint32 par_max; /* the maximal total width down from the last
50 paragraph start, to the *beginning* of the
51 line */
52 };
53
54 struct _DwPageWord {
55 /* todo: perhaps add a x_left? */
56 DwRequisition size;
57 /* Space after the word, only if it's not a break: */
58 gint32 orig_space; /* from font, set by a_Dw_page_add_space */
59 gint32 eff_space; /* effective space, set by Dw_page_word_wrap,
60 used for drawing etc. */
61 gint hl_start[DW_HIGHLIGHT_NUM_LAYERS], hl_end[DW_HIGHLIGHT_NUM_LAYERS];
62
63 DwContent content;
64
65 DwStyle *style;
66 DwStyle *space_style; /* initially the same as of the word, later
67 set by a_Dw_page_add_space */
68 };
69
70
71 struct _DwPage
72 {
73 DwContainer container;
74
75 /* These fields provide some ad-hoc-functionality, used by sub-classes. */
76 gboolean list_item; /* If TRUE, the first word of the page is treated
77 specially (search in source). */
78 gint32 inner_padding; /* This is an additional padding on the left side
79 (used by DwListItem). */
80 gint32 line1_offset; /* This is an additional offset of the first line.
81 May be negative (shift to left) or positive
82 (shift to right). */
83 gint32 line1_offset_eff; /* The "effective" value of line1_offset, may
84 differ from line1_offset when
85 ignore_line1_offset_sometimes is set to TRUE */
86
87 /* The following is really hackish: It is used for DwTableCell (see
88 * comment in dw_table_cell.c), to avoid too wide table columns. If
89 * set to TRUE, it has following effects:
90 *
91 * (i) line1_offset is ignored in calculating the minimal width
92 * (which is used by DwTable!), and
93 * (ii) line1_offset is ignored (line1_offset_eff is set to 0),
94 * when line1_offset plus the width of the first word is
95 * greater than the the available witdh.
96 *
97 * todo: Eliminate all these ad-hoc features by a new, simpler and
98 * more elegant design. ;-)
99 */
100 gboolean ignore_line1_offset_sometimes;
101
102 gboolean must_queue_resize;
103
104 /* These values are set by set_... */
105 gint32 avail_width, avail_ascent, avail_descent;
106
107 gint32 last_line_width;
108 gint32 last_line_par_min;
109 gint32 last_line_par_max;
110 gint wrap_ref; /* [0 based] */
111
112 DwPageLine *lines;
113 gint num_lines;
114 gint num_lines_max; /* number allocated */
115
116 DwPageWord *words;
117 gint num_words;
118 gint num_words_max; /* number allocated */
119
120 /* The word index of the link under a button press, and the char
121 * position */
122 gint link_pressed_index;
123 gint link_pressed_char_pos;
124
125 gint hover_link; /* The link under the button. */
126 DwTooltip *hover_tooltip; /* The tooltip under the button. No ref hold. */
127 };
128
129
130 struct _DwPageClass
131 {
132 DwContainerClass parent_class;
133
134 gboolean (*link_entered) (DwPage *page,
135 gint link, gint x, gint y);
136 gboolean (*link_pressed) (DwPage *page,
137 gint link, gint x, gint y,
138 GdkEventButton *event);
139 gboolean (*link_released) (DwPage *page,
140 gint link, gint x, gint y,
141 GdkEventButton *event);
142 gboolean (*link_clicked) (DwPage *page,
143 gint link, gint x, gint y,
144 GdkEventButton *event);
145
146 void (*word_wrap) (DwPage *page,
147 gint word_ind);
148 };
149
150
151 DwWidget* a_Dw_page_new (void);
152 GtkType a_Dw_page_get_type (void);
153
154 void a_Dw_page_flush (DwPage *page);
155 void a_Dw_page_set_voffset (DwPage *page, gint32 voffset);
156 void a_Dw_page_add_text (DwPage *page, char *text, DwStyle *style);
157 void a_Dw_page_add_widget (DwPage *page,
158 DwWidget *widget,
159 DwStyle *style);
160 gboolean a_Dw_page_add_anchor (DwPage *page, const gchar *name, DwStyle *style);
161 void a_Dw_page_add_space(DwPage *page, DwStyle *style);
162 void a_Dw_page_add_parbreak (DwPage *page, gint space, DwStyle *style);
163 void a_Dw_page_add_linebreak (DwPage *page, DwStyle *style);
164 void a_Dw_page_hand_over_break (DwPage *page, DwStyle *style);
165 void a_Dw_page_change_link_color (DwPage *page, gint link, gint32 new_color);
166 void a_Dw_page_change_word_style (DwPage *page, gint from, gint to,
167 DwStyle *style, gboolean include_first_space,
168 gboolean include_last_space);
169
170
171 #ifdef __cplusplus
172 }
173 #endif /* __cplusplus */
174
175
176 #endif /* __DW_PAGE_H__ */
+0
-1031
src/dw_style.c less more
0 /*
1 * File: dw_style.c
2 *
3 * Copyright (C) 2001-2003 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "dw_style.h"
12 #include "dw_widget.h"
13
14 #include <gdk/gdk.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 /*#define DEBUG_LEVEL 1*/
20 #include "debug.h"
21
22 #define EQUIV(a, b) (((a) && (b)) || (!(a) && !(b)))
23
24 #define Dw_style_font_ref(font) ((font)->ref_count++)
25 #define Dw_style_font_unref(font) if (--((font)->ref_count) == 0) \
26 Dw_style_font_remove (font)
27
28 #define Dw_style_color_ref(color) ((color)->ref_count++)
29 #define Dw_style_color_unref(color) if (--((color)->ref_count) == 0) \
30 Dw_style_color_remove (color)
31
32 #define Dw_style_shaded_color_ref(color) ((color)->ref_count++)
33 #define Dw_style_shaded_color_unref(color) if (--((color)->ref_count) == 0) \
34 Dw_style_shaded_color_remove \
35 (color) \
36
37 static GHashTable *fonts_table;
38 static GHashTable *colors_table;
39 static GHashTable *shaded_colors_table;
40
41 static gint styles_num = 0;
42
43 /* Used by a_Dw_style_numtostr(). */
44 static const char
45 *roman_I0[] ={"","I","II","III","IV","V","VI","VII","VIII","IX"},
46 *roman_I1[] ={"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"},
47 *roman_I2[] ={"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"},
48 *roman_I3[] ={"","M","MM","MMM","MMMM"};
49
50
51 static gint Dw_style_font_equal (gconstpointer v1, gconstpointer v2);
52 static guint Dw_style_font_hash (gconstpointer key);
53 static void Dw_style_font_remove (DwStyleFont *font);
54
55 static void Dw_style_color_remove (DwStyleColor *color);
56 static void Dw_style_shaded_color_remove (DwStyleShadedColor *color);
57
58
59 /* ----------------------------------------------------------------------
60 *
61 * Initialization / Cleaning up
62 *
63 * ----------------------------------------------------------------------
64 */
65
66 /*
67 * Initialize the DwStyle submodule.
68 */
69 void a_Dw_style_init (void)
70 {
71 fonts_table = g_hash_table_new (Dw_style_font_hash, Dw_style_font_equal);
72 colors_table = g_hash_table_new (g_direct_hash, g_direct_equal);
73 shaded_colors_table = g_hash_table_new (g_direct_hash, g_direct_equal);
74 }
75
76
77 /*
78 * Called by a_Dw_style_freeall.
79 */
80 static void Dw_style_count_fonts (gpointer key,
81 gpointer value,
82 gpointer user_data)
83 {
84 DwStyleFont *font = (DwStyleFont*) key;
85 gint *count = (int*)user_data;
86
87 count[0]++;
88 count[1] += font->ref_count;
89 }
90
91 /*
92 * Called by a_Dw_style_freeall.
93 */
94 static void Dw_style_count_colors (gpointer key,
95 gpointer value,
96 gpointer user_data)
97 {
98 DwStyleColor *color = (DwStyleColor*) value;
99 gint *count = (int*)user_data;
100
101 count[0]++;
102 count[1] += color->ref_count;
103 }
104
105 /*
106 * Called by a_Dw_style_freeall.
107 */
108 static void Dw_style_count_shaded_colors (gpointer key,
109 gpointer value,
110 gpointer user_data)
111 {
112 DwStyleShadedColor *color = (DwStyleShadedColor*) value;
113 gint *count = (int*)user_data;
114
115 count[0]++;
116 count[1] += color->ref_count;
117 }
118
119
120 /*
121 * Free variables used by DwStyle, and do a check whether memory
122 * management works properly.
123 */
124 void a_Dw_style_freeall (void)
125 {
126 gint count[2];
127
128 if (styles_num)
129 g_warning ("%d styles left", styles_num);
130
131 count[0] = count[1] = 0;
132 g_hash_table_foreach (fonts_table, Dw_style_count_fonts, count);
133 if (count[0] || count[1])
134 g_warning ("%d fonts (%d references) left", count[0], count[1]);
135
136 count[0] = count[1] = 0;
137 g_hash_table_foreach (colors_table, Dw_style_count_colors, count);
138 if (count[0] || count[1])
139 g_warning ("%d colors (%d references) left", count[0], count[1]);
140
141 count[0] = count[1] = 0;
142 g_hash_table_foreach (shaded_colors_table, Dw_style_count_shaded_colors,
143 count);
144 if (count[0] || count[1])
145 g_warning ("%d shaded colors (%d references) left", count[0], count[1]);
146
147 g_hash_table_destroy (fonts_table);
148 g_hash_table_destroy (colors_table);
149 }
150
151
152 /* ----------------------------------------------------------------------
153 *
154 * Styles
155 *
156 * ----------------------------------------------------------------------
157 */
158
159
160 /*
161 * Set all style fields except font and color to reasonable defaults.
162 */
163 void a_Dw_style_init_values (DwStyle *style_attrs,
164 GdkWindow *window)
165 {
166 style_attrs->x_link = -1;
167 style_attrs->x_tooltip = NULL;
168 style_attrs->text_decoration = 0;
169 style_attrs->text_align = DW_STYLE_TEXT_ALIGN_LEFT;
170 style_attrs->list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
171 style_attrs->valign = DW_STYLE_VALIGN_MIDDLE;
172 style_attrs->background_color = NULL;
173 style_attrs->width = DW_STYLE_LENGTH_AUTO;
174 style_attrs->height = DW_STYLE_LENGTH_AUTO;
175
176 a_Dw_style_box_set_val (&style_attrs->margin, 0);
177 a_Dw_style_box_set_val (&style_attrs->border_width, 0);
178 a_Dw_style_box_set_val (&style_attrs->padding, 0);
179 a_Dw_style_box_set_border_color (style_attrs, NULL);
180 a_Dw_style_box_set_border_style (style_attrs, DW_STYLE_BORDER_NONE);
181 style_attrs->border_spacing = 0;
182
183 style_attrs->display = DW_STYLE_DISPLAY_INLINE;
184 style_attrs->white_space = DW_STYLE_WHITE_SPACE_NORMAL;
185 }
186
187
188 /*
189 * Reset those style attributes to their standard values, which are
190 * not inherited, according to CSS.
191 */
192 void a_Dw_style_reset_values (DwStyle *style_attrs,
193 GdkWindow *window)
194 {
195 style_attrs->x_link = -1;
196 style_attrs->background_color = NULL;
197 style_attrs->x_tooltip = NULL;
198
199 style_attrs->text_align = DW_STYLE_TEXT_ALIGN_LEFT; /* ??? */
200 style_attrs->valign = DW_STYLE_VALIGN_MIDDLE;
201 style_attrs->text_align_char = '.';
202 style_attrs->background_color = NULL;
203 style_attrs->width = DW_STYLE_LENGTH_AUTO;
204 style_attrs->height = DW_STYLE_LENGTH_AUTO;
205
206 a_Dw_style_box_set_val (&style_attrs->margin, 0);
207 a_Dw_style_box_set_val (&style_attrs->border_width, 0);
208 a_Dw_style_box_set_val (&style_attrs->padding, 0);
209 a_Dw_style_box_set_border_color (style_attrs, NULL);
210 a_Dw_style_box_set_border_style (style_attrs, DW_STYLE_BORDER_NONE);
211 style_attrs->border_spacing = 0;
212
213 style_attrs->display = DW_STYLE_DISPLAY_INLINE;
214 style_attrs->white_space = DW_STYLE_WHITE_SPACE_NORMAL;
215 }
216
217 /*
218 * Return a new DwStyle, with increased reference pointer.
219 */
220 DwStyle* a_Dw_style_new (DwStyle *style_attrs,
221 GdkWindow *window)
222 {
223 DwStyle *style;
224
225 style = g_new (DwStyle, 1);
226 *style = *style_attrs;
227 style->ref_count = 1;
228 Dw_style_font_ref (style->font);
229 Dw_style_color_ref (style->color);
230 if (style->background_color)
231 Dw_style_color_ref (style->background_color);
232 if (style->border_color.top)
233 Dw_style_shaded_color_ref (style->border_color.top);
234 if (style->border_color.right)
235 Dw_style_shaded_color_ref (style->border_color.right);
236 if (style->border_color.bottom)
237 Dw_style_shaded_color_ref (style->border_color.bottom);
238 if (style->border_color.left)
239 Dw_style_shaded_color_ref (style->border_color.left);
240 if (style->x_tooltip)
241 a_Dw_tooltip_ref (style->x_tooltip);
242
243 styles_num++;
244 return style;
245 }
246
247 /*
248 * Remove a style (called when ref_count == 0).
249 */
250 void Dw_style_remove (DwStyle *style)
251 {
252 Dw_style_font_unref (style->font);
253 Dw_style_color_unref (style->color);
254 if (style->background_color)
255 Dw_style_color_unref (style->background_color);
256 if (style->border_color.top)
257 Dw_style_shaded_color_unref (style->border_color.top);
258 if (style->border_color.right)
259 Dw_style_shaded_color_unref (style->border_color.right);
260 if (style->border_color.bottom)
261 Dw_style_shaded_color_unref (style->border_color.bottom);
262 if (style->border_color.left)
263 Dw_style_shaded_color_unref (style->border_color.left);
264 if (style->x_tooltip)
265 a_Dw_tooltip_unref (style->x_tooltip);
266
267 g_free (style);
268 styles_num--;
269 }
270
271
272
273 /* ----------------------------------------------------------------------
274 *
275 * Fonts
276 *
277 * ----------------------------------------------------------------------
278 */
279
280 /*
281 * Create the GdkFont. font->name contains one name. If try_all is
282 * TRUE, try also standard fonts, if anything else fails.
283 */
284 static void Dw_style_font_realize (DwStyleFont *font, gboolean try_all)
285 {
286 char fontname[256], *style_char_1 = NULL, *style_char_2 = NULL;
287
288 switch (font->style) {
289 case DW_STYLE_FONT_STYLE_NORMAL:
290 style_char_1 = style_char_2 = "r";
291 break;
292 case DW_STYLE_FONT_STYLE_ITALIC:
293 style_char_1 = "i";
294 style_char_2 = "o";
295 break;
296 case DW_STYLE_FONT_STYLE_OBLIQUE:
297 style_char_1 = "o";
298 style_char_2 = "i";
299 break;
300 }
301
302 sprintf (fontname, "-*-%s-%s-%s-*-*-%d-*-75-75-*-*-iso8859-1",
303 font->name,
304 (font->weight >= 500) ? "bold" : "medium",
305 style_char_1, font->size);
306 font->font = gdk_font_load (fontname);
307
308 if (font->font == NULL && font->style != DW_STYLE_FONT_STYLE_NORMAL) {
309 sprintf (fontname, "-*-%s-%s-%s-*-*-%d-*-75-75-*-*-iso8859-1",
310 font->name,
311 (font->weight >= 500) ? "bold" : "medium",
312 style_char_2, font->size);
313 font->font = gdk_font_load (fontname);
314 }
315
316 if (try_all) {
317 if (font->font == NULL) {
318 /* Can't load the font - substitute the default instead. */
319 font->font =
320 gdk_font_load
321 ("-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-iso8859-1");
322 }
323
324 if (font->font == NULL) {
325 /* Try another platform-font that should be available. (iPaq) */
326 font->font =
327 gdk_font_load
328 ("-misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1");
329 }
330
331 if (font->font == NULL) {
332 /* Can't load any suitable font! */
333 g_warning ("Can't load any ISO8859-1 font!?! :(");
334 font->font =
335 gdk_font_load ("-adobe-helvetica-*-*-*--*-*-*-*-*-*-*-*");
336 }
337 }
338
339 if (font->font) {
340 font->space_width = gdk_char_width (font->font, ' ');
341 font->x_height = gdk_char_height (font->font, 'x');
342 }
343 }
344
345
346 /*
347 * Used for the font_table hash table.
348 */
349 static gint Dw_style_font_equal (gconstpointer v1, gconstpointer v2)
350 {
351 const DwStyleFont *font1 = (DwStyleFont*) v1, *font2 = (DwStyleFont*) v2;
352
353 return (font1->size == font2->size &&
354 font1->weight == font2->weight &&
355 font1->style == font2->style &&
356 strcmp (font1->name, font2->name) == 0);
357 }
358
359
360 /*
361 * Used for the font_table hash table.
362 */
363 static guint Dw_style_font_hash (gconstpointer key)
364 {
365 const DwStyleFont *font = (DwStyleFont*) key;
366 guint h;
367
368 h = g_str_hash (font->name);
369 h = (h << 5) - h + font->size;
370 h = (h << 5) - h + font->weight;
371 h = (h << 5) - h + font->style;
372 return h;
373 }
374
375
376 /*
377 * Returns a new or already existing font. This function is only used
378 * internally, and called by a_Dw_style_font_new and
379 * a_Dw_style_font_new_from_list.
380 *
381 * Note that the reference counter is not increased/set to zero, it
382 * will be increased by a_Dw_style_new. If 'try_all' is TRUE, try also
383 * standard fonts, if anything else fails.
384 */
385 static DwStyleFont* Dw_style_font_new_internal (DwStyleFont *font_attrs,
386 gboolean try_all)
387 {
388 DwStyleFont *font;
389
390 g_return_val_if_fail (font_attrs->name != NULL, NULL);
391
392 if ((font = g_hash_table_lookup (fonts_table, font_attrs))) {
393 return font;
394 } else {
395 font = g_new (DwStyleFont, 1);
396 font->size = font_attrs->size;
397 font->weight = font_attrs->weight;
398 font->style = font_attrs->style;
399 font->name = g_strdup (font_attrs->name);
400 font->ref_count = 0;
401
402 Dw_style_font_realize (font, try_all);
403 if (font->font) {
404 g_hash_table_insert (fonts_table, font, font);
405 return font;
406 } else {
407 g_free (font->name);
408 g_free (font);
409 return NULL;
410 }
411 }
412 }
413
414
415 /*
416 * Return a new or already existing font, with one name in
417 * font_attrs->name. See also Dw_style_font_new_internal.
418 */
419 DwStyleFont* a_Dw_style_font_new (DwStyleFont *font_attrs)
420 {
421 DwStyleFont *font;
422
423 font = Dw_style_font_new_internal (font_attrs, TRUE);
424 if (font == NULL)
425 g_error ("Could not find any font.");
426
427 return font;
428 }
429
430
431 /*
432 * Return a new or already existing font, with font_attrs->name
433 * containing a comma separated list of names. See also
434 * Dw_style_font_new_internal.
435 */
436 DwStyleFont* a_Dw_style_font_new_from_list (DwStyleFont *font_attrs,
437 gchar *default_family)
438 {
439 DwStyleFont *font = NULL, font_attrs2;
440 char *comma, *list, *current;
441
442 font_attrs2 = *font_attrs;
443 current = list = g_strdup (font_attrs->name);
444
445 while (current && (font == NULL)) {
446 comma = strchr (current, ',');
447 if (comma) *comma = 0;
448
449 font_attrs2.name = current;
450 font = Dw_style_font_new_internal (&font_attrs2, FALSE);
451 if (font)
452 break;
453
454 if (comma) {
455 current = comma + 1;
456 while (isspace (*current)) current++;
457 } else
458 current = NULL;
459 }
460
461 g_free (list);
462
463 if (font == NULL) {
464 font_attrs2.name = default_family;
465 font = Dw_style_font_new_internal (&font_attrs2, TRUE);
466 }
467
468 if (font == NULL)
469 g_error ("Could not find any font.");
470
471 return font;
472 }
473
474
475 /*
476 * Remove a font (called when ref_count == 0).
477 */
478 static void Dw_style_font_remove (DwStyleFont *font)
479 {
480 g_hash_table_remove (fonts_table, font);
481 g_free (font->name);
482 gdk_font_unref (font->font);
483 g_free (font);
484 }
485
486
487 /* ----------------------------------------------------------------------
488 *
489 * Colors
490 *
491 * ----------------------------------------------------------------------
492 */
493
494 /*
495 * Copied from gtkstyle.c.
496 * Convert RGB into HLS.
497 */
498 static void Dw_style_rgb_to_hls (gdouble *r,
499 gdouble *g,
500 gdouble *b)
501 {
502 gdouble min;
503 gdouble max;
504 gdouble red;
505 gdouble green;
506 gdouble blue;
507 gdouble h, l, s;
508 gdouble delta;
509
510 red = *r;
511 green = *g;
512 blue = *b;
513
514 if (red > green) {
515 if (red > blue)
516 max = red;
517 else
518 max = blue;
519
520 if (green < blue)
521 min = green;
522 else
523 min = blue;
524 } else {
525 if (green > blue)
526 max = green;
527 else
528 max = blue;
529
530 if (red < blue)
531 min = red;
532 else
533 min = blue;
534 }
535
536 l = (max + min) / 2;
537 s = 0;
538 h = 0;
539
540 if (max != min) {
541 if (l <= 0.5)
542 s = (max - min) / (max + min);
543 else
544 s = (max - min) / (2 - max - min);
545
546 delta = max -min;
547 if (red == max)
548 h = (green - blue) / delta;
549 else if (green == max)
550 h = 2 + (blue - red) / delta;
551 else if (blue == max)
552 h = 4 + (red - green) / delta;
553
554 h *= 60;
555 if (h < 0.0)
556 h += 360;
557 }
558
559 *r = h;
560 *g = l;
561 *b = s;
562 }
563
564 /*
565 * Copied from gtkstyle.c.
566 * Convert HLS into RGB.
567 */
568 static void Dw_style_hls_to_rgb (gdouble *h,
569 gdouble *l,
570 gdouble *s)
571 {
572 gdouble hue;
573 gdouble lightness;
574 gdouble saturation;
575 gdouble m1, m2;
576 gdouble r, g, b;
577
578 lightness = *l;
579 saturation = *s;
580
581 if (lightness <= 0.5)
582 m2 = lightness * (1 + saturation);
583 else
584 m2 = lightness + saturation - lightness * saturation;
585 m1 = 2 * lightness - m2;
586
587 if (saturation == 0) {
588 *h = lightness;
589 *l = lightness;
590 *s = lightness;
591 } else {
592 hue = *h + 120;
593 while (hue > 360)
594 hue -= 360;
595 while (hue < 0)
596 hue += 360;
597
598 if (hue < 60)
599 r = m1 + (m2 - m1) * hue / 60;
600 else if (hue < 180)
601 r = m2;
602 else if (hue < 240)
603 r = m1 + (m2 - m1) * (240 - hue) / 60;
604 else
605 r = m1;
606
607 hue = *h;
608 while (hue > 360)
609 hue -= 360;
610 while (hue < 0)
611 hue += 360;
612
613 if (hue < 60)
614 g = m1 + (m2 - m1) * hue / 60;
615 else if (hue < 180)
616 g = m2;
617 else if (hue < 240)
618 g = m1 + (m2 - m1) * (240 - hue) / 60;
619 else
620 g = m1;
621
622 hue = *h - 120;
623 while (hue > 360)
624 hue -= 360;
625 while (hue < 0)
626 hue += 360;
627
628 if (hue < 60)
629 b = m1 + (m2 - m1) * hue / 60;
630 else if (hue < 180)
631 b = m2;
632 else if (hue < 240)
633 b = m1 + (m2 - m1) * (240 - hue) / 60;
634 else
635 b = m1;
636
637 *h = r;
638 *l = g;
639 *s = b;
640 }
641 }
642
643
644 /*
645 * Create the necessary recources (GdkColor, GdkGC).
646 * k is a factor the color is multiplied with before, this is needed
647 * for shaded colors.
648 */
649 static void Dw_style_color_create (gint color_val,
650 GdkWindow *window,
651 GdkColor *color,
652 GdkGC **gc,
653 gint d)
654 {
655 gint red, green, blue;
656 gdouble hue, lightness, saturation;
657
658 red = (color_val >> 16) & 255;
659 green = (color_val >> 8) & 255;
660 blue = color_val & 255;
661
662 if (d) {
663 hue = (gdouble)red / 255;
664 lightness = (gdouble)green / 255;
665 saturation = (gdouble)blue / 255;
666
667 DEBUG_MSG (1, "Shaded by %1.3g: (%1.3g, %1.3g, %1.3g) -> ",
668 k, hue, lightness, saturation);
669 Dw_style_rgb_to_hls (&hue, &lightness, &saturation);
670
671 if (lightness > 0.8) {
672 if (d > 0)
673 lightness -= 0.2;
674 else
675 lightness -= 0.4;
676 } else if (lightness < 0.2) {
677 if (d > 0)
678 lightness += 0.4;
679 else
680 lightness += 0.2;
681 } else
682 lightness += d * 0.2;
683
684 Dw_style_hls_to_rgb (&hue, &lightness, &saturation);
685 DEBUG_MSG (1, "(%1.3g, %1.3g, %1.3g)\n", hue, lightness, saturation);
686
687 red = hue * 255;
688 green = lightness * 255;
689 blue = saturation * 255;
690 }
691
692 blue |= blue << 8;
693 red |= red << 8;
694 green |= green << 8;
695
696 color->red = red;
697 color->green = green;
698 color->blue = blue;
699 gdk_color_alloc (gdk_window_get_colormap (window), color);
700
701 *gc = gdk_gc_new (window);
702 gdk_gc_set_foreground (*gc, color);
703 }
704
705 /*
706 Return a new or already existing color. color_val has the form
707 0xrrggbb.
708 */
709 DwStyleColor* a_Dw_style_color_new (gint color_val,
710 GdkWindow *window)
711 {
712 DwStyleColor *color;
713
714 color = g_hash_table_lookup (colors_table, GINT_TO_POINTER (color_val));
715 if (color == NULL) {
716 color = g_new (DwStyleColor, 1);
717 color->ref_count = 0;
718 color->color_val = color_val;
719
720 Dw_style_color_create (color_val, window,
721 &color->color, &color->gc, 0);
722 Dw_style_color_create (color_val ^ 0xffffff, window,
723 &color->inverse_color, &color->inverse_gc, 0);
724 g_hash_table_insert (colors_table, GINT_TO_POINTER (color_val), color);
725 g_hash_table_insert (colors_table, GINT_TO_POINTER (color_val), color);
726 }
727
728 return color;
729 }
730
731 /*
732 * Remove a color (called when ref_count == 0).
733 */
734 static void Dw_style_color_remove (DwStyleColor *color)
735 {
736 g_hash_table_remove (colors_table, GINT_TO_POINTER (color->color_val));
737 gdk_gc_destroy (color->gc);
738 gdk_gc_destroy (color->inverse_gc);
739 g_free (color);
740 }
741
742
743 /*
744 * Return a new or already existing shaded color. color_val has the
745 * form 0xrrggbb.
746 */
747 DwStyleShadedColor* a_Dw_style_shaded_color_new (gint color_val,
748 GdkWindow *window)
749 {
750 DwStyleShadedColor *color;
751
752 color =
753 g_hash_table_lookup (shaded_colors_table, GINT_TO_POINTER (color_val));
754 if (color == NULL) {
755 color = g_new (DwStyleShadedColor, 1);
756 color->ref_count = 0;
757 color->color_val = color_val;
758
759 Dw_style_color_create (color_val, window,
760 &color->color, &color->gc, 0);
761 Dw_style_color_create (color_val ^ 0xffffff, window,
762 &color->inverse_color, &color->inverse_gc, 0);
763 Dw_style_color_create (color_val, window,
764 &color->color_dark, &color->gc_dark, -1);
765 Dw_style_color_create (color_val, window,
766 &color->color_light, &color->gc_light, +1);
767 g_hash_table_insert (shaded_colors_table,
768 GINT_TO_POINTER (color_val), color);
769 }
770
771 return color;
772 }
773
774 /*
775 * Remove a shaded color (called when ref_count == 0).
776 */
777 static void Dw_style_shaded_color_remove (DwStyleShadedColor *color)
778 {
779 g_hash_table_remove (shaded_colors_table,
780 GINT_TO_POINTER (color->color_val));
781 gdk_gc_destroy (color->gc);
782 gdk_gc_destroy (color->gc_dark);
783 gdk_gc_destroy (color->gc_light);
784 g_free (color);
785 }
786
787 /*
788 * This function returns whether something may change its size, when
789 * its style changes from style1 to style2. It is mainly for
790 * optimizing style changes where only colors etc change (where FALSE
791 * would be returned), in some cases it may return TRUE, although a
792 * size change does not actually happen (e.g. when in a certain
793 * context a particular attribute is ignored).
794 */
795 gboolean a_Dw_style_size_diffs (DwStyle *style1,
796 DwStyle *style2)
797 {
798 /* todo: Should for CSS implemented properly. Currently, size
799 * changes are not needed, so always FALSE is returned. See also
800 * a_Dw_widget_set_style(). */
801 return FALSE;
802 }
803
804
805 /* ----------------------------------------------------------------------
806 *
807 * Drawing functions
808 *
809 * ----------------------------------------------------------------------
810 */
811
812 /*
813 * Draw a part of a border.
814 */
815 static void Dw_style_draw_polygon (GdkDrawable *drawable,
816 GdkGC *gc,
817 gint32 x1, gint32 y1, gint32 x2, gint32 y2,
818 gint32 width, gint32 w1, gint32 w2)
819 {
820 GdkPoint points[4];
821
822 if (width != 0) {
823 if (width == 1) {
824 if (x1 == x2)
825 gdk_draw_line (drawable, gc, x1, y1, x2, y2 - 1);
826 else
827 gdk_draw_line (drawable, gc, x1, y1, x2 - 1, y2);
828 } else if (width == -1) {
829 if (x1 == x2)
830 gdk_draw_line (drawable, gc, x1 - 1, y1, x2 - 1, y2 - 1);
831 else
832 gdk_draw_line (drawable, gc, x1, y1 - 1, x2 - 1, y2 - 1);
833 } else {
834 points[0].x = x1;
835 points[0].y = y1;
836 points[1].x = x2;
837 points[1].y = y2;
838
839 if (x1 == x2) {
840 points[2].x = x1 + width;
841 points[2].y = y2 + w2;
842 points[3].x = x1 + width;
843 points[3].y = y1 + w1;
844 } else {
845 points[2].x = x2 + w2;
846 points[2].y = y1 + width;
847 points[3].x = x1 + w1;
848 points[3].y = y1 + width;
849 }
850
851 gdk_draw_polygon (drawable, gc, TRUE, points, 4);
852 }
853 }
854 }
855
856
857 /*
858 * Draw the border of a region in window, according to style. Used by
859 * Dw_widget_draw_box and Dw_widget_draw_widget_box.
860 */
861
862 #define LIMIT(v) if ((v) < -30000) v = -30000; \
863 if ((v) > 30000) v = 30000
864
865 void p_Dw_style_draw_border (GdkWindow *window,
866 GdkRectangle *area,
867 gint32 vx,
868 gint32 vy,
869 gint32 x,
870 gint32 y,
871 gint32 width,
872 gint32 height,
873 DwStyle *style,
874 gboolean inverse)
875 {
876 /* todo: a lot! */
877 GdkGC *dark_gc, *light_gc, *normal_gc;
878 GdkGC *top_gc, *right_gc, *bottom_gc, *left_gc;
879 gint32 xb1, yb1, xb2, yb2, xp1, yp1, xp2, yp2;
880
881 if (style->border_style.top == DW_STYLE_BORDER_NONE)
882 return;
883
884 xb1 = x + style->margin.left - vx;
885 yb1 = y + style->margin.top - vy;
886 xb2 = xb1 + width - style->margin.left - style->margin.right;
887 yb2 = yb1 + height - style->margin.top - style->margin.bottom;
888
889 xp1 = xb1 + style->border_width.top;
890 yp1 = yb1 + style->border_width.left;
891 xp2 = xb2 + style->border_width.bottom;
892 yp2 = yb2 + style->border_width.right;
893
894 /* Make sure that we pass 16-bit values to Gdk functions. */
895 LIMIT (xb1);
896 LIMIT (xb2);
897 LIMIT (yb1);
898 LIMIT (yb2);
899
900 light_gc = inverse ? style->border_color.top->gc_dark :
901 style->border_color.top->gc_light;
902 dark_gc = inverse ? style->border_color.top->gc_light :
903 style->border_color.top->gc_dark;
904 normal_gc = inverse ? style->border_color.top->inverse_gc :
905 style->border_color.top->gc;
906
907 switch (style->border_style.top) {
908 case DW_STYLE_BORDER_INSET:
909 top_gc = left_gc = dark_gc;
910 right_gc = bottom_gc = light_gc;
911 break;
912
913 case DW_STYLE_BORDER_OUTSET:
914 top_gc = left_gc = light_gc;
915 right_gc = bottom_gc = dark_gc;
916 break;
917
918 default:
919 top_gc = right_gc = bottom_gc = left_gc = normal_gc;
920 break;
921 }
922
923 Dw_style_draw_polygon (window, top_gc, xb1, yb1, xb2, yb1,
924 style->border_width.top,
925 style->border_width.left,
926 - style->border_width.right);
927 Dw_style_draw_polygon (window, right_gc, xb2, yb1, xb2, yb2,
928 - style->border_width.right,
929 style->border_width.top,
930 - style->border_width.bottom);
931 Dw_style_draw_polygon (window, bottom_gc, xb1, yb2, xb2, yb2,
932 - style->border_width.bottom,
933 style->border_width.left,
934 - style->border_width.right);
935 Dw_style_draw_polygon (window, left_gc, xb1, yb1, xb1, yb2,
936 style->border_width.left,
937 style->border_width.top,
938 - style->border_width.bottom);
939 }
940
941
942 /*
943 * Draw the background (content plus padding) of a region in window,
944 * according to style. Used by Dw_widget_draw_box and
945 * Dw_widget_draw_widget_box.
946 */
947 void p_Dw_style_draw_background (GdkWindow *window,
948 GdkRectangle *area,
949 gint32 vx,
950 gint32 vy,
951 gint32 x,
952 gint32 y,
953 gint32 width,
954 gint32 height,
955 DwStyle *style,
956 gboolean inverse)
957 {
958 DwRectangle dw_area, bg_area, intersection;
959
960 if (style->background_color) {
961 dw_area.x = area->x + vx;
962 dw_area.y = area->y + vy;
963 dw_area.width = area->width;
964 dw_area.height = area->height;
965
966 bg_area.x = x + style->margin.left + style->border_width.left;
967 bg_area.y = y + style->margin.top + style->border_width.top;
968 bg_area.width =
969 width - style->margin.left - style->border_width.left -
970 style->margin.right - style->border_width.right;
971 bg_area.height =
972 height - style->margin.top - style->border_width.top -
973 style->margin.bottom - style->border_width.bottom;
974
975 if (p_Dw_rectangle_intersect (&dw_area, &bg_area, &intersection))
976 gdk_draw_rectangle (window,
977 inverse ? style->background_color->inverse_gc :
978 style->background_color->gc,
979 TRUE, intersection.x - vx, intersection.y - vy,
980 intersection.width, intersection.height);
981 }
982 }
983
984
985 /*
986 * Convert a number into a string, in a given style. Used for ordered lists.
987 */
988 void a_Dw_style_numtostr (gint num,
989 gchar *buf,
990 gint buflen,
991 DwStyleListStyleType list_style_tyle)
992 {
993 int i3, i2, i1, i0;
994 gboolean low = FALSE;
995 int start_ch = 'A';
996
997 switch(list_style_tyle){
998 case DW_STYLE_LIST_STYLE_TYPE_LOWER_ALPHA:
999 start_ch = 'a';
1000 case DW_STYLE_LIST_STYLE_TYPE_UPPER_ALPHA:
1001 i0 = num - 1;
1002 i1 = i0/26 - 1; i2 = i1/26 - 1;
1003 if (i2 > 25) /* more than 26+26^2+26^3=18278 elements ? */
1004 sprintf(buf, "****.");
1005 else
1006 sprintf(buf, "%c%c%c.",
1007 i2<0 ? ' ' : start_ch + i2%26,
1008 i1<0 ? ' ' : start_ch + i1%26,
1009 i0<0 ? ' ' : start_ch + i0%26);
1010 break;
1011 case DW_STYLE_LIST_STYLE_TYPE_LOWER_ROMAN:
1012 low = TRUE;
1013 case DW_STYLE_LIST_STYLE_TYPE_UPPER_ROMAN:
1014 i0 = num;
1015 i1 = i0/10; i2 = i1/10; i3 = i2/10;
1016 i0 %= 10; i1 %= 10; i2 %= 10;
1017 if (num < 0 || i3 > 4) /* more than 4999 elements ? */
1018 sprintf(buf, "****.");
1019 else
1020 g_snprintf(buf, buflen, "%s%s%s%s.", roman_I3[i3], roman_I2[i2],
1021 roman_I1[i1], roman_I0[i0]);
1022 if (low)
1023 g_strdown(buf);
1024 break;
1025 case DW_STYLE_LIST_STYLE_TYPE_DECIMAL:
1026 default:
1027 g_snprintf(buf, buflen, "%d.", num);
1028 break;
1029 }
1030 }
+0
-305
src/dw_style.h less more
0 #ifndef __DW_STYLE_H__
1 #define __DW_STYLE_H__
2
3 #include <gdk/gdktypes.h>
4 #include "dw_tooltip.h"
5
6 #define DW_STYLE_ALIGN_LEFT 1
7 #define DW_STYLE_ALIGN_RIGHT 2
8
9 typedef enum {
10 DW_STYLE_BORDER_NONE,
11 DW_STYLE_BORDER_HIDDEN,
12 DW_STYLE_BORDER_DOTTED,
13 DW_STYLE_BORDER_DASHED,
14 DW_STYLE_BORDER_SOLID,
15 DW_STYLE_BORDER_DOUBLE,
16 DW_STYLE_BORDER_GROOVE,
17 DW_STYLE_BORDER_RIDGE,
18 DW_STYLE_BORDER_INSET,
19 DW_STYLE_BORDER_OUTSET
20 } DwStyleBorderStyle;
21
22 typedef enum {
23 DW_STYLE_TEXT_ALIGN_LEFT,
24 DW_STYLE_TEXT_ALIGN_RIGHT,
25 DW_STYLE_TEXT_ALIGN_CENTER,
26 DW_STYLE_TEXT_ALIGN_JUSTIFY,
27 DW_STYLE_TEXT_ALIGN_STRING
28 } DwStyleTextAlignType;
29
30 typedef enum {
31 DW_STYLE_VALIGN_TOP,
32 DW_STYLE_VALIGN_BOTTOM,
33 DW_STYLE_VALIGN_MIDDLE,
34 DW_STYLE_VALIGN_BASELINE,
35 DW_STYLE_VALIGN_SUB,
36 DW_STYLE_VALIGN_SUPER
37 } DwStyleVAlignType;
38
39 typedef enum {
40 /* todo: incomplete */
41 DW_STYLE_DISPLAY_BLOCK,
42 DW_STYLE_DISPLAY_INLINE,
43 DW_STYLE_DISPLAY_LIST_ITEM,
44 DW_STYLE_DISPLAY_TABLE,
45 DW_STYLE_DISPLAY_TABLE_ROW_GROUP,
46 DW_STYLE_DISPLAY_TABLE_HEADER_GROUP,
47 DW_STYLE_DISPLAY_TABLE_FOOTER_GROUP,
48 DW_STYLE_DISPLAY_TABLE_ROW,
49 DW_STYLE_DISPLAY_TABLE_CELL,
50 DW_STYLE_DISPLAY_LAST
51 } DwStyleDisplayType;
52
53
54 typedef enum {
55 DW_STYLE_LIST_STYLE_TYPE_DISC,
56 DW_STYLE_LIST_STYLE_TYPE_CIRCLE,
57 DW_STYLE_LIST_STYLE_TYPE_SQUARE,
58 DW_STYLE_LIST_STYLE_TYPE_DECIMAL,
59 DW_STYLE_LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO,
60 DW_STYLE_LIST_STYLE_TYPE_LOWER_ROMAN,
61 DW_STYLE_LIST_STYLE_TYPE_UPPER_ROMAN,
62 DW_STYLE_LIST_STYLE_TYPE_LOWER_GREEK,
63 DW_STYLE_LIST_STYLE_TYPE_LOWER_ALPHA,
64 DW_STYLE_LIST_STYLE_TYPE_LOWER_LATIN,
65 DW_STYLE_LIST_STYLE_TYPE_UPPER_ALPHA,
66 DW_STYLE_LIST_STYLE_TYPE_UPPER_LATIN,
67 DW_STYLE_LIST_STYLE_TYPE_HEBREW,
68 DW_STYLE_LIST_STYLE_TYPE_ARMENIAN,
69 DW_STYLE_LIST_STYLE_TYPE_GEORGIAN,
70 DW_STYLE_LIST_STYLE_TYPE_CJK_IDEOGRAPHIC,
71 DW_STYLE_LIST_STYLE_TYPE_HIRAGANA,
72 DW_STYLE_LIST_STYLE_TYPE_KATAKANA,
73 DW_STYLE_LIST_STYLE_TYPE_HIRAGANA_IROHA,
74 DW_STYLE_LIST_STYLE_TYPE_KATAKANA_IROHA,
75 DW_STYLE_LIST_STYLE_TYPE_NONE
76 } DwStyleListStyleType;
77
78 typedef enum {
79 DW_STYLE_FONT_STYLE_NORMAL,
80 DW_STYLE_FONT_STYLE_ITALIC,
81 DW_STYLE_FONT_STYLE_OBLIQUE
82 } DwStyleFontStyle;
83
84 typedef enum {
85 DW_STYLE_TEXT_DECORATION_UNDERLINE = 1 << 0,
86 DW_STYLE_TEXT_DECORATION_OVERLINE = 1 << 1,
87 DW_STYLE_TEXT_DECORATION_LINE_THROUGH = 1 << 2,
88 DW_STYLE_TEXT_DECORATION_BLINK = 1 << 3
89 } DwStyleTextDecoration;
90
91 typedef enum {
92 DW_STYLE_WHITE_SPACE_NORMAL,
93 DW_STYLE_WHITE_SPACE_PRE,
94 DW_STYLE_WHITE_SPACE_NOWRAP
95 } DwStyleWhiteSpace;
96
97 typedef struct _DwStyle DwStyle;
98 typedef struct _DwStyleFont DwStyleFont;
99 typedef struct _DwStyleColor DwStyleColor;
100 typedef struct _DwStyleShadedColor DwStyleShadedColor;
101 typedef struct _DwStyleBox DwStyleBox;
102
103
104 /*
105 * Lengths are gint's. Absolute lengths are represented in the following way:
106 *
107 * +---+ - - - - - - - - - -+---+---+
108 * | pixel value (integer) | 0 | 1 |
109 * +---+ - - - - - - - - - -+---+---+
110 *
111 * Percentages:
112 *
113 * +---+ - - - +---+---+- - - - - -+---+---+---+
114 * | integer part | decimal fraction | 1 | 0 |
115 * +---+ - - - +---+---+- - - - - -+---+---+---+
116 * n-1 18 17 2 1 0
117 *
118 * | <------ fixed point value ------> |
119 *
120 * Relative lengths (only used in HTML):
121 *
122 * +---+ - - - +---+---+- - - - - -+---+---+---+
123 * | integer part | decimal fraction | 1 | 1 |
124 * +---+ - - - +---+---+- - - - - -+---+---+---+
125 * n-1 18 17 2 1 0
126 *
127 * | <------ fixed point value ------> |
128 *
129 * "auto" lenghths are represented as DW_STYLE_LENGTH_AUTO.
130 */
131
132 typedef gint DwStyleLength;
133
134 #define DW_STYLE_CREATE_ABS_LENGTH(n) (((n) << 2) | 1)
135 #define DW_STYLE_CREATE_PER_LENGTH(v) ( ( (gint)((v) * (1 << 18)) & ~3 ) | 2 )
136 #define DW_STYLE_CREATE_REL_LENGTH(v) ( ( (gint)((v) * (1 << 18)) & ~3 ) | 3 )
137
138 #define DW_STYLE_IS_ABS_LENGTH(l) (((l) & 3) == 1)
139 #define DW_STYLE_IS_PER_LENGTH(l) (((l) & 3) == 2)
140 #define DW_STYLE_IS_REL_LENGTH(l) (((l) & 3) == 3)
141
142 #define DW_STYLE_ABS_LENGTH_VAL(l) ((l) >> 2)
143 #define DW_STYLE_PER_LENGTH_VAL(l) ( ( (gfloat)((l) & ~3) ) / (1 << 18) )
144 #define DW_STYLE_REL_LENGTH_VAL(l) ( ( (gfloat)((l) & ~3) ) / (1 << 18) )
145
146 #define DW_STYLE_LENGTH_AUTO 0
147
148
149 struct _DwStyleBox
150 {
151 /* in future also percentages */
152 gint32 top, right, bottom, left;
153 };
154
155 struct _DwStyle
156 {
157 gint ref_count;
158
159 DwStyleFont *font;
160 DwStyleTextDecoration text_decoration;
161 DwStyleColor *color, *background_color;
162
163 DwStyleTextAlignType text_align;
164 DwStyleVAlignType valign;
165 gchar text_align_char; /* In future, strings will be supported. */
166
167 gint32 border_spacing;
168 DwStyleLength width, height;
169
170 DwStyleBox margin, border_width, padding;
171 struct { DwStyleShadedColor *top, *right, *bottom, *left; } border_color;
172 struct { DwStyleBorderStyle top, right, bottom, left; } border_style;
173
174 DwStyleDisplayType display;
175 DwStyleWhiteSpace white_space;
176 DwStyleListStyleType list_style_type;
177
178 gint x_link;
179 DwTooltip *x_tooltip;
180 };
181
182 struct _DwStyleFont
183 {
184 gint ref_count;
185
186 char *name;
187 gint32 size;
188 gint weight;
189 DwStyleFontStyle style;
190
191 #ifdef USE_TYPE1
192 gint t1fontid;
193 #else
194 GdkFont *font;
195 #endif
196 gint space_width;
197 gint x_height;
198 };
199
200
201 struct _DwStyleColor
202 {
203 gint ref_count;
204 gint color_val;
205 GdkColor color, inverse_color;
206 GdkGC *gc, *inverse_gc;
207 };
208
209
210 struct _DwStyleShadedColor
211 {
212 gint ref_count;
213 gint color_val;
214 GdkColor color, inverse_color, color_dark, color_light;
215 GdkGC *gc, *inverse_gc, *gc_dark, *gc_light;
216 };
217
218
219 void a_Dw_style_init (void);
220 void a_Dw_style_freeall (void);
221
222 void a_Dw_style_init_values (DwStyle *style_attrs,
223 GdkWindow *window);
224 void a_Dw_style_reset_values (DwStyle *style_attrs,
225 GdkWindow *window);
226
227 DwStyle* a_Dw_style_new (DwStyle *style_attrs,
228 GdkWindow *window);
229 DwStyleFont* a_Dw_style_font_new (DwStyleFont *font_attrs);
230 DwStyleFont* a_Dw_style_font_new_from_list (DwStyleFont *font_attrs,
231 gchar *default_family);
232 DwStyleColor* a_Dw_style_color_new (gint color_val,
233 GdkWindow *window);
234 DwStyleShadedColor* a_Dw_style_shaded_color_new (gint color_val,
235 GdkWindow *window);
236
237 gboolean a_Dw_style_size_diffs (DwStyle *style1,
238 DwStyle *style2);
239
240 #define a_Dw_style_ref(style) ((style)->ref_count++)
241 #define a_Dw_style_unref(style) if (--((style)->ref_count) == 0) \
242 Dw_style_remove (style)
243
244
245 /* Don't use this function directly! */
246 void Dw_style_remove (DwStyle *style);
247
248 #define a_Dw_style_box_set_val(box, val) \
249 ((box)->top = (box)->right = (box)->bottom = (box)->left = (val))
250 #define a_Dw_style_box_set_border_color(style, val) \
251 ((style)->border_color.top = (style)->border_color.right = \
252 (style)->border_color.bottom = (style)->border_color.left = (val))
253 #define a_Dw_style_box_set_border_style(style, val) \
254 ((style)->border_style.top = (style)->border_style.right = \
255 (style)->border_style.bottom = (style)->border_style.left = (val))
256
257 /* For use of widgets */
258 #define DW_INFINITY 1000000 /* random */
259
260 void p_Dw_style_draw_border (GdkWindow *window,
261 GdkRectangle *area,
262 gint32 vx,
263 gint32 vy,
264 gint32 x,
265 gint32 y,
266 gint32 width,
267 gint32 height,
268 DwStyle *style,
269 gboolean inverse);
270 void p_Dw_style_draw_background (GdkWindow *window,
271 GdkRectangle *area,
272 gint32 vx,
273 gint32 vy,
274 gint32 x,
275 gint32 y,
276 gint32 width,
277 gint32 height,
278 DwStyle *style,
279 gboolean inverse);
280
281 void a_Dw_style_numtostr (gint num,
282 gchar *buf,
283 gint buflen,
284 DwStyleListStyleType list_style_tyle);
285
286 #define p_Dw_style_box_offset_x(style) ((style)->margin.left + \
287 (style)->border_width.left + \
288 (style)->padding.left)
289 #define p_Dw_style_box_rest_width(style) ((style)->margin.right + \
290 (style)->border_width.right + \
291 (style)->padding.right)
292 #define p_Dw_style_box_diff_width(style) (p_Dw_style_box_offset_x(style) + \
293 p_Dw_style_box_rest_width(style))
294
295 #define p_Dw_style_box_offset_y(style) ((style)->margin.top + \
296 (style)->border_width.top + \
297 (style)->padding.top)
298 #define p_Dw_style_box_rest_height(style) ((style)->margin.bottom + \
299 (style)->border_width.bottom + \
300 (style)->padding.bottom)
301 #define p_Dw_style_box_diff_height(style) (p_Dw_style_box_offset_y(style) + \
302 p_Dw_style_box_rest_height(style))
303
304 #endif /* __DW_STYLE_H__ */
+0
-1772
src/dw_table.c less more
0 /*
1 * File: dw_table.c
2 *
3 * Copyright (C) 2001-2003 Sebastian Geerken <sgeerken@users.sourceforge.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <string.h>
12
13 #include "dw_table.h"
14 #include "list.h"
15 #include "prefs.h"
16 #include "dw_gtk_viewport.h"
17
18 #define DEBUG_CALC_LEVEL 10
19 #define DEBUG_EXTR_LEVEL 10
20 #define DEBUG_WIDTH_LEVEL 10
21 #define DEBUG_ITERATOR_LEVEL 0
22
23 /*#define DEBUG_LEVEL 10*/
24 #include "debug.h"
25
26 /*
27 * Some notes:
28 *
29 * - table->num_children always has the value table->num_cols *
30 * table->num_rows, and shows the minimal size of
31 * table->children, *not* the number of children.
32 *
33 * - table->children[i] may be NULL (unused cells), or a pointer to
34 * DwTableChild. A DwTableChild may be a span space (blocked by
35 * COLSPAN or ROWSPAN of another cell), or a cell starting at
36 * this position. In the last case, the macro CHILD_DEFINED
37 * returns TRUE.
38 */
39
40 /*#define BORDERS*/
41
42 #define CHILD_DEFINED(table, n) ((n) < (table)->num_children && \
43 (table)->children[(n)] != NULL && \
44 (table)->children[(n)]->type != \
45 DW_TABLE_SPAN_SPACE)
46
47
48 static void Dw_table_init (DwTable *table);
49 static void Dw_table_class_init (DwTableClass *klass);
50
51 static void Dw_table_destroy (GtkObject *object);
52
53 static void Dw_table_size_request (DwWidget *widget,
54 DwRequisition *requisition);
55 static void Dw_table_get_extremes (DwWidget *widget,
56 DwExtremes *extremes);
57 static void Dw_table_size_allocate (DwWidget *widget,
58 DwAllocation *allocation);
59 static void Dw_table_set_width (DwWidget *widget,
60 gint32 width);
61 static void Dw_table_set_ascent (DwWidget *widget,
62 gint32 ascent);
63 static void Dw_table_set_descent (DwWidget *widget,
64 gint32 descent);
65 static void Dw_table_draw (DwWidget *widget,
66 DwRectangle *area,
67 GdkEventExpose *event);
68 static gboolean Dw_table_button_press (DwWidget *widget,
69 gint32 x,
70 gint32 y,
71 GdkEventButton *event);
72 static gboolean Dw_table_button_release(DwWidget *widget,
73 gint32 x,
74 gint32 y,
75 GdkEventButton *event);
76 static gboolean Dw_table_motion_notify (DwWidget *widget,
77 gint32 x,
78 gint32 y,
79 GdkEventMotion *event);
80
81 static void Dw_table_add (DwContainer *container,
82 DwWidget *widget);
83 static void Dw_table_remove (DwContainer *container,
84 DwWidget *widget);
85 static void Dw_table_forall (DwContainer *container,
86 DwCallback callback,
87 gpointer callback_data);
88
89 static DwIterator* Dw_table_iterator (DwWidget *widget,
90 gint mask,
91 gboolean at_end);
92 static gboolean Dw_table_iterator_next (DwIterator *it);
93 static gboolean Dw_table_iterator_prev (DwIterator *it);
94
95
96 static void Dw_table_realloc_children (DwTable *table);
97
98 static void Dw_table_sub_create (DwTable *table);
99 static void Dw_table_sub_free (DwTableSub *sub);
100 static void Dw_table_sub_get_extremes (DwTableSub *sub);
101 static void Dw_table_sub_calc_col_widths (DwTableSub *sub,
102 gint32 width,
103 gint32 total_width);
104
105
106 static DwContainerClass *parent_class;
107
108 enum {
109 /* values of use_percentage */
110 USE_PERCENTAGE_NO, /* No percentage in subtable, and USE_PERCENTAGE_NO
111 * in all sub-subtables.
112 */
113 USE_PERCENTAGE_SOME, /* None of the other cases. This affects the minimal,
114 * but not the maximal width of the subtable.
115 */
116 USE_PERCENTAGE_ALL /* Percentage in subtable, or USE_PERCENTAGE_ALL in
117 * all sub-subtables. This affects the minimal and
118 * the maximal width of the subtable.
119 */
120 };
121
122 /*
123 * Standard Gtk+ function.
124 */
125 GtkType a_Dw_table_get_type (void)
126 {
127 static GtkType type = 0;
128
129 if (!type) {
130 GtkTypeInfo info = {
131 "DwTable",
132 sizeof (DwTable),
133 sizeof (DwTableClass),
134 (GtkClassInitFunc) Dw_table_class_init,
135 (GtkObjectInitFunc) Dw_table_init,
136 (GtkArgSetFunc) NULL,
137 (GtkArgGetFunc) NULL,
138 (GtkClassInitFunc) NULL
139 };
140
141 type = gtk_type_unique (DW_TYPE_CONTAINER, &info);
142 }
143
144 return type;
145 }
146
147
148 /*
149 * Standard Gtk+ function.
150 */
151 DwWidget* a_Dw_table_new (void)
152 {
153 GtkObject *object;
154
155 object = gtk_object_new (DW_TYPE_TABLE, NULL);
156 DBG_OBJ_CREATE (object, "DwTable");
157 return DW_WIDGET (object);
158 }
159
160
161 /*
162 * Standard Gtk+ function.
163 */
164 static void Dw_table_init (DwTable *table)
165 {
166 DW_WIDGET_SET_FLAGS (table, DW_USES_HINTS);
167
168 /* random value */
169 table->avail_width = 100;
170
171 table->cur_row = -1;
172 table->cur_col = 0;
173
174 table->num_children_max = 1; /* 16 */
175 table->num_children = 0;
176 table->children = NULL;
177
178 table->num_col_width_max = 1; /* 8 */
179 table->num_cols = 0;
180 table->col_width = NULL;
181
182 table->cum_height_max = 1; /* row cumulative height array */
183 table->num_rows = 0; /* num_cum_height is (num_rows + 1) */
184 table->cum_height = g_new(gint32, table->cum_height_max);
185
186 table->row_style_max = 1;
187 table->row_style = g_new(DwStyle*, table->row_style_max);
188
189 table->baseline_max = 1;
190 table->baseline = g_new(gint32, table->baseline_max);
191
192 table->sub = NULL;
193 }
194
195
196 /*
197 * Standard Gtk+ function.
198 */
199 static void Dw_table_class_init (DwTableClass *klass)
200 {
201 GtkObjectClass *object_class;
202 DwWidgetClass *widget_class;
203 DwContainerClass *container_class;
204
205 object_class = (GtkObjectClass*) klass;
206 widget_class = (DwWidgetClass*) klass;
207 container_class = (DwContainerClass*) klass;
208
209 parent_class = gtk_type_class (DW_TYPE_CONTAINER);
210
211 object_class->destroy = Dw_table_destroy;
212
213 widget_class->size_request = Dw_table_size_request;
214 widget_class->get_extremes = Dw_table_get_extremes;
215 widget_class->size_allocate = Dw_table_size_allocate;
216 widget_class->set_width = Dw_table_set_width;
217 widget_class->set_ascent = Dw_table_set_ascent;
218 widget_class->set_descent = Dw_table_set_descent;
219 widget_class->draw = Dw_table_draw;
220 widget_class->button_press_event = Dw_table_button_press;
221 widget_class->button_release_event = Dw_table_button_release;
222 widget_class->motion_notify_event = Dw_table_motion_notify;
223 widget_class->iterator = Dw_table_iterator;
224
225 container_class->add = Dw_table_add;
226 container_class->remove = Dw_table_remove;
227 container_class->forall = Dw_table_forall;
228 }
229
230
231 /*
232 * Standard Gtk+ function.
233 */
234 static void Dw_table_destroy (GtkObject *object)
235 {
236 DwTable *table = DW_TABLE (object);
237 gint i;
238
239 for (i = 0; i < table->num_children; i++)
240 if (table->children[i]) {
241 if (table->children[i]->type == DW_TABLE_CELL)
242 gtk_object_unref(GTK_OBJECT(table->children[i]->data.cell.widget));
243 g_free (table->children[i]);
244 }
245
246 g_free (table->children);
247 table->num_children = 0;
248 g_free (table->col_width);
249 g_free (table->cum_height);
250
251 for (i = 0; i < table->num_rows; i++)
252 if (table->row_style[i])
253 a_Dw_style_unref (table->row_style[i]);
254 g_free (table->row_style);
255 g_free (table->baseline);
256
257 if (table->sub)
258 Dw_table_sub_free (table->sub);
259
260 GTK_OBJECT_CLASS(parent_class)->destroy (object);
261 }
262
263 /*
264 * Standard Dw function.
265 * Furthermore, table->width and table->cum_height are filled.
266 */
267 static void Dw_table_size_request (DwWidget *widget,
268 DwRequisition *requisition)
269 {
270 DwExtremes extremes;
271 gint32 avail_width;
272 DwTable *table = DW_TABLE (widget);
273 gint row, row2, col, col2, n;
274 gint32 child_height, set_width;
275 DwWidget *child;
276 DwRequisition child_requisition;
277
278 /* Calculate the widths of the columns, and store it in table->col_width. */
279 if (table->num_cols > 0) {
280 p_Dw_widget_get_extremes (DW_WIDGET (table), &extremes);
281
282 if (DW_STYLE_IS_ABS_LENGTH (widget->style->width))
283 avail_width = DW_STYLE_ABS_LENGTH_VAL (widget->style->width);
284 else if (DW_STYLE_IS_PER_LENGTH (widget->style->width))
285 /*
286 * If the width is > 100%, we use 100%, this prevents ugly
287 * results. (May be changed in future, when a more powerful
288 * rendering is implemented, to handle fixed positions etc.,
289 * as defined by CSS2.)
290 */
291 avail_width = table->avail_width
292 * MIN (DW_STYLE_PER_LENGTH_VAL (widget->style->width), 1.0);
293 else
294 avail_width = table->avail_width;
295
296 if (avail_width < extremes.min_width)
297 avail_width = extremes.min_width;
298 if (widget->style->width == DW_STYLE_LENGTH_AUTO &&
299 avail_width > extremes.max_width)
300 avail_width = extremes.max_width;
301
302 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3,
303 "--> Calculating column widths for (%d x %d) table.\n",
304 table->num_cols, table->num_rows);
305 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3,
306 " [ %d --(%d, %d)--> %d ]\n",
307 table->avail_width, extremes.min_width, extremes.max_width,
308 avail_width);
309
310 avail_width -= (2 * widget->style->border_spacing +
311 p_Dw_style_box_diff_width (widget->style));
312 Dw_table_sub_calc_col_widths
313 (table->sub, avail_width,
314 avail_width - (table->num_cols - 1) * widget->style->border_spacing);
315
316 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "<-- END\n");
317 }
318
319 /* Calculate the total width. */
320 requisition->width = p_Dw_style_box_diff_width (widget->style) +
321 (table->num_cols + 1) * widget->style->border_spacing;
322 for (col = 0; col < table->num_cols; col++)
323 requisition->width += table->col_width[col];
324
325 /* Calculate the height. */
326 for (row = 0; row <= table->num_rows; row++)
327 table->cum_height[row] = widget->style->border_spacing;
328
329 for (row = 0; row < table->num_rows; row++) {
330 /* Calculate the baselines, for cells only with valign=baseline. */
331 table->baseline[row] = 0;
332 for (col = 0; col < table->num_cols; col++) {
333 n = col + row * table->num_cols;
334 if (CHILD_DEFINED (table, n)) {
335 child = table->children[n]->data.cell.widget;
336 if (child->style->valign == DW_STYLE_VALIGN_BASELINE) {
337 p_Dw_widget_size_request (child, &child_requisition);
338 if (child_requisition.ascent > table->baseline[row])
339 table->baseline[row] = child_requisition.ascent;
340 }
341 }
342 }
343
344 for (col = 0; col < table->num_cols; col++) {
345 n = col + row * table->num_cols;
346 if (CHILD_DEFINED (table, n)) {
347 child = table->children[n]->data.cell.widget;
348
349 set_width = 0;
350 for (col2 = col;
351 col2 < col + table->children[n]->data.cell.colspan;
352 col2++)
353 set_width += table->col_width[col2];
354
355 p_Dw_widget_set_width (child, set_width);
356 p_Dw_widget_set_ascent (child, table->avail_ascent);
357 p_Dw_widget_set_descent (child, table->avail_descent);
358
359 p_Dw_widget_size_request (child, &child_requisition);
360 /* Cells with valign=baseline have a height which may
361 * be greater than the height returned by size_request. */
362 if (child->style->valign == DW_STYLE_VALIGN_BASELINE)
363 child_height =
364 table->baseline[row] + child_requisition.descent;
365 else
366 child_height =
367 child_requisition.ascent + child_requisition.descent;
368
369 row2 = row + table->children[n]->data.cell.rowspan;
370
371 /* row2 is the row directly forced downward by the child */
372 if (table->cum_height[row2] <
373 table->cum_height[row] + child_height
374 + widget->style->border_spacing ) {
375 table->cum_height[row2] =
376 table->cum_height[row] + child_height
377 + widget->style->border_spacing;
378 DEBUG_MSG (1, "Row %d starts at %d.\n",
379 row2, table->cum_height[row + table->children[n]
380 ->data.cell.rowspan]);
381 /* todo: column widths should be adjusted a bit. */
382 }
383 }
384 }
385 /* if we've hit a row that contains no defined children, we need
386 to force cumulative heights to trickle down to lower rows */
387
388 if (table->cum_height[row + 1] < table->cum_height[row])
389 table->cum_height[row + 1] = table->cum_height[row];
390 }
391 requisition->ascent = table->cum_height[table->num_rows] +
392 p_Dw_style_box_diff_height (widget->style);
393 requisition->descent = 0;
394
395 DEBUG_MSG (2, "Dw_table_size_request: %d x %d x %d\n",
396 requisition->width, requisition->ascent, requisition->descent);
397 }
398
399
400 /*
401 * Standard Dw function.
402 */
403 static void Dw_table_get_extremes (DwWidget *widget,
404 DwExtremes *extremes)
405 {
406 DwTable *table = DW_TABLE (widget);
407 gint32 diff;
408
409 if (table->num_cols > 0) {
410 if (table->sub == NULL)
411 Dw_table_sub_create (table);
412
413 DEBUG_MSG (DEBUG_EXTR_LEVEL + 3,
414 "--> [%p] Dw_table_get_extremes for (%d x %d) table.\n",
415 table, table->num_cols, table->num_rows);
416
417 Dw_table_sub_get_extremes (table->sub);
418 diff = 2 * widget->style->border_spacing +
419 p_Dw_style_box_diff_width (widget->style);
420 extremes->min_width = table->sub->total_extremes.min_width + diff;
421 extremes->max_width = table->sub->total_extremes.max_width + diff;
422
423 DEBUG_MSG (DEBUG_EXTR_LEVEL + 2, "<-- [%p] END\n", table);
424 } else {
425 extremes->min_width = widget->style->border_spacing
426 + p_Dw_style_box_diff_width (widget->style);
427 extremes->max_width = widget->style->border_spacing
428 + p_Dw_style_box_diff_width (widget->style);
429 }
430 }
431
432
433 /*
434 * Standard Dw function.
435 */
436 static void Dw_table_size_allocate (DwWidget *widget,
437 DwAllocation *allocation)
438 {
439 DwTable *table = DW_TABLE (widget);
440 gint row, col, col2, n;
441 gint32 col_offset, cell_width, cell_height, height_diff, vdiff;
442 DwWidget *child;
443 DwAllocation child_allocation;
444 DwRequisition child_requisition;
445
446 /*
447 * todo: This implementation depends on that size_request has been
448 * called before. Can this be assumed?
449 */
450
451 DEBUG_MSG (2, "Dw_table_size_allocate: %d x %d x %d\n",
452 allocation->width, allocation->ascent, allocation->descent);
453
454 vdiff = allocation->y + p_Dw_style_box_offset_y (widget->style);
455
456 for (row = 0; row < table->num_rows; row++) {
457 col_offset = allocation->x + widget->style->border_spacing
458 + p_Dw_style_box_offset_x (widget->style);
459
460 for (col = 0; col < table->num_cols; col++) {
461 n = col + row * table->num_cols;
462 if (CHILD_DEFINED (table, n)) {
463 cell_width = (table->children[n]->data.cell.colspan - 1)
464 * widget->style->border_spacing;
465 for (col2 = col;
466 col2 < col + table->children[n]->data.cell.colspan;
467 col2++)
468 cell_width += table->col_width[col2];
469
470 child = table->children[n]->data.cell.widget;
471 cell_height =
472 table->cum_height[row + table->children[n]->data.cell.rowspan] -
473 table->cum_height[row] - widget->style->border_spacing;
474 child_allocation.x = col_offset;
475 child_allocation.y = vdiff + table->cum_height[row];
476 child_allocation.width = cell_width;
477
478 /* Vertical alignment is done via allocation. */
479 p_Dw_widget_size_request (child, &child_requisition);
480 switch (child->style->valign) {
481 case DW_STYLE_VALIGN_TOP:
482 child_allocation.ascent = child_requisition.ascent;
483 child_allocation.descent =
484 cell_height - child_requisition.ascent;
485 break;
486 case DW_STYLE_VALIGN_BOTTOM:
487 child_allocation.ascent =
488 cell_height - child_requisition.descent;
489 child_allocation.descent = child_requisition.descent;
490 break;
491 case DW_STYLE_VALIGN_MIDDLE:
492 height_diff = cell_height - (child_requisition.ascent +
493 child_requisition.descent);
494 child_allocation.ascent =
495 child_requisition.ascent + height_diff / 2;
496 child_allocation.descent =
497 child_requisition.descent + (height_diff + 1) / 2;
498 /* The "+ 1" is to avoid rounding errors. */
499 break;
500 case DW_STYLE_VALIGN_BASELINE:
501 child_allocation.ascent = table->baseline[row];
502 child_allocation.descent =
503 cell_height - table->baseline[row];
504 break;
505 default:
506 g_assert_not_reached();
507 break;
508 }
509
510 p_Dw_widget_size_allocate (child, &child_allocation);
511 }
512
513 col_offset += table->col_width[col] + widget->style->border_spacing;
514 }
515 }
516 }
517
518
519 /*
520 * Standard Dw function.
521 */
522 static void Dw_table_set_width (DwWidget *widget,
523 gint32 width)
524 {
525 DwTable *table;
526
527 table = DW_TABLE (widget);
528 /* If limit_text_width is set to YES, a queue_resize may also be
529 necessary. */
530 if (table->avail_width != width || prefs.limit_text_width) {
531 table->avail_width = width;
532 p_Dw_widget_queue_resize (widget, 0, FALSE);
533 }
534 }
535
536
537 /*
538 * Standard Dw function.
539 */
540 static void Dw_table_set_ascent (DwWidget *widget,
541 gint32 ascent)
542 {
543 DwTable *table;
544
545 table = DW_TABLE (widget);
546 if (table->avail_ascent != ascent) {
547 table->avail_ascent = ascent;
548 p_Dw_widget_queue_resize (widget, 0, FALSE);
549 }
550 }
551
552
553 /*
554 * Standard Dw function.
555 */
556 static void Dw_table_set_descent (DwWidget *widget,
557 gint32 descent)
558 {
559 DwTable *table;
560
561 table = DW_TABLE (widget);
562 if (table->avail_descent != descent) {
563 table->avail_descent = descent;
564 p_Dw_widget_queue_resize (widget, 0, FALSE);
565 }
566 }
567
568 /*
569 * Standard Dw function.
570 */
571 static void Dw_table_draw (DwWidget *widget,
572 DwRectangle *area,
573 GdkEventExpose *event)
574 {
575 /* can be optimized, by iterating on the lines in area */
576 DwTable *table = DW_TABLE (widget);
577 DwRectangle child_area;
578 DwWidget *child;
579 gint i;
580 gint32 offx, offy, width;
581
582 p_Dw_widget_draw_widget_box (widget, area, FALSE);
583
584 offx = p_Dw_style_box_offset_x (widget->style);
585 offy = p_Dw_style_box_offset_y (widget->style);
586 width = DW_WIDGET_CONTENT_WIDTH(widget);
587
588 for (i = 0; i < table->num_rows; i++)
589 if (table->row_style[i])
590 p_Dw_widget_draw_box (widget, table->row_style[i], area,
591 offx, offy + table->cum_height[i],
592 width,
593 table->cum_height[i + 1] - table->cum_height[i]
594 - widget->style->border_spacing, FALSE);
595
596 for (i = 0; i < table->num_children; i++)
597 if (CHILD_DEFINED (table, i)) {
598 child = table->children[i]->data.cell.widget;
599 if (p_Dw_widget_intersect (child, area, &child_area)) {
600 #ifdef BORDERS
601 gdk_draw_rectangle
602 (DW_WIDGET_WINDOW (child),
603 widget->viewport->style->dark_gc[widget->viewport->state],
604 FALSE,
605 Dw_widget_x_world_to_viewport (child, child->allocation.x) - 1,
606 Dw_widget_y_world_to_viewport (child, child->allocation.y) - 1,
607 child->allocation.width + 2,
608 DW_WIDGET_HEIGHT(child) + 2);
609 #endif
610 p_Dw_widget_draw (child, &child_area, event);
611 }
612 }
613
614 #ifdef BORDERS
615 gdk_draw_rectangle (DW_WIDGET_WINDOW (widget),
616 widget->style->color->gc, FALSE,
617 Dw_widget_x_world_to_viewport (widget,
618 widget->allocation.x),
619 Dw_widget_y_world_to_viewport (widget,
620 widget->allocation.y),
621 widget->allocation.width,
622 DW_WIDGET_HEIGHT(widget));
623 #endif
624 }
625
626
627 /*
628 * Construct an iterator for a cell.
629 */
630 static DwIterator *Dw_table_construct_iterator (DwTable *table,
631 gint cell_index)
632 {
633 DwIterator *it;
634
635 it = Dw_table_iterator (DW_WIDGET (table), DW_CONTENT_ALL, FALSE);
636 ((DwIteratorInt*)it)->pos = cell_index;
637 it->content.type = DW_CONTENT_WIDGET;
638 it->content.data.widget = table->children[cell_index]->data.cell.widget;
639 return it;
640 }
641
642 /*
643 * Construct an iterator pointing to the end of the table.
644 */
645 static DwIterator *Dw_table_construct_last_iterator (DwTable *table,
646 int *char_pos)
647 {
648 int n;
649 DwIterator *it = NULL;
650 DwTableChild *child = NULL;
651
652 *char_pos = 0;
653 n = table->num_children -1;
654 while (n >= 0 && ((child = table->children[n]) == NULL ||
655 child->type != DW_TABLE_CELL))
656 n--;
657 if (n >= 0) {
658 /* Try to construct an iterator for the last child of the table. */
659 it = a_Dw_widget_iterator (child->data.cell.widget,
660 DW_CONTENT_ALL, TRUE);
661 if (it != NULL && a_Dw_iterator_prev (it)) {
662 if (it->content.type == DW_CONTENT_TEXT)
663 *char_pos = strlen (it->content.data.text);
664 } else {
665 a_Dw_iterator_free (it);
666 it = NULL;
667 }
668
669 if (it == NULL)
670 /* This widget has no content, construct an iterator for the table. */
671 it = Dw_table_construct_iterator (table, n);
672 }
673
674 return it;
675 }
676
677 /*
678 * Send event to selection.
679 */
680 static gboolean Dw_table_send_selection_event (DwTable *table,
681 gint (*fn) (Selection*,
682 DwIterator *,
683 gint, gint,
684 GdkEventButton*,
685 gboolean
686 within_content),
687 gint32 x, gint32 y,
688 GdkEventButton *event)
689 {
690 int row, col, n, char_pos;
691 DwIterator *it;
692 DwTableChild *child;
693 gint32 offy, cellspacing, cum_width;
694
695 it = NULL;
696 char_pos = 0;
697 cellspacing = DW_WIDGET(table)->style->border_spacing;
698
699 /* Search row.
700 * Just a note: Improving the speed of this does not give very much,
701 * since events (in dw_widget.c) are always much slower. */
702 offy = p_Dw_style_box_offset_y (DW_WIDGET(table)->style);
703 row = 0;
704 while (row < table->num_rows &&
705 y > table->cum_height[row + 1] + offy - cellspacing)
706 row++;
707
708 if (row == table->num_rows)
709 /* Below the last row: We create an iterator of the last child,
710 * starting at the end (if possible). */
711 it = Dw_table_construct_last_iterator (table, &char_pos);
712 else {
713 if (y < table->cum_height[row] + offy) {
714 /* Before the row: Start at first column. */
715 col = 0;
716 } else {
717 /* Within the row: Search column.
718 * NOTE: col may be table->num_cols, this is intended, see below */
719 cum_width =
720 p_Dw_style_box_offset_x (DW_WIDGET(table)->style) +
721 cellspacing + table->col_width[0];
722 col = 0;
723 while (col < table->num_cols && x > cum_width) {
724 col++;
725 cum_width += table->col_width[col] + cellspacing;
726 }
727
728 /* Already right of the middle of the next column?
729 * (Actually, this feature is only needed for cases where widgets
730 * are added to a table, which do not process mouse events. This
731 * is never the case in dillo. But it works.) */
732 if (col < table->num_cols &&
733 x >= cum_width - table->col_width[col] / 2)
734 col++;
735 }
736
737 n = col + row * table->num_cols;
738 /* If col == table->num_cols, this may be happen: */
739 if (n == table->num_children)
740 it = Dw_table_construct_last_iterator (table, &char_pos);
741 else {
742 /* some corrections */
743 for (; n < table->num_children; n++) {
744 if ((child = table->children[n]) &&
745 child->type == DW_TABLE_CELL) {
746 it = Dw_table_construct_iterator (table, n);
747 break;
748 }
749 }
750 }
751 }
752
753 if (it) {
754 return fn (GTK_DW_VIEWPORT(DW_WIDGET(table)->viewport)->selection,
755 it, char_pos, -1, event, FALSE);
756 } else
757 return FALSE;
758 }
759
760
761 /*
762 * Standard Dw function.
763 */
764 static gboolean Dw_table_button_press (DwWidget *widget,
765 gint32 x,
766 gint32 y,
767 GdkEventButton *event)
768 {
769 return Dw_table_send_selection_event (DW_TABLE (widget),
770 a_Selection_button_press, x, y,
771 event);
772 }
773
774 /*
775 * Standard Dw function.
776 */
777 static gboolean Dw_table_button_release (DwWidget *widget,
778 gint32 x,
779 gint32 y,
780 GdkEventButton *event)
781 {
782 return Dw_table_send_selection_event (DW_TABLE (widget),
783 a_Selection_button_release, x, y,
784 event);
785 }
786
787
788 /*
789 * Standard Dw function.
790 */
791 static gboolean Dw_table_motion_notify (DwWidget *widget,
792 gint32 x,
793 gint32 y,
794 GdkEventMotion *event)
795 {
796 if (event && (event->state & GDK_BUTTON1_MASK))
797 return Dw_table_send_selection_event (DW_TABLE (widget),
798 a_Selection_button_motion, x, y,
799 NULL);
800 else
801 return FALSE;
802 }
803
804
805 /*
806 * Standard Dw function.
807 */
808 static void Dw_table_add (DwContainer *container,
809 DwWidget *widget)
810 {
811 a_Dw_table_add_cell (DW_TABLE (container), widget, 1, 1);
812 }
813
814
815 /*
816 * Standard Dw function.
817 */
818 static void Dw_table_remove (DwContainer *container,
819 DwWidget *widget)
820 {
821 /* used? */
822 }
823
824
825 /*
826 * Standard Dw function.
827 */
828 static void Dw_table_forall (DwContainer *container,
829 DwCallback callback,
830 gpointer callback_data)
831 {
832 gint i;
833 DwTable *table = DW_TABLE (container);
834
835 for (i = 0; i < table->num_children; i++)
836 if (CHILD_DEFINED (table, i))
837 callback (table->children[i]->data.cell.widget, callback_data);
838 }
839
840
841 /*
842 * Make sure that there is enough space for table->children,
843 * table->cum_height etc., and fill the rest of table->children with
844 * NULL.
845 */
846 static void Dw_table_realloc_children (DwTable *table)
847 {
848 gint old_num_children, i;
849
850 table->num_rows++;
851 a_List_resize (table->cum_height, table->num_rows, table->cum_height_max);
852 table->num_rows--;
853 a_List_resize (table->row_style, table->num_rows, table->row_style_max);
854 a_List_resize (table->baseline, table->num_rows, table->baseline_max);
855
856 old_num_children = table->num_children;
857 table->num_children = table->num_cols * table->num_rows;
858 a_List_resize (table->children, table->num_children,
859 table->num_children_max);
860
861 for (i = old_num_children; i < table->num_children; i++)
862 table->children[i] = NULL;
863 }
864
865
866 /*
867 * Add widget as a cell to table. colspan and rowspan have the same
868 * meaning as the attributes of <td> and <th>.
869 */
870 void a_Dw_table_add_cell (DwTable *table,
871 DwWidget *widget,
872 gint colspan,
873 gint rowspan)
874 {
875 gint col, row, old_num_cols, old_num_rows;
876 DwTableChild *child;
877
878 /* We limit the values for colspan and rowspan to 50, to avoid
879 * attacks by malicious web pages.
880 * todo: Some warnings would be nice. */
881 if (colspan > 50)
882 colspan = 50;
883 if (rowspan > 50)
884 rowspan = 50;
885
886 if (table->num_rows == 0)
887 /* to prevent a crash */
888 a_Dw_table_add_row (table, NULL);
889
890 /* todo: {col|row}span = 0 is not supported yet: */
891 if (colspan < 1)
892 colspan = 1;
893 if (rowspan < 1)
894 rowspan = 1;
895
896 while (table->cur_col < table->num_cols &&
897 (child = table->children[table->cur_row * table->num_cols
898 + table->cur_col]) != NULL &&
899 child->type == DW_TABLE_SPAN_SPACE)
900 table->cur_col++;
901
902 if (table->cur_row + rowspan > table->num_rows) {
903 old_num_rows = table->num_rows;
904 table->num_rows = table->cur_row + rowspan;
905 Dw_table_realloc_children (table);
906 for (row = old_num_rows; row < table->num_rows; row++)
907 table->row_style[row] = NULL;
908 }
909
910 if (table->cur_col + colspan > table->num_cols) {
911 old_num_cols = table->num_cols;
912 table->num_cols = table->cur_col + colspan;
913 a_List_resize (table->col_width, table->num_cols,
914 table->num_col_width_max);
915
916 table->num_children = table->num_cols * table->num_rows;
917 a_List_resize (table->children, table->num_children,
918 table->num_children_max);
919
920 for (row = table->num_rows - 1; row >= 0; row--) {
921 for (col = old_num_cols - 1; col >= 0; col--) {
922 table->children[row * table->num_cols + col] =
923 table->children[row * old_num_cols + col];
924
925 }
926
927 for (col = old_num_cols; col < table->num_cols; col++)
928 table->children[row * table->num_cols + col] = NULL;
929 }
930 }
931
932 for (col = 0; col < colspan; col++)
933 for (row = 0; row < rowspan; row++)
934 if (!(col == 0 && row == 0)) {
935 child = g_new (DwTableChild, 1);
936 child->type = DW_TABLE_SPAN_SPACE;
937 child->data.span_space.start_col = table->cur_col;
938 child->data.span_space.start_row = table->cur_row;
939 table->children[(table->cur_row + row) * table->num_cols
940 + (table->cur_col + col)] = child;
941 }
942
943 child = g_new (DwTableChild, 1);
944 child->type = DW_TABLE_CELL;
945 child->data.cell.widget = widget;
946 child->data.cell.colspan = colspan;
947 child->data.cell.rowspan = rowspan;
948 table->children[table->cur_row * table->num_cols + table->cur_col] = child;
949 table->cur_col += colspan;
950
951 p_Dw_widget_set_parent (widget, DW_WIDGET (table));
952 if (table->row_style[table->cur_row] != NULL)
953 p_Dw_widget_set_bg_color
954 (widget, table->row_style[table->cur_row]->background_color);
955 p_Dw_widget_queue_resize (widget, 0, TRUE);
956
957 /* The cell structure has changed, so the subtables have to be rebuilt. */
958 if (table->sub) {
959 Dw_table_sub_free (table->sub);
960 table->sub = NULL;
961 }
962 }
963
964
965 /*
966 * Add a new row to the table, and start adding cells from the
967 * left-most column.
968 */
969 void a_Dw_table_add_row (DwTable *table,
970 DwStyle *style)
971 {
972 gint row, old_num_rows;
973
974 table->cur_row++;
975
976 if (table->cur_row >= table->num_rows) {
977 old_num_rows = table->num_rows;
978 table->num_rows = table->cur_row + 1;
979 Dw_table_realloc_children (table);
980 for (row = old_num_rows; row < table->num_rows; row++)
981 table->row_style[row] = NULL;
982 }
983
984 if (table->row_style[table->cur_row])
985 a_Dw_style_unref (table->row_style[table->cur_row]);
986 table->row_style[table->cur_row] = style;
987 if (style)
988 a_Dw_style_ref (style);
989
990 table->cur_col = 0;
991 }
992
993 /*
994 * Returns the DwTableCell to be passed to a_Dw_table_cell_new().
995 */
996 DwTableCell* a_Dw_table_get_cell_ref (DwTable *table)
997 {
998 gint row, n;
999 DwWidget *child;
1000
1001 for (row = 0; row <= table->num_rows; row++) {
1002 n = table->cur_col + row * table->num_cols;
1003 if (CHILD_DEFINED (table, n)) {
1004 child = table->children[n]->data.cell.widget;
1005 if (DW_IS_TABLE_CELL (child))
1006 return DW_TABLE_CELL (child);
1007 }
1008 }
1009
1010 return NULL;
1011 }
1012
1013
1014 /* ----------------------------------------------------------------------
1015 *
1016 * General Functions for Subtables
1017 *
1018 * ---------------------------------------------------------------------- */
1019
1020 static gboolean Dw_table_sub_spans_width (DwTableSub *sub,
1021 gint row)
1022 {
1023 gint n, start_col, start_row, start_n, colspan;
1024
1025 n = sub->start_col + sub->table->num_cols * row;
1026
1027 if (sub->table->children[n]) {
1028 if (sub->table->children[n]->type == DW_TABLE_CELL) {
1029 start_col = sub->start_col;
1030 start_row = row;
1031 } else {
1032 start_col = sub->table->children[n]->data.span_space.start_col;
1033 start_row = sub->table->children[n]->data.span_space.start_row;
1034 }
1035
1036 start_n = start_col + sub->table->num_cols * start_row;
1037 colspan = sub->table->children[start_n]->data.cell.colspan;
1038
1039 return (start_col <= sub->start_col &&
1040 start_col + colspan >= sub->end_col);
1041 } else
1042 return FALSE;
1043 }
1044
1045 /*
1046 * Fill the DwTableSub and create the sub DwTableSub's. Only for use
1047 * by Dw_table_get_sub.
1048 */
1049 static void Dw_table_sub_calc_subs (DwTableSub *sub)
1050 {
1051 gint col, row, n, start_n, colspan, most_right, start_col, start_row;
1052
1053 if (sub->start_col + 1 == sub->end_col) {
1054 DEBUG_MSG (DEBUG_CALC_LEVEL + 2, " single column at %d\n",
1055 sub->start_col);
1056 sub->num_subs = 0;
1057 sub->subs = NULL;
1058
1059 for (row = 0; row < sub->table->num_rows; row++)
1060 if (!a_Bitvec_get_bit (sub->removed_rows, row)) {
1061 a_Bitvec_set_bit (sub->spanning_rows, row);
1062 DEBUG_MSG (DEBUG_CALC_LEVEL + 1,
1063 " row %d spans width\n", row);
1064
1065 }
1066 } else {
1067 DEBUG_MSG (DEBUG_CALC_LEVEL + 2,
1068 " --> complex subtable from %d to %d\n",
1069 sub->start_col, sub->end_col);
1070
1071 for (row = 0; row < sub->table->num_rows; row++)
1072 if (!a_Bitvec_get_bit (sub->removed_rows, row) &&
1073 Dw_table_sub_spans_width (sub, row)) {
1074 a_Bitvec_set_bit (sub->spanning_rows, row);
1075 DEBUG_MSG (DEBUG_CALC_LEVEL + 1,
1076 " row %d spans width\n", row);
1077
1078 }
1079
1080 sub->num_subs = 0;
1081 sub->subs = g_new (DwTableSub, sub->end_col - sub->start_col);
1082
1083 col = sub->start_col;
1084 while (col < sub->end_col) {
1085 DEBUG_MSG (DEBUG_CALC_LEVEL + 1, " column %d\n", col);
1086
1087 sub->subs[sub->num_subs].table = sub->table;
1088 sub->subs[sub->num_subs].start_col = col;
1089 sub->subs[sub->num_subs].removed_rows =
1090 a_Bitvec_new (sub->table->num_rows);
1091 sub->subs[sub->num_subs].spanning_rows =
1092 a_Bitvec_new (sub->table->num_rows);
1093
1094 /* Search for the greatest colspan value. */
1095 most_right = col + 1;
1096 for (row = 0; row < sub->table->num_rows; row++)
1097 if (a_Bitvec_get_bit (sub->removed_rows, row) ||
1098 a_Bitvec_get_bit (sub->spanning_rows, row))
1099 a_Bitvec_set_bit (sub->subs[sub->num_subs].removed_rows, row);
1100 else {
1101 n = col + sub->table->num_cols * row;
1102
1103 if (sub->table->children[n]) {
1104 if (sub->table->children[n]->type == DW_TABLE_CELL) {
1105 start_col = col;
1106 start_row = row;
1107 start_n = n;
1108 } else {
1109 start_col =
1110 sub->table->children[n]->data.span_space.start_col;
1111 start_row =
1112 sub->table->children[n]->data.span_space.start_row;
1113 start_n = start_col + sub->table->num_cols * start_row;
1114 }
1115
1116 if (row == start_row) {
1117 colspan =
1118 sub->table->children[start_n]->data.cell.colspan -
1119 (col - start_col);
1120
1121 if (start_col + colspan > most_right &&
1122 start_col + colspan <= sub->end_col)
1123 most_right = start_col + colspan;
1124 }
1125 }
1126 }
1127
1128 sub->subs[sub->num_subs].end_col = most_right;
1129 Dw_table_sub_calc_subs (&sub->subs[sub->num_subs]);
1130
1131 sub->num_subs++;
1132 col = most_right;
1133 }
1134
1135 DEBUG_MSG (DEBUG_CALC_LEVEL + 1, " <-- END\n");
1136 }
1137 }
1138
1139 /*
1140 * Create a DwTableSub for a table.
1141 */
1142 static void Dw_table_sub_create (DwTable *table)
1143 {
1144 if (table->sub)
1145 Dw_table_sub_free (table->sub);
1146
1147 DEBUG_MSG (DEBUG_CALC_LEVEL + 3,
1148 "--> Dw_table_sub_create widths for (%d x %d) table.\n",
1149 table->num_cols, table->num_rows);
1150
1151 table->sub = g_new (DwTableSub, 1);
1152 table->sub->table = table;
1153 table->sub->start_col = 0;
1154 table->sub->end_col = table->num_cols;
1155 table->sub->removed_rows = a_Bitvec_new (table->num_rows);
1156 table->sub->spanning_rows = a_Bitvec_new (table->num_rows);
1157
1158 Dw_table_sub_calc_subs (table->sub);
1159
1160 DEBUG_MSG (DEBUG_CALC_LEVEL + 2, "<-- END\n");
1161 }
1162
1163 /*
1164 * Clean up a DwTableSub. Only for use by Dw_table_free_sub.
1165 */
1166 static void Dw_table_sub_clean_up (DwTableSub *sub)
1167 {
1168 gint i;
1169
1170 a_Bitvec_free (sub->removed_rows);
1171 a_Bitvec_free (sub->spanning_rows);
1172 if (sub->subs) {
1173 for (i = 0; i < sub->num_subs; i++)
1174 Dw_table_sub_clean_up (&sub->subs[i]);
1175 g_free (sub->subs);
1176 }
1177 }
1178
1179 /*
1180 * Free a DwTableSub.
1181 */
1182 static void Dw_table_sub_free (DwTableSub *sub)
1183 {
1184 Dw_table_sub_clean_up (sub);
1185 g_free (sub);
1186 }
1187
1188
1189 /* ----------------------------------------------------------------------
1190 *
1191 * Specific Functions for Subtables
1192 *
1193 * NOTE: The spaces left of the left most column and right of the
1194 * right most column do not belong to a subtable.
1195 *
1196 * ---------------------------------------------------------------------- */
1197
1198 /*
1199 * Calculate the extremes of a subtable.
1200 */
1201 static void Dw_table_sub_get_extremes (DwTableSub *sub)
1202 {
1203 gint i, row, n, start_col, start_row, start_n, num_cols, colspan;
1204 DwWidget *child, *widget;
1205 DwExtremes child_extremes, sum_extremes;
1206 gint32 col_width;
1207 gint32 border_spacing = DW_WIDGET(sub->table)->style->border_spacing;
1208 gfloat percentage;
1209 gfloat sum_percentage;
1210 gint use_percentage_map[3][3] = {
1211 { USE_PERCENTAGE_NO, USE_PERCENTAGE_SOME, -1 },
1212 { USE_PERCENTAGE_SOME, USE_PERCENTAGE_SOME, USE_PERCENTAGE_SOME },
1213 { USE_PERCENTAGE_SOME, USE_PERCENTAGE_SOME, USE_PERCENTAGE_ALL } };
1214
1215 num_cols = sub->end_col - sub->start_col;
1216 sub->use_percentage = USE_PERCENTAGE_NO;
1217 sub->fixed_width = FALSE;
1218 sub->percentage = 0;
1219
1220 DEBUG_MSG (DEBUG_EXTR_LEVEL + 2, " [%p] subtable from %d to %d\n",
1221 sub->table, sub->start_col, sub->end_col);
1222
1223 /* 1. cells spanning the whole width */
1224 sub->span_extremes.min_width = 0;
1225 sub->span_extremes.max_width = 0;
1226
1227 for (row = 0; row < sub->table->num_rows; row++) {
1228 if (!a_Bitvec_get_bit (sub->removed_rows, row) &&
1229 a_Bitvec_get_bit (sub->spanning_rows, row)) {
1230 n = sub->start_col + row * sub->table->num_cols;
1231 if (sub->table->children[n]) {
1232 if (sub->table->children[n]->type == DW_TABLE_CELL) {
1233 start_row = row;
1234 start_n = n;
1235 } else {
1236 start_col = sub->table->children[n]->data.span_space.start_col;
1237 start_row = sub->table->children[n]->data.span_space.start_row;
1238 start_n = start_col + sub->table->num_cols * start_row;
1239 }
1240
1241 if (row == start_row) {
1242 child = sub->table->children[start_n]->data.cell.widget;
1243 p_Dw_widget_get_extremes (child, &child_extremes);
1244 colspan = sub->table->children[start_n]->data.cell.colspan;
1245
1246 /* Adjust width argument of the cell */
1247 if (DW_STYLE_IS_ABS_LENGTH (child->style->width)) {
1248 col_width =
1249 DW_STYLE_ABS_LENGTH_VAL (child->style->width);
1250 if (col_width < child_extremes.min_width)
1251 col_width = child_extremes.min_width;
1252 child_extremes.min_width = col_width;
1253 child_extremes.max_width = col_width;
1254 sub->fixed_width = TRUE;
1255 DEBUG_MSG (DEBUG_EXTR_LEVEL, " [%p] following "
1256 "adjusted by %d pixels:\n",
1257 sub->table, col_width);
1258 } else if (DW_STYLE_IS_PER_LENGTH (child->style->width)) {
1259 percentage = DW_STYLE_PER_LENGTH_VAL (child->style->width);
1260 sub->percentage = MAX (sub->percentage, percentage);
1261 sub->use_percentage = USE_PERCENTAGE_ALL;
1262 DEBUG_MSG (DEBUG_EXTR_LEVEL,
1263 " [%p] following adjusted by %g %%\n",
1264 sub->table, 100 * sub->percentage);
1265 }
1266
1267 if (num_cols != colspan) {
1268 child_extremes.min_width =
1269 child_extremes.min_width * num_cols / colspan;
1270 child_extremes.max_width =
1271 child_extremes.max_width * num_cols / colspan;
1272 }
1273
1274 DEBUG_MSG (DEBUG_EXTR_LEVEL,
1275 " [%p], row %d: cell extremes: %d / %d "
1276 "[was multiplied by %d / %d]\n",
1277 sub->table, row, child_extremes.min_width,
1278 child_extremes.max_width,
1279 num_cols, colspan);
1280
1281 if (child_extremes.min_width > sub->span_extremes.min_width)
1282 sub->span_extremes.min_width = child_extremes.min_width;
1283 if (child_extremes.max_width > sub->span_extremes.max_width)
1284 sub->span_extremes.max_width = child_extremes.max_width;
1285 } else {
1286 DEBUG_MSG (DEBUG_EXTR_LEVEL,
1287 " [%p] row %d: omitted\n",
1288 sub->table, row);
1289 }
1290 }
1291 }
1292 }
1293
1294 DEBUG_MSG (DEBUG_EXTR_LEVEL + 1, " [%p] spanning subs: %d / %d\n",
1295 sub->table, sub->span_extremes.min_width,
1296 sub->span_extremes.max_width);
1297
1298 if (sub->table->sub == sub) {
1299 /* Adjust width argument of the table (fixed, percentages
1300 later in the code). */
1301 widget = DW_WIDGET (sub->table);
1302 if (DW_STYLE_IS_ABS_LENGTH (widget->style->width)) {
1303 col_width = DW_STYLE_ABS_LENGTH_VAL (widget->style->width);
1304 if (col_width < sub->span_extremes.min_width)
1305 col_width = sub->span_extremes.min_width;
1306 sub->span_extremes.min_width = col_width;
1307 sub->span_extremes.max_width = col_width;
1308 }
1309 }
1310
1311 /* 2. subtables */
1312 sum_extremes.min_width = (sub->num_subs - 1) * border_spacing;
1313 sum_extremes.max_width = (sub->num_subs - 1) * border_spacing;
1314 sum_percentage = 0;
1315
1316 if (num_cols > 1)
1317 for (i = 0; i < sub->num_subs; i++) {
1318 Dw_table_sub_get_extremes (&sub->subs[i]);
1319 sum_extremes.min_width += sub->subs[i].total_extremes.min_width;
1320 sum_extremes.max_width += sub->subs[i].total_extremes.max_width;
1321 sum_percentage += sub->subs[i].percentage;
1322 if ((sub->use_percentage =
1323 use_percentage_map
1324 [sub->use_percentage][sub->subs[i].use_percentage]) == -1)
1325 sub->use_percentage =
1326 (i == 0) ? USE_PERCENTAGE_ALL : USE_PERCENTAGE_SOME;
1327 }
1328
1329 DEBUG_MSG (DEBUG_EXTR_LEVEL + 1, " [%p] sum of subsubs: %d / %d\n",
1330 sub->table, sum_extremes.min_width, sum_extremes.max_width);
1331
1332 if (sub->fixed_width) {
1333 sum_extremes.max_width = sum_extremes.min_width;
1334 sub->span_extremes.max_width = sub->span_extremes.min_width;
1335 }
1336
1337 sub->total_extremes.min_width = MAX (sum_extremes.min_width,
1338 sub->span_extremes.min_width);
1339 sub->total_extremes.max_width = MAX (sum_extremes.max_width,
1340 sub->span_extremes.max_width);
1341 sub->percentage = MAX (sub->percentage, sum_percentage);
1342
1343 DEBUG_MSG (DEBUG_EXTR_LEVEL + 1, " [%p] final: %d / %d\n",
1344 sub->table, sub->total_extremes.min_width,
1345 sub->total_extremes.max_width);
1346 }
1347
1348 /*
1349 * Corrent minima or maxima, used by Dw_table_sub_calc_col_widths.
1350 */
1351 #define EXTR_VALUE(e) (max ? (e).max_width : (e).min_width)
1352
1353 static void Dw_table_sub_adjust_col_widths (DwTableSub *sub,
1354 gboolean max,
1355 gint32 width,
1356 DwExtremes *sub_extremes,
1357 gint num_nf_subs,
1358 gint num_nf_cols,
1359 gint32 sum_nf_sub_widths)
1360 {
1361 gint i, cols_per_sub, rest_n, rest_w;
1362 gint32 sum_sub_widths, sum_orig_sub_widths, sub_extr_width, delta;
1363
1364 sum_sub_widths = 0;
1365 sum_orig_sub_widths = 0;
1366
1367 for (i = 0; i < sub->num_subs; i++) {
1368 sum_orig_sub_widths += EXTR_VALUE (sub->subs[i].total_extremes);
1369 sum_sub_widths += EXTR_VALUE (sub_extremes[i]);
1370 }
1371
1372 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1373 " Comparing %s: total: %d, sum: %d\n",
1374 (max ? "max" : "min"), width, sum_sub_widths);
1375
1376 if (num_nf_subs == -1) {
1377 /* If all subs are fixed, unfix them all. */
1378 sum_nf_sub_widths = sum_sub_widths;
1379 num_nf_cols = sub->end_col - sub->start_col;
1380 }
1381
1382 if (sum_sub_widths < width) {
1383 if (sum_nf_sub_widths == 0) {
1384 /* All non-fixed columns zero: Apportion the rest to the
1385 * non-fixed columns, according to the columns per subtable.
1386 */
1387 rest_w = width - sum_sub_widths;
1388 rest_n = num_nf_cols;
1389
1390 for (i = 0; i < sub->num_subs; i++) {
1391 if (num_nf_subs == -1 ||
1392 !(sub->subs[i].fixed_width ||
1393 sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)) {
1394 cols_per_sub = (sub->subs[i].end_col - sub->subs[i].start_col);
1395 sub_extr_width = rest_w * cols_per_sub / rest_n;
1396 rest_w -= sub_extr_width;
1397 rest_n -= cols_per_sub;
1398 if (max)
1399 sub_extremes[i].max_width = sub_extr_width;
1400 else
1401 sub_extremes[i].min_width = sub_extr_width;
1402 }
1403 }
1404 } else {
1405 /* Apportion the rest, according to current values. */
1406 rest_w = width - sum_sub_widths;
1407 rest_n = sum_nf_sub_widths;
1408
1409 for (i = 0; i < sub->num_subs; i++)
1410 if (num_nf_subs == -1 ||
1411 !(sub->subs[i].fixed_width ||
1412 sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)) {
1413 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1414 " increasing sub %d: %d -> ",
1415 i, EXTR_VALUE (sub_extremes[i]));
1416 if (EXTR_VALUE (sub_extremes[i]) > 0) {
1417 delta = rest_w * EXTR_VALUE (sub_extremes[i]) / rest_n;
1418 rest_w -= delta;
1419 rest_n -= EXTR_VALUE (sub_extremes[i]);
1420 if (max)
1421 sub_extremes[i].max_width += delta;
1422 else
1423 sub_extremes[i].min_width += delta;
1424 }
1425 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "%d\n",
1426 EXTR_VALUE (sub_extremes[i]));
1427 }
1428 }
1429 }
1430 }
1431
1432
1433 /*
1434 * Calculate the column widths of a subtable, fill table->width.
1435 */
1436 static void Dw_table_sub_calc_col_widths (DwTableSub *sub,
1437 gint32 width,
1438 gint32 total_width)
1439 {
1440 enum { AT_MIN, AT_MAX, AT_NORM }; /* for sub_status */
1441 gint *sub_status;
1442 gint32 avail_width, sub_width, width_norm_cols, diff_spacing;
1443 gint32 col_width, sum_sub_min_widths, sum_orig_sub_min_widths;
1444 gint i, num_cols, cols_per_sub, num_norm_cols, num_nf_subs, num_nf_cols;
1445 gint rest_w, rest_n, delta;
1446 gboolean success;
1447 gint32 border_spacing, diff;
1448 DwExtremes extremes, *sub_extremes, sum_nf_sub_extremes;
1449
1450 num_cols = sub->end_col - sub->start_col;
1451
1452 if (num_cols == 1) {
1453 /* single column */
1454 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " single column at %d: width = %d\n",
1455 sub->start_col, width);
1456 sub->table->col_width[sub->start_col] = width;
1457 } else {
1458 /* complex subtable
1459 *
1460 * The comments "STEP <n>" refer to the documentation in
1461 * ../doc/DwTable.txt, "Calculating column widths". Read this
1462 * parallel.
1463 */
1464 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1465 " --> complex subtable from %d to %d, width = %d "
1466 "(total = %d)\n",
1467 sub->start_col, sub->end_col, width, total_width);
1468
1469 border_spacing = DW_WIDGET (sub->table)->style->border_spacing;
1470 diff = (sub->num_subs - 1) * border_spacing;
1471 avail_width = width - diff;
1472
1473 extremes = sub->total_extremes;
1474 if (width > extremes.max_width)
1475 extremes.max_width = width;
1476
1477 sub_extremes = g_new (DwExtremes, sub->num_subs);
1478 sum_sub_min_widths = 0;
1479
1480 /* ---- STEP 1: Calculate relative widths. ---- */
1481 for (i = 0; i < sub->num_subs; i++) {
1482 sub_extremes[i] = sub->subs[i].total_extremes;
1483
1484 if (sub->subs[i].use_percentage != USE_PERCENTAGE_NO) {
1485 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1486 " sub %d [%s] has width of %g%% "
1487 "-> adjusting: %d/%d -> ",
1488 i, sub->subs[i].fixed_width ? "fixed" : "variable",
1489 100 * sub->subs[i].percentage,
1490 sub_extremes[i].min_width, sub_extremes[i].max_width);
1491 col_width = sub->subs[i].percentage * total_width;
1492 if (col_width < sub_extremes[i].min_width)
1493 col_width = sub_extremes[i].min_width;
1494 sub_extremes[i].min_width = col_width;
1495 if (sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)
1496 sub_extremes[i].max_width = col_width;
1497
1498 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "%d/%d\n",
1499 sub_extremes[i].min_width, sub_extremes[i].max_width);
1500 } else {
1501 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1502 " sub %d [%s]: %d/%d\n", i,
1503 sub->subs[i].fixed_width ? "fixed" : "variable",
1504 sub_extremes[i].min_width, sub_extremes[i].max_width);
1505 }
1506
1507 sum_sub_min_widths += sub_extremes[i].min_width;
1508 }
1509
1510 /* ---- STEP 2: Eventually, decrease them again. ---- */
1511 if (sum_sub_min_widths > width) {
1512 sum_orig_sub_min_widths = 0;
1513 for (i = 0; i < sub->num_subs; i++)
1514 sum_orig_sub_min_widths += sub->subs[i].total_extremes.min_width;
1515
1516 rest_w = sum_sub_min_widths - (width -diff);
1517 rest_n = sum_sub_min_widths - sum_orig_sub_min_widths;
1518 for (i = 0; i < sub->num_subs; i++) {
1519 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1520 " decreasing sub %d: %d -> ",
1521 i, sub_extremes[i].min_width);
1522 if (sub_extremes[i].min_width !=
1523 sub->subs[i].total_extremes.min_width) {
1524 delta =
1525 rest_w * (sub_extremes[i].min_width
1526 - sub->subs[i].total_extremes.min_width) / rest_n;
1527 rest_w -= delta;
1528 rest_n -= (sub_extremes[i].min_width
1529 - sub->subs[i].total_extremes.min_width);
1530 } else
1531 delta = 0;
1532
1533 sub_width = sub_extremes[i].min_width - delta;
1534 Dw_table_sub_calc_col_widths (&sub->subs[i], sub_width,
1535 total_width);
1536 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "%d\n", sub_width);
1537 }
1538 } else {
1539 /* ---- STEP 3: If necessary, increase the sub-subtable ----
1540 * ---- extremes. ---- */
1541 num_nf_subs = 0;
1542 num_nf_cols = 0;
1543 sum_nf_sub_extremes.min_width = 0;
1544 sum_nf_sub_extremes.max_width = 0;
1545
1546 for (i = 0; i < sub->num_subs; i++)
1547 if (!(sub->subs[i].fixed_width ||
1548 sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)) {
1549 num_nf_cols += (sub->subs[i].end_col - sub->subs[i].start_col);
1550 num_nf_subs++;
1551 sum_nf_sub_extremes.min_width += sub_extremes[i].min_width;
1552 sum_nf_sub_extremes.max_width += sub_extremes[i].max_width;
1553 }
1554
1555 /* If all subtables are fixed, unfix them all. */
1556 if (num_nf_subs == 0) {
1557 num_nf_subs = -1;
1558 num_nf_cols = num_cols;
1559 }
1560
1561 diff_spacing =
1562 (sub->end_col - sub->start_col - 1)
1563 * DW_WIDGET(sub->table)->style->border_spacing;
1564
1565 Dw_table_sub_adjust_col_widths
1566 (sub, FALSE, extremes.min_width - diff_spacing, sub_extremes,
1567 num_nf_subs, num_nf_cols, sum_nf_sub_extremes.min_width);
1568 Dw_table_sub_adjust_col_widths
1569 (sub, TRUE, extremes.max_width - diff_spacing, sub_extremes,
1570 num_nf_subs, num_nf_cols, sum_nf_sub_extremes.max_width);
1571
1572 /* ---- STEP 4: Finally, calculate the widths. ---- */
1573 sub_status = g_new (gint, sub->num_subs);
1574
1575 /* First, assume that all columns have the same width. */
1576 for (i = 0; i < sub->num_subs; i++)
1577 sub_status[i] = AT_NORM;
1578
1579 do {
1580 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, " columns: ");
1581
1582 /* Calculate the normal width, and the number of columns at this
1583 width. */
1584 width_norm_cols = avail_width;
1585 num_norm_cols = num_cols;
1586 for (i = 0; i < sub->num_subs; i++) {
1587 if (sub_status[i] != AT_NORM) {
1588 num_norm_cols -=
1589 (sub->subs[i].end_col - sub->subs[i].start_col);
1590 if (sub_status[i] == AT_MIN)
1591 width_norm_cols -= sub_extremes[i].min_width;
1592 else
1593 width_norm_cols -= sub_extremes[i].max_width;
1594 }
1595
1596 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, "%c", "ian"[sub_status[i]]);
1597 }
1598
1599 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, "\n");
1600 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " norm: %d, width = %d\n",
1601 num_norm_cols, width_norm_cols);
1602
1603 /*
1604 * Iteratively, test for minimum/maximum, and correct the
1605 * status. As soon as one test fails, the status is
1606 * changed, and the iteration starts again from the
1607 * beginning.
1608 */
1609 success = TRUE;
1610 for (i = 0; success && i < sub->num_subs; i++) {
1611 cols_per_sub = (sub->subs[i].end_col - sub->subs[i].start_col);
1612
1613 switch (sub_status[i]) {
1614 case AT_NORM:
1615 /* Columns at normal width must between min and max. */
1616 if (width_norm_cols * cols_per_sub
1617 < num_norm_cols * sub_extremes[i].min_width) {
1618 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1619 " sub %d at min\n", i);
1620 sub_status[i] = AT_MIN;
1621 success = FALSE;
1622 } else if (width_norm_cols * cols_per_sub
1623 > num_norm_cols * sub_extremes[i].max_width) {
1624 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1625 " sub %d at max\n", i);
1626 sub_status[i] = AT_MAX;
1627 success = FALSE;
1628 }
1629 break;
1630
1631 case AT_MIN:
1632 /* If the column is at min, the the normal width (which has
1633 been changed), must tested against the min. */
1634 if (width_norm_cols * cols_per_sub
1635 > num_norm_cols * sub_extremes[i].min_width) {
1636 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1637 " sub %d at norm\n", i);
1638 sub_status[i] = AT_NORM;
1639 success = FALSE;
1640 }
1641 break;
1642
1643 case AT_MAX:
1644 /* If the column is at max, the the normal width (which has
1645 been changed), must tested against the max. */
1646 if (width_norm_cols * cols_per_sub
1647 < num_norm_cols * sub_extremes[i].max_width) {
1648 DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2,
1649 " sub %d at norm\n", i);
1650 sub_status[i] = AT_NORM;
1651 success = FALSE;
1652 }
1653 break;
1654 }
1655 }
1656 } while (!success);
1657
1658 /* ---- STEP 5: Apply calculate the widths. ---- */
1659 rest_n= num_norm_cols;
1660 rest_w = width_norm_cols;
1661 for (i = 0; i < sub->num_subs; i++) {
1662 if (sub_status[i] == AT_MIN)
1663 sub_width = sub_extremes[i].min_width;
1664 else if (sub_status[i] == AT_MAX)
1665 sub_width = sub_extremes[i].max_width;
1666 else {
1667 cols_per_sub = (sub->subs[i].end_col - sub->subs[i].start_col);
1668 sub_width = rest_w * cols_per_sub / rest_n;
1669 rest_w -= sub_width;
1670 rest_n-= cols_per_sub;
1671 }
1672
1673 Dw_table_sub_calc_col_widths (&sub->subs[i], sub_width,
1674 total_width);
1675 }
1676
1677 g_free (sub_status);
1678 }
1679
1680 g_free (sub_extremes);
1681 }
1682 }
1683
1684
1685 static DwIterator *Dw_table_iterator (DwWidget *widget,
1686 gint mask,
1687 gboolean at_end)
1688 {
1689 DwIteratorInt *it = g_new (DwIteratorInt, 1);
1690 it->it.widget = widget;
1691 it->it.mask = mask;
1692 it->it.next = Dw_table_iterator_next;
1693 it->it.prev = Dw_table_iterator_prev;
1694 it->it.clone = p_Dw_iterator_clone_std_int;
1695 it->it.compare = p_Dw_iterator_compare_std_int;
1696 it->it.free = p_Dw_iterator_free_std;
1697 it->it.highlight = p_Dw_iterator_highlight_std;
1698 it->it.get_allocation = p_Dw_iterator_get_allocation_std_only_widgets;
1699
1700 if (at_end) {
1701 it->it.content.type = DW_CONTENT_END;
1702 it->pos = DW_TABLE(widget)->num_children;
1703 } else {
1704 it->it.content.type = DW_CONTENT_START;
1705 it->pos = -1;
1706 }
1707
1708 return (DwIterator*)it;
1709 }
1710
1711 static gboolean Dw_table_iterator_next (DwIterator *it)
1712 {
1713 DwTable *table = DW_TABLE (it->widget);
1714 DwIteratorInt *ii = (DwIteratorInt*)it;
1715
1716 DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_table_iterator_next: %d of %d\n",
1717 ii->pos, table->num_children);
1718
1719 if (it->content.type == DW_CONTENT_END)
1720 return FALSE;
1721
1722 /* tables only contain widgets: */
1723 if ((it->mask & DW_CONTENT_WIDGET) == 0) {
1724 it->content.type = DW_CONTENT_END;
1725 return FALSE;
1726 }
1727
1728 do {
1729 ii->pos++;
1730 if (ii->pos >= table->num_children) {
1731 it->content.type = DW_CONTENT_END;
1732 return FALSE;
1733 }
1734 } while (table->children[ii->pos] == NULL ||
1735 table->children[ii->pos]->type != DW_TABLE_CELL);
1736
1737 it->content.type = DW_CONTENT_WIDGET;
1738 it->content.data.widget = table->children[ii->pos]->data.cell.widget;
1739 return TRUE;
1740 }
1741
1742 static gboolean Dw_table_iterator_prev (DwIterator *it)
1743 {
1744 DwTable *table = DW_TABLE (it->widget);
1745 DwIteratorInt *ii = (DwIteratorInt*)it;
1746
1747 if (it->content.type == DW_CONTENT_START)
1748 return FALSE;
1749
1750 DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_table_iterator_prev: %d of %d\n",
1751 ii->pos, table->num_children);
1752
1753 /* tables only contain widgets: */
1754 if ((it->mask & DW_CONTENT_WIDGET) == 0) {
1755 it->content.type = DW_CONTENT_START;
1756 return FALSE;
1757 }
1758
1759 do {
1760 ii->pos--;
1761 if (ii->pos < 0) {
1762 it->content.type = DW_CONTENT_START;
1763 return FALSE;
1764 }
1765 } while (table->children[ii->pos] == NULL ||
1766 table->children[ii->pos]->type != DW_TABLE_CELL);
1767
1768 it->content.type = DW_CONTENT_WIDGET;
1769 it->content.data.widget = table->children[ii->pos]->data.cell.widget;
1770 return TRUE;
1771 }
+0
-118
src/dw_table.h less more
0 /*
1 * File: dw_table.h
2 *
3 * Copyright (C) 2001 Sebastian Geerken <sgeerken@users.sourceforge.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #ifndef __DW_TABLE_H__
12 #define __DW_TABLE_H__
13
14 #include "dw_container.h"
15 #include "dw_table_cell.h"
16 #include "bitvec.h"
17
18 #ifdef __cplusplus
19 extern "C" {
20 #endif /* __cplusplus */
21
22 #define DW_TYPE_TABLE (a_Dw_table_get_type ())
23 #define DW_TABLE(obj) GTK_CHECK_CAST (obj,DW_TYPE_TABLE, DwTable)
24 #define DW_TABLE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, DW_TYPE_TABLE, \
25 DwTableClass)
26 #define DW_IS_TABLE(obj) GTK_CHECK_TYPE (obj, DW_TYPE_TABLE)
27
28
29 typedef struct _DwTable DwTable;
30 typedef struct _DwTableClass DwTableClass;
31 typedef struct _DwTableChild DwTableChild;
32 typedef struct _DwTableSub DwTableSub;
33
34 struct _DwTableChild
35 {
36 enum {
37 DW_TABLE_CELL, /* cell starts here */
38 DW_TABLE_SPAN_SPACE /* part of a spanning cell */
39 } type;
40
41 union {
42 struct {
43 DwWidget *widget;
44 gint colspan, rowspan;
45 } cell;
46 struct {
47 gint start_col, start_row; /* where the cell starts */
48 } span_space;
49 } data;
50 };
51
52 struct _DwTableSub
53 {
54 DwTable *table;
55 gint start_col, end_col;
56 bitvec_t *spanning_rows, *removed_rows;
57 DwExtremes span_extremes, total_extremes;
58 gboolean fixed_width;
59 gint use_percentage;
60 gfloat percentage;
61
62 DwTableSub *subs;
63 gint num_subs;
64 };
65
66 struct _DwTable
67 {
68 DwContainer container;
69
70 /* set by set_... */
71 gint32 avail_width, avail_ascent, avail_descent;
72
73 gint cur_row, cur_col;
74
75 gint32 *col_width; /* num_cols members */
76 gint num_cols;
77 gint num_col_width_max;
78
79 gint32 *cum_height; /* row cumulative height array: */
80 gint num_rows; /* num_cum_height is (num_rows + 1), */
81 gint cum_height_max; /* cum_height[0] is always SPACING, */
82 /* cum_height[num_rows] is the total table height */
83
84 DwStyle **row_style;
85 gint row_style_max;
86
87 gint32 *baseline;
88 gint baseline_max;
89
90 DwTableSub *sub;
91
92 DwTableChild **children;
93 gint num_children;
94 gint num_children_max; /* number allocated */
95 };
96
97 struct _DwTableClass
98 {
99 DwContainerClass parent_class;
100 };
101
102 GtkType a_Dw_table_get_type (void);
103 DwWidget* a_Dw_table_new (void);
104
105 void a_Dw_table_add_cell (DwTable *table,
106 DwWidget *widget,
107 gint colspan,
108 gint rowspan);
109 void a_Dw_table_add_row (DwTable *table,
110 DwStyle *style);
111 DwTableCell* a_Dw_table_get_cell_ref (DwTable *table);
112
113 #ifdef __cplusplus
114 }
115 #endif /* __cplusplus */
116
117 #endif /* __DW_TABLE_H__ */
+0
-196
src/dw_table_cell.c less more
0 /*
1 * File: dw_table_cell.c
2 *
3 * Copyright (C) 2003 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This widget is used for table cells with character alignment.
13 *
14 * Some notes: If the character is at a position quite far within the
15 * text, this would result in very wide table columns. Since I do not
16 * think that an author intends this, DwTableCell makes use of the
17 * DwPage::ignore_line1_offset_sometimes feature, which will in this
18 * case disable alignment (see comment in dw_page.h). Actually for the
19 * whole column, which is simpler to implement (DwTableCell still deals
20 * with too large values, but DwPage simply ignores them).
21 */
22
23 #include "dw_table_cell.h"
24
25 /*#define DEBUG_LEVEL 1*/
26 #include "debug.h"
27
28 static void Dw_table_cell_class_init (DwTableCellClass *klass);
29 static void Dw_table_cell_init (DwTableCell *table_cell);
30
31 static void Dw_table_cell_word_wrap (DwPage *page,
32 gint word_ind);
33 static gint32 Dw_table_cell_get_value (DwAlignedPage *aligned_page);
34 static void Dw_table_cell_set_max_value (DwAlignedPage *aligned_page,
35 gint32 max_value,
36 gint32 value);
37 static DwAlignedPageClass *parent_class;
38
39
40 /*
41 * Standard Gtk+ function.
42 */
43 GtkType a_Dw_table_cell_get_type (void)
44 {
45 static GtkType type = 0;
46
47 if (!type) {
48 GtkTypeInfo info = {
49 "DwTableCell",
50 sizeof (DwTableCell),
51 sizeof (DwTableCellClass),
52 (GtkClassInitFunc) Dw_table_cell_class_init,
53 (GtkObjectInitFunc) Dw_table_cell_init,
54 (GtkArgSetFunc) NULL,
55 (GtkArgGetFunc) NULL,
56 (GtkClassInitFunc) NULL
57 };
58
59 type = gtk_type_unique (DW_TYPE_ALIGNED_PAGE, &info);
60 }
61
62 return type;
63 }
64
65
66 /*
67 * Standard Gtk+ function.
68 */
69 static void Dw_table_cell_init (DwTableCell *table_cell)
70 {
71 DW_PAGE(table_cell)->ignore_line1_offset_sometimes = TRUE;
72 table_cell->char_word_index = -1;
73 }
74
75
76 /*
77 * Standard Gtk+ function: Create a new table_cell item.
78 * ref_cell is either another table cell in the same column, or NULL for
79 * the first DwTableCell in the column. Typically used with
80 * a_Dw_table_get_cell_ref().
81 */
82 DwWidget* a_Dw_table_cell_new (DwTableCell *ref_cell)
83 {
84 GtkObject *object;
85
86 object = gtk_object_new (DW_TYPE_TABLE_CELL, NULL);
87 DBG_OBJ_CREATE (object, "DwTableCell");
88 p_Dw_aligned_page_set_ref_page (DW_ALIGNED_PAGE (object),
89 (DwAlignedPage*)ref_cell);
90 return DW_WIDGET (object);
91 }
92
93
94 /*
95 * Standard Gtk+ function.
96 */
97 static void Dw_table_cell_class_init (DwTableCellClass *klass)
98 {
99 DwAlignedPageClass *aligned_page_class = DW_ALIGNED_PAGE_CLASS (klass);
100
101 DW_PAGE_CLASS(klass)->word_wrap = Dw_table_cell_word_wrap;
102 aligned_page_class->get_value = Dw_table_cell_get_value;
103 aligned_page_class->set_max_value = Dw_table_cell_set_max_value;
104
105 parent_class = gtk_type_class (DW_TYPE_ALIGNED_PAGE);
106 }
107
108 /*
109 * Standard DwPage function.
110 * Extended to use for the first word containing the alignnment character.
111 */
112 static void Dw_table_cell_word_wrap (DwPage *page,
113 gint word_ind)
114 {
115 DwTableCell *cell = DW_TABLE_CELL (page);
116 DwPageWord *word;
117 char *p;
118
119 DW_PAGE_CLASS(parent_class)->word_wrap (page, word_ind);
120
121 if (cell->char_word_index == -1) {
122 word = &page->words[word_ind];
123 if (word->content.type == DW_CONTENT_TEXT) {
124 if ((p = strchr (word->content.data.text,
125 word->style->text_align_char))) {
126 cell->char_word_index = word_ind;
127 cell->char_word_pos = p - word->content.data.text + 1;
128 } else if (word->style->text_align_char == ' ' &&
129 word->content.space) {
130 cell->char_word_index = word_ind + 1;
131 cell->char_word_pos = 0;
132 }
133 }
134 }
135
136 if (word_ind == cell->char_word_index)
137 p_Dw_aligned_page_update_value (DW_ALIGNED_PAGE (cell));
138 }
139
140
141 /*
142 * Implementation of DwAlignedPage::get_value.
143 */
144 static gint32 Dw_table_cell_get_value (DwAlignedPage *aligned_page)
145 {
146 DwTableCell *cell = DW_TABLE_CELL (aligned_page);
147 DwPage *page = DW_PAGE (aligned_page);
148 DwPageWord *word;
149 int i, word_index;
150 gint32 w;
151
152 if (cell->char_word_index == -1)
153 word_index = page->num_words -1;
154 else
155 word_index = cell->char_word_index;
156
157 w = 0;
158 for (i = 0; i < word_index; i++) {
159 word = &page->words[i];
160 w += word->size.width + word->orig_space;
161 }
162
163 if (cell->char_word_index == -1) {
164 if (page->num_words > 0) {
165 word = &page->words[page->num_words - 1];
166 w += word->size.width;
167 }
168 } else {
169 word = &page->words[cell->char_word_index];
170 w += gdk_text_width (word->style->font->font, word->content.data.text,
171 cell->char_word_pos);
172 }
173
174 return w;
175 }
176
177
178 /*
179 * Implementation of DwAlignedPage::set_max_value.
180 */
181 static void Dw_table_cell_set_max_value (DwAlignedPage *aligned_page,
182 gint32 max_value,
183 gint32 value)
184 {
185 DEBUG_MSG (1, "Dw_table_cell_set_max_value([\"%s\" ...], %d, %d)\n",
186 (DW_PAGE(aligned_page)->num_words > 0 ?
187 (DW_PAGE(aligned_page)->words[0].content.type
188 == DW_CONTENT_TEXT ?
189 DW_PAGE(aligned_page)->words[0].content.data.text : "(?)")
190 : "(empty)"),
191 max_value, value);
192
193 DW_PAGE(aligned_page)->line1_offset = max_value - value;
194 p_Dw_widget_queue_resize (DW_WIDGET (aligned_page), 0, TRUE);
195 }
+0
-40
src/dw_table_cell.h less more
0 #ifndef __DW_TABLE_CELL_H__
1 #define __DW_TABLE_CELL_H__
2
3 #include "dw_aligned_page.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 #define DW_TYPE_TABLE_CELL (a_Dw_table_cell_get_type ())
10 #define DW_TABLE_CELL(obj) GTK_CHECK_CAST (obj,DW_TYPE_TABLE_CELL, \
11 DwTableCell)
12 #define DW_TABLE_CELL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, \
13 DW_TYPE_TABLE_CELL, DwTableCellClass)
14 #define DW_IS_TABLE_CELL(obj) GTK_CHECK_TYPE (obj, DW_TYPE_TABLE_CELL)
15
16
17 typedef struct _DwTableCell DwTableCell;
18 typedef struct _DwTableCellClass DwTableCellClass;
19
20 struct _DwTableCell
21 {
22 DwAlignedPage aligned_page;
23 gint char_word_index, char_word_pos;
24 };
25
26 struct _DwTableCellClass
27 {
28 DwAlignedPageClass parent_class;
29 };
30
31
32 GtkType a_Dw_table_cell_get_type (void);
33 DwWidget* a_Dw_table_cell_new (DwTableCell *ref_cell);
34
35 #ifdef __cplusplus
36 }
37 #endif /* __cplusplus */
38
39 #endif /* __DW_TABLE_CELL_H__ */
+0
-165
src/dw_tooltip.c less more
0 /*
1 * A few notes:
2 *
3 * - Currently, a window is created every time before it is shown, and
4 * destroyed, before it is hidden. This saves (probably?) some
5 * memory, but can simply be changed. An alternative is having a
6 * global window for all tooltips.
7 *
8 * - Tooltips are positioned near the pointer, as opposed to Gtk+
9 * tooltips, which are positioned near the widget.
10 *
11 * Sebastian
12 */
13
14 #include <gtk/gtk.h>
15 #include "dw_tooltip.h"
16
17 /* The amount of space around the text, including the border. */
18 #define PADDING 4
19
20 /* The difference between pointer position and upper left corner of the
21 * tooltip. */
22 #define DIFF 10
23
24 static gboolean Dw_tooltip_draw (DwTooltip *tooltip);
25
26 static DwTooltip *Dw_tooltip_new0 (const char *text, gint ref_count)
27 {
28 DwTooltip *tooltip;
29
30 tooltip = g_new (DwTooltip, 1);
31 tooltip->ref_count = ref_count;
32 tooltip->window = NULL;
33 tooltip->timeout_id = 0;
34 tooltip->text = g_strdup (text);
35 return tooltip;
36 }
37
38 /*
39 * Create a new tooltip, with ref_count set to 1.
40 */
41 DwTooltip* a_Dw_tooltip_new (const gchar *text)
42 {
43 return Dw_tooltip_new0(text, 1);
44 }
45
46 /*
47 * Create a new tooltip, with ref_count set to 0. Tyically used for DwStyle.
48 */
49 DwTooltip* a_Dw_tooltip_new_no_ref (const gchar *text)
50 {
51 return Dw_tooltip_new0(text, 0);
52 }
53
54
55 /*
56 * Destroy the tooltip. Used by Dw_tooltip_unref.
57 */
58 void Dw_tooltip_destroy (DwTooltip *tooltip)
59 {
60 a_Dw_tooltip_on_leave (tooltip);
61 g_free (tooltip->text);
62 g_free (tooltip);
63 }
64
65
66 /*
67 * Call this function if the pointer has entered the widget/word.
68 */
69 void a_Dw_tooltip_on_enter (DwTooltip *tooltip)
70 {
71 a_Dw_tooltip_on_leave (tooltip);
72 tooltip->timeout_id = gtk_timeout_add (500, (GtkFunction)Dw_tooltip_draw,
73 tooltip);
74 }
75
76
77 /*
78 * Call this function if the pointer has left the widget/word.
79 */
80 void a_Dw_tooltip_on_leave (DwTooltip *tooltip)
81 {
82 if (tooltip->timeout_id != 0) {
83 gtk_timeout_remove (tooltip->timeout_id);
84 tooltip->timeout_id = 0;
85 }
86
87 if (tooltip->window != NULL) {
88 gtk_widget_destroy (tooltip->window);
89 tooltip->window = NULL;
90 }
91 }
92
93
94 /*
95 * Call this function if the pointer has moved within the widget/word.
96 */
97 void a_Dw_tooltip_on_motion (DwTooltip *tooltip)
98 {
99 a_Dw_tooltip_on_enter (tooltip);
100 }
101
102 /*
103 * Draw the tooltip. Called as a timeout function.
104 */
105 static gboolean Dw_tooltip_draw (DwTooltip *tooltip)
106 {
107 GtkStyle *style;
108 gint px, py, x, y, width, ascent, descent, screen_w, screen_h, ttw, tth;
109
110 gdk_window_get_pointer (NULL, &px, &py, NULL);
111 x = px + DIFF;
112 y = py + DIFF;
113
114 tooltip->window = gtk_window_new (GTK_WINDOW_POPUP);
115 gtk_widget_set_app_paintable (tooltip->window, TRUE);
116 gtk_widget_set_name (tooltip->window, "gtk-tooltips");
117 gtk_widget_ensure_style (tooltip->window);
118 style = tooltip->window->style;
119 width = gdk_string_width (style->font, tooltip->text);
120 ascent = style->font->ascent;
121 descent = style->font->descent;
122
123 ttw = width + 2 * PADDING;
124 tth = ascent + descent + 2 * PADDING;
125 gtk_widget_set_usize (tooltip->window, ttw, tth);
126
127 screen_w = gdk_screen_width();
128 screen_h = gdk_screen_height();
129
130 if (ttw >= screen_w)
131 /* If the width of a tooltips does not fit into the screen, put
132 * them at x = 0. (Yes, that's far from perfect ...) */
133 x = 0;
134 else if (x + ttw > screen_w)
135 /* If they would otherwise reach out of the screen, move them
136 * a bit left. */
137 x = screen_w - ttw;
138
139 /* The case that the height of a tooltip of the screen is greater
140 * that the screen height is ignored ;-) If the tooltip reaches
141 * out of the screen at the bottom, it is displayed *above* the
142 * pointer: to process events properly, it is necessary to keep
143 * the pointer out of the tooltip. */
144 if (y + tth > screen_h)
145 y = py - tth - DIFF;
146
147 gtk_widget_popup (tooltip->window, x, y);
148 style = tooltip->window->style;
149 gtk_paint_flat_box (style, tooltip->window->window,
150 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
151 NULL, GTK_WIDGET (tooltip->window), "tooltip",
152 0, 0, -1, -1);
153 gtk_paint_string (style, tooltip->window->window,
154 GTK_STATE_NORMAL,
155 NULL, GTK_WIDGET (tooltip->window), "tooltip",
156 PADDING, ascent + PADDING,
157 tooltip->text);
158
159 tooltip->timeout_id = 0;
160 return FALSE;
161 }
162
163
164
+0
-30
src/dw_tooltip.h less more
0 #ifndef __DW_TOOLTIP_H__
1 #define __DW_TOOLTIP_H__
2
3 #include <gtk/gtkwidget.h>
4
5 typedef struct _DwTooltip DwTooltip;
6
7 struct _DwTooltip
8 {
9 gint ref_count;
10 GtkWidget *window;
11 gchar *text;
12 guint timeout_id;
13 };
14
15 DwTooltip* a_Dw_tooltip_new (const gchar *text);
16 DwTooltip* a_Dw_tooltip_new_no_ref (const gchar *text);
17
18 void a_Dw_tooltip_on_enter (DwTooltip *tooltip);
19 void a_Dw_tooltip_on_leave (DwTooltip *tooltip);
20 void a_Dw_tooltip_on_motion (DwTooltip *tooltip);
21
22 #define a_Dw_tooltip_ref(tooltip) ((tooltip)->ref_count++)
23 #define a_Dw_tooltip_unref(tooltip) if (--((tooltip)->ref_count) == 0) \
24 Dw_tooltip_destroy (tooltip)
25
26 /* Don't use this function directly! */
27 void Dw_tooltip_destroy (DwTooltip *tooltip);
28
29 #endif /* __DW_TOOLTIP_H__ */
+0
-1642
src/dw_widget.c less more
0 /*
1 * File: dw_widget.c
2 *
3 * Copyright (C) 2001-2003 Sebastian Geerken <S.Geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <gtk/gtk.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include "msg.h"
15 #include "dw_widget.h"
16 #include "dw_marshal.h"
17 #include "dw_container.h"
18 #include "dw_gtk_viewport.h"
19
20 #define DEBUG_EVENT 10
21 #define DEBUG_SIZE 0
22 #define DEBUG_ALLOC 0
23
24 /*#define DEBUG_LEVEL 10*/
25 #include "debug.h"
26
27 static void Dw_widget_init (DwWidget *widget);
28 static void Dw_widget_class_init (DwWidgetClass *klass);
29
30 static void Dw_widget_destroy (GtkObject *object);
31
32 static void Dw_widget_real_size_request (DwWidget *widget,
33 DwRequisition *requisition);
34 static void Dw_widget_real_get_extremes (DwWidget *widget,
35 DwExtremes *extremes);
36
37 enum
38 {
39 BUTTON_PRESS_EVENT,
40 BUTTON_RELEASE_EVENT,
41 MOTION_NOTIFY_EVENT,
42 ENTER_NOTIFY_EVENT,
43 LEAVE_NOTIFY_EVENT,
44 LAST_SIGNAL
45 };
46
47
48 static GtkObjectClass *parent_class;
49 static guint widget_signals[LAST_SIGNAL] = { 0 };
50
51 /*
52 * Standard Gtk+ function
53 */
54 GtkType a_Dw_widget_get_type (void)
55 {
56 static GtkType type = 0;
57
58 if (!type) {
59 GtkTypeInfo info = {
60 "DwWidget",
61 sizeof (DwWidget),
62 sizeof (DwWidgetClass),
63 (GtkClassInitFunc) Dw_widget_class_init,
64 (GtkObjectInitFunc) Dw_widget_init,
65 (GtkArgSetFunc) NULL,
66 (GtkArgGetFunc) NULL,
67 (GtkClassInitFunc) NULL
68 };
69
70 type = gtk_type_unique (GTK_TYPE_OBJECT, &info);
71 }
72
73 return type;
74 }
75
76
77 /*
78 * Standard Gtk+ function
79 */
80 static void Dw_widget_init (DwWidget *widget)
81 {
82 widget->flags = DW_NEEDS_RESIZE | DW_EXTREMES_CHANGED | DW_HAS_CONTENT;
83 widget->parent = NULL;
84 widget->viewport = NULL;
85
86 widget->allocation.x = -1;
87 widget->allocation.y = -1;
88 widget->allocation.width = 1;
89 widget->allocation.ascent = 1;
90 widget->allocation.descent = 0;
91
92 widget->cursor = NULL;
93 widget->style = NULL;
94 widget->bg_color = NULL;
95 widget->button_sensitive = TRUE;
96 widget->button_sensitive_set = FALSE;
97 }
98
99
100 /*
101 * Standard Gtk+ function
102 */
103 static void Dw_widget_class_init (DwWidgetClass *klass)
104 {
105 GtkObjectClass *object_class;
106
107 parent_class = gtk_type_class (gtk_object_get_type ());
108
109 object_class = GTK_OBJECT_CLASS (klass);
110
111 widget_signals[BUTTON_PRESS_EVENT] =
112 gtk_signal_new ("button_press_event",
113 GTK_RUN_LAST,
114 object_class->type,
115 GTK_SIGNAL_OFFSET (DwWidgetClass, button_press_event),
116 p_Dw_marshal_BOOL__INT_INT_POINTER,
117 GTK_TYPE_BOOL,
118 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
119 widget_signals[BUTTON_RELEASE_EVENT] =
120 gtk_signal_new ("button_release_event",
121 GTK_RUN_LAST,
122 object_class->type,
123 GTK_SIGNAL_OFFSET (DwWidgetClass, button_release_event),
124 p_Dw_marshal_BOOL__INT_INT_POINTER,
125 GTK_TYPE_BOOL,
126 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
127 widget_signals[MOTION_NOTIFY_EVENT] =
128 gtk_signal_new ("motion_notify_event",
129 GTK_RUN_LAST,
130 object_class->type,
131 GTK_SIGNAL_OFFSET (DwWidgetClass, motion_notify_event),
132 p_Dw_marshal_BOOL__INT_INT_POINTER,
133 GTK_TYPE_BOOL,
134 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
135 widget_signals[ENTER_NOTIFY_EVENT] =
136 gtk_signal_new ("enter_notify_event",
137 GTK_RUN_LAST,
138 object_class->type,
139 GTK_SIGNAL_OFFSET (DwWidgetClass, enter_notify_event),
140 p_Dw_marshal_BOOL__POINTER_POINTER,
141 GTK_TYPE_BOOL,
142 2, GTK_TYPE_POINTER, GTK_TYPE_GDK_EVENT);
143 widget_signals[LEAVE_NOTIFY_EVENT] =
144 gtk_signal_new ("leave_notify_event",
145 GTK_RUN_LAST,
146 object_class->type,
147 GTK_SIGNAL_OFFSET (DwWidgetClass, leave_notify_event),
148 p_Dw_marshal_BOOL__POINTER_POINTER,
149 GTK_TYPE_BOOL,
150 2, GTK_TYPE_POINTER, GTK_TYPE_GDK_EVENT);
151 gtk_object_class_add_signals (object_class, widget_signals, LAST_SIGNAL);
152
153 object_class->destroy = Dw_widget_destroy;
154
155 klass->size_request = Dw_widget_real_size_request;
156 klass->get_extremes = Dw_widget_real_get_extremes;
157 klass->size_allocate = NULL;
158 klass->mark_size_change = NULL;
159 klass->mark_extremes_change = NULL;
160 klass->set_width = NULL;
161 klass->set_ascent = NULL;
162 klass->set_descent = NULL;
163 klass->draw = NULL;
164 klass->button_press_event = NULL;
165 klass->button_release_event = NULL;
166 klass->motion_notify_event = NULL;
167 klass->enter_notify_event = NULL;
168 klass->leave_notify_event = NULL;
169 klass->iterator = NULL;
170 }
171
172
173 /*
174 * Standard Gtk+ function
175 */
176 static void Dw_widget_destroy (GtkObject *object)
177 {
178 DwWidget *widget;
179
180 widget = DW_WIDGET (object);
181
182 /* The widget the pointer is in? */
183 if (widget->viewport != NULL &&
184 widget == GTK_DW_VIEWPORT(widget->viewport)->last_entered)
185 /* todo: perhaps call the leave_notify function? */
186 GTK_DW_VIEWPORT(widget->viewport)->last_entered = NULL;
187
188 if (widget->style)
189 a_Dw_style_unref (widget->style);
190
191 if (widget->parent)
192 Dw_container_remove (DW_CONTAINER (widget->parent), widget);
193 else
194 Dw_gtk_viewport_remove_dw (GTK_DW_VIEWPORT (widget->viewport));
195
196 parent_class->destroy (object);
197 }
198
199 /*
200 * Standard Dw function
201 */
202 static void Dw_widget_real_size_request (DwWidget *widget,
203 DwRequisition *requisition)
204 {
205 g_warning ("DwWidget::size_request not implemented for `%s'",
206 gtk_type_name (GTK_OBJECT_TYPE (widget)));
207
208 /* return random size to prevent crashes*/
209 requisition->width = 50;
210 requisition->ascent = 50;
211 requisition->descent = 50;
212 }
213
214
215 /*
216 * Standard Dw function
217 */
218 static void Dw_widget_real_get_extremes (DwWidget *widget,
219 DwExtremes *extremes)
220 {
221 /* Simply return the requisition width */
222 DwRequisition requisition;
223
224 p_Dw_widget_size_request (widget, &requisition);
225 extremes->min_width = extremes->max_width = requisition.width;
226 }
227
228
229 /*
230 * This function is a wrapper for DwWidget::size_request; it calls
231 * this method only when needed.
232 */
233 void p_Dw_widget_size_request (DwWidget *widget,
234 DwRequisition *requisition)
235 {
236 DwWidgetClass *klass;
237
238 if (DW_WIDGET_NEEDS_RESIZE (widget)) {
239 /* todo: check requisition == &(widget->requisition) and do what? */
240 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
241 (* (klass->size_request)) (widget, requisition);
242 widget->requisition = *requisition;
243 DW_WIDGET_UNSET_FLAGS (widget, DW_NEEDS_RESIZE);
244
245 DBG_OBJ_SET_NUM (widget, "requisition.width", widget->requisition.width);
246 DBG_OBJ_SET_NUM (widget, "requisition.ascent",
247 widget->requisition.ascent);
248 DBG_OBJ_SET_NUM (widget, "requisition.descent",
249 widget->requisition.descent);
250 } else
251 *requisition = widget->requisition;
252 }
253
254 /*
255 * Wrapper for DwWidget::get_extremes.
256 */
257 void p_Dw_widget_get_extremes (DwWidget *widget,
258 DwExtremes *extremes)
259 {
260 DwWidgetClass *klass;
261
262 if (DW_WIDGET_EXTREMES_CHANGED (widget)) {
263 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
264 (* (klass->get_extremes)) (widget, extremes);
265 widget->extremes = *extremes;
266 DW_WIDGET_UNSET_FLAGS (widget, DW_EXTREMES_CHANGED);
267
268 DBG_OBJ_SET_NUM (widget, "extremes.min_width",
269 widget->extremes.min_width);
270 DBG_OBJ_SET_NUM (widget, "extremes.max_width",
271 widget->extremes.max_width);
272 } else
273 *extremes = widget->extremes;
274 }
275
276
277 /*
278 * Wrapper for DwWidget::size_allocate, only called when needed.
279 */
280 void p_Dw_widget_size_allocate (DwWidget *widget,
281 DwAllocation *allocation)
282 {
283 DwWidgetClass *klass;
284
285 if (DW_WIDGET_NEEDS_ALLOCATE (widget) ||
286 allocation->x != widget->allocation.x ||
287 allocation->y != widget->allocation.y ||
288 allocation->width != widget->allocation.width ||
289 allocation->ascent != widget->allocation.ascent ||
290 allocation->descent != widget->allocation.descent) {
291
292 DEBUG_MSG (DEBUG_ALLOC,
293 "a %stop-level %s with parent_ref = %d is newly allocated "
294 "from %d, %d, %d x %d x %d ...\n",
295 widget->parent ? "non-" : "",
296 gtk_type_name (GTK_OBJECT_TYPE (widget)), widget->parent_ref,
297 widget->allocation.x, widget->allocation.y,
298 widget->allocation.width, widget->allocation.ascent,
299 widget->allocation.descent);
300
301 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
302 if (klass->size_allocate)
303 (* (klass->size_allocate)) (widget, allocation);
304
305 DEBUG_MSG (DEBUG_ALLOC, "... to %d, %d, %d x %d x %d\n",
306 widget->allocation.x, widget->allocation.y,
307 widget->allocation.width, widget->allocation.ascent,
308 widget->allocation.descent);
309
310 widget->allocation = *allocation;
311 DW_WIDGET_UNSET_FLAGS (widget, DW_NEEDS_ALLOCATE);
312
313 DBG_OBJ_SET_NUM (widget, "allocation.x", widget->allocation.x);
314 DBG_OBJ_SET_NUM (widget, "allocation.y", widget->allocation.y);
315 DBG_OBJ_SET_NUM (widget, "allocation.width", widget->allocation.width);
316 DBG_OBJ_SET_NUM (widget, "allocation.ascent", widget->allocation.ascent);
317 DBG_OBJ_SET_NUM (widget, "allocation.descent",
318 widget->allocation.descent);
319
320 }
321
322 /*DW_WIDGET_UNSET_FLAGS (widget, DW_NEEDS_RESIZE);*/
323 }
324
325
326 void p_Dw_widget_set_width (DwWidget *widget,
327 gint32 width)
328 {
329 DwWidgetClass *klass;
330 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
331 if (klass->set_width)
332 (* (klass->set_width)) (widget, width);
333 }
334
335
336 void p_Dw_widget_set_ascent (DwWidget *widget,
337 gint32 ascent)
338 {
339 DwWidgetClass *klass;
340 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
341 if (klass->set_ascent)
342 (* (klass->set_ascent)) (widget, ascent);
343 }
344
345
346 void p_Dw_widget_set_descent (DwWidget *widget,
347 gint32 descent)
348 {
349 DwWidgetClass *klass;
350 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
351 if (klass->set_descent)
352 (* (klass->set_descent)) (widget, descent);
353 }
354
355
356 void p_Dw_widget_draw (DwWidget *widget,
357 DwRectangle *area,
358 GdkEventExpose *event)
359 {
360 /* NOTE: This function depends on that widgets are always drawn top-down.
361 * The initial draw call is done for the top-level widget by the viewport,
362 * all other draw calls are done for children. */
363 GtkDwViewport *viewport;
364 GdkDrawable *orig_pixmap, *dest;
365 gint x, y;
366 DwWidgetClass *klass;
367
368 orig_pixmap = widget->parent ? widget->parent->clip_pixmap : NULL;
369 widget->clip_pixmap = orig_pixmap;
370 klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
371 if (klass->draw)
372 (* (klass->draw)) (widget, area, event);
373
374 if (widget->clip_pixmap &&
375 (widget->parent == NULL ||
376 widget->clip_pixmap != widget->parent->clip_pixmap)) {
377 /* Copy clipping pixmap into backing pixmap, when this widget has called
378 * p_Dw_widget_will_clip(). */
379 viewport = GTK_DW_VIEWPORT (widget->viewport);
380 dest = orig_pixmap ? orig_pixmap : viewport->back_pixmap;
381 x = area->x + widget->allocation.x -
382 gtk_layout_get_hadjustment(GTK_LAYOUT(viewport))->value;
383 y = area->y + widget->allocation.y -
384 gtk_layout_get_vadjustment(GTK_LAYOUT(viewport))->value;
385 gdk_draw_pixmap (dest, widget->viewport->style->black_gc,
386 widget->clip_pixmap, x, y,
387 x, y, area->width, area->height);
388 gdk_pixmap_unref (widget->clip_pixmap);
389 }
390 }
391
392
393 /*
394 * Handles a mouse event.
395 *
396 * This function is called by Dw_gtk_viewport_mouse_event, the type of
397 * the event is determined by event->type. x and y are world coordinates.
398 * widget may be NULL (if the pointer is outside the top-level widget).
399 *
400 * When event is NULL, GDK_MOTION_NOTIFY is used as the type. This will
401 * soon be the case when GDK_MOTION_NOTIFY events are simulated as a
402 * result of viewport changes (bug #94)
403 */
404 gint Dw_widget_mouse_event (DwWidget *widget,
405 GtkWidget *viewwidget,
406 gint32 x,
407 gint32 y,
408 GdkEvent *event)
409 {
410 gint signal_no;
411 gboolean return_val;
412 DwWidgetClass *klass;
413 GtkDwViewport *viewport = GTK_DW_VIEWPORT (viewwidget);
414 GdkEventType event_type;
415 DwWidget *ancestor, *w1, *w2, **track;
416 gint track_len, i;
417
418 DEBUG_MSG(DEBUG_EVENT,
419 "------------------------- EVENT -------------------------\n");
420
421 /* simulate crossing events */
422 /* todo: resizing/moving widgets */
423 if (widget != viewport->last_entered) {
424 DEBUG_MSG (DEBUG_EVENT, "----------> crossing event\n");
425 DEBUG_MSG (DEBUG_EVENT, " last = %p, now = %p\n",
426 viewport->last_entered, widget);
427
428 /* Determine the next common ancestor of the widgets. */
429 if (viewport->last_entered == NULL || widget == NULL)
430 ancestor = NULL;
431 else {
432 /* There is probably a faster algorithm. ;-) */
433 ancestor = NULL;
434 for (w1 = viewport->last_entered; ancestor == NULL && w1 != NULL;
435 w1 = w1->parent)
436 for (w2 = widget; ancestor == NULL && w2 != NULL; w2 = w2->parent)
437 if (w1 == w2)
438 ancestor = w1;
439 }
440
441 /* Construct the track:
442 * viewport->last_entered => anchestor => (current) widget */
443
444 DEBUG_MSG (DEBUG_EVENT, "common ancestor: %s %p\n",
445 (ancestor ?
446 gtk_type_name (GTK_OBJECT_TYPE (ancestor)) : "(none)"),
447 ancestor);
448
449 track_len = 0;
450 if (viewport->last_entered)
451 for (w1 = viewport->last_entered; w1 != ancestor; w1 = w1->parent)
452 track_len++;
453 if (ancestor)
454 track_len++; /* for the ancestor */
455 if (widget)
456 for (w1 = widget; w1 != ancestor; w1 = w1->parent)
457 track_len++;
458
459 track = g_new (DwWidget*, track_len);
460 i = 0;
461 if (viewport->last_entered)
462 for (w1 = viewport->last_entered; w1 != ancestor; w1 = w1->parent)
463 track[i++] = w1;
464 if (ancestor)
465 track[i++] = ancestor;
466 if (widget) {
467 i = track_len - 1;
468 for (w1 = widget; w1 != ancestor; w1 = w1->parent)
469 track[i--] = w1;
470 }
471
472 /* Send events to all events on the track */
473 /* todo: emit signals */
474 for (i = 0; i < track_len; i++) {
475 klass = DW_WIDGET_CLASS (GTK_OBJECT(track[i])->klass);
476 if (i != 0) {
477 if (klass->enter_notify_event)
478 klass->enter_notify_event (track[i], track[i - 1],
479 (GdkEventMotion*) event);
480 DEBUG_MSG (DEBUG_EVENT, "entering %s %p\n",
481 gtk_type_name (GTK_OBJECT_TYPE (track[i])), track[i]);
482 }
483 if (i != track_len - 1) {
484 if (klass->leave_notify_event)
485 klass->leave_notify_event (track[i], track[i + 1],
486 (GdkEventMotion*) event);
487 DEBUG_MSG (DEBUG_EVENT, "leaving %s %p\n",
488 gtk_type_name (GTK_OBJECT_TYPE (track[i])), track[i]);
489 }
490 }
491
492 DEBUG_MSG (DEBUG_EVENT, "<----------\n");
493
494 g_free (track);
495 viewport->last_entered = widget;
496 if (widget)
497 Dw_widget_update_cursor(widget);
498 else
499 gdk_window_set_cursor (GTK_LAYOUT(viewport)->bin_window, NULL);
500 }
501
502 /* other events */
503 event_type = event ? event->type : GDK_MOTION_NOTIFY;
504
505 while (widget) {
506 switch (event_type) {
507 case GDK_BUTTON_PRESS:
508 case GDK_2BUTTON_PRESS:
509 case GDK_3BUTTON_PRESS:
510 if (widget->button_sensitive)
511 signal_no = widget_signals[BUTTON_PRESS_EVENT];
512 else
513 signal_no = -1;
514 break;
515
516 case GDK_BUTTON_RELEASE:
517 if (widget->button_sensitive)
518 signal_no = widget_signals[BUTTON_RELEASE_EVENT];
519 else
520 signal_no = -1;
521 break;
522
523 case GDK_MOTION_NOTIFY:
524 signal_no = widget_signals[MOTION_NOTIFY_EVENT];
525 break;
526
527 default:
528 signal_no = -1;
529 break;
530 }
531
532 DEBUG_MSG (DEBUG_EVENT, "Sending %s event to %p, a %s.\n",
533 (event_type == GDK_MOTION_NOTIFY ? "motion notify" :
534 event_type == GDK_BUTTON_RELEASE ? "button release" :
535 "button press"),
536 widget, gtk_type_name (GTK_OBJECT_TYPE (widget)));
537
538 if (signal_no != -1) {
539 return_val = FALSE;
540 gtk_signal_emit (GTK_OBJECT (widget), signal_no,
541 x - widget->allocation.x, y - widget->allocation.y,
542 event, &return_val);
543 if (return_val) {
544 DEBUG_MSG (DEBUG_EVENT, "-> Processed.\n");
545 return TRUE;
546 }
547 }
548
549 widget = widget->parent;
550 }
551
552 DEBUG_MSG (DEBUG_EVENT, "-> Not processed at all.\n");
553 return FALSE;
554 }
555
556
557 /*
558 * Change the style of a widget. The old style is automatically
559 * unreferred, the new is referred. If this call causes the widget to
560 * change its size, Dw_widget_queue_resize is called.
561 */
562 void a_Dw_widget_set_style (DwWidget *widget,
563 DwStyle *style)
564 {
565 gboolean size_changed;
566
567 if (widget->style) {
568 a_Dw_style_unref (widget->style);
569 size_changed = a_Dw_style_size_diffs (widget->style, style);
570 } else
571 size_changed = TRUE;
572
573 a_Dw_style_ref (style);
574 widget->style = style;
575
576 if (widget->parent == NULL && widget->viewport != NULL)
577 Dw_gtk_viewport_update_background (GTK_DW_VIEWPORT (widget->viewport));
578
579 if (size_changed)
580 p_Dw_widget_queue_resize (widget, 0, TRUE);
581 else
582 p_Dw_widget_queue_draw (widget);
583 }
584
585
586 /*
587 * Set the cursor of the viewport.
588 * Called from several other functions.
589 */
590 void Dw_widget_update_cursor (DwWidget *widget)
591 {
592 GtkDwViewport *viewport = GTK_DW_VIEWPORT (widget->viewport);
593 DwWidget *cursor_widget;
594
595 if (GTK_WIDGET_REALIZED (viewport)) {
596 /* Search cursor to use, going up from last_entered (not from widget!).
597 */
598 cursor_widget = viewport->last_entered;
599 while (cursor_widget && cursor_widget->cursor == NULL)
600 cursor_widget = cursor_widget->parent;
601
602 if (cursor_widget)
603 gdk_window_set_cursor (GTK_LAYOUT(viewport)->bin_window,
604 cursor_widget->cursor);
605 else
606 gdk_window_set_cursor (GTK_LAYOUT(viewport)->bin_window,
607 NULL);
608 }
609 }
610
611 /*
612 * Set the cursor for a DwWidget. cursor has to be stored elsewhere, it
613 * is not copied (and not destroyed). If cursor is NULL, the cursor of
614 * the parent widget is used.
615 */
616 void a_Dw_widget_set_cursor (DwWidget *widget,
617 GdkCursor *cursor)
618 {
619 widget->cursor = cursor;
620 Dw_widget_update_cursor (widget);
621 }
622
623
624 /*
625 * If this function is called with button_sensitive == FALSE, the widget will
626 * never receive button press/release events, instead they are sent to the
627 * parent widgets. This attribute is inherited from the parent, if this
628 * function is never called.
629 *
630 * TODO: A bit hackish, this is only needed for disabling selection
631 * within <BUTTON>'s, and so make the button accessible at all.
632 * It would be better to handle this problem in a way links are handled,
633 * but this case is much more complicated, since a button is more complex
634 * than a link.
635 *
636 * NOTE: This may be replaced by somehow using signals for events.
637 */
638 void a_Dw_widget_set_button_sensitive (DwWidget *widget,
639 gboolean button_sensitive)
640 {
641 widget->button_sensitive = button_sensitive;
642 widget->button_sensitive_set = TRUE;
643 }
644
645
646 /*
647 * ...
648 */
649 DwWidget *a_Dw_widget_get_toplevel (DwWidget *widget)
650 {
651 while (widget->parent)
652 widget = widget->parent;
653
654 return widget;
655 }
656
657 /*
658 * Scrolls the viewport, so that the region [x, y, width, height] (widget
659 * coordinates) is seen, according to hpos and vpos.
660 */
661 void a_Dw_widget_scroll_to (DwWidget *widget,
662 DwHPosition hpos,
663 DwVPosition vpos,
664 gint32 x,
665 gint32 y,
666 gint32 width,
667 gint32 height)
668 {
669 Dw_gtk_viewport_scroll_to (GTK_DW_VIEWPORT (widget->viewport),
670 hpos, vpos,
671 x + widget->allocation.x,
672 y + widget->allocation.y,
673 width, height);
674 }
675
676 /*
677 * Retreive an iterator pointing before the first content element
678 * of the widget.
679 */
680 DwIterator* a_Dw_widget_iterator (DwWidget *widget,
681 gint mask,
682 gboolean at_end)
683 {
684 DwWidgetClass *klass = DW_WIDGET_CLASS (GTK_OBJECT(widget)->klass);
685 if (klass->iterator)
686 return klass->iterator(widget, mask, at_end);
687 else
688 return NULL;
689 }
690
691 /*
692 * This implementation of DwIterator::clone() can be used, when the
693 * structure DwIterator is used.
694 */
695 DwIterator* p_Dw_iterator_clone_std (DwIterator *it)
696 {
697 DwIterator *it2 = g_new (DwIterator, 1);
698 *it2 = *it;
699 return it2;
700 }
701
702 /*
703 * An implementation of DwIterator::free(), which should be sufficient for most
704 * iterator implementations.
705 */
706 void p_Dw_iterator_free_std (DwIterator *it)
707 {
708 g_free (it);
709 }
710
711 /*
712 * No-op implementation of DwIterator::highlight().
713 */
714 void p_Dw_iterator_highlight_std (DwIterator *it,
715 gint start,
716 gint end,
717 DwHighlightLayer layer)
718 {
719 /* do nothing */
720 }
721
722 /*
723 * This implementation of DwIterator::get_allocation can be used by all
724 * widgets, whose iterators only return widgets themselves.
725 */
726 void p_Dw_iterator_get_allocation_std_only_widgets (DwIterator *it,
727 gint start,
728 gint end,
729 DwAllocation *allocation)
730 {
731 g_return_if_fail (it->content.type == DW_CONTENT_WIDGET);
732
733 *allocation = it->content.data.widget->allocation;
734 }
735
736 /*
737 * Scrolls the viewport, so that the region between it1 and it2 is
738 * seen, according to hpos and vpos. The parameters start and end have
739 * the same meaning as in DwIterator::get_allocation(); start refers
740 * to it1, while end ferers to it2.
741 *
742 * If it1 and it2 point to the same location (see code below), only
743 * it1 is regarded, and both start and end refer to it.
744 */
745 void a_Dw_iterator_scroll_to (DwIterator *it1,
746 DwIterator *it2,
747 gint start,
748 gint end,
749 DwHPosition hpos,
750 DwVPosition vpos)
751 {
752 DwAllocation alloc1, alloc2, alloc;
753 gint32 x1, x2, y1, y2, vp_width, vp_height;
754 DwExtIterator *eit1, *eit2, *eit;
755 gint cur_start, cur_end, cmp;
756 gboolean at_start;
757
758 DBG_MSG (it1->widget->viewport, "scrolling", 0, "a_Dw_iterator_scroll_to");
759 DBG_MSG_START (it1->widget->viewport);
760
761 if (it1 == it2 ||
762 (it1->widget == it2->widget && a_Dw_iterator_compare (it1, it2) == 0)) {
763 a_Dw_iterator_get_allocation (it1, start, end, &alloc);
764 Dw_gtk_viewport_scroll_to (GTK_DW_VIEWPORT (it1->widget->viewport),
765 hpos, vpos, alloc.x, alloc.y,
766 alloc.width, alloc.ascent + alloc.descent);
767 } else {
768 /* First, determine the rectangle all iterators from it1 and it2
769 * allocate, i.e. the smallest rectangle containing all allocations of
770 * these iterators. */
771 eit1 = a_Dw_ext_iterator_new (it1);
772 eit2 = a_Dw_ext_iterator_new (it2);
773
774 x1 = DW_INFINITY;
775 x2 = - DW_INFINITY;
776 y1 = DW_INFINITY;
777 y2 = - DW_INFINITY;
778
779 DBG_MSG_START (it1->widget->viewport);
780 for (eit = a_Dw_ext_iterator_clone (eit1), at_start = TRUE;
781 (cmp = a_Dw_ext_iterator_compare (eit, eit2)) <= 0;
782 a_Dw_ext_iterator_next (eit), at_start = FALSE) {
783 if (at_start)
784 cur_start = start;
785 else
786 cur_start = 0;
787
788 if (cmp == 0)
789 cur_end = end;
790 else
791 cur_end = DW_INFINITY;
792
793 a_Dw_ext_iterator_get_allocation (eit, cur_start, cur_end, &alloc);
794 DBG_MSGF (it1->widget->viewport, "scrolling", 0,
795 "allocation of %s, from %d to %d: [%d, %d, %d x %d x %d]\n",
796 a_Dw_content_html (&eit->content), cur_start, cur_end,
797 alloc.x, alloc.y, alloc.width, alloc.ascent, alloc.descent);
798 x1 = MIN (x1, alloc.x);
799 x2 = MAX (x2, alloc.x + alloc.width);
800 y1 = MIN (y1, alloc.y);
801 y2 = MAX (y2, alloc.y + alloc.ascent + alloc.descent);
802 DBG_MSGF (it1->widget->viewport, "scrolling", 0,
803 "result is [%d, %d, %d x %d]", x1, y1, x2 - x1, y2 - y1);
804 }
805 DBG_MSG_END (it1->widget->viewport);
806
807 DBG_MSGF (it1->widget->viewport, "scrolling", 0,
808 "1. region is [%d, %d, %d x %d]", x1, y1, x2 - x1, y2 - y1);
809
810 a_Dw_iterator_get_allocation (it1, start, DW_INFINITY, &alloc1);
811 a_Dw_iterator_get_allocation (it2, 0, end, &alloc2);
812
813 DBG_MSGF (it1->widget->viewport, "scrolling", 0,
814 "alloc1 = [%d, %d, %d x %d x %d]",
815 alloc1.x, alloc1.y,
816 alloc1.width, alloc1.ascent, alloc1.descent);
817 DBG_MSGF (it2->widget->viewport, "scrolling", 0,
818 "alloc2 = [%d, %d, %d x %d x %d]",
819 alloc2.x, alloc2.y,
820 alloc2.width, alloc2.ascent, alloc2.descent);
821
822 if (alloc1.x > alloc2.x) {
823 /*
824 * This is due to a line break within the region. When the line is
825 * longer than the viewport, and the region is actually quite short,
826 * the user would not see anything of the region, as in this figure
827 * (with region marked as "#"):
828 *
829 * +----------+ ,-- alloc1
830 * | | V
831 * | | ### ###
832 * ### ### | |
833 * ^ | | <-- viewport
834 * | +----------+
835 * `-- alloc2
836 * |----------------------------|
837 * width
838 *
839 * Therefor, we the region smaller, so that the region will be
840 * displayed like this:
841 *
842 * ,-- alloc1
843 * +----|-----+
844 * | V |
845 * | ### ###|
846 * ### ### | |
847 * ^ | | <-- viewport
848 * `-- alloc2 +----------+
849 * |----------|
850 * width
851 *
852 * todo: Changes in the viewport size, until the idle function is
853 * called, are not regarded.
854 */
855
856 vp_width = it1->widget->viewport->allocation.width
857 - GTK_CONTAINER(it1->widget->viewport)->border_width;
858 DBG_MSGF (it2->widget->viewport, "scrolling", 0,
859 "vp_width = %d", vp_width);
860 if (x2 - x1 > vp_width) {
861 x1 = x2 - vp_width;
862 x2 = x1 + vp_width;
863 }
864 }
865
866 if (alloc1.y > alloc2.y) {
867 /* This is similar to the case above, e.g. if the region ends in
868 * another table column. */
869 vp_height = it1->widget->viewport->allocation.height
870 - GTK_CONTAINER(it1->widget->viewport)->border_width;
871 DBG_MSGF (it2->widget->viewport, "scrolling", 0,
872 "vp_height = %d", vp_height);
873 if (y2 - y1 > vp_height) {
874 y1 = y2 - vp_height;
875 y2 = y1 + vp_height;
876 }
877 }
878
879 DBG_MSGF (it1->widget->viewport, "scrolling", 0,
880 "2. region is [%d, %d, %d x %d]", x1, y1, x2 - x1, y2 - y1);
881
882 Dw_gtk_viewport_scroll_to (GTK_DW_VIEWPORT (it1->widget->viewport),
883 hpos, vpos, x1, y1, x2 - x1, y2 - y1);
884 }
885
886 DBG_MSG_END (it1->widget->viewport);
887 }
888
889 /*
890 * For DwIteratorInt.
891 */
892 DwIterator* p_Dw_iterator_clone_std_int (DwIterator *it)
893 {
894 DwIteratorInt *it2 = g_new (DwIteratorInt, 1);
895 *it2 = *(DwIteratorInt*)it;
896 return (DwIterator*)it2;
897 }
898
899 /*
900 * For DwIteratorInt.
901 */
902 gint p_Dw_iterator_compare_std_int (DwIterator *it1,
903 DwIterator *it2)
904 {
905 DwIteratorInt *ii1 = (DwIteratorInt*)it1;
906 DwIteratorInt *ii2 = (DwIteratorInt*)it2;
907
908 g_return_val_if_fail (it1->widget == it2->widget, 0);
909
910 if (ii1->pos == ii2->pos)
911 return 0;
912 if (ii1->pos < ii2->pos)
913 return -1;
914 else
915 return +1;
916 }
917
918 /*
919 * This function returns a descriptive text for a piece of content,
920 * useful for debugging.
921 * NOTE: This function is not very reliable, and should really only
922 * used for non-critical tasks like debugging.
923 */
924 gchar *a_Dw_content_text (DwContent *content)
925 {
926 /* We cycle through several buffers, so that printf should have
927 * no problems. */
928 #define BUF_NUM 5
929 #define BUF_SIZE 2048
930 static gchar buf[BUF_SIZE * BUF_NUM];
931 static int cur_buf = 0;
932 gchar *ptr;
933
934 ptr = buf + cur_buf * BUF_SIZE;
935 cur_buf = (cur_buf + 1) % BUF_NUM;
936
937 switch (content->type) {
938 case DW_CONTENT_START:
939 sprintf (ptr, "<start>");
940 break;
941 case DW_CONTENT_END:
942 sprintf (ptr, "<end>");
943 break;
944 case DW_CONTENT_TEXT:
945 sprintf (ptr, "\"%s\"", content->data.text);
946 break;
947 case DW_CONTENT_WIDGET:
948 sprintf (ptr, "the %s %p",
949 gtk_type_name (GTK_OBJECT_TYPE (content->data.widget)),
950 content->data.widget);
951 break;
952 case DW_CONTENT_ANCHOR:
953 sprintf (ptr, "#%s", content->data.anchor);
954 break;
955 case DW_CONTENT_BREAK:
956 sprintf (ptr, "<break(%d)>", content->data.break_space);
957 break;
958 default:
959 sprintf (ptr, "<unknown %d>", content->type);
960 break;
961 }
962
963 return ptr;
964 }
965
966 /*
967 * Like a_Dw_content_text, but returns HTML.
968 * NOTE: This function is not very reliable, and should really only
969 * used for non-critical tasks like debugging.
970 */
971 gchar* a_Dw_content_html (DwContent *content)
972 {
973 /* We cycle through several buffers, so that printf should have
974 * no problems. */
975 #define BUF_NUM 5
976 #define BUF_SIZE 2048
977 static gchar buf[BUF_SIZE * BUF_NUM];
978 static int cur_buf = 0;
979 gchar *ptr, *ptr1, *ptr2;
980
981 ptr = buf + cur_buf * BUF_SIZE;
982 cur_buf = (cur_buf + 1) % BUF_NUM;
983
984 switch (content->type) {
985 case DW_CONTENT_START:
986 sprintf (ptr, "<i>&lt;start&gt;</i>");
987 break;
988 case DW_CONTENT_END:
989 sprintf (ptr, "<i>&lt;end&gt;</i>");
990 break;
991 case DW_CONTENT_TEXT:
992 *ptr = '"';
993 ptr2 = ptr + 1;
994
995 for (ptr1 = content->data.text; *ptr1; ptr1++) {
996 switch (*ptr1) {
997 case '<':
998 strcpy (ptr2, "&lt;");
999 ptr2 += 4;
1000 break;
1001 case '>':
1002 strcpy (ptr2, "&gt;");
1003 ptr2 += 4;
1004 break;
1005 case '&':
1006 strcpy (ptr2, "&amp;");
1007 ptr2 += 5;
1008 break;
1009 default:
1010 *ptr2 = *ptr1;
1011 ptr2++;
1012 }
1013 }
1014
1015 ptr2[0] = '"';
1016 ptr2[1] = 0;
1017 break;
1018 case DW_CONTENT_WIDGET:
1019 sprintf (ptr, "the %s %p",
1020 gtk_type_name (GTK_OBJECT_TYPE (content->data.widget)),
1021 content->data.widget);
1022 break;
1023 case DW_CONTENT_ANCHOR:
1024 sprintf (ptr, "#%s", content->data.anchor);
1025 break;
1026 case DW_CONTENT_BREAK:
1027 sprintf (ptr, "<i>&lt;break(%d)&gt;</i>", content->data.break_space);
1028 break;
1029 default:
1030 sprintf (ptr, "<i>&lt;unknown %d&gt;</i>", content->type);
1031 break;
1032 }
1033
1034 return ptr;
1035 }
1036
1037 /*
1038 * This function returns a descriptive text for an iterator, useful
1039 * for debugging.
1040 * NOTE: This function is not very reliable, and should really only
1041 * used for non-critical tasks like debugging.
1042 */
1043 gchar *a_Dw_iterator_text (DwIterator *it)
1044 {
1045 /* We cycle through several buffers, so that printf should have
1046 * no problems. */
1047 #define BUF_NUM 5
1048 #define BUF_SIZE 2048
1049 static gchar buf[BUF_SIZE * BUF_NUM];
1050 static int cur_buf = 0;
1051 gchar *ptr;
1052
1053 ptr = buf + cur_buf * BUF_SIZE;
1054 cur_buf = (cur_buf + 1) % BUF_NUM;
1055
1056 if (it)
1057 sprintf (ptr, "[%s in the %s %p]",
1058 a_Dw_content_text (&it->content),
1059 gtk_type_name (GTK_OBJECT_TYPE (it->widget)), it->widget);
1060 else
1061 strcpy (ptr, "[NULL]");
1062
1063 return ptr;
1064 }
1065
1066 /*
1067 * This function prints the contents of a whole widget tree.
1068 * NOTE: This function is not very reliable, and should really only
1069 * used for non-critical tasks like debugging.
1070 */
1071 static void Dw_widget_print_tree0 (DwWidget *widget, int indent)
1072 {
1073 DwIterator *it;
1074
1075 MSG ("%*sthe %s %p, contains:\n",
1076 indent, "", gtk_type_name (GTK_OBJECT_TYPE (widget)), widget);
1077 it = a_Dw_widget_iterator (widget, 0xff, FALSE);
1078 while (a_Dw_iterator_next (it)) {
1079 if (it->content.type == DW_CONTENT_WIDGET)
1080 Dw_widget_print_tree0 (it->content.data.widget, indent + 3);
1081 else
1082 MSG ("%*s%s\n", indent + 3, "", a_Dw_content_text (&it->content));
1083 }
1084 }
1085
1086 void a_Dw_widget_print_tree (DwWidget *widget)
1087 {
1088 MSG ("--- START OF WIDGET TREE ---\n");
1089 Dw_widget_print_tree0 (widget, 0);
1090 MSG ("--- END OF WIDGET TREE ---\n");
1091 }
1092
1093
1094 /*
1095 * The following is a standard implementation for iterators containing
1096 * exactly one piece of text.
1097 */
1098
1099 DwIterator* p_Dw_widget_text_iterator (DwWidget *widget,
1100 gint32 mask,
1101 gboolean at_end,
1102 gchar *text)
1103 {
1104 DwIteratorText *it;
1105
1106 if (mask & DW_CONTENT_TEXT) {
1107 it = g_new (DwIteratorText, 1);
1108 it->it.widget = widget;
1109 it->it.mask = mask;
1110 it->it.content.type = (at_end ? DW_CONTENT_END : DW_CONTENT_START);
1111 it->it.next = p_Dw_iterator_text_next;
1112 it->it.prev = p_Dw_iterator_text_prev;
1113 it->it.clone = p_Dw_iterator_text_clone;
1114 it->it.compare = p_Dw_iterator_text_compare;
1115 it->it.free = p_Dw_iterator_free_std;
1116 it->it.highlight = p_Dw_iterator_highlight_std;
1117 it->it.get_allocation = p_Dw_iterator_text_get_allocation;
1118 it->text = text;
1119 } else
1120 it = NULL;
1121
1122 return (DwIterator*)it;
1123 }
1124
1125 gboolean p_Dw_iterator_text_next (DwIterator *it)
1126 {
1127 if (it->content.type == DW_CONTENT_START) {
1128 it->content.type = DW_CONTENT_TEXT;
1129 it->content.data.text = ((DwIteratorText*)it)->text;
1130 return TRUE;
1131 } else {
1132 it->content.type = DW_CONTENT_END;
1133 return FALSE;
1134 }
1135 }
1136
1137 gboolean p_Dw_iterator_text_prev (DwIterator *it)
1138 {
1139 if (it->content.type == DW_CONTENT_END) {
1140 it->content.type = DW_CONTENT_TEXT;
1141 it->content.data.text = ((DwIteratorText*)it)->text;
1142 return TRUE;
1143 } else {
1144 it->content.type = DW_CONTENT_START;
1145 return FALSE;
1146 }
1147 }
1148
1149 DwIterator* p_Dw_iterator_text_clone (DwIterator *it)
1150 {
1151 DwIteratorText *it2 = g_new (DwIteratorText, 1);
1152 *it2 = *(DwIteratorText*)it;
1153 return (DwIterator*)it2;
1154 }
1155
1156 gint p_Dw_iterator_text_compare (DwIterator *it1,
1157 DwIterator *it2)
1158 {
1159 if (it1->content.type == it2->content.type)
1160 return 0;
1161
1162 switch (it1->content.type) {
1163 case DW_CONTENT_START:
1164 return -1;
1165 case DW_CONTENT_TEXT:
1166 if (it2->content.type == DW_CONTENT_START)
1167 return +1;
1168 else
1169 return -1;
1170 case DW_CONTENT_END:
1171 return +1;
1172 default:
1173 return 0;
1174 }
1175 }
1176
1177 void p_Dw_iterator_text_get_allocation (DwIterator *it,
1178 gint start,
1179 gint end,
1180 DwAllocation *allocation)
1181 {
1182 /*
1183 * Return the allocation of the widget. This is a bit incorrect, since
1184 * start and end are not regarded, but should be correct enough for most
1185 * purposes.
1186 */
1187 *allocation = it->widget->allocation;
1188 }
1189
1190 /*
1191 * Calculates the intersection of widget->allocation and area, returned in
1192 * intersection (in widget coordinates!). Typically used by containers when
1193 * drawing their children. Returns whether intersection is not empty.
1194 */
1195 gint p_Dw_widget_intersect (DwWidget *widget,
1196 DwRectangle *area,
1197 DwRectangle *intersection)
1198 {
1199 #if 1
1200 DwRectangle parent_area, child_area;
1201
1202 parent_area = *area;
1203 parent_area.x += widget->parent->allocation.x;
1204 parent_area.y += widget->parent->allocation.y;
1205
1206 child_area.x = widget->allocation.x;
1207 child_area.y = widget->allocation.y;
1208 child_area.width = widget->allocation.width;
1209 child_area.height = DW_WIDGET_HEIGHT(widget);
1210
1211 if (p_Dw_rectangle_intersect (&parent_area, &child_area, intersection)) {
1212 intersection->x -= widget->allocation.x;
1213 intersection->y -= widget->allocation.y;
1214 return TRUE;
1215 } else
1216 return FALSE;
1217 #else
1218 intersection->x = 0;
1219 intersection->y = 0;
1220 intersection->width = widget->allocation.width;
1221 intersection->height = DW_WIDGET_HEIGHT(widget);
1222
1223 return TRUE;
1224 #endif
1225 }
1226
1227
1228 void p_Dw_widget_set_parent (DwWidget *widget,
1229 DwWidget *parent)
1230 {
1231 gtk_object_ref (GTK_OBJECT (widget));
1232 gtk_object_sink (GTK_OBJECT (widget));
1233 widget->parent = parent;
1234 widget->viewport = parent->viewport;
1235 /*widget->window = parent->window;*/
1236
1237 if (!widget->button_sensitive_set)
1238 widget->button_sensitive = parent->button_sensitive;
1239
1240 DBG_OBJ_ASSOC (widget, parent);
1241 }
1242
1243
1244 /*
1245 * Converting between coordinates.
1246 */
1247
1248 gint32 p_Dw_widget_x_viewport_to_world (DwWidget *widget,
1249 gint16 viewport_x)
1250 {
1251 GtkAdjustment *adjustment;
1252
1253 g_return_val_if_fail (widget && widget->viewport, 0);
1254 adjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (widget->viewport));
1255 g_return_val_if_fail (adjustment != NULL, 0);
1256
1257 return viewport_x + (gint32)adjustment->value;
1258 }
1259
1260
1261 gint32 p_Dw_widget_y_viewport_to_world (DwWidget *widget,
1262 gint16 viewport_y)
1263 {
1264 GtkAdjustment *adjustment;
1265
1266 g_return_val_if_fail (widget && widget->viewport, 0);
1267 adjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (widget->viewport));
1268 g_return_val_if_fail (adjustment != NULL, 0);
1269
1270 return viewport_y + (gint32)adjustment->value;
1271 }
1272
1273
1274 gint16 p_Dw_widget_x_world_to_viewport (DwWidget *widget,
1275 gint32 world_x)
1276 {
1277 GtkAdjustment *adjustment;
1278
1279 g_return_val_if_fail (widget && widget->viewport, 0);
1280 adjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (widget->viewport));
1281 g_return_val_if_fail (adjustment != NULL, 0);
1282
1283 return world_x - (gint32)adjustment->value;
1284 }
1285
1286
1287 gint16 p_Dw_widget_y_world_to_viewport (DwWidget *widget,
1288 gint32 world_y)
1289 {
1290 GtkAdjustment *adjustment;
1291
1292 g_return_val_if_fail (widget && widget->viewport, 0);
1293 adjustment = gtk_layout_get_vadjustment (GTK_LAYOUT (widget->viewport));
1294 g_return_val_if_fail (adjustment != NULL, 0);
1295
1296 return world_y - (gint32)adjustment->value;
1297 }
1298
1299
1300 /*
1301 * Calculate the intersection of (x, y, width, height) (widget
1302 * coordinates) and the current viewport area. gdk_intersection has
1303 * (of course) viewport coordinates, the return value is TRUE iff the
1304 * intersection is not empty.
1305 */
1306 static gboolean Dw_widget_intersect_viewport (DwWidget *widget,
1307 gint32 x,
1308 gint32 y,
1309 gint32 width,
1310 gint32 height,
1311 GdkRectangle *gdk_intersection)
1312 {
1313 GtkLayout *layout;
1314 DwRectangle widget_area, viewport_area, intersection;
1315
1316 g_return_val_if_fail (widget && widget->viewport, FALSE);
1317
1318 layout = GTK_LAYOUT (widget->viewport);
1319
1320 widget_area.x = widget->allocation.x + x;
1321 widget_area.y = widget->allocation.y + y;
1322 widget_area.width = width;
1323 widget_area.height = height;
1324
1325 viewport_area.x = layout->xoffset;
1326 viewport_area.y = layout->yoffset;
1327 viewport_area.width = widget->viewport->allocation.width;
1328 viewport_area.height = widget->viewport->allocation.height;
1329
1330 if (p_Dw_rectangle_intersect (&widget_area, &viewport_area,
1331 &intersection)) {
1332 gdk_intersection->x = intersection.x - layout->xoffset;
1333 gdk_intersection->y = intersection.y - layout->yoffset;
1334 gdk_intersection->width = intersection.width;
1335 gdk_intersection->height = intersection.height;
1336 return TRUE;
1337 } else
1338 return FALSE;
1339 }
1340
1341
1342 /*
1343 * ...
1344 */
1345 void p_Dw_widget_queue_draw (DwWidget *widget)
1346 {
1347 p_Dw_widget_queue_draw_area (widget, 0, 0, widget->allocation.width,
1348 DW_WIDGET_HEIGHT(widget));
1349 }
1350
1351
1352 /*
1353 * ...
1354 */
1355 void p_Dw_widget_queue_draw_area (DwWidget *widget,
1356 gint32 x,
1357 gint32 y,
1358 gint32 width,
1359 gint32 height)
1360 {
1361 /* todo: maybe only the intersection? */
1362 Dw_gtk_viewport_queue_draw (GTK_DW_VIEWPORT (widget->viewport),
1363 x + widget->allocation.x,
1364 y + widget->allocation.y, width, height);
1365 }
1366
1367
1368 /*
1369 * Resizing of Widgets.
1370 * The interface was adopted by Gtk+, but the implementation is far simpler,
1371 * since Gtk+ handles a lot of cases which are irrelevant to Dw.
1372 */
1373
1374 /*
1375 * This function should be called, if the widget changed its size.
1376 */
1377 void p_Dw_widget_queue_resize (DwWidget *widget,
1378 gint ref,
1379 gboolean extremes_changed)
1380 {
1381 DwWidget *widget2, *child;
1382 DwWidgetClass *klass;
1383
1384 DEBUG_MSG (DEBUG_SIZE,
1385 "a %stop-level %s with parent_ref = %d has changed its size\n",
1386 widget->parent ? "non-" : "",
1387 gtk_type_name (GTK_OBJECT_TYPE (widget)), widget->parent_ref);
1388
1389 klass = (DwWidgetClass*)(((GtkObject*)widget)->klass);
1390 DW_WIDGET_SET_FLAGS (widget, DW_NEEDS_RESIZE);
1391 if (klass->mark_size_change)
1392 klass->mark_size_change (widget, ref);
1393
1394 if (extremes_changed) {
1395 DW_WIDGET_SET_FLAGS (widget, DW_EXTREMES_CHANGED);
1396 if (klass->mark_extremes_change)
1397 klass->mark_extremes_change (widget, ref);
1398 }
1399
1400 for (widget2 = widget->parent, child = widget;
1401 widget2;
1402 child = widget2, widget2 = widget2->parent) {
1403 klass = (DwWidgetClass*)(((GtkObject*)widget2)->klass);
1404 DW_WIDGET_SET_FLAGS (widget2, DW_NEEDS_RESIZE);
1405 if (klass->mark_size_change)
1406 klass->mark_size_change (widget2, child->parent_ref);
1407 DW_WIDGET_SET_FLAGS (widget2, DW_NEEDS_ALLOCATE);
1408
1409 DEBUG_MSG (DEBUG_ALLOC,
1410 "setting DW_NEEDS_ALLOCATE for a %stop-level %s "
1411 "with parent_ref = %d\n",
1412 widget2->parent ? "non-" : "",
1413 gtk_type_name (GTK_OBJECT_TYPE (widget2)),
1414 widget2->parent_ref);
1415
1416 if (extremes_changed) {
1417 DW_WIDGET_SET_FLAGS (widget2, DW_EXTREMES_CHANGED);
1418 if (klass->mark_extremes_change)
1419 klass->mark_extremes_change (widget2, child->parent_ref);
1420 }
1421 }
1422
1423 if (widget->viewport)
1424 Dw_gtk_viewport_queue_resize (GTK_DW_VIEWPORT (widget->viewport));
1425 }
1426
1427 /*
1428 * If a widget might draw outside of its allocation, this function should be
1429 * called at the beginning of the draw method. It will cause drawings be done
1430 * in a temporary pixmap, which is later copied into the backing pixmap, so
1431 * that drawings outside of the allocation are later discarded.
1432 *
1433 * Clipping causes a bit overhead due to the second copying, so it should
1434 * only used when neccessary.
1435 */
1436 void p_Dw_widget_will_clip (DwWidget *widget)
1437 {
1438 DwWidget *widget2;
1439
1440 widget->clip_pixmap =
1441 gdk_pixmap_new (widget->viewport->window,
1442 widget->viewport->allocation.width,
1443 widget->viewport->allocation.height,
1444 GTK_DW_VIEWPORT(widget->viewport)->depth);
1445
1446 /* Determine the effective background color. (There is a defined background
1447 * at least for the top-level widget.) */
1448 for (widget2 = widget;
1449 widget2 != NULL && widget2->style->background_color == NULL;
1450 widget2 = widget2->parent)
1451 ;
1452
1453 g_return_if_fail (widget2 != NULL);
1454
1455 gdk_draw_rectangle (widget->clip_pixmap,
1456 widget2->style->background_color->gc,
1457 TRUE, 0, 0,
1458 widget->viewport->allocation.width,
1459 widget->viewport->allocation.height);
1460 }
1461
1462
1463 /*
1464 * Set the background "behind" the widget, if it is not the background of the
1465 * parent widget, e.g. the background of a table row.
1466 */
1467 void p_Dw_widget_set_bg_color (DwWidget *widget,
1468 DwStyleColor *color)
1469 {
1470 widget->bg_color = color;
1471 }
1472
1473 /*
1474 * Get the actual background of a widget.
1475 */
1476 DwStyleColor* p_Dw_widget_get_bg_color (DwWidget *widget)
1477 {
1478 while (widget != NULL) {
1479 if (widget->style->background_color)
1480 return widget->style->background_color;
1481 if (widget->bg_color)
1482 return widget->bg_color;
1483
1484 widget = widget->parent;
1485 }
1486
1487 g_warning ("No background color found!");
1488 return NULL;
1489 }
1490
1491
1492 /*
1493 * Draw borders and background of a widget part, which allocation is
1494 * given by (x, y, width, height) (widget coordinates).
1495 */
1496 void p_Dw_widget_draw_box (DwWidget *widget,
1497 DwStyle *style,
1498 DwRectangle *area,
1499 gint32 x,
1500 gint32 y,
1501 gint32 width,
1502 gint32 height,
1503 gboolean inverse)
1504 {
1505 GdkRectangle gdk_area;
1506 gint32 vx, vy;
1507
1508 if (Dw_widget_intersect_viewport (widget, area->x, area->y,
1509 area->width, area->height, &gdk_area)) {
1510 vx = p_Dw_widget_x_viewport_to_world (widget, 0);
1511 vy = p_Dw_widget_y_viewport_to_world (widget, 0);
1512
1513 p_Dw_style_draw_border (DW_WIDGET_WINDOW (widget), &gdk_area,
1514 vx, vy,
1515 widget->allocation.x + x,
1516 widget->allocation.y + y,
1517 width, height,
1518 style, inverse);
1519
1520 if (style->background_color)
1521 p_Dw_style_draw_background (DW_WIDGET_WINDOW (widget), &gdk_area,
1522 vx, vy,
1523 widget->allocation.x + x,
1524 widget->allocation.y + y,
1525 width, height,
1526 style, inverse);
1527 }
1528 }
1529
1530
1531 /*
1532 * Draw borders and background of a widget.
1533 */
1534 void p_Dw_widget_draw_widget_box (DwWidget *widget,
1535 DwRectangle *area,
1536 gboolean inverse)
1537 {
1538 GdkRectangle gdk_area;
1539 gint32 vx, vy;
1540
1541 if (Dw_widget_intersect_viewport (widget, area->x, area->y,
1542 area->width, area->height, &gdk_area)) {
1543 vx = p_Dw_widget_x_viewport_to_world (widget, 0);
1544 vy = p_Dw_widget_y_viewport_to_world (widget, 0);
1545
1546 p_Dw_style_draw_border (DW_WIDGET_WINDOW (widget), &gdk_area,
1547 vx, vy,
1548 widget->allocation.x,
1549 widget->allocation.y,
1550 widget->allocation.width,
1551 DW_WIDGET_HEIGHT(widget),
1552 widget->style, inverse);
1553
1554 /* - Toplevel widget background colors are set as viewport
1555 * background color. This is not crucial for the rendering, but
1556 * looks a bit nicer when scrolling. Furthermore, the viewport
1557 * does anything else in this case.
1558 *
1559 * - Since widgets are always drawn from top to bottom, it is
1560 * *not* necessary to draw the background if
1561 * widget->style->background_color is NULL (shining through).
1562 */
1563 if (widget->parent && widget->style->background_color)
1564 p_Dw_style_draw_background (DW_WIDGET_WINDOW (widget), &gdk_area,
1565 vx, vy,
1566 widget->allocation.x,
1567 widget->allocation.y,
1568 widget->allocation.width,
1569 DW_WIDGET_HEIGHT(widget),
1570 widget->style, inverse);
1571 }
1572 }
1573
1574 /*
1575 * This function is used by some widgets, when they are selected (as a whole).
1576 *
1577 * todo: This could be accelerated by using clipping bitmaps. Two important
1578 * issues:
1579 *
1580 * (i) There should always been a pixel in the upper-left corner of the
1581 * *widget*, so probably two different clipping bitmaps have to be
1582 * used (10/01 and 01/10).
1583 *
1584 * (ii) Should a new GC always be created?
1585 */
1586 void p_Dw_widget_draw_selected (DwWidget *widget,
1587 DwRectangle *area)
1588 {
1589 GdkRectangle gdk_area;
1590 /* All coordinates are widget coordinates. */
1591 gint32 x, y, startxa, startya, startxb, startyb, endx, endy, ix, iy;
1592 gint32 dx, dy; /* the difference between viewport and widget */
1593 DwStyleColor *bg_color;
1594 GdkWindow *window;
1595
1596 if (Dw_widget_intersect_viewport (widget, area->x, area->y,
1597 area->width, area->height, &gdk_area)) {
1598 /* Calculate from where to start the respective drawing loops below.
1599 * There should always been a pixel in the upper-left corner of the
1600 * *widget*, so the start depends on whether the drawing area (in widget
1601 * coordinates) has even or odd offsets. */
1602
1603 /* the intersection in widget coordinates */
1604 ix = p_Dw_widget_x_viewport_to_world (widget, gdk_area.x) -
1605 widget->allocation.x;
1606 iy = p_Dw_widget_y_viewport_to_world (widget, gdk_area.y) -
1607 widget->allocation.y;
1608
1609 if (ix % 2 == 0) {
1610 startxa = ix;
1611 startxb = ix + 1;
1612 } else {
1613 startxa = ix + 1;
1614 startxb = ix;
1615 }
1616
1617 if (iy % 2 == 0) {
1618 startya = iy;
1619 startyb = iy + 1;
1620 } else {
1621 startya = iy + 1;
1622 startyb = iy;
1623 }
1624
1625 dx = p_Dw_widget_x_world_to_viewport (widget, widget->allocation.x);
1626 dy = p_Dw_widget_y_world_to_viewport (widget, widget->allocation.y);
1627 endx = ix + gdk_area.width;
1628 endy = iy + gdk_area.height;
1629
1630 bg_color = p_Dw_widget_get_bg_color (widget);
1631 window = DW_WIDGET_WINDOW (widget);
1632
1633 for (x = startxa; x < endx; x += 2)
1634 for (y = startya; y < endy; y += 2)
1635 gdk_draw_point (window, bg_color->inverse_gc, x + dx, y + dy);
1636
1637 for (x = startxb; x < endx; x += 2)
1638 for (y = startyb; y < endy; y += 2)
1639 gdk_draw_point (window, bg_color->inverse_gc, x + dx, y + dy);
1640 }
1641 }
+0
-483
src/dw_widget.h less more
0 #ifndef __DW_WIDGET_H__
1 #define __DW_WIDGET_H__
2
3 #include <gtk/gtkobject.h>
4 #include <gtk/gtkwidget.h>
5 #include <gdk/gdktypes.h>
6
7 #include "dw_style.h"
8 #include "dw.h"
9
10 #ifdef __cplusplus
11 extern "C" {
12 #endif /* __cplusplus */
13
14 #define DW_TYPE_WIDGET (a_Dw_widget_get_type ())
15 #define DW_WIDGET(obj) GTK_CHECK_CAST (obj, DW_TYPE_WIDGET, DwWidget)
16 #define DW_WIDGET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, DW_TYPE_WIDGET, \
17 DwWidgetClass)
18 #define DW_IS_WIDGET(obj) GTK_CHECK_TYPE (obj, DW_TYPE_WIDGET)
19
20
21 #define DW_WIDGET_SET_FLAGS(wid, flag) (DW_WIDGET(wid)->flags |= (flag))
22 #define DW_WIDGET_UNSET_FLAGS(wid, flag) (DW_WIDGET(wid)->flags &= ~(flag))
23
24 #define DW_WIDGET_NEEDS_RESIZE(wid) (DW_WIDGET(wid)->flags & \
25 DW_NEEDS_RESIZE)
26 #define DW_WIDGET_NEEDS_ALLOCATE(wid) (DW_WIDGET(wid)->flags & \
27 DW_NEEDS_ALLOCATE)
28 #define DW_WIDGET_EXTREMES_CHANGED(wid) (DW_WIDGET(wid)->flags & \
29 DW_EXTREMES_CHANGED)
30
31 #define DW_WIDGET_USES_HINTS(wid) (DW_WIDGET(wid)->flags & \
32 DW_USES_HINTS)
33 #define DW_WIDGET_HAS_CONTENT(wid) (DW_WIDGET(wid)->flags & \
34 DW_HAS_CONTENT)
35
36 #define DW_WIDGET_HEIGHT(wid) ((wid)->allocation.ascent + \
37 (wid)->allocation.descent)
38 #define DW_WIDGET_CONTENT_HEIGHT(wid) \
39 ((wid)->allocation.ascent + (wid)->allocation.descent - \
40 p_Dw_style_box_diff_height((wid)->style))
41
42 #define DW_WIDGET_CONTENT_WIDTH(wid) ((wid)->allocation.width - \
43 p_Dw_style_box_diff_width((wid)->style))
44
45 typedef enum {
46 DW_NEEDS_RESIZE = 1 << 0,
47 DW_NEEDS_ALLOCATE = 1 << 1,
48 DW_EXTREMES_CHANGED = 1 << 2,
49 DW_USES_HINTS = 1 << 3,
50 DW_HAS_CONTENT = 1 << 4,
51 } DwWidgetFlags;
52
53 typedef enum
54 {
55 DW_HPOS_LEFT,
56 DW_HPOS_CENTER,
57 DW_HPOS_RIGHT,
58 DW_HPOS_INTO_VIEW, /* scroll only, until the content in question comes
59 * into view */
60 DW_HPOS_NO_CHANGE
61 } DwHPosition;
62
63 typedef enum
64 {
65 DW_VPOS_TOP,
66 DW_VPOS_CENTER,
67 DW_VPOS_BOTTOM,
68 DW_VPOS_INTO_VIEW, /* scroll only, until the content in question comes
69 * into view */
70 DW_VPOS_NO_CHANGE
71 } DwVPosition;
72
73 /* content types for iterator data */
74 typedef enum
75 {
76 DW_CONTENT_START = 1 << 0,
77 DW_CONTENT_END = 1 << 1,
78 DW_CONTENT_TEXT = 1 << 2,
79 DW_CONTENT_WIDGET = 1 << 3,
80 DW_CONTENT_ANCHOR = 1 << 4,
81 DW_CONTENT_BREAK = 1 << 5,
82 DW_CONTENT_ALL = 0xff
83 } DwContentType;
84
85 /* Different "layers" may be highlighted in a widget. */
86 typedef enum
87 {
88 DW_HIGHLIGHT_SELECTION,
89 DW_HIGHLIGHT_FINDTEXT,
90 DW_HIGHLIGHT_NUM_LAYERS
91 } DwHighlightLayer;
92
93 #define DW_WIDGET_WINDOW(widget) \
94 ((widget)->clip_pixmap ? (widget)->clip_pixmap : \
95 ((GtkDwViewport*)(widget)->viewport)->back_pixmap)
96
97 typedef struct _DwAllocation DwAllocation;
98 typedef struct _DwRequisition DwRequisition;
99 typedef struct _DwExtremes DwExtremes;
100 typedef struct _DwRectPosition DwRectPosition;
101 typedef struct _DwContent DwContent;
102 typedef struct _DwIterator DwIterator;
103 typedef struct _DwIteratorInt DwIteratorInt;
104 typedef struct _DwIteratorText DwIteratorText;
105
106 typedef struct _DwWidget DwWidget;
107 typedef struct _DwWidgetClass DwWidgetClass;
108
109 #define DW_PAINT_DEFAULT_BGND 0xd6d6c0
110
111
112 struct _DwAllocation
113 {
114 gint32 x;
115 gint32 y;
116 gint32 width;
117 gint32 ascent;
118 gint32 descent;
119 };
120
121
122 struct _DwRequisition
123 {
124 gint32 width;
125 gint32 ascent;
126 gint32 descent;
127 };
128
129
130 struct _DwExtremes
131 {
132 gint32 min_width;
133 gint32 max_width;
134 };
135
136
137 struct _DwRectPosition
138 {
139 gint32 x, y, width, height;
140 DwHPosition hpos;
141 DwVPosition vpos;
142 };
143
144
145 struct _DwContent
146 {
147 DwContentType type;
148 gboolean space;
149 union {
150 char *text;
151 DwWidget *widget;
152 char *anchor;
153 gint break_space;
154 } data;
155 };
156
157 struct _DwIterator
158 {
159 DwWidget *widget;
160 gint mask;
161
162 /* the current data, after first call of next */
163 DwContent content;
164
165 /* For simplicity, static stuff is put into the structure. */
166
167 /*
168 * Move iterator forward and store content it. Returns TRUE on
169 * success.
170 */
171 gboolean (*next) (DwIterator*);
172
173 /*
174 * Move iterator backward and store content it. Returns TRUE on
175 * success.
176 */
177 gboolean (*prev) (DwIterator*);
178
179 /*
180 * Highlight a part of the current content. Unhighlight the current
181 * content by passing -1 as start. For text, start and end define the
182 * characters, otherwise, the region is defined as [0, 1], i.e. for
183 * highlighting a whole DwContent, pass 0 and >= 1.
184 */
185 void (*highlight) (DwIterator*,
186 gint start,
187 gint end,
188 DwHighlightLayer layer);
189
190 /*
191 * Return the region, which a part of the item, the iterator points on,
192 * allocates. The parameters start and end have the same meaning as in
193 * DwIterator::highlight().
194 */
195 void (*get_allocation) (DwIterator*,
196 gint start,
197 gint end,
198 DwAllocation*);
199 /*
200 * Create an exact copy of the iterator, which then can be used
201 * independantly of the original one.
202 */
203 DwIterator* (*clone) (DwIterator*);
204
205 /*
206 * Return a value < 0, if first iterator is smaller, > 0, if it is
207 * greater, or 0 for equal iterators. The implementation may assume
208 * that both iterators belong to the same widget.
209 */
210 gint (*compare) (DwIterator*,
211 DwIterator*);
212
213 /*
214 * Free memory of iterator.
215 */
216 void (*free) (DwIterator*);
217 };
218
219 /* This iterator type is quite commonly used. */
220 struct _DwIteratorInt
221 {
222 DwIterator it;
223 int pos;
224 };
225
226 struct _DwIteratorText
227 {
228 DwIterator it;
229 gchar *text;
230 };
231
232 struct _DwWidget
233 {
234 GtkObject object;
235
236 /* the parent widget, NULL for top-level widgets */
237 DwWidget *parent;
238
239 /* This value is defined by the parent widget, and used for incremential
240 * resizing. See Dw.txt for an explanation. */
241 gint parent_ref;
242
243 /* the viewport in which the widget is shown */
244 GtkWidget *viewport;
245
246 /* see definition at the beginning */
247 DwWidgetFlags flags;
248
249 /* the current allocation: size and position, always relative to the
250 * scrolled area! */
251 DwAllocation allocation;
252
253 /* a_Dw_widget_size_request stores the result of the last call of
254 * Dw_xxx_size_request here, don't read this directly, but call
255 * a_Dw_widget_size_request. */
256 DwRequisition requisition;
257
258 /* analogue to requisition */
259 DwExtremes extremes;
260
261 GdkCursor *cursor; /* todo: move this to style */
262 DwStyle *style;
263
264 /* See p_Dw_widget_set_bg_color(). */
265 DwStyleColor *bg_color;
266
267 /* See a_Dw_widget_set_button_sensitive(). */
268 gboolean button_sensitive;
269 gboolean button_sensitive_set;
270
271 /* A temporary pixmap used for clipping. See p_Dw_widget_will_clip(). */
272 GdkPixmap *clip_pixmap;
273 };
274
275
276 struct _DwWidgetClass
277 {
278 GtkObjectClass parent_class;
279
280 void (*size_request) (DwWidget *widget,
281 DwRequisition *requisition);
282 void (*get_extremes) (DwWidget *widget,
283 DwExtremes *extremes);
284 void (*size_allocate) (DwWidget *widget,
285 DwAllocation *allocation);
286 void (*mark_size_change) (DwWidget *widget,
287 gint ref);
288 void (*mark_extremes_change) (DwWidget *widget,
289 gint ref);
290 void (*set_width) (DwWidget *widget,
291 gint32 width);
292 void (*set_ascent) (DwWidget *widget,
293 gint32 ascent);
294 void (*set_descent) (DwWidget *widget,
295 gint32 descent);
296 void (*draw) (DwWidget *widget,
297 DwRectangle *area,
298 GdkEventExpose *event);
299
300 gboolean (*button_press_event) (DwWidget *widget,
301 gint32 x,
302 gint32 y,
303 GdkEventButton *event);
304 gboolean (*button_release_event) (DwWidget *widget,
305 gint32 x,
306 gint32 y,
307 GdkEventButton *event);
308 gboolean (*motion_notify_event) (DwWidget *widget,
309 gint32 x,
310 gint32 y,
311 GdkEventMotion *event);
312 gboolean (*enter_notify_event) (DwWidget *widget,
313 DwWidget *last_widget,
314 GdkEventMotion *event);
315 gboolean (*leave_notify_event) (DwWidget *widget,
316 DwWidget *next_widget,
317 GdkEventMotion *event);
318
319 DwIterator* (*iterator) (DwWidget*,
320 gint32 mask,
321 gboolean at_end);
322 };
323
324
325 GtkType a_Dw_widget_get_type (void);
326
327 void p_Dw_widget_size_request (DwWidget *widget,
328 DwRequisition *requisition);
329 void p_Dw_widget_get_extremes (DwWidget *widget,
330 DwExtremes *extremes);
331 void p_Dw_widget_size_allocate (DwWidget *widget,
332 DwAllocation *allocation);
333 void p_Dw_widget_set_width (DwWidget *widget,
334 gint32 width);
335 void p_Dw_widget_set_ascent (DwWidget *widget,
336 gint32 ascent);
337 void p_Dw_widget_set_descent (DwWidget *widget,
338 gint32 descent);
339 void p_Dw_widget_draw (DwWidget *widget,
340 DwRectangle *area,
341 GdkEventExpose *event);
342
343 void a_Dw_widget_set_style (DwWidget *widget,
344 DwStyle *style);
345 void a_Dw_widget_set_cursor (DwWidget *widget,
346 GdkCursor *cursor);
347
348 void a_Dw_widget_set_button_sensitive (DwWidget *widget,
349 gboolean button_sensitive);
350
351 DwWidget *a_Dw_widget_get_toplevel (DwWidget *widget);
352
353 void a_Dw_widget_scroll_to (DwWidget *widget,
354 DwHPosition hpos,
355 DwVPosition vpos,
356 gint32 x,
357 gint32 y,
358 gint32 width,
359 gint32 height);
360
361 /* iterators */
362 DwIterator* a_Dw_widget_iterator (DwWidget *widget,
363 gint mask,
364 gboolean at_end);
365 #define a_Dw_iterator_next(it) ((it) ? (it)->next(it) : FALSE)
366 #define a_Dw_iterator_prev(it) ((it) ? (it)->prev(it) : FALSE)
367 #define a_Dw_iterator_highlight(it, s, e, l) ((it) ? \
368 (it)->highlight(it, s, e, l) : (void)0)
369 #define a_Dw_iterator_get_allocation(it, s, e, a) \
370 ((it) ? \
371 (it1)->get_allocation(it, s, e, a)\
372 : (void)0)
373 #define a_Dw_iterator_clone(it) ((it) ? (it)->clone(it) : NULL)
374 #define a_Dw_iterator_compare(it1, it2) ((it1)->compare(it1, it2))
375 #define a_Dw_iterator_free(it) ((it) ? (it)->free(it) : (void)0)
376
377 /* for convenience */
378 #define a_Dw_iterator_unhighlight(it, l) ((it) ? \
379 (it)->highlight(it, -1, -1, l) : (void)0)
380 void a_Dw_iterator_scroll_to (DwIterator *it1,
381 DwIterator *it2,
382 gint start,
383 gint end,
384 DwHPosition hpos,
385 DwVPosition vpos);
386
387 DwIterator* p_Dw_iterator_clone_std (DwIterator *it);
388 void p_Dw_iterator_free_std (DwIterator *it);
389 void p_Dw_iterator_highlight_std (DwIterator *it,
390 gint from,
391 gint start,
392 DwHighlightLayer layer);
393
394 void p_Dw_iterator_get_allocation_std_only_widgets (DwIterator *it,
395 gint start,
396 gint end,
397 DwAllocation
398 *allocation);
399
400 DwIterator* p_Dw_iterator_clone_std_int (DwIterator *it);
401 gint p_Dw_iterator_compare_std_int (DwIterator *it1,
402 DwIterator *it2);
403
404 /* for debugging */
405 gchar* a_Dw_content_text (DwContent *content);
406 gchar* a_Dw_content_html (DwContent *content);
407 gchar* a_Dw_iterator_text (DwIterator *it);
408 void a_Dw_widget_print_tree (DwWidget *widget);
409
410 DwIterator* p_Dw_widget_text_iterator (DwWidget* widget,
411 gint32 mask,
412 gboolean at_end,
413 gchar *text);
414 gboolean p_Dw_iterator_text_next (DwIterator *it);
415 gboolean p_Dw_iterator_text_prev (DwIterator *it);
416 DwIterator* p_Dw_iterator_text_clone (DwIterator *it);
417 gint p_Dw_iterator_text_compare (DwIterator *it1,
418 DwIterator *it2);
419 void p_Dw_iterator_text_get_allocation (DwIterator *it,
420 gint start,
421 gint end,
422 DwAllocation *allocation);
423
424 /* Only for Dw module */
425 gint p_Dw_widget_intersect (DwWidget *widget,
426 DwRectangle *area,
427 DwRectangle *intersection);
428 void p_Dw_widget_set_parent (DwWidget *widget,
429 DwWidget *parent);
430
431 gint32 p_Dw_widget_x_viewport_to_world (DwWidget *widget,
432 gint16 viewport_x);
433 gint32 p_Dw_widget_y_viewport_to_world (DwWidget *widget,
434 gint16 viewport_y);
435 gint16 p_Dw_widget_x_world_to_viewport (DwWidget *widget,
436 gint32 world_x);
437 gint16 p_Dw_widget_y_world_to_viewport (DwWidget *widget,
438 gint32 world_y);
439
440 gint Dw_widget_mouse_event (DwWidget *widget,
441 GtkWidget *viewwidget,
442 gint32 x,
443 gint32 y,
444 GdkEvent *event);
445 void Dw_widget_update_cursor (DwWidget *widget);
446 void p_Dw_widget_queue_draw (DwWidget *widget);
447 void p_Dw_widget_queue_draw_area (DwWidget *widget,
448 gint32 x,
449 gint32 y,
450 gint32 width,
451 gint32 height);
452 void p_Dw_widget_queue_resize (DwWidget *widget,
453 gint ref,
454 gboolean extremes_changed);
455 void p_Dw_widget_will_clip (DwWidget *widget);
456
457 void p_Dw_widget_set_bg_color (DwWidget *widget,
458 DwStyleColor *color);
459 DwStyleColor* p_Dw_widget_get_bg_color(DwWidget *widget);
460
461 /* Wrappers for Dw_style_draw_box */
462 void p_Dw_widget_draw_box (DwWidget *widget,
463 DwStyle *style,
464 DwRectangle *area,
465 gint32 x,
466 gint32 y,
467 gint32 width,
468 gint32 height,
469 gboolean inverse);
470 void p_Dw_widget_draw_widget_box (DwWidget *widget,
471 DwRectangle *area,
472 gboolean inverse);
473
474 void p_Dw_widget_draw_selected (DwWidget *widget,
475 DwRectangle *area);
476
477
478 #ifdef __cplusplus
479 }
480 #endif /* __cplusplus */
481
482 #endif /* __DW_WIDGET_H__ */
0 /*
1 * File: findbar.cc
2 *
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <FL/Fl.H>
12 #include <FL/Fl_Window.H>
13 #include "findbar.hh"
14
15 #include "msg.h"
16 #include "pixmaps.h"
17 #include "uicmd.hh"
18 #include "bw.h"
19
20 /*
21 * Local sub class
22 * (Used to handle escape in the findbar, may also avoid some shortcuts).
23 */
24 class MyInput : public Fl_Input {
25 public:
26 MyInput (int x, int y, int w, int h, const char* l=0) :
27 Fl_Input(x,y,w,h,l) {};
28 int handle(int e);
29 };
30
31 int MyInput::handle(int e)
32 {
33 _MSG("findbar MyInput::handle()\n");
34 int ret = 1, k = Fl::event_key();
35 unsigned modifier = Fl::event_state() & (FL_SHIFT| FL_CTRL| FL_ALT|FL_META);
36
37 if (e == FL_KEYBOARD) {
38 if (modifier == FL_SHIFT) {
39 if (k == FL_Left || k == FL_Right) {
40 // Let these keys get to the UI
41 return 0;
42 }
43 } else if (k == FL_Escape && modifier == 0) {
44 // Avoid clearing the text with Esc, just hide the findbar.
45 return 0;
46 }
47 }
48
49 if (ret)
50 ret = Fl_Input::handle(e);
51 return ret;
52 };
53
54 /*
55 * Find next occurrence of input key
56 */
57 void Findbar::search_cb(Fl_Widget *, void *vfb)
58 {
59 Findbar *fb = (Findbar *)vfb;
60 const char *key = fb->i->value();
61 bool case_sens = fb->check_btn->value();
62
63 if (key[0] != '\0')
64 a_UIcmd_findtext_search(a_UIcmd_get_bw_by_widget(fb),
65 key, case_sens, false);
66 }
67
68 /*
69 * Find previous occurrence of input key
70 */
71 void Findbar::searchBackwards_cb(Fl_Widget *, void *vfb)
72 {
73 Findbar *fb = (Findbar *)vfb;
74 const char *key = fb->i->value();
75 bool case_sens = fb->check_btn->value();
76
77 if (key[0] != '\0') {
78 a_UIcmd_findtext_search(a_UIcmd_get_bw_by_widget(fb),
79 key, case_sens, true);
80 }
81 }
82
83 /*
84 * Hide the search bar
85 */
86 void Findbar::hide_cb(Fl_Widget *, void *vfb)
87 {
88 ((Findbar *)vfb)->hide();
89 }
90
91 /*
92 * Construct text search bar
93 */
94 Findbar::Findbar(int width, int height) :
95 Fl_Group(0, 0, width, height)
96 {
97 int button_width = 70;
98 int gap = 2;
99 int border = 2;
100 int input_width = width - (2 * border + 4 * (button_width + gap));
101 int x = 0;
102
103 Fl_Group::current(0);
104
105 height -= 2 * border;
106
107 box(FL_THIN_UP_BOX);
108
109 hide_btn = new Fl_Button(x, border, 16, height, 0);
110 hideImg = new Fl_Pixmap(new_s_xpm);
111 hide_btn->image(hideImg);
112 x += 16 + gap;
113 hide_btn->callback(hide_cb, this);
114 hide_btn->clear_visible_focus();
115 hide_btn->box(FL_THIN_UP_BOX);
116 add(hide_btn);
117
118 i = new MyInput(x, border, input_width, height);
119 x += input_width + gap;
120 resizable(i);
121 i->color(206);
122 i->when(FL_WHEN_NEVER);
123 add(i);
124
125 next_btn = new Fl_Button(x, border, button_width, height, "Next");
126 x += button_width + gap;
127 next_btn->shortcut(FL_Enter);
128 next_btn->callback(search_cb, this);
129 next_btn->clear_visible_focus();
130 next_btn->box(FL_THIN_UP_BOX);
131 add(next_btn);
132
133 prev_btn= new Fl_Button(x, border, button_width, height, "Previous");
134 x += button_width + gap;
135 prev_btn->shortcut(FL_SHIFT+FL_Enter);
136 prev_btn->callback(searchBackwards_cb, this);
137 prev_btn->clear_visible_focus();
138 prev_btn->box(FL_THIN_UP_BOX);
139 add(prev_btn);
140
141 check_btn = new Fl_Check_Button(x, border, 2*button_width, height,
142 "Case-sensitive");
143 x += 2 * button_width + gap;
144 check_btn->clear_visible_focus();
145 add(check_btn);
146
147 if (prefs.show_tooltip) {
148 hide_btn->tooltip("Hide");
149 next_btn->tooltip("Find next occurrence of the search phrase\n"
150 "shortcut: Enter");
151 prev_btn->tooltip("Find previous occurrence of the search phrase\n"
152 "shortcut: Shift+Enter");
153 }
154 }
155
156 Findbar::~Findbar()
157 {
158 delete hideImg;
159 }
160
161 /*
162 * Handle events. Used to catch FL_Escape events.
163 */
164 int Findbar::handle(int event)
165 {
166 int ret = 0;
167 int k = Fl::event_key();
168 unsigned modifier = Fl::event_state() & (FL_SHIFT| FL_CTRL| FL_ALT|FL_META);
169
170 if (event == FL_KEYBOARD && modifier == 0 && k == FL_Escape) {
171 hide();
172 ret = 1;
173 }
174
175 if (ret == 0)
176 ret = Fl_Group::handle(event);
177
178 return ret;
179 }
180
181 /*
182 * Show the findbar and focus the input field
183 */
184 void Findbar::show()
185 {
186 BrowserWindow *bw = a_UIcmd_get_bw_by_widget(this);
187 dReturn_if (bw == NULL);
188
189 // It takes more than just calling show() to do the trick
190 //a_UIcmd_findbar_toggle(bw, 1);
191 Fl_Group::show();
192
193 /* select text even if already focused */
194 i->take_focus();
195 i->position(i->size(), 0);
196 }
197
198 /*
199 * Hide the findbar and reset the search state
200 */
201 void Findbar::hide()
202 {
203 BrowserWindow *bw = a_UIcmd_get_bw_by_widget(this);
204 dReturn_if (bw == NULL);
205
206 // It takes more than just calling hide() to do the trick
207 Fl_Group::hide();
208 a_UIcmd_findbar_toggle(bw, 0);
209
210 a_UIcmd_findtext_reset(bw);
211 a_UIcmd_focus_main_area(bw);
212 }
0 #ifndef __FINDBAR_HH__
1 #define __FINDBAR_HH__
2
3 #include <FL/Fl_Pixmap.H>
4 #include <FL/Fl_Widget.H>
5 #include <FL/Fl_Button.H>
6 #include <FL/Fl_Input.H>
7 #include <FL/Fl_Group.H>
8 #include <FL/Fl_Check_Button.H>
9
10 /*
11 * Searchbar to find text in page.
12 */
13 class Findbar : public Fl_Group {
14 Fl_Button *clrb;
15 Fl_Button *hide_btn, *next_btn, *prev_btn;
16 Fl_Check_Button *check_btn;
17 Fl_Pixmap *hideImg;
18 Fl_Input *i;
19
20 static void search_cb (Fl_Widget *, void *);
21 static void searchBackwards_cb (Fl_Widget *, void *);
22 static void hide_cb (Fl_Widget *, void *);
23
24 public:
25 Findbar(int width, int height);
26 ~Findbar();
27 int handle(int event);
28 void show();
29 void hide();
30 };
31
32 #endif // __FINDBAR_HH__
+0
-439
src/findtext.c less more
0 /*
1 * File: findtext.c
2 *
3 * Copyright 2002 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This module contains all the functionality for finding text, based
13 * on DwWordIterator.
14 *
15 * Search string semantics: "foo" searches for a word containing
16 * "foo", " foo" for a word beginning with "foo", "foo " for a word
17 * ending with "foo", and " foo " for the word "foo" itself. Similar,
18 * if you specify several words, spaces at the beginning and at the
19 * end will be treated in the same way.
20 *
21 * Searching is done in a slight variant of Knuth-Morris-Pratt, with
22 * words instead of characters, and an extended comparison, via
23 * Findtext_compare, both in the construction of the next table
24 * (Findtext_key_new), and the actual searching (Findtext_search0).
25 * This works, since other comparison that FINDTEXT_FULL are only
26 * allowed at the beginning (FINDTEXT_RIGHT), and at the end
27 * (FINDTEXT_LEFT); they have following effects:
28 *
29 * - Constructing the next table (Findtext_key_new): In
30 * comparisons, the first word is only used as the "needle", so
31 * there may be a state for which nexttab has the value 0
32 * (nexttab[0] itself is always -1). In Findtext_search0, this
33 * will cause the comparison correctly start again with the
34 * second word, since the word in the text before, which is
35 * /identical/ with the respective word in the key, so contains
36 * the first word in the key. The last word is never used in
37 * Findtext_key_new.
38 *
39 * - Actual searching (Findtext_search0): Since nexttab[0] is
40 * always -1, a failed comparison will restart the search. The
41 * state, which is switched to after the comparison with the last
42 * word failed depends only on the words before.
43 */
44
45 /*
46 * todo: If a one-word key is found twice in a word, the second
47 * occurence is ignored (a side-effect of word-by-word
48 * searching).
49 */
50
51 #include "msg.h"
52 #include "findtext.h"
53 #include "misc.h"
54 #include "debug.h"
55 #include <string.h>
56 #include <ctype.h>
57
58 /*
59 * Searches needle in haystack, according to type:
60 *
61 * FINDTEXT_FULL Needle equals haystack.
62 * FINDTEXT_FREE Haystack contains needle.
63 * FINDTEXT_LEFT Needle is the left part of haystack.
64 * FINDTEXT_RIGHT Needle is the right part of haystack.
65 *
66 * Returns pointer of occurrence in haystack, or NULL.
67 */
68 static gchar *Findtext_compare (gchar *haystack, gchar *needle,
69 FindtextPatternType type,
70 gboolean case_sens)
71 {
72 gchar *p;
73
74 if (case_sens)
75 switch (type) {
76 case FINDTEXT_FULL:
77 return (strcmp (haystack, needle) == 0) ? haystack : NULL;
78 case FINDTEXT_FREE:
79 return strstr (haystack, needle);
80 case FINDTEXT_LEFT:
81 return (strncmp (haystack, needle, strlen(needle)) == 0) ?
82 haystack : NULL;
83 case FINDTEXT_RIGHT:
84 p = haystack + strlen (haystack) - strlen (needle);
85 return (strcmp (p, needle) == 0) ? p : NULL;
86 }
87 else
88 switch (type) {
89 case FINDTEXT_FULL:
90 return (g_strcasecmp (haystack, needle) == 0) ? haystack : NULL;
91 case FINDTEXT_FREE:
92 return a_Misc_stristr (haystack, needle);
93 case FINDTEXT_LEFT:
94 return (g_strncasecmp (haystack, needle, strlen(needle)) == 0) ?
95 haystack : NULL;
96 case FINDTEXT_RIGHT:
97 p = haystack + strlen (haystack) - strlen (needle);
98 return (g_strcasecmp (p, needle) == 0) ? p : NULL;
99 }
100
101 /* compiler happiness */
102 return FALSE;
103 }
104
105 /*
106 * Creates a new key from a string.
107 */
108 static FindtextKey *Findtext_key_new (gchar *string, gboolean case_sens)
109 {
110 FindtextKey *key;
111 gint i, j, *splitpos;
112 gboolean first_spaces, last_spaces;
113
114 key = g_new (FindtextKey, 1);
115 splitpos = a_Misc_strsplitpos (string, " \t\n");
116
117 if (splitpos[0] == -1) {
118 /* Only spaces in the key. Construct a key with the words
119 * <empty-right> <space> <empty-left>, which will find spaces. */
120 key->len = 2;
121 key->words = g_new (gchar*, key->len);
122 key->types = g_new (FindtextPatternType, key->len);
123 key->nexttab = g_new (gint, key->len);
124
125 key->words[0] = g_strdup ("");
126 key->words[1] = g_strdup ("");
127 key->nexttab[0] = key->nexttab[1] = -1; /* Actually not needed, since
128 * comparison always succeeds. */
129 key->types[0] = FINDTEXT_RIGHT;
130 key->types[1] = FINDTEXT_LEFT;
131 } else {
132 first_spaces = (string[0] == ' ');
133 last_spaces = (string[strlen(string) - 1] == ' ');
134
135 key->len = 0;
136 while (splitpos[2 * key->len] != -1)
137 key->len++;
138
139 key->words = g_new (gchar*, key->len);
140 for (i = 0; i < key->len; i++)
141 key->words[i] =
142 a_Misc_strpdup (string, splitpos[2 * i], splitpos[2 * i + 1]);
143
144 key->types = g_new (FindtextPatternType, key->len);
145 key->nexttab = g_new (gint, key->len);
146
147 if (key->len == 1) {
148 key->nexttab[0] = -1;
149 if (first_spaces && last_spaces)
150 key->types[0] = FINDTEXT_FULL;
151 else if (!first_spaces && !last_spaces)
152 key->types[0] = FINDTEXT_FREE;
153 else if (first_spaces && !last_spaces)
154 key->types[0] = FINDTEXT_LEFT;
155 else /*if (!first_spaces && last_spaces)*/
156 key->types[0] = FINDTEXT_RIGHT;
157 } else {
158 for (i = 0; i < key->len; i++)
159 key->types[i] = FINDTEXT_FULL;
160
161 /* Begin and end. */
162 if (!first_spaces)
163 key->types[0] = FINDTEXT_RIGHT;
164 if (!last_spaces)
165 key->types[key->len - 1] = FINDTEXT_LEFT;
166
167 /* Build the next table. */
168 i = 0;
169 j = -1;
170 key->nexttab[0] = -1;
171
172 do {
173 if (j == -1 || Findtext_compare (key->words[i], key->words[j],
174 key->types[j], case_sens)) {
175 i++;
176 j++;
177 key->nexttab[i] = j;
178 _MSG ("nexttab[%d] = %d\n", i, j);
179 } else {
180 j = key->nexttab[j];
181 }
182 } while (i < key->len - 1);
183 }
184 }
185
186 g_free (splitpos);
187 return key;
188 }
189
190 /*
191 * Frees the memory allocated by a key.
192 */
193 static void Findtext_key_destroy (FindtextKey *key)
194 {
195 int i;
196 for (i = 0; i < key->len; i++)
197 g_free (key->words[i]);
198 g_free (key->words);
199 g_free (key->types);
200 g_free (key->nexttab);
201 g_free (key);
202 }
203
204
205
206 /*
207 * Creates a new findtext state. The widget must be set by
208 * a_Findtext_state_set_widget before the search can be started.
209 */
210 FindtextState *a_Findtext_state_new (void)
211 {
212 FindtextState *fts;
213
214 fts = g_new0 (FindtextState, 1);
215 DBG_OBJ_CREATE (fts, "FindtextState");
216 return fts;
217 }
218
219
220 /*
221 * Frees the memory allocated by a findtext state.
222 */
223 void a_Findtext_state_destroy (FindtextState *state)
224 {
225 if (state->key)
226 Findtext_key_destroy (state->key);
227 g_free (state->keystr);
228 if (state->iterator)
229 a_Dw_word_iterator_free (state->iterator);
230 if (state->hl_iterator)
231 a_Dw_word_iterator_free (state->hl_iterator);
232 g_free (state);
233 }
234
235
236 /*
237 * Sets the widget to search in. Widget may be NULL, in which case
238 * a_Findtext_search will always return FINDTEXT_NOT_FOUND.
239 */
240 void a_Findtext_state_set_widget (FindtextState *state,
241 DwWidget *widget)
242 {
243 state->widget = widget;
244 if (state->iterator)
245 a_Dw_word_iterator_free (state->iterator);
246 state->iterator = NULL;
247 if (state->hl_iterator)
248 a_Dw_word_iterator_free (state->hl_iterator);
249 state->hl_iterator = NULL;
250 /* A widget change will restart the search. */
251 if (state->key)
252 Findtext_key_destroy (state->key);
253 state->key = NULL;
254 g_free (state->keystr);
255 state->keystr = NULL;
256 }
257
258 static gboolean Findtext_search0 (FindtextState *state,
259 gboolean case_sens)
260 {
261 int i, j;
262 gboolean nextit;
263 gchar *p = NULL;
264
265 if (state->iterator->word == NULL)
266 return FALSE;
267
268 j = 0;
269 nextit = TRUE;
270
271 do {
272 if (j == -1 || (p = Findtext_compare (state->iterator->word,
273 state->key->words[j],
274 state->key->types[j],
275 case_sens))) {
276 if (j == 0)
277 state->first_hl_start = p - state->iterator->word;
278 if (j == state->key->len - 1) {
279 state->last_hl_end = strlen (state->key->words[j]);
280 if (j == 0)
281 state->last_hl_end += state->first_hl_start;
282 }
283
284 j++;
285 nextit = a_Dw_word_iterator_next (state->iterator);
286 } else
287 j = state->key->nexttab[j];
288 } while (nextit && j < state->key->len);
289
290 if (j >= state->key->len) {
291 /* Go back to where the word was found. */
292 for (i = 0; i < state->key->len; i++)
293 a_Dw_word_iterator_prev (state->iterator);
294 return TRUE;
295 } else
296 return FALSE;
297 }
298
299
300
301 FindtextResult a_Findtext_search (FindtextState *state,
302 gchar *str,
303 gboolean case_sens)
304 {
305 int i;
306 gboolean new_key = FALSE, was_highlighted = FALSE, first_trial;
307 FindtextResult result2;
308
309 if (state->widget == NULL)
310 return FINDTEXT_NOT_FOUND;
311
312 /* If there is still highlighted text */
313 if (state->hl_iterator) {
314 was_highlighted = TRUE;
315 for (i = 0; i < state->key->len - 1; i++) {
316 a_Dw_word_iterator_unhighlight (state->hl_iterator,
317 DW_HIGHLIGHT_FINDTEXT);
318 a_Dw_word_iterator_next (state->hl_iterator);
319 }
320 a_Dw_word_iterator_unhighlight (state->hl_iterator,
321 DW_HIGHLIGHT_FINDTEXT);
322 a_Dw_word_iterator_free (state->hl_iterator);
323 state->hl_iterator = NULL;
324 }
325
326 /* If the key (or the widget) changes (including case sensitivity),
327 the search is started from the beginning. */
328 if (state->keystr == NULL || state->case_sens != case_sens ||
329 strcmp(state->keystr, str) != 0) {
330 new_key = TRUE;
331
332 g_free (state->keystr);
333 state->keystr = g_strdup (str);
334 state->case_sens = case_sens;
335
336 if (state->key != NULL)
337 Findtext_key_destroy (state->key);
338 state->key = Findtext_key_new (str, case_sens);
339
340 if (state->iterator)
341 a_Dw_word_iterator_free (state->iterator);
342 state->iterator = a_Dw_word_iterator_new (state->widget);
343 a_Dw_word_iterator_next (state->iterator);
344 }
345
346 first_trial = !was_highlighted || new_key;
347
348 if (Findtext_search0 (state, case_sens)) {
349 /* Highlighlighting is done with a clone. */
350 state->hl_iterator = a_Dw_word_iterator_clone (state->iterator);
351 if (state->key->len == 1) {
352 a_Dw_word_iterator_scroll_to (state->iterator, state->iterator,
353 state->first_hl_start,
354 state->last_hl_end,
355 DW_HPOS_INTO_VIEW, DW_VPOS_CENTER);
356 a_Dw_word_iterator_highlight (state->hl_iterator,
357 state->first_hl_start,
358 state->last_hl_end,
359 DW_HIGHLIGHT_FINDTEXT);
360 DBG_MSGF (state, "findtext", 0, "highlighting %s from %d to %d",
361 state->hl_iterator->word,
362 state->first_hl_start, state->last_hl_end);
363 } else {
364 a_Dw_word_iterator_highlight (state->hl_iterator,
365 state->first_hl_start,
366 strlen (state->hl_iterator->word) + 1,
367 DW_HIGHLIGHT_FINDTEXT);
368 DBG_MSGF (state, "findtext", 0, "highlighting '%s' from %d to %d",
369 state->hl_iterator->word, state->first_hl_start,
370 strlen (state->hl_iterator->word) + 1);
371 a_Dw_word_iterator_next (state->hl_iterator);
372 for (i = 1; i < state->key->len - 1; i++) {
373 a_Dw_word_iterator_highlight (state->hl_iterator, 0,
374 strlen (state->hl_iterator->word)
375 + 1,
376 DW_HIGHLIGHT_FINDTEXT);
377 DBG_MSGF (state, "findtext", 0, "highlighting '%s' from %d to %d",
378 state->hl_iterator->word,
379 0, strlen (state->hl_iterator->word) + 1);
380 a_Dw_word_iterator_next (state->hl_iterator);
381 }
382 a_Dw_word_iterator_highlight (state->hl_iterator, 0,
383 state->last_hl_end,
384 DW_HIGHLIGHT_FINDTEXT);
385 DBG_MSGF (state, "findtext", 0, "highlighting '%s' from %d to %d",
386 state->hl_iterator->word, 0, state->last_hl_end);
387
388 a_Dw_word_iterator_scroll_to (state->iterator, state->hl_iterator,
389 state->first_hl_start,
390 state->last_hl_end,
391 DW_HPOS_INTO_VIEW, DW_VPOS_CENTER);
392 }
393 for (i = 0; i < state->key->len - 1; i++)
394 a_Dw_word_iterator_prev (state->hl_iterator);
395
396 /* The search will continue from the word after the found position. */
397 a_Dw_word_iterator_next (state->iterator);
398
399 return FINDTEXT_SUCCESS;
400 }
401
402 if (first_trial)
403 return FINDTEXT_NOT_FOUND;
404 else {
405 /* Nothing found anymore, reset the state for the next trial. */
406 a_Dw_word_iterator_free (state->iterator);
407 state->iterator = a_Dw_word_iterator_new (state->widget);
408 a_Dw_word_iterator_next (state->iterator);
409
410 /* We expect a success. */
411 result2 = a_Findtext_search (state, str, case_sens);
412 g_assert (result2 == FINDTEXT_SUCCESS);
413 return FINDTEXT_RESTART;
414 }
415 }
416
417 /*
418 * This function is called when the user closes the "find text" dialog.
419 */
420 void a_Findtext_reset_search (FindtextState *state)
421 {
422 int i;
423
424 g_free (state->keystr);
425 state->keystr = NULL;
426
427 if (state->hl_iterator) {
428 for (i = 0; i < state->key->len - 1; i++) {
429 a_Dw_word_iterator_unhighlight (state->hl_iterator,
430 DW_HIGHLIGHT_FINDTEXT);
431 a_Dw_word_iterator_next (state->hl_iterator);
432 }
433 a_Dw_word_iterator_unhighlight (state->hl_iterator,
434 DW_HIGHLIGHT_FINDTEXT);
435 a_Dw_word_iterator_free (state->hl_iterator);
436 state->hl_iterator = NULL;
437 }
438 }
+0
-66
src/findtext.h less more
0 #ifndef __FINDTEXT_H__
1 #define __FINDTEXT_H__
2
3 #include "dw_ext_iterator.h"
4
5 /*
6 * Return values of a_Findtext_search.
7 */
8 typedef enum {
9 FINDTEXT_SUCCESS, /* The next occurance of the pattern has been
10 found. */
11 FINDTEXT_RESTART, /* There is no further occurance of the pattern,
12 instead, the first occurance has been selected. */
13 FINDTEXT_NOT_FOUND /* The patten does not at all occur in the text. */
14 } FindtextResult;
15
16 /*
17 * See Findtext_compare for an explanation.
18 */
19 typedef enum {
20 FINDTEXT_FULL,
21 FINDTEXT_FREE,
22 FINDTEXT_LEFT,
23 FINDTEXT_RIGHT
24 } FindtextPatternType;
25
26 typedef struct _FindtextState FindtextState;
27 typedef struct _FindtextKey FindtextKey;
28
29 /*
30 * This structure is associated by the GtkDwViewport.
31 */
32 struct _FindtextState
33 {
34 FindtextKey *key;
35 gchar *keystr;
36 gboolean case_sens;
37 DwWidget *widget; /* The top of the widget tree, in which
38 the search is done. From this, the
39 iterator will be constructed. */
40 DwWordIterator *iterator; /* The position from where the next search
41 will start. */
42 DwWordIterator *hl_iterator; /* The position from where key->len words
43 are highlighted. */
44 gint first_hl_start, last_hl_end;
45 };
46
47 struct _FindtextKey
48 {
49 gint len;
50 gchar **words;
51 FindtextPatternType *types;
52 gint *nexttab;
53 };
54
55 FindtextState* a_Findtext_state_new (void);
56 void a_Findtext_state_destroy (FindtextState *state);
57 void a_Findtext_state_set_widget (FindtextState *state,
58 DwWidget *widget);
59
60 FindtextResult a_Findtext_search (FindtextState *state,
61 gchar *str,
62 gboolean case_sens);
63 void a_Findtext_reset_search (FindtextState *state);
64
65 #endif /* __FINDTEXT_H__ */
0 /*
1 * File: form.cc
2 *
3 * Copyright 2008 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "form.hh"
12 #include "html_common.hh"
13
14 #include <errno.h>
15 #include <iconv.h>
16
17 #include "lout/misc.hh"
18 #include "dw/core.hh"
19 #include "dw/textblock.hh"
20
21 #include "misc.h"
22 #include "msg.h"
23 #include "prefs.h"
24 #include "uicmd.hh"
25
26 using namespace lout;
27 using namespace dw;
28 using namespace dw::core;
29 using namespace dw::core::style;
30 using namespace dw::core::ui;
31
32 /*
33 * Forward declarations
34 */
35
36 class DilloHtmlReceiver;
37 class DilloHtmlSelect;
38 class DilloHtmlOption;
39
40 static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize);
41
42 static void Html_option_finish(DilloHtml *html);
43
44 /*
45 * Typedefs
46 */
47
48 typedef enum {
49 DILLO_HTML_INPUT_UNKNOWN,
50 DILLO_HTML_INPUT_TEXT,
51 DILLO_HTML_INPUT_PASSWORD,
52 DILLO_HTML_INPUT_CHECKBOX,
53 DILLO_HTML_INPUT_RADIO,
54 DILLO_HTML_INPUT_IMAGE,
55 DILLO_HTML_INPUT_FILE,
56 DILLO_HTML_INPUT_BUTTON,
57 DILLO_HTML_INPUT_HIDDEN,
58 DILLO_HTML_INPUT_SUBMIT,
59 DILLO_HTML_INPUT_RESET,
60 DILLO_HTML_INPUT_BUTTON_SUBMIT,
61 DILLO_HTML_INPUT_BUTTON_RESET,
62 DILLO_HTML_INPUT_SELECT,
63 DILLO_HTML_INPUT_SEL_LIST,
64 DILLO_HTML_INPUT_TEXTAREA,
65 DILLO_HTML_INPUT_INDEX
66 } DilloHtmlInputType;
67
68 /*
69 * Class declarations
70 */
71
72 class DilloHtmlForm {
73 friend class DilloHtmlReceiver;
74 friend class DilloHtmlInput;
75
76 DilloHtml *html;
77 bool showing_hiddens;
78 bool enabled;
79 void eventHandler(Resource *resource, EventButton *event);
80 DilloUrl *buildQueryUrl(DilloHtmlInput *active_input);
81 Dstr *buildQueryData(DilloHtmlInput *active_submit);
82 char *makeMultipartBoundary(iconv_t char_encoder,
83 DilloHtmlInput *active_submit);
84 Dstr *encodeText(iconv_t char_encoder, Dstr **input);
85 void strUrlencodeAppend(Dstr *dstr, const char *str);
86 void inputUrlencodeAppend(Dstr *data, const char *name, const char *value);
87 void inputMultipartAppend(Dstr *data, const char *boundary,
88 const char *name, const char *value);
89 void filesInputMultipartAppend(Dstr* data, const char *boundary,
90 const char *name, Dstr *file,
91 const char *filename);
92 void imageInputUrlencodeAppend(Dstr *data, Dstr *name, Dstr *x, Dstr *y);
93 void imageInputMultipartAppend(Dstr *data, const char *boundary, Dstr *name,
94 Dstr *x, Dstr *y);
95
96 public: //BUG: for now everything is public
97 DilloHtmlMethod method;
98 DilloUrl *action;
99 DilloHtmlEnc content_type;
100 char *submit_charset;
101
102 lout::misc::SimpleVector<DilloHtmlInput*> *inputs;
103
104 int num_entry_fields;
105
106 DilloHtmlReceiver *form_receiver;
107
108 public:
109 DilloHtmlForm (DilloHtml *html,
110 DilloHtmlMethod method, const DilloUrl *action,
111 DilloHtmlEnc content_type, const char *charset,
112 bool enabled);
113 ~DilloHtmlForm ();
114 DilloHtmlInput *getInput (Resource *resource);
115 DilloHtmlInput *getRadioInput (const char *name);
116 void submit(DilloHtmlInput *active_input, EventButton *event);
117 void reset ();
118 void display_hiddens(bool display);
119 void addInput(DilloHtmlInput *input, DilloHtmlInputType type);
120 void setEnabled(bool enabled);
121 };
122
123 class DilloHtmlReceiver:
124 public Resource::ActivateReceiver,
125 public Resource::ClickedReceiver
126 {
127 friend class DilloHtmlForm;
128 DilloHtmlForm* form;
129 DilloHtmlReceiver (DilloHtmlForm* form2) { form = form2; }
130 ~DilloHtmlReceiver () { }
131 void activate (Resource *resource);
132 void enter (Resource *resource);
133 void leave (Resource *resource);
134 void clicked (Resource *resource, EventButton *event);
135 };
136
137 class DilloHtmlInput {
138
139 // DilloHtmlForm::addInput() calls connectTo()
140 friend class DilloHtmlForm;
141
142 public: //BUG: for now everything is public
143 DilloHtmlInputType type;
144 Embed *embed; /* May be NULL (think: hidden input) */
145 char *name;
146 char *init_str; /* note: some overloading - for buttons, init_str
147 is simply the value of the button; for text
148 entries, it is the initial value */
149 DilloHtmlSelect *select;
150 bool init_val; /* only meaningful for buttons */
151 Dstr *file_data; /* only meaningful for file inputs.
152 TODO: may become a list... */
153
154 private:
155 void connectTo(DilloHtmlReceiver *form_receiver);
156 void activate(DilloHtmlForm *form, int num_entry_fields,EventButton *event);
157 void readFile(BrowserWindow *bw);
158
159 public:
160 DilloHtmlInput (DilloHtmlInputType type, Embed *embed,
161 const char *name, const char *init_str, bool init_val);
162 ~DilloHtmlInput ();
163 void appendValuesTo(Dlist *values, bool is_active_submit);
164 void reset();
165 void setEnabled(bool enabled) {if (embed) embed->setEnabled(enabled); };
166 };
167
168 class DilloHtmlSelect {
169 friend class DilloHtmlInput;
170 private:
171 lout::misc::SimpleVector<DilloHtmlOption *> *options;
172 DilloHtmlSelect ();
173 ~DilloHtmlSelect ();
174 public:
175 DilloHtmlOption *getCurrentOption ();
176 void addOption (char *value, bool selected, bool enabled);
177 void ensureSelection ();
178 void addOptionsTo (SelectionResource *res);
179 void appendValuesTo (Dlist *values, SelectionResource *res);
180 };
181
182 class DilloHtmlOption {
183 friend class DilloHtmlSelect;
184 public:
185 char *value, *content;
186 bool selected, enabled;
187 private:
188 DilloHtmlOption (char *value, bool selected, bool enabled);
189 ~DilloHtmlOption ();
190 };
191
192 /*
193 * Form API
194 */
195
196 DilloHtmlForm *a_Html_form_new (DilloHtml *html, DilloHtmlMethod method,
197 const DilloUrl *action,
198 DilloHtmlEnc content_type, const char *charset,
199 bool enabled)
200 {
201 return new DilloHtmlForm (html, method, action, content_type, charset,
202 enabled);
203 }
204
205 void a_Html_form_delete (DilloHtmlForm *form)
206 {
207 delete form;
208 }
209
210 void a_Html_input_delete (DilloHtmlInput *input)
211 {
212 delete input;
213 }
214
215 void a_Html_form_submit2(void *vform)
216 {
217 ((DilloHtmlForm *)vform)->submit(NULL, NULL);
218 }
219
220 void a_Html_form_reset2(void *vform)
221 {
222 ((DilloHtmlForm *)vform)->reset();
223 }
224
225 void a_Html_form_display_hiddens2(void *vform, bool display)
226 {
227 ((DilloHtmlForm *)vform)->display_hiddens(display);
228 }
229
230 /*
231 * Form parsing functions
232 */
233
234 /*
235 * Add an HTML control
236 */
237 static void Html_add_input(DilloHtml *html, DilloHtmlInputType type,
238 Embed *embed, const char *name,
239 const char *init_str, bool init_val)
240 {
241 _MSG("name=[%s] init_str=[%s] init_val=[%d]\n", name, init_str, init_val);
242 DilloHtmlInput *input = new DilloHtmlInput(type, embed, name, init_str,
243 init_val);
244 if (html->InFlags & IN_FORM) {
245 html->getCurrentForm()->addInput(input, type);
246 } else {
247 int ni = html->inputs_outside_form->size();
248 html->inputs_outside_form->increase();
249 html->inputs_outside_form->set(ni, input);
250
251 if (html->bw->NumPendingStyleSheets > 0) {
252 input->setEnabled(false);
253 }
254 }
255 }
256
257 /*
258 * Find radio input by name
259 */
260 static DilloHtmlInput *Html_get_radio_input(DilloHtml *html, const char *name)
261 {
262 if (name) {
263 lout::misc::SimpleVector<DilloHtmlInput*>* inputs;
264
265 if (html->InFlags & IN_FORM)
266 inputs = html->getCurrentForm()->inputs;
267 else
268 inputs = html->inputs_outside_form;
269
270 for (int idx = 0; idx < inputs->size(); idx++) {
271 DilloHtmlInput *input = inputs->get(idx);
272 if (input->type == DILLO_HTML_INPUT_RADIO &&
273 input->name && !dStrcasecmp(input->name, name))
274 return input;
275 }
276 }
277 return NULL;
278 }
279
280 /*
281 * Get the current input.
282 * Note that this _assumes_ that there _is_ a current input.
283 */
284 static DilloHtmlInput *Html_get_current_input(DilloHtml *html)
285 {
286 lout::misc::SimpleVector<DilloHtmlInput*>* inputs;
287
288 if (html->InFlags & IN_FORM)
289 inputs = html->getCurrentForm()->inputs;
290 else
291 inputs = html->inputs_outside_form;
292
293 return inputs->get (inputs->size() - 1);
294 }
295
296 /*
297 * Handle <FORM> tag
298 */
299 void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize)
300 {
301 DilloUrl *action;
302 DilloHtmlMethod method;
303 DilloHtmlEnc content_type;
304 char *charset, *first;
305 const char *attrbuf;
306
307 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
308
309 if (html->InFlags & IN_FORM) {
310 BUG_MSG("nested forms\n");
311 return;
312 }
313 html->InFlags |= IN_FORM;
314 html->InFlags &= ~IN_SELECT;
315 html->InFlags &= ~IN_OPTION;
316 html->InFlags &= ~IN_TEXTAREA;
317
318 method = DILLO_HTML_METHOD_GET;
319 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "method"))) {
320 if (!dStrcasecmp(attrbuf, "post")) {
321 method = DILLO_HTML_METHOD_POST;
322 } else if (dStrcasecmp(attrbuf, "get")) {
323 BUG_MSG("Unknown form submission method \"%s\"\n", attrbuf);
324 }
325 }
326 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "action")))
327 action = a_Html_url_new(html, attrbuf, NULL, 0);
328 else {
329 BUG_MSG("action attribute required for <form>\n");
330 action = a_Url_dup(html->base_url);
331 }
332 content_type = DILLO_HTML_ENC_URLENCODED;
333 if ((method == DILLO_HTML_METHOD_POST) &&
334 ((attrbuf = a_Html_get_attr(html, tag, tagsize, "enctype")))) {
335 if (!dStrcasecmp(attrbuf, "multipart/form-data"))
336 content_type = DILLO_HTML_ENC_MULTIPART;
337 }
338 charset = NULL;
339 first = NULL;
340 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "accept-charset"))) {
341 /* a list of acceptable charsets, separated by commas or spaces */
342 char *ptr = first = dStrdup(attrbuf);
343 while (ptr && !charset) {
344 char *curr = dStrsep(&ptr, " ,");
345 if (!dStrcasecmp(curr, "utf-8")) {
346 charset = curr;
347 } else if (!dStrcasecmp(curr, "UNKNOWN")) {
348 /* defined to be whatever encoding the document is in */
349 charset = html->charset;
350 }
351 }
352 if (!charset)
353 charset = first;
354 }
355 if (!charset)
356 charset = html->charset;
357 html->formNew(method, action, content_type, charset);
358 dFree(first);
359 a_Url_free(action);
360 }
361
362 void Html_tag_close_form(DilloHtml *html, int TagIdx)
363 {
364 // DilloHtmlForm *form;
365 // int i;
366 //
367 // if (html->InFlags & IN_FORM) {
368 // form = html->getCurrentForm ();
369 //
370 // /* Make buttons sensitive again */
371 // for (i = 0; i < form->inputs->size(); i++) {
372 // input_i = form->inputs->get(i);
373 // /* Check for tricky HTML (e.g. <input type=image>) */
374 // if (!input_i->widget)
375 // continue;
376 // if (input_i->type == DILLO_HTML_INPUT_SUBMIT ||
377 // input_i->type == DILLO_HTML_INPUT_RESET) {
378 // gtk_widget_set_sensitive(input_i->widget, TRUE);
379 // } else if (input_i->type == DILLO_HTML_INPUT_IMAGE ||
380 // input_i->type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
381 // input_i->type == DILLO_HTML_INPUT_BUTTON_RESET) {
382 // a_Dw_button_set_sensitive(DW_BUTTON(input_i->widget), TRUE);
383 // }
384 // }
385 // }
386
387 html->InFlags &= ~IN_FORM;
388 html->InFlags &= ~IN_SELECT;
389 html->InFlags &= ~IN_OPTION;
390 html->InFlags &= ~IN_TEXTAREA;
391 }
392
393 /*
394 * get size, restrict it to reasonable value
395 */
396 static int Html_input_get_size(DilloHtml *html, const char *attrbuf)
397 {
398 const int MAX_SIZE = 1024;
399 int size = 20;
400
401 if (attrbuf) {
402 size = strtol(attrbuf, NULL, 10);
403 if (size < 1 || size > MAX_SIZE) {
404 int badSize = size;
405 size = (size < 1 ? 20 : MAX_SIZE);
406 BUG_MSG("input size=%d, using size=%d instead\n", badSize, size);
407 }
408 }
409 return size;
410 }
411
412 /*
413 * Add a new input to current form
414 */
415 void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
416 {
417 DilloHtmlInputType inp_type;
418 Resource *resource = NULL;
419 Embed *embed = NULL;
420 char *value, *name, *type, *init_str;
421 const char *attrbuf, *label;
422 bool init_val = false;
423 ResourceFactory *factory;
424
425 if (html->InFlags & IN_SELECT) {
426 BUG_MSG("<input> element inside <select>\n");
427 return;
428 }
429 if (html->InFlags & IN_BUTTON) {
430 BUG_MSG("<input> element inside <button>\n");
431 return;
432 }
433
434 factory = HT2LT(html)->getResourceFactory();
435
436 /* Get 'value', 'name' and 'type' */
437 value = a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
438 name = a_Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
439 type = a_Html_get_attr_wdef(html, tag, tagsize, "type", "");
440
441 init_str = NULL;
442 inp_type = DILLO_HTML_INPUT_UNKNOWN;
443 if (!dStrcasecmp(type, "password")) {
444 inp_type = DILLO_HTML_INPUT_PASSWORD;
445 attrbuf = a_Html_get_attr(html, tag, tagsize, "size");
446 int size = Html_input_get_size(html, attrbuf);
447 resource = factory->createEntryResource (size, true, NULL);
448 init_str = value;
449 } else if (!dStrcasecmp(type, "checkbox")) {
450 inp_type = DILLO_HTML_INPUT_CHECKBOX;
451 resource = factory->createCheckButtonResource(false);
452 init_val = (a_Html_get_attr(html, tag, tagsize, "checked") != NULL);
453 init_str = (value) ? value : dStrdup("on");
454 } else if (!dStrcasecmp(type, "radio")) {
455 inp_type = DILLO_HTML_INPUT_RADIO;
456 RadioButtonResource *rb_r = NULL;
457 DilloHtmlInput *input = Html_get_radio_input(html, name);
458 if (input)
459 rb_r = (RadioButtonResource*) input->embed->getResource();
460 resource = factory->createRadioButtonResource(rb_r, false);
461 init_val = (a_Html_get_attr(html, tag, tagsize, "checked") != NULL);
462 init_str = value;
463 } else if (!dStrcasecmp(type, "hidden")) {
464 inp_type = DILLO_HTML_INPUT_HIDDEN;
465 init_str = value;
466 int size = Html_input_get_size(html, NULL);
467 resource = factory->createEntryResource(size, false, name);
468 } else if (!dStrcasecmp(type, "submit")) {
469 inp_type = DILLO_HTML_INPUT_SUBMIT;
470 init_str = (value) ? value : dStrdup("submit");
471 resource = factory->createLabelButtonResource(init_str);
472 // gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
473 } else if (!dStrcasecmp(type, "reset")) {
474 inp_type = DILLO_HTML_INPUT_RESET;
475 init_str = (value) ? value : dStrdup("Reset");
476 resource = factory->createLabelButtonResource(init_str);
477 // gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
478 } else if (!dStrcasecmp(type, "image")) {
479 if (URL_FLAGS(html->base_url) & URL_SpamSafe) {
480 /* Don't request the image; make a text submit button instead */
481 inp_type = DILLO_HTML_INPUT_SUBMIT;
482 attrbuf = a_Html_get_attr(html, tag, tagsize, "alt");
483 label = attrbuf ? attrbuf : value ? value : name ? name : "Submit";
484 init_str = dStrdup(label);
485 resource = factory->createLabelButtonResource(init_str);
486 // gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
487 } else {
488 inp_type = DILLO_HTML_INPUT_IMAGE;
489 /* use a dw_image widget */
490 embed = Html_input_image(html, tag, tagsize);
491 init_str = value;
492 }
493 } else if (!dStrcasecmp(type, "file")) {
494 bool valid = true;
495 if (html->InFlags & IN_FORM) {
496 DilloHtmlForm *form = html->getCurrentForm();
497 if (form->method != DILLO_HTML_METHOD_POST) {
498 valid = false;
499 BUG_MSG("Forms with file input MUST use HTTP POST method\n");
500 MSG("File input ignored in form not using HTTP POST method\n");
501 } else if (form->content_type != DILLO_HTML_ENC_MULTIPART) {
502 valid = false;
503 BUG_MSG("Forms with file input MUST use multipart/form-data"
504 " encoding\n");
505 MSG("File input ignored in form not using multipart/form-data"
506 " encoding\n");
507 }
508 }
509 if (valid) {
510 inp_type = DILLO_HTML_INPUT_FILE;
511 init_str = dStrdup("File selector");
512 resource = factory->createLabelButtonResource(init_str);
513 }
514 } else if (!dStrcasecmp(type, "button")) {
515 inp_type = DILLO_HTML_INPUT_BUTTON;
516 if (value) {
517 init_str = value;
518 resource = factory->createLabelButtonResource(init_str);
519 }
520 } else if (!dStrcasecmp(type, "text") || !*type) {
521 /* Text input, which also is the default */
522 inp_type = DILLO_HTML_INPUT_TEXT;
523 attrbuf = a_Html_get_attr(html, tag, tagsize, "size");
524 int size = Html_input_get_size(html, attrbuf);
525 resource = factory->createEntryResource(size, false, NULL);
526 init_str = value;
527 } else {
528 /* Unknown input type */
529 BUG_MSG("Unknown input type: \"%s\"\n", type);
530 }
531
532 if (resource)
533 embed = new Embed (resource);
534
535 if (inp_type != DILLO_HTML_INPUT_UNKNOWN) {
536 Html_add_input(html, inp_type, embed, name,
537 (init_str) ? init_str : "", init_val);
538 }
539
540 if (embed != NULL && inp_type != DILLO_HTML_INPUT_IMAGE &&
541 inp_type != DILLO_HTML_INPUT_UNKNOWN) {
542 if (inp_type == DILLO_HTML_INPUT_HIDDEN) {
543 /* TODO Perhaps do this with access to current form setting */
544 embed->setDisplayed(false);
545 }
546 if (inp_type == DILLO_HTML_INPUT_TEXT ||
547 inp_type == DILLO_HTML_INPUT_PASSWORD) {
548 if (a_Html_get_attr(html, tag, tagsize, "readonly"))
549 ((EntryResource *) resource)->setEditable(false);
550
551 // /* Maximum length of the text in the entry */
552 // if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "maxlength")))
553 // gtk_entry_set_max_length(GTK_ENTRY(widget),
554 // strtol(attrbuf, NULL, 10));
555 }
556 if (prefs.show_tooltip &&
557 (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
558
559 html->styleEngine->setNonCssHint (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING,
560 attrbuf);
561 }
562 HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle());
563 }
564 dFree(type);
565 dFree(name);
566 if (init_str != value)
567 dFree(init_str);
568 dFree(value);
569 }
570
571 /*
572 * The ISINDEX tag is just a deprecated form of <INPUT type=text> with
573 * implied FORM, afaics.
574 */
575 void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize)
576 {
577 DilloUrl *action;
578 Embed *embed;
579 const char *attrbuf;
580
581 if (html->InFlags & IN_FORM) {
582 MSG("<isindex> inside <form> not handled.\n");
583 return;
584 }
585
586 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "action")))
587 action = a_Html_url_new(html, attrbuf, NULL, 0);
588 else
589 action = a_Url_dup(html->base_url);
590
591 html->formNew(DILLO_HTML_METHOD_GET, action, DILLO_HTML_ENC_URLENCODED,
592 html->charset);
593 html->InFlags |= IN_FORM;
594
595 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
596
597 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "prompt")))
598 HT2TB(html)->addText(attrbuf, html->styleEngine->wordStyle ());
599
600 ResourceFactory *factory = HT2LT(html)->getResourceFactory();
601 EntryResource *entryResource = factory->createEntryResource (20,false,NULL);
602 embed = new Embed (entryResource);
603 Html_add_input(html, DILLO_HTML_INPUT_INDEX, embed, NULL, NULL, FALSE);
604
605 HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
606
607 a_Url_free(action);
608 html->InFlags &= ~IN_FORM;
609 }
610
611 /*
612 * The textarea tag
613 * (TODO: It doesn't support wrapping).
614 */
615 void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize)
616 {
617 const int MAX_COLS=1024, MAX_ROWS=10000;
618
619 char *name;
620 const char *attrbuf;
621 int cols, rows;
622
623 if (html->InFlags & IN_TEXTAREA) {
624 BUG_MSG("nested <textarea>\n");
625 html->ReqTagClose = TRUE;
626 return;
627 }
628 if (html->InFlags & IN_SELECT) {
629 BUG_MSG("<textarea> element inside <select>\n");
630 return;
631 }
632
633 html->InFlags |= IN_TEXTAREA;
634 a_Html_stash_init(html);
635 S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
636
637 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cols"))) {
638 cols = strtol(attrbuf, NULL, 10);
639 } else {
640 BUG_MSG("cols attribute is required for <textarea>\n");
641 cols = 20;
642 }
643 if (cols < 1 || cols > MAX_COLS) {
644 int badCols = cols;
645 cols = (cols < 1 ? 20 : MAX_COLS);
646 BUG_MSG("textarea cols=%d, using cols=%d instead\n", badCols, cols);
647 }
648 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rows"))) {
649 rows = strtol(attrbuf, NULL, 10);
650 } else {
651 BUG_MSG("rows attribute is required for <textarea>\n");
652 rows = 10;
653 }
654 if (rows < 1 || rows > MAX_ROWS) {
655 int badRows = rows;
656 rows = (rows < 1 ? 2 : MAX_ROWS);
657 BUG_MSG("textarea rows=%d, using rows=%d instead\n", badRows, rows);
658 }
659 name = NULL;
660 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name")))
661 name = dStrdup(attrbuf);
662
663 ResourceFactory *factory = HT2LT(html)->getResourceFactory();
664 MultiLineTextResource *textres =
665 factory->createMultiLineTextResource (cols, rows);
666
667 Embed *embed = new Embed(textres);
668 /* Readonly or not? */
669 if (a_Html_get_attr(html, tag, tagsize, "readonly"))
670 textres->setEditable(false);
671 Html_add_input(html, DILLO_HTML_INPUT_TEXTAREA, embed, name, NULL, false);
672
673 HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
674 dFree(name);
675 }
676
677 /*
678 * Close textarea
679 * (TEXTAREA is parsed in VERBATIM mode, and entities are handled here)
680 */
681 void Html_tag_close_textarea(DilloHtml *html, int TagIdx)
682 {
683 char *str;
684 DilloHtmlInput *input;
685 int i;
686
687 if (html->InFlags & IN_TEXTAREA) {
688 /* Remove the line ending that follows the opening tag */
689 if (html->Stash->str[0] == '\r')
690 dStr_erase(html->Stash, 0, 1);
691 if (html->Stash->str[0] == '\n')
692 dStr_erase(html->Stash, 0, 1);
693
694 /* As the spec recommends to canonicalize line endings, it is safe
695 * to replace '\r' with '\n'. It will be canonicalized anyway! */
696 for (i = 0; i < html->Stash->len; ++i) {
697 if (html->Stash->str[i] == '\r') {
698 if (html->Stash->str[i + 1] == '\n')
699 dStr_erase(html->Stash, i, 1);
700 else
701 html->Stash->str[i] = '\n';
702 }
703 }
704
705 /* The HTML3.2 spec says it can have "text and character entities". */
706 str = a_Html_parse_entities(html, html->Stash->str, html->Stash->len);
707 input = Html_get_current_input(html);
708 input->init_str = str;
709 ((MultiLineTextResource *)input->embed->getResource ())->setText(str);
710
711 html->InFlags &= ~IN_TEXTAREA;
712 }
713 }
714
715 /*
716 * <SELECT>
717 */
718 /* The select tag is quite tricky, because of gorpy html syntax. */
719 void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize)
720 {
721 const char *attrbuf;
722 int rows = 0;
723
724 if (html->InFlags & IN_SELECT) {
725 BUG_MSG("nested <select>\n");
726 return;
727 }
728 html->InFlags |= IN_SELECT;
729 html->InFlags &= ~IN_OPTION;
730
731 char *name = a_Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
732 ResourceFactory *factory = HT2LT(html)->getResourceFactory ();
733 DilloHtmlInputType type;
734 SelectionResource *res;
735 bool multi = a_Html_get_attr(html, tag, tagsize, "multiple") != NULL;
736
737 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size"))) {
738 rows = strtol(attrbuf, NULL, 10);
739 if (rows > 100)
740 rows = 100;
741 }
742 if (rows < 1)
743 rows = multi ? 10 : 1;
744
745 if (rows == 1 && multi == false) {
746 type = DILLO_HTML_INPUT_SELECT;
747 res = factory->createOptionMenuResource ();
748 } else {
749 type = DILLO_HTML_INPUT_SEL_LIST;
750 res = factory->createListResource (multi ?
751 ListResource::SELECTION_MULTIPLE :
752 ListResource::SELECTION_EXACTLY_ONE,
753 rows);
754 }
755 Embed *embed = new Embed(res);
756
757 HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
758
759 Html_add_input(html, type, embed, name, NULL, false);
760 a_Html_stash_init(html);
761 dFree(name);
762 }
763
764 /*
765 * ?
766 */
767 void Html_tag_close_select(DilloHtml *html, int TagIdx)
768 {
769 if (html->InFlags & IN_SELECT) {
770 if (html->InFlags & IN_OPTION)
771 Html_option_finish(html);
772 html->InFlags &= ~IN_SELECT;
773 html->InFlags &= ~IN_OPTION;
774
775 DilloHtmlInput *input = Html_get_current_input(html);
776 DilloHtmlSelect *select = input->select;
777
778 // BUG(?): should not do this for MULTI selections
779 select->ensureSelection ();
780
781 SelectionResource *res = (SelectionResource*)input->embed->getResource();
782 select->addOptionsTo (res);
783 }
784 }
785
786 /*
787 * <OPTION>
788 */
789 void Html_tag_open_option(DilloHtml *html, const char *tag, int tagsize)
790 {
791 if (!(html->InFlags & IN_SELECT)) {
792 BUG_MSG("<option> element outside <select>\n");
793 return;
794 }
795 if (html->InFlags & IN_OPTION)
796 Html_option_finish(html);
797 html->InFlags |= IN_OPTION;
798
799 DilloHtmlInput *input = Html_get_current_input(html);
800
801 if (input->type == DILLO_HTML_INPUT_SELECT ||
802 input->type == DILLO_HTML_INPUT_SEL_LIST) {
803 char *value = a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
804 bool selected = (a_Html_get_attr(html, tag, tagsize,"selected") != NULL);
805 bool enabled = (a_Html_get_attr(html, tag, tagsize, "disabled") == NULL);
806 input->select->addOption(value, selected, enabled);
807 }
808
809 a_Html_stash_init(html);
810 }
811
812 /*
813 * <BUTTON>
814 */
815 void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize)
816 {
817 /*
818 * Buttons are rendered on one line, this is (at several levels) a
819 * bit simpler. May be changed in the future.
820 */
821 DilloHtmlInputType inp_type;
822 char *type;
823
824 if (html->InFlags & IN_BUTTON) {
825 BUG_MSG("nested <button>\n");
826 return;
827 }
828 if (html->InFlags & IN_SELECT) {
829 BUG_MSG("<button> element inside <select>\n");
830 return;
831 }
832 html->InFlags |= IN_BUTTON;
833
834 type = a_Html_get_attr_wdef(html, tag, tagsize, "type", "");
835
836 if (!dStrcasecmp(type, "button")) {
837 inp_type = DILLO_HTML_INPUT_BUTTON;
838 } else if (!dStrcasecmp(type, "reset")) {
839 inp_type = DILLO_HTML_INPUT_BUTTON_RESET;
840 } else if (!dStrcasecmp(type, "submit") || !*type) {
841 /* submit button is the default */
842 inp_type = DILLO_HTML_INPUT_BUTTON_SUBMIT;
843 } else {
844 inp_type = DILLO_HTML_INPUT_UNKNOWN;
845 BUG_MSG("Unknown button type: \"%s\"\n", type);
846 }
847
848 if (inp_type != DILLO_HTML_INPUT_UNKNOWN) {
849 /* Render the button */
850 Widget *page;
851 Embed *embed;
852 char *name, *value;
853
854 page = new Textblock (prefs.limit_text_width);
855 page->setStyle (html->styleEngine->backgroundStyle ());
856
857 ResourceFactory *factory = HT2LT(html)->getResourceFactory();
858 Resource *resource = factory->createComplexButtonResource(page, true);
859 embed = new Embed(resource);
860 // a_Dw_button_set_sensitive (DW_BUTTON (button), FALSE);
861
862 HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
863 HT2TB(html)->addWidget (embed, html->styleEngine->style ());
864 HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
865
866 S_TOP(html)->textblock = html->dw = page;
867
868 value = a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
869 name = a_Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
870
871 Html_add_input(html, inp_type, embed, name, value, FALSE);
872 dFree(name);
873 dFree(value);
874 }
875 dFree(type);
876 }
877
878 /*
879 * Handle close <BUTTON>
880 */
881 void Html_tag_close_button(DilloHtml *html, int TagIdx)
882 {
883 html->InFlags &= ~IN_BUTTON;
884 }
885
886 /*
887 * Class implementations
888 */
889
890 /*
891 * DilloHtmlForm
892 */
893
894 /*
895 * Constructor
896 */
897 DilloHtmlForm::DilloHtmlForm (DilloHtml *html2,
898 DilloHtmlMethod method2,
899 const DilloUrl *action2,
900 DilloHtmlEnc content_type2,
901 const char *charset, bool enabled)
902 {
903 html = html2;
904 method = method2;
905 action = a_Url_dup(action2);
906 content_type = content_type2;
907 submit_charset = dStrdup(charset);
908 inputs = new misc::SimpleVector <DilloHtmlInput*> (4);
909 num_entry_fields = 0;
910 showing_hiddens = false;
911 this->enabled = enabled;
912 form_receiver = new DilloHtmlReceiver (this);
913 }
914
915 /*
916 * Destructor
917 */
918 DilloHtmlForm::~DilloHtmlForm ()
919 {
920 a_Url_free(action);
921 dFree(submit_charset);
922 for (int j = 0; j < inputs->size(); j++)
923 delete inputs->get(j);
924 delete(inputs);
925 if (form_receiver)
926 delete(form_receiver);
927 }
928
929 void DilloHtmlForm::eventHandler(Resource *resource, EventButton *event)
930 {
931 _MSG("DilloHtmlForm::eventHandler\n");
932 if (event && (event->button == 3)) {
933 a_UIcmd_form_popup(html->bw, html->page_url, this, showing_hiddens);
934 } else {
935 DilloHtmlInput *input = getInput(resource);
936 if (input) {
937 input->activate (this, num_entry_fields, event);
938 } else {
939 MSG("DilloHtmlForm::eventHandler: ERROR, input not found!\n");
940 }
941 }
942 }
943
944 /*
945 * Submit.
946 * (Called by eventHandler())
947 */
948 void DilloHtmlForm::submit(DilloHtmlInput *active_input, EventButton *event)
949 {
950 DilloUrl *url = buildQueryUrl(active_input);
951 if (url) {
952 if (event && event->button == 2) {
953 if (prefs.middle_click_opens_new_tab) {
954 int focus = prefs.focus_new_tab ? 1 : 0;
955 if (event->state == SHIFT_MASK) focus = !focus;
956 a_UIcmd_open_url_nt(html->bw, url, focus);
957 } else {
958 a_UIcmd_open_url_nw(html->bw, url);
959 }
960 } else {
961 a_UIcmd_open_url(html->bw, url);
962 }
963 a_Url_free(url);
964 }
965 // /* now, make the rendered area have its focus back */
966 // gtk_widget_grab_focus(GTK_BIN(bw->render_main_scroll)->child);
967 }
968
969 /*
970 * Build a new query URL.
971 * (Called by submit())
972 */
973 DilloUrl *DilloHtmlForm::buildQueryUrl(DilloHtmlInput *active_input)
974 {
975 DilloUrl *new_url = NULL;
976
977 if ((method == DILLO_HTML_METHOD_GET) ||
978 (method == DILLO_HTML_METHOD_POST)) {
979 Dstr *DataStr;
980 DilloHtmlInput *active_submit = NULL;
981
982 _MSG("DilloHtmlForm::buildQueryUrl: action=%s\n",URL_STR_(action));
983
984 if (active_input) {
985 if ((active_input->type == DILLO_HTML_INPUT_SUBMIT) ||
986 (active_input->type == DILLO_HTML_INPUT_IMAGE) ||
987 (active_input->type == DILLO_HTML_INPUT_BUTTON_SUBMIT)) {
988 active_submit = active_input;
989 }
990 }
991
992 DataStr = buildQueryData(active_submit);
993 if (DataStr) {
994 /* action was previously resolved against base URL */
995 char *action_str = dStrdup(URL_STR(action));
996
997 if (method == DILLO_HTML_METHOD_POST) {
998 new_url = a_Url_new(action_str, NULL);
999 /* new_url keeps the dStr and sets DataStr to NULL */
1000 a_Url_set_data(new_url, &DataStr);
1001 a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_Post);
1002 if (content_type == DILLO_HTML_ENC_MULTIPART)
1003 a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_MultipartEnc);
1004 } else {
1005 /* remove <fragment> and <query> sections if present */
1006 char *url_str, *p;
1007 if ((p = strchr(action_str, '#')))
1008 *p = 0;
1009 if ((p = strchr(action_str, '?')))
1010 *p = 0;
1011
1012 url_str = dStrconcat(action_str, "?", DataStr->str, NULL);
1013 new_url = a_Url_new(url_str, NULL);
1014 a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_Get);
1015 dFree(url_str);
1016 }
1017 dStr_free(DataStr, 1);
1018 dFree(action_str);
1019 }
1020 } else {
1021 MSG("DilloHtmlForm::buildQueryUrl: Method unknown\n");
1022 }
1023
1024 return new_url;
1025 }
1026
1027 /*
1028 * Construct the data for a query URL
1029 */
1030 Dstr *DilloHtmlForm::buildQueryData(DilloHtmlInput *active_submit)
1031 {
1032 Dstr *DataStr = NULL;
1033 char *boundary = NULL;
1034 iconv_t char_encoder = (iconv_t) -1;
1035
1036 if (submit_charset && dStrcasecmp(submit_charset, "UTF-8")) {
1037 char_encoder = iconv_open(submit_charset, "UTF-8");
1038 if (char_encoder == (iconv_t) -1) {
1039 MSG_WARN("Cannot convert to character encoding '%s'\n",
1040 submit_charset);
1041 } else {
1042 MSG("Form character encoding: '%s'\n", submit_charset);
1043 }
1044 }
1045
1046 if (content_type == DILLO_HTML_ENC_MULTIPART) {
1047 if (!(boundary = makeMultipartBoundary(char_encoder, active_submit)))
1048 MSG_ERR("Cannot generate multipart/form-data boundary.\n");
1049 }
1050
1051 if ((content_type == DILLO_HTML_ENC_URLENCODED) || (boundary != NULL)) {
1052 Dlist *values = dList_new(5);
1053
1054 DataStr = dStr_sized_new(4096);
1055 for (int i = 0; i < inputs->size(); i++) {
1056 DilloHtmlInput *input = inputs->get (i);
1057 Dstr *name = dStr_new(input->name);
1058 bool is_active_submit = (input == active_submit);
1059 int valcount;
1060
1061 name = encodeText(char_encoder, &name);
1062
1063 input->appendValuesTo(values, is_active_submit);
1064
1065 if ((valcount = dList_length(values)) > 0) {
1066 if (input->type == DILLO_HTML_INPUT_FILE) {
1067 if (valcount > 1)
1068 MSG_WARN("multiple files per form control not supported\n");
1069 Dstr *file = (Dstr *) dList_nth_data(values, 0);
1070 dList_remove(values, file);
1071
1072 /* Get filename and encode it. Do not encode file contents. */
1073 LabelButtonResource *lbr =
1074 (LabelButtonResource*) input->embed->getResource();
1075 const char *filename = lbr->getLabel();
1076 if (filename[0] && strcmp(filename, input->init_str)) {
1077 const char *p = strrchr(filename, '/');
1078 if (p)
1079 filename = p + 1; /* don't reveal path */
1080 Dstr *dfilename = dStr_new(filename);
1081 dfilename = encodeText(char_encoder, &dfilename);
1082 filesInputMultipartAppend(DataStr, boundary, name->str,
1083 file, dfilename->str);
1084 dStr_free(dfilename, 1);
1085 }
1086 dStr_free(file, 1);
1087 } else if (input->type == DILLO_HTML_INPUT_INDEX) {
1088 /* no name */
1089 Dstr *val = (Dstr *) dList_nth_data(values, 0);
1090 dList_remove(values, val);
1091 val = encodeText(char_encoder, &val);
1092 strUrlencodeAppend(DataStr, val->str);
1093 dStr_free(val, 1);
1094 } else if (input->type == DILLO_HTML_INPUT_IMAGE) {
1095 Dstr *x, *y;
1096 x = (Dstr *) dList_nth_data(values, 0);
1097 dList_remove(values, x);
1098 y = (Dstr *) dList_nth_data(values, 0);
1099 dList_remove(values, y);
1100 if (content_type == DILLO_HTML_ENC_URLENCODED)
1101 imageInputUrlencodeAppend(DataStr, name, x, y);
1102 else if (content_type == DILLO_HTML_ENC_MULTIPART)
1103 imageInputMultipartAppend(DataStr, boundary, name, x, y);
1104 dStr_free(x, 1);
1105 dStr_free(y, 1);
1106 } else {
1107 for (int j = 0; j < valcount; j++) {
1108 Dstr *val = (Dstr *) dList_nth_data(values, 0);
1109 dList_remove(values, val);
1110 val = encodeText(char_encoder, &val);
1111 if (content_type == DILLO_HTML_ENC_URLENCODED)
1112 inputUrlencodeAppend(DataStr, name->str, val->str);
1113 else if (content_type == DILLO_HTML_ENC_MULTIPART)
1114 inputMultipartAppend(DataStr, boundary, name->str,
1115 val->str);
1116 dStr_free(val, 1);
1117 }
1118 }
1119 }
1120 dStr_free(name, 1);
1121 }
1122 if (DataStr->len > 0) {
1123 if (content_type == DILLO_HTML_ENC_URLENCODED) {
1124 if (DataStr->str[DataStr->len - 1] == '&')
1125 dStr_truncate(DataStr, DataStr->len - 1);
1126 } else if (content_type == DILLO_HTML_ENC_MULTIPART) {
1127 dStr_append(DataStr, "--");
1128 }
1129 }
1130 dList_free(values);
1131 }
1132 dFree(boundary);
1133 if (char_encoder != (iconv_t) -1)
1134 (void)iconv_close(char_encoder);
1135 return DataStr;
1136 }
1137
1138 /*
1139 * Generate a boundary string for use in separating the parts of a
1140 * multipart/form-data submission.
1141 */
1142 char *DilloHtmlForm::makeMultipartBoundary(iconv_t char_encoder,
1143 DilloHtmlInput *active_submit)
1144 {
1145 const int max_tries = 10;
1146 Dlist *values = dList_new(5);
1147 Dstr *DataStr = dStr_new("");
1148 Dstr *boundary = dStr_new("");
1149 char *ret = NULL;
1150
1151 /* fill DataStr with names, filenames, and values */
1152 for (int i = 0; i < inputs->size(); i++) {
1153 Dstr *dstr;
1154 DilloHtmlInput *input = inputs->get (i);
1155 bool is_active_submit = (input == active_submit);
1156 input->appendValuesTo(values, is_active_submit);
1157
1158 if (input->name) {
1159 dstr = dStr_new(input->name);
1160 dstr = encodeText(char_encoder, &dstr);
1161 dStr_append_l(DataStr, dstr->str, dstr->len);
1162 dStr_free(dstr, 1);
1163 }
1164 if (input->type == DILLO_HTML_INPUT_FILE) {
1165 LabelButtonResource *lbr =
1166 (LabelButtonResource*)input->embed->getResource();
1167 const char *filename = lbr->getLabel();
1168 if (filename[0] && strcmp(filename, input->init_str)) {
1169 dstr = dStr_new(filename);
1170 dstr = encodeText(char_encoder, &dstr);
1171 dStr_append_l(DataStr, dstr->str, dstr->len);
1172 dStr_free(dstr, 1);
1173 }
1174 }
1175 int length = dList_length(values);
1176 for (int i = 0; i < length; i++) {
1177 dstr = (Dstr *) dList_nth_data(values, 0);
1178 dList_remove(values, dstr);
1179 if (input->type != DILLO_HTML_INPUT_FILE)
1180 dstr = encodeText(char_encoder, &dstr);
1181 dStr_append_l(DataStr, dstr->str, dstr->len);
1182 dStr_free(dstr, 1);
1183 }
1184 }
1185
1186 /* generate a boundary that is not contained within the data */
1187 for (int i = 0; i < max_tries && !ret; i++) {
1188 // Firefox-style boundary
1189 dStr_sprintf(boundary, "---------------------------%d%d%d",
1190 rand(), rand(), rand());
1191 dStr_truncate(boundary, 70);
1192 if (dStr_memmem(DataStr, boundary) == NULL)
1193 ret = boundary->str;
1194 }
1195 dList_free(values);
1196 dStr_free(DataStr, 1);
1197 dStr_free(boundary, (ret == NULL));
1198 return ret;
1199 }
1200
1201 /*
1202 * Pass input text through character set encoder.
1203 * Return value: same input Dstr if no encoding is needed.
1204 * new Dstr when encoding (input Dstr is freed).
1205 */
1206 Dstr *DilloHtmlForm::encodeText(iconv_t char_encoder, Dstr **input)
1207 {
1208 int rc = 0;
1209 Dstr *output;
1210 const int bufsize = 128;
1211 inbuf_t *inPtr;
1212 char *buffer, *outPtr;
1213 size_t inLeft, outRoom;
1214 bool bad_chars = false;
1215
1216 if ((char_encoder == (iconv_t) -1) || *input == NULL || (*input)->len == 0)
1217 return *input;
1218
1219 output = dStr_new("");
1220 inPtr = (*input)->str;
1221 inLeft = (*input)->len;
1222 buffer = dNew(char, bufsize);
1223
1224 while ((rc != EINVAL) && (inLeft > 0)) {
1225
1226 outPtr = buffer;
1227 outRoom = bufsize;
1228
1229 rc = iconv(char_encoder, &inPtr, &inLeft, &outPtr, &outRoom);
1230
1231 // iconv() on success, number of bytes converted
1232 // -1, errno == EILSEQ illegal byte sequence found
1233 // EINVAL partial character ends source buffer
1234 // E2BIG destination buffer is full
1235 //
1236 // GNU iconv has the undocumented(!) behavior that EILSEQ is also
1237 // returned when a character cannot be converted.
1238
1239 dStr_append_l(output, buffer, bufsize - outRoom);
1240
1241 if (rc == -1) {
1242 rc = errno;
1243 }
1244 if (rc == EILSEQ){
1245 /* count chars? (would be utf-8-specific) */
1246 bad_chars = true;
1247 inPtr++;
1248 inLeft--;
1249 dStr_append_c(output, '?');
1250 } else if (rc == EINVAL) {
1251 MSG_ERR("Html_decode_text: bad source string\n");
1252 }
1253 }
1254
1255 if (bad_chars) {
1256 /*
1257 * It might be friendly to inform the caller, who would know whether
1258 * it is safe to display the beginning of the string in a message
1259 * (isn't, e.g., a password).
1260 */
1261 MSG_WARN("String cannot be converted cleanly.\n");
1262 }
1263
1264 dFree(buffer);
1265 dStr_free(*input, 1);
1266
1267 return output;
1268 }
1269
1270 /*
1271 * Urlencode 'str' and append it to 'dstr'
1272 */
1273 void DilloHtmlForm::strUrlencodeAppend(Dstr *dstr, const char *str)
1274 {
1275 char *encoded = a_Url_encode_hex_str(str);
1276 dStr_append(dstr, encoded);
1277 dFree(encoded);
1278 }
1279
1280 /*
1281 * Append a name-value pair to url data using url encoding.
1282 */
1283 void DilloHtmlForm::inputUrlencodeAppend(Dstr *data, const char *name,
1284 const char *value)
1285 {
1286 if (name && name[0]) {
1287 strUrlencodeAppend(data, name);
1288 dStr_append_c(data, '=');
1289 strUrlencodeAppend(data, value);
1290 dStr_append_c(data, '&');
1291 }
1292 }
1293
1294 /*
1295 * Append files to URL data using multipart encoding.
1296 * Currently only accepts one file.
1297 */
1298 void DilloHtmlForm::filesInputMultipartAppend(Dstr* data,
1299 const char *boundary,
1300 const char *name,
1301 Dstr *file,
1302 const char *filename)
1303 {
1304 const char *ctype, *ext;
1305
1306 if (name && name[0]) {
1307 (void)a_Misc_get_content_type_from_data(file->str, file->len, &ctype);
1308 /* Heuristic: text/plain with ".htm[l]" extension -> text/html */
1309 if ((ext = strrchr(filename, '.')) &&
1310 !dStrcasecmp(ctype, "text/plain") &&
1311 (!dStrcasecmp(ext, ".html") || !dStrcasecmp(ext, ".htm"))) {
1312 ctype = "text/html";
1313 }
1314
1315 if (data->len == 0) {
1316 dStr_append(data, "--");
1317 dStr_append(data, boundary);
1318 }
1319 // TODO: encode name, filename
1320 dStr_sprintfa(data,
1321 "\r\n"
1322 "Content-Disposition: form-data; name=\"%s\"; "
1323 "filename=\"", name);
1324 /*
1325 * Servers don't seem to like encoded names yet, but let's at least
1326 * replace the characters that are the most likely to damage things.
1327 */
1328 for (int i = 0; char c = filename[i]; i++) {
1329 if (c == '\"' || c == '\r' || c == '\n')
1330 c = '_';
1331 dStr_append_c(data, c);
1332 }
1333 dStr_sprintfa(data,
1334 "\"\r\n"
1335 "Content-Type: %s\r\n"
1336 "\r\n", ctype);
1337
1338 dStr_append_l(data, file->str, file->len);
1339
1340 dStr_sprintfa(data,
1341 "\r\n"
1342 "--%s", boundary);
1343 }
1344 }
1345
1346 /*
1347 * Append a name-value pair to url data using multipart encoding.
1348 */
1349 void DilloHtmlForm::inputMultipartAppend(Dstr *data,
1350 const char *boundary,
1351 const char *name,
1352 const char *value)
1353 {
1354 if (name && name[0]) {
1355 if (data->len == 0) {
1356 dStr_append(data, "--");
1357 dStr_append(data, boundary);
1358 }
1359 // TODO: encode name (RFC 2231)
1360 dStr_sprintfa(data,
1361 "\r\n"
1362 "Content-Disposition: form-data; name=\"%s\"\r\n"
1363 "\r\n"
1364 "%s\r\n"
1365 "--%s",
1366 name, value, boundary);
1367 }
1368 }
1369
1370 /*
1371 * Append an image button click position to url data using url encoding.
1372 */
1373 void DilloHtmlForm::imageInputUrlencodeAppend(Dstr *data, Dstr *name, Dstr *x,
1374 Dstr *y)
1375 {
1376 if (name->len) {
1377 strUrlencodeAppend(data, name->str);
1378 dStr_sprintfa(data, ".x=%s&", x->str);
1379 strUrlencodeAppend(data, name->str);
1380 dStr_sprintfa(data, ".y=%s&", y->str);
1381 } else
1382 dStr_sprintfa(data, "x=%s&y=%s&", x->str, y->str);
1383 }
1384
1385 /*
1386 * Append an image button click position to url data using multipart encoding.
1387 */
1388 void DilloHtmlForm::imageInputMultipartAppend(Dstr *data, const char *boundary,
1389 Dstr *name, Dstr *x, Dstr *y)
1390 {
1391 int orig_len = name->len;
1392
1393 if (orig_len)
1394 dStr_append_c(name, '.');
1395 dStr_append_c(name, 'x');
1396
1397 inputMultipartAppend(data, boundary, name->str, x->str);
1398 dStr_truncate(name, name->len - 1);
1399 dStr_append_c(name, 'y');
1400 inputMultipartAppend(data, boundary, name->str, y->str);
1401 dStr_truncate(name, orig_len);
1402 }
1403
1404 /*
1405 * Reset all inputs containing reset to their initial values. In
1406 * general, reset is the reset button for the form.
1407 */
1408 void DilloHtmlForm::reset ()
1409 {
1410 int size = inputs->size();
1411 for (int i = 0; i < size; i++)
1412 inputs->get(i)->reset();
1413 }
1414
1415 /*
1416 * Show/hide "hidden" form controls
1417 */
1418 void DilloHtmlForm::display_hiddens(bool display)
1419 {
1420 int size = inputs->size();
1421 for (int i = 0; i < size; i++) {
1422 DilloHtmlInput *input = inputs->get(i);
1423 if (input->type == DILLO_HTML_INPUT_HIDDEN) {
1424 input->embed->setDisplayed(display);
1425 }
1426 }
1427 showing_hiddens = display;
1428 }
1429
1430 void DilloHtmlForm::setEnabled(bool enabled)
1431 {
1432 for (int i = 0; i < inputs->size(); i++)
1433 inputs->get(i)->setEnabled(enabled);
1434 }
1435
1436 /*
1437 * Add a new input.
1438 */
1439 void DilloHtmlForm::addInput(DilloHtmlInput *input, DilloHtmlInputType type)
1440 {
1441 input->connectTo (form_receiver);
1442 input->setEnabled (enabled);
1443 int ni = inputs->size ();
1444 inputs->increase ();
1445 inputs->set (ni,input);
1446
1447 /* some stats */
1448 if (type == DILLO_HTML_INPUT_PASSWORD ||
1449 type == DILLO_HTML_INPUT_TEXT) {
1450 num_entry_fields++;
1451 }
1452 }
1453
1454 /*
1455 * Return the input with a given resource.
1456 */
1457 DilloHtmlInput *DilloHtmlForm::getInput (Resource *resource)
1458 {
1459 for (int idx = 0; idx < inputs->size(); idx++) {
1460 DilloHtmlInput *input = inputs->get(idx);
1461 if (input->embed &&
1462 resource == input->embed->getResource())
1463 return input;
1464 }
1465 return NULL;
1466 }
1467
1468 /*
1469 * Return a Radio input for the given name.
1470 */
1471 DilloHtmlInput *DilloHtmlForm::getRadioInput (const char *name)
1472 {
1473 for (int idx = 0; idx < inputs->size(); idx++) {
1474 DilloHtmlInput *input = inputs->get(idx);
1475 if (input->type == DILLO_HTML_INPUT_RADIO &&
1476 input->name && !dStrcasecmp(input->name, name))
1477 return input;
1478 }
1479 return NULL;
1480 }
1481
1482 /*
1483 * DilloHtmlReceiver
1484 *
1485 * TODO: Currently there's "clicked" for buttons, we surely need "enter" for
1486 * textentries, and maybe the "mouseover, ...." set for Javascript.
1487 */
1488
1489 void DilloHtmlReceiver::activate (Resource *resource)
1490 {
1491 form->eventHandler(resource, NULL);
1492 }
1493
1494 /*
1495 * Enter a form control, as in "onmouseover".
1496 * For _pressing_ enter in a text control, see activate().
1497 */
1498 void DilloHtmlReceiver::enter (Resource *resource)
1499 {
1500 DilloHtml *html = form->html;
1501 DilloHtmlInput *input = form->getInput(resource);
1502 const char *msg = "";
1503
1504 if ((input->type == DILLO_HTML_INPUT_SUBMIT) ||
1505 (input->type == DILLO_HTML_INPUT_IMAGE) ||
1506 (input->type == DILLO_HTML_INPUT_BUTTON_SUBMIT) ||
1507 (input->type == DILLO_HTML_INPUT_INDEX) ||
1508 ((prefs.enterpress_forces_submit || form->num_entry_fields == 1) &&
1509 ((input->type == DILLO_HTML_INPUT_PASSWORD) ||
1510 (input->type == DILLO_HTML_INPUT_TEXT)))) {
1511 /* The control can submit form. Show action URL. */
1512 msg = URL_STR(form->action);
1513 }
1514 a_UIcmd_set_msg(html->bw, "%s", msg);
1515 }
1516
1517 /*
1518 * Leave a form control, or "onmouseout".
1519 */
1520 void DilloHtmlReceiver::leave (Resource *resource)
1521 {
1522 DilloHtml *html = form->html;
1523 a_UIcmd_set_msg(html->bw, "");
1524 }
1525
1526 void DilloHtmlReceiver::clicked (Resource *resource,
1527 EventButton *event)
1528 {
1529 form->eventHandler(resource, event);
1530 }
1531
1532 /*
1533 * DilloHtmlInput
1534 */
1535
1536 /*
1537 * Constructor
1538 */
1539 DilloHtmlInput::DilloHtmlInput (DilloHtmlInputType type2, Embed *embed2,
1540 const char *name2, const char *init_str2,
1541 bool init_val2)
1542 {
1543 type = type2;
1544 embed = embed2;
1545 name = (name2) ? dStrdup(name2) : NULL;
1546 init_str = (init_str2) ? dStrdup(init_str2) : NULL;
1547 init_val = init_val2;
1548 select = NULL;
1549 switch (type) {
1550 case DILLO_HTML_INPUT_SELECT:
1551 case DILLO_HTML_INPUT_SEL_LIST:
1552 select = new DilloHtmlSelect;
1553 break;
1554 default:
1555 break;
1556 }
1557 file_data = NULL;
1558 reset ();
1559 }
1560
1561 /*
1562 * Destructor
1563 */
1564 DilloHtmlInput::~DilloHtmlInput ()
1565 {
1566 dFree(name);
1567 dFree(init_str);
1568 dStr_free(file_data, 1);
1569 if (select)
1570 delete select;
1571 }
1572
1573 /*
1574 * Connect to a receiver.
1575 */
1576 void DilloHtmlInput::connectTo(DilloHtmlReceiver *form_receiver)
1577 {
1578 Resource *resource;
1579 if (embed && (resource = embed->getResource())) {
1580 resource->connectClicked (form_receiver);
1581 if (type == DILLO_HTML_INPUT_SUBMIT ||
1582 type == DILLO_HTML_INPUT_RESET ||
1583 type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
1584 type == DILLO_HTML_INPUT_BUTTON_RESET ||
1585 type == DILLO_HTML_INPUT_IMAGE ||
1586 type == DILLO_HTML_INPUT_FILE ||
1587 type == DILLO_HTML_INPUT_TEXT ||
1588 type == DILLO_HTML_INPUT_PASSWORD ||
1589 type == DILLO_HTML_INPUT_INDEX) {
1590 resource->connectActivate (form_receiver);
1591 }
1592 }
1593 }
1594
1595 /*
1596 * Activate a form
1597 */
1598 void DilloHtmlInput::activate(DilloHtmlForm *form, int num_entry_fields,
1599 EventButton *event)
1600 {
1601 switch (type) {
1602 case DILLO_HTML_INPUT_FILE:
1603 readFile (form->html->bw);
1604 break;
1605 case DILLO_HTML_INPUT_RESET:
1606 case DILLO_HTML_INPUT_BUTTON_RESET:
1607 form->reset();
1608 break;
1609 case DILLO_HTML_INPUT_TEXT:
1610 case DILLO_HTML_INPUT_PASSWORD:
1611 if (!(prefs.enterpress_forces_submit || num_entry_fields == 1)) {
1612 break;
1613 } else {
1614 /* fall through */
1615 }
1616 case DILLO_HTML_INPUT_SUBMIT:
1617 case DILLO_HTML_INPUT_BUTTON_SUBMIT:
1618 case DILLO_HTML_INPUT_IMAGE:
1619 case DILLO_HTML_INPUT_INDEX:
1620 form->submit(this, event);
1621 break;
1622 default:
1623 break;
1624 }
1625 }
1626
1627 /*
1628 * Read a file into cache
1629 */
1630 void DilloHtmlInput::readFile (BrowserWindow *bw)
1631 {
1632 const char *filename = a_UIcmd_select_file();
1633 if (filename) {
1634 a_UIcmd_set_msg(bw, "Loading file...");
1635 dStr_free(file_data, 1);
1636 file_data = a_Misc_file2dstr(filename);
1637 if (file_data) {
1638 a_UIcmd_set_msg(bw, "File loaded.");
1639 LabelButtonResource *lbr = (LabelButtonResource*)embed->getResource();
1640 lbr->setLabel(filename);
1641 } else {
1642 a_UIcmd_set_msg(bw, "ERROR: can't load: %s", filename);
1643 }
1644 }
1645 }
1646
1647 /*
1648 * Get the values for a "successful control".
1649 */
1650 void DilloHtmlInput::appendValuesTo(Dlist *values, bool is_active_submit)
1651 {
1652 switch (type) {
1653 case DILLO_HTML_INPUT_TEXT:
1654 case DILLO_HTML_INPUT_PASSWORD:
1655 case DILLO_HTML_INPUT_INDEX:
1656 case DILLO_HTML_INPUT_HIDDEN:
1657 {
1658 EntryResource *entryres = (EntryResource*)embed->getResource();
1659 dList_append(values, dStr_new(entryres->getText()));
1660 }
1661 break;
1662 case DILLO_HTML_INPUT_TEXTAREA:
1663 {
1664 MultiLineTextResource *textres =
1665 (MultiLineTextResource*)embed->getResource();
1666 dList_append(values, dStr_new(textres->getText()));
1667 }
1668 break;
1669 case DILLO_HTML_INPUT_CHECKBOX:
1670 case DILLO_HTML_INPUT_RADIO:
1671 {
1672 ToggleButtonResource *cb_r =
1673 (ToggleButtonResource*)embed->getResource();
1674 if (name && init_str && cb_r->isActivated()) {
1675 dList_append(values, dStr_new(init_str));
1676 }
1677 }
1678 break;
1679 case DILLO_HTML_INPUT_SUBMIT:
1680 case DILLO_HTML_INPUT_BUTTON_SUBMIT:
1681 if (is_active_submit)
1682 dList_append(values, dStr_new(init_str));
1683 break;
1684 case DILLO_HTML_INPUT_SELECT:
1685 case DILLO_HTML_INPUT_SEL_LIST:
1686 {
1687 SelectionResource *sel_res = (SelectionResource*)embed->getResource();
1688 select->appendValuesTo (values, sel_res);
1689 }
1690 break;
1691 case DILLO_HTML_INPUT_FILE:
1692 {
1693 LabelButtonResource *lbr = (LabelButtonResource*)embed->getResource();
1694 const char *filename = lbr->getLabel();
1695 if (filename[0] && strcmp(filename, init_str)) {
1696 if (file_data) {
1697 Dstr *file = dStr_sized_new(file_data->len);
1698 dStr_append_l(file, file_data->str, file_data->len);
1699 dList_append(values, file);
1700 } else {
1701 MSG("FORM file input \"%s\" not loaded.\n", filename);
1702 }
1703 }
1704 }
1705 break;
1706 case DILLO_HTML_INPUT_IMAGE:
1707 if (is_active_submit) {
1708 ComplexButtonResource *cbr =
1709 (ComplexButtonResource*)embed->getResource();
1710 Dstr *strX = dStr_new("");
1711 Dstr *strY = dStr_new("");
1712 dStr_sprintf(strX, "%d", cbr->getClickX());
1713 dStr_sprintf(strY, "%d", cbr->getClickY());
1714 dList_append(values, strX);
1715 dList_append(values, strY);
1716 }
1717 break;
1718 default:
1719 break;
1720 }
1721 }
1722
1723 /*
1724 * Reset to the initial value.
1725 */
1726 void DilloHtmlInput::reset ()
1727 {
1728 switch (type) {
1729 case DILLO_HTML_INPUT_TEXT:
1730 case DILLO_HTML_INPUT_PASSWORD:
1731 case DILLO_HTML_INPUT_INDEX:
1732 case DILLO_HTML_INPUT_HIDDEN:
1733 {
1734 EntryResource *entryres = (EntryResource*)embed->getResource();
1735 entryres->setText(init_str ? init_str : "");
1736 }
1737 break;
1738 case DILLO_HTML_INPUT_CHECKBOX:
1739 case DILLO_HTML_INPUT_RADIO:
1740 {
1741 ToggleButtonResource *tb_r =
1742 (ToggleButtonResource*)embed->getResource();
1743 tb_r->setActivated(init_val);
1744 }
1745 break;
1746 case DILLO_HTML_INPUT_SELECT:
1747 if (select != NULL) {
1748 /* this is in reverse order so that, in case more than one was
1749 * selected, we get the last one, which is consistent with handling
1750 * of multiple selected options in the layout code. */
1751 // for (i = select->num_options - 1; i >= 0; i--) {
1752 // if (select->options[i].init_val) {
1753 // gtk_menu_item_activate(GTK_MENU_ITEM
1754 // (select->options[i].menuitem));
1755 // Html_select_set_history(input);
1756 // break;
1757 // }
1758 // }
1759 }
1760 break;
1761 case DILLO_HTML_INPUT_SEL_LIST:
1762 if (!select)
1763 break;
1764 // for (i = 0; i < select->num_options; i++) {
1765 // if (select->options[i].init_val) {
1766 // if (select->options[i].menuitem->state == GTK_STATE_NORMAL)
1767 // gtk_list_select_child(GTK_LIST(select->menu),
1768 // select->options[i].menuitem);
1769 // } else {
1770 // if (select->options[i].menuitem->state==GTK_STATE_SELECTED)
1771 // gtk_list_unselect_child(GTK_LIST(select->menu),
1772 // select->options[i].menuitem);
1773 // }
1774 // }
1775 break;
1776 case DILLO_HTML_INPUT_TEXTAREA:
1777 if (init_str != NULL) {
1778 MultiLineTextResource *textres =
1779 (MultiLineTextResource*)embed->getResource();
1780 textres->setText(init_str ? init_str : "");
1781 }
1782 break;
1783 case DILLO_HTML_INPUT_FILE:
1784 {
1785 LabelButtonResource *lbr = (LabelButtonResource*)embed->getResource();
1786 lbr->setLabel(init_str);
1787 }
1788 break;
1789 default:
1790 break;
1791 }
1792 }
1793
1794 /*
1795 * DilloHtmlSelect
1796 */
1797
1798 /*
1799 * Constructor
1800 */
1801 DilloHtmlSelect::DilloHtmlSelect ()
1802 {
1803 options = new misc::SimpleVector<DilloHtmlOption *> (4);
1804 }
1805
1806 /*
1807 * Destructor
1808 */
1809 DilloHtmlSelect::~DilloHtmlSelect ()
1810 {
1811 int size = options->size ();
1812 for (int k = 0; k < size; k++)
1813 delete options->get (k);
1814 delete options;
1815 }
1816
1817 DilloHtmlOption *DilloHtmlSelect::getCurrentOption ()
1818 {
1819 return options->get (options->size() - 1);
1820 }
1821
1822 void DilloHtmlSelect::addOption (char *value, bool selected, bool enabled)
1823 {
1824 DilloHtmlOption *option =
1825 new DilloHtmlOption (value, selected, enabled);
1826 int size = options->size ();
1827 options->increase ();
1828 options->set (size, option);
1829 }
1830
1831 /*
1832 * Select the first option if nothing else is selected.
1833 */
1834 void DilloHtmlSelect::ensureSelection()
1835 {
1836 int size = options->size ();
1837 if (size > 0) {
1838 for (int i = 0; i < size; i++) {
1839 DilloHtmlOption *option = options->get (i);
1840 if (option->selected)
1841 return;
1842 }
1843 DilloHtmlOption *option = options->get (0);
1844 option->selected = true;
1845 }
1846 }
1847
1848 void DilloHtmlSelect::addOptionsTo (SelectionResource *res)
1849 {
1850 int size = options->size ();
1851 for (int i = 0; i < size; i++) {
1852 DilloHtmlOption *option = options->get (i);
1853 res->addItem(option->content, option->enabled, option->selected);
1854 }
1855 }
1856
1857 void DilloHtmlSelect::appendValuesTo (Dlist *values, SelectionResource *res)
1858 {
1859 int size = options->size ();
1860 for (int i = 0; i < size; i++) {
1861 if (res->isSelected (i)) {
1862 DilloHtmlOption *option = options->get (i);
1863 char *val = option->value ? option->value : option->content;
1864 dList_append(values, dStr_new(val));
1865 }
1866 }
1867 }
1868
1869 /*
1870 * DilloHtmlOption
1871 */
1872
1873 /*
1874 * Constructor
1875 */
1876 DilloHtmlOption::DilloHtmlOption (char *value2,
1877 bool selected2,
1878 bool enabled2)
1879 {
1880 value = value2;
1881 content = NULL;
1882 selected = selected2;
1883 enabled = enabled2;
1884 }
1885
1886 /*
1887 * Destructor
1888 */
1889 DilloHtmlOption::~DilloHtmlOption ()
1890 {
1891 dFree(value);
1892 dFree(content);
1893 }
1894
1895 /*
1896 * Utilities
1897 */
1898
1899 /*
1900 * Create input image for the form
1901 */
1902 static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize)
1903 {
1904 const char *attrbuf;
1905 DilloImage *Image;
1906 Embed *button = NULL;
1907 DilloUrl *url = NULL;
1908
1909 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "src")) &&
1910 (url = a_Html_url_new(html, attrbuf, NULL, 0))) {
1911
1912 html->styleEngine->setPseudoLink ();
1913
1914 /* create new image and add it to the button */
1915 if ((Image = a_Html_image_new(html, tag, tagsize, url))) {
1916 IM2DW(Image)->setStyle (html->styleEngine->backgroundStyle ());
1917 ResourceFactory *factory = HT2LT(html)->getResourceFactory();
1918 ComplexButtonResource *complex_b_r =
1919 factory->createComplexButtonResource(IM2DW(Image), false);
1920 button = new Embed(complex_b_r);
1921 HT2TB(html)->addWidget (button, html->styleEngine->style ());
1922 // gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
1923
1924 } else {
1925 a_Url_free(url);
1926 }
1927 }
1928 if (!button)
1929 MSG("Html_input_image: unable to create image submit.\n");
1930 return button;
1931 }
1932
1933 /*
1934 * ?
1935 */
1936 static void Html_option_finish(DilloHtml *html)
1937 {
1938 DilloHtmlInput *input = Html_get_current_input(html);
1939 if (input->type == DILLO_HTML_INPUT_SELECT ||
1940 input->type == DILLO_HTML_INPUT_SEL_LIST) {
1941 DilloHtmlOption *option =
1942 input->select->getCurrentOption ();
1943 option->content = dStrndup(html->Stash->str, html->Stash->len);
1944 }
1945 }
0 #ifndef __FORM_HH__
1 #define __FORM_HH__
2
3 #include "url.h"
4
5 /*
6 * Typedefs
7 */
8
9 typedef enum {
10 DILLO_HTML_METHOD_UNKNOWN,
11 DILLO_HTML_METHOD_GET,
12 DILLO_HTML_METHOD_POST
13 } DilloHtmlMethod;
14
15 typedef enum {
16 DILLO_HTML_ENC_URLENCODED,
17 DILLO_HTML_ENC_MULTIPART
18 } DilloHtmlEnc;
19
20 /*
21 * Classes
22 */
23
24 class DilloHtmlForm;
25 class DilloHtmlInput;
26 class DilloHtml;
27
28 /*
29 * Form API
30 */
31
32 DilloHtmlForm *a_Html_form_new(DilloHtml *html,
33 DilloHtmlMethod method,
34 const DilloUrl *action,
35 DilloHtmlEnc enc,
36 const char *charset, bool enabled);
37
38 void a_Html_form_delete(DilloHtmlForm* form);
39 void a_Html_input_delete(DilloHtmlInput* input);
40 void a_Html_form_submit2(void *v_form);
41 void a_Html_form_reset2(void *v_form);
42 void a_Html_form_display_hiddens2(void *v_form, bool display);
43
44
45 /*
46 * Form parsing functions
47 */
48
49 void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize);
50 void Html_tag_close_form(DilloHtml *html, int TagIdx);
51 void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize);
52 void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize);
53 void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize);
54 void Html_tag_close_textarea(DilloHtml *html, int TagIdx);
55 void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize);
56 void Html_tag_close_select(DilloHtml *html, int TagIdx);
57 void Html_tag_open_option(DilloHtml *html, const char *tag, int tagsize);
58 void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize);
59 void Html_tag_close_button(DilloHtml *html, int TagIdx);
60
61 #endif /* __FORM_HH__ */
11 * File: gif.c
22 *
33 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 2000-2002 Jorge Arellano Cid <jcid@dillo.org>
4 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
8 * the Free Software Foundation; either version 3 of the License, or
99 * (at your option) any later version.
1010 */
1111
5353 */
5454
5555
56 /* todo:
56 /* TODO:
5757 * + Make sure to handle error cases gracefully (including aborting the
5858 * connection, if necessary).
5959 */
6464 #include <stdio.h> /* for sprintf */
6565 #include <string.h> /* for memcpy and memmove */
6666
67 #include <gtk/gtk.h>
6867 #include "msg.h"
69 #include "image.h"
70 #include "web.h"
68 #include "image.hh"
7169 #include "cache.h"
7270 #include "dicache.h"
73 #include "prefs.h"
74
75 #define DEBUG_LEVEL 6
76 #include "debug.h"
7771
7872 #define INTERLACE 0x40
7973 #define LOCALCOLORMAP 0x80
8074
81 #define LM_to_uint(a,b) ((((guchar)b)<<8)|((guchar)a))
75 #define LM_to_uint(a,b) ((((uchar_t)b)<<8)|((uchar_t)a))
8276
8377 #define MAXCOLORMAPSIZE 256
8478 #define MAX_LWZ_BITS 12
8781 typedef struct _DilloGif {
8882 DilloImage *Image;
8983 DilloUrl *url;
90 gint version;
91
92 gint state;
84 int version;
85
86 int state;
9387 size_t Start_Ofs;
94 guint Flags;
95
96 guchar input_code_size;
97 guchar *linebuf;
98 gint pass;
99
100 guint y;
88 uint_t Flags;
89
90 uchar_t input_code_size;
91 uchar_t *linebuf;
92 int pass;
93
94 uint_t y;
10195
10296 /* state for lwz_read_byte */
103 gint code_size;
97 int code_size;
10498
10599 /* The original GifScreen from giftopnm */
106 guint Width;
107 guint Height;
100 uint_t Width;
101 uint_t Height;
108102 size_t ColorMap_ofs;
109 guint ColorResolution;
110 guint NumColors;
111 gint Background;
112 guint spill_line_index;
103 uint_t ColorResolution;
104 uint_t NumColors;
105 int Background;
106 uint_t spill_line_index;
113107 #if 0
114 guint AspectRatio; /* AspectRatio (not used) */
108 uint_t AspectRatio; /* AspectRatio (not used) */
115109 #endif
116110
117111 /* Gif89 extensions */
118 gint transparent;
112 int transparent;
119113 #if 0
120114 /* None are used: */
121 gint delayTime;
122 gint inputFlag;
123 gint disposal;
115 int delayTime;
116 int inputFlag;
117 int disposal;
124118 #endif
125119
126120 /* state for the new push-oriented decoder */
127 gint packet_size; /* The amount of the data block left to process */
128 guint window;
129 gint bits_in_window;
130 guint last_code; /* Last "compressed" code in the look up table */
131 guint line_index;
132 guchar **spill_lines;
133 gint num_spill_lines_max;
134 gint length[(1 << MAX_LWZ_BITS) + 1];
135 gint code_and_byte[(1 << MAX_LWZ_BITS) + 1];
121 int packet_size; /* The amount of the data block left to process */
122 uint_t window;
123 int bits_in_window;
124 uint_t last_code; /* Last "compressed" code in the look up table */
125 uint_t line_index;
126 uchar_t **spill_lines;
127 int num_spill_lines_max;
128 int length[(1 << MAX_LWZ_BITS) + 1];
129 int code_and_byte[(1 << MAX_LWZ_BITS) + 1];
136130 } DilloGif;
137131
138132 /* Some invariants:
146140 /*
147141 * Forward declarations
148142 */
149 static void Gif_write(DilloGif *gif, void *Buf, guint BufSize);
143 static void Gif_write(DilloGif *gif, void *Buf, uint_t BufSize);
150144 static void Gif_close(DilloGif *gif, CacheClient_t *Client);
151 static size_t Gif_process_bytes(DilloGif *gif, const guchar *buf,
152 gint bufsize, void *Buf);
153 static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, gint version);
154 static void Gif_callback(int Op, CacheClient_t *Client);
155
156 /* exported function */
157 DwWidget *a_Gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
158 void **Data);
159
160
161 /*
162 * MIME handler for "image/gif" type
163 * (Sets Gif_callback as cache-client)
164 */
165 DwWidget *a_Gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
166 void **Data)
167 {
168 DilloWeb *web = Ptr;
169 DICacheEntry *DicEntry;
170
171 if ( !web->Image )
172 web->Image = a_Image_new(0, 0, NULL, prefs.bg_color);
173 /* todo: get the backgound color from the parent widget -- Livio. */
174
175 /* Add an extra reference to the Image (for dicache usage) */
176 a_Image_ref(web->Image);
177
178 DicEntry = a_Dicache_get_entry(web->url);
179 if ( !DicEntry ) {
180 /* Let's create an entry for this image... */
181 DicEntry = a_Dicache_add_entry(web->url);
182
183 /* ... and let the decoder feed it! */
184 *Data = Gif_new(web->Image, DicEntry->url, DicEntry->version);
185 *Call = (CA_Callback_t) Gif_callback;
186 } else {
187 /* Let's feed our client from the dicache */
188 a_Dicache_ref(DicEntry->url, DicEntry->version);
189 *Data = web->Image;
190 *Call = (CA_Callback_t) a_Dicache_callback;
191 }
192 return DW_WIDGET (web->Image->dw);
193 }
145 static size_t Gif_process_bytes(DilloGif *gif, const uchar_t *buf,
146 int bufsize, void *Buf);
147
194148
195149 /*
196150 * Create a new gif structure for decoding a gif into a RGB buffer
197151 */
198 static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, gint version)
199 {
200 DilloGif *gif = g_malloc(sizeof(DilloGif));
152 void *a_Gif_new(DilloImage *Image, DilloUrl *url, int version)
153 {
154 DilloGif *gif = dMalloc(sizeof(DilloGif));
155 _MSG("a_Gif_new: gif=%p\n", gif);
201156
202157 gif->Image = Image;
203158 gif->url = url;
219174 }
220175
221176 /*
177 * Free the gif-decoding data structure.
178 */
179 static void Gif_free(DilloGif *gif)
180 {
181 int i;
182
183 _MSG("Gif_free: gif=%p\n", gif);
184
185 dFree(gif->linebuf);
186 if (gif->spill_lines != NULL) {
187 for (i = 0; i < gif->num_spill_lines_max; i++)
188 dFree(gif->spill_lines[i]);
189 dFree(gif->spill_lines);
190 }
191 dFree(gif);
192 }
193
194 /*
222195 * This function is a cache client, it receives data from the cache
223196 * and dispatches it to the appropriate gif-processing functions
224197 */
225 static void Gif_callback(int Op, CacheClient_t *Client)
226 {
227 if ( Op )
198 void a_Gif_callback(int Op, void *data)
199 {
200 if (Op == CA_Send) {
201 CacheClient_t *Client = data;
202 Gif_write(Client->CbData, Client->Buf, Client->BufSize);
203 } else if (Op == CA_Close) {
204 CacheClient_t *Client = data;
228205 Gif_close(Client->CbData, Client);
229 else
230 Gif_write(Client->CbData, Client->Buf, Client->BufSize);
206 } else if (Op == CA_Abort) {
207 Gif_free(data);
208 }
231209 }
232210
233211 /*
234212 * Receive and process new chunks of GIF image data
235213 */
236 static void Gif_write(DilloGif *gif, void *Buf, guint BufSize)
237 {
238 guchar *buf;
239 gint bufsize, bytes_consumed;
214 static void Gif_write(DilloGif *gif, void *Buf, uint_t BufSize)
215 {
216 uchar_t *buf;
217 int bufsize, bytes_consumed;
240218
241219 /* Sanity checks */
242220 if (!Buf || !gif->Image || BufSize == 0)
243221 return;
244222
245 buf = ((guchar *) Buf) + gif->Start_Ofs;
223 buf = ((uchar_t *) Buf) + gif->Start_Ofs;
246224 bufsize = BufSize - gif->Start_Ofs;
247225
248 DEBUG_MSG(5, "Gif_write: %u bytes\n", BufSize);
226 _MSG("Gif_write: %u bytes\n", BufSize);
249227
250228 /* Process the bytes in the input buffer. */
251229 bytes_consumed = Gif_process_bytes(gif, buf, bufsize, Buf);
254232 return;
255233 gif->Start_Ofs += bytes_consumed;
256234
257 DEBUG_MSG(5, "exit Gif_write, bufsize=%ld\n", (glong)bufsize);
235 _MSG("exit Gif_write, bufsize=%ld\n", (long)bufsize);
258236 }
259237
260238 /*
262240 */
263241 static void Gif_close(DilloGif *gif, CacheClient_t *Client)
264242 {
265 gint i;
266
267 DEBUG_MSG(5, "destroy gif %p\n", gif);
268
243 _MSG("Gif_close: destroy gif %p\n", gif);
269244 a_Dicache_close(gif->url, gif->version, Client);
270
271 g_free(gif->linebuf);
272
273 if (gif->spill_lines != NULL) {
274 for (i = 0; i < gif->num_spill_lines_max; i++)
275 g_free(gif->spill_lines[i]);
276 g_free(gif->spill_lines);
277 }
278 g_free(gif);
245 Gif_free(gif);
279246 }
280247
281248
289256 * 0 = There wasn't enough bytes read yet to read the whole datablock
290257 * otherwise the size of the data blocks
291258 */
292 static inline size_t Gif_data_blocks(const guchar *Buf, size_t BSize)
259 static inline size_t Gif_data_blocks(const uchar_t *Buf, size_t BSize)
293260 {
294261 size_t Size = 0;
295262
313280 * 0 -- block not processed
314281 * otherwise the size of the extension label.
315282 */
316 static inline size_t Gif_do_generic_ext(const guchar *Buf, size_t BSize)
283 static inline size_t Gif_do_generic_ext(const uchar_t *Buf, size_t BSize)
317284 {
318285 size_t Size = Buf[0] + 1, DSize;
319286
334301 * ?
335302 */
336303 static inline size_t
337 Gif_do_gc_ext(DilloGif *gif, const guchar *Buf, size_t BSize)
304 Gif_do_gc_ext(DilloGif *gif, const uchar_t *Buf, size_t BSize)
338305 {
339306 /* Graphic Control Extension */
340307 size_t Size = Buf[0] + 2;
341 guint Flags;
308 uint_t Flags;
342309
343310 if (Size > BSize)
344311 return 0;
371338 * Return value:
372339 * TRUE when the extension is over
373340 */
374 static size_t Gif_do_extension(DilloGif *gif, guint Label,
375 const guchar *buf,
341 static size_t Gif_do_extension(DilloGif *gif, uint_t Label,
342 const uchar_t *buf,
376343 size_t BSize)
377344 {
378345 switch (Label) {
383350 return Gif_data_blocks(buf, BSize);
384351
385352 case Txt_Ext: /* Plain text Extension */
386 /* This extension allows (rcm thinks) the image to be rendered as text.
387 */
388353 case App_Ext: /* Application Extension */
389354 default:
390355 return Gif_do_generic_ext(buf, BSize); /*Ignore Extension */
400365 static void Gif_lwz_init(DilloGif *gif)
401366 {
402367 gif->num_spill_lines_max = 1;
403 gif->spill_lines = g_malloc(sizeof(guchar *) * gif->num_spill_lines_max);
404
405 gif->spill_lines[0] = g_malloc(gif->Width);
368 gif->spill_lines = dMalloc(sizeof(uchar_t *) * gif->num_spill_lines_max);
369
370 gif->spill_lines[0] = dMalloc(gif->Width);
406371 gif->bits_in_window = 0;
407372
408373 /* First code in table = clear_code +1
419384 /*
420385 * Send the image line to the dicache, also handling the interlacing.
421386 */
422 static void Gif_emit_line(DilloGif *gif, const guchar *linebuf)
423 {
424 a_Dicache_write(gif->Image, gif->url, gif->version, linebuf, 0, gif->y);
387 static void Gif_emit_line(DilloGif *gif, const uchar_t *linebuf)
388 {
389 a_Dicache_write(gif->url, gif->version, linebuf, gif->y);
425390 if (gif->Flags & INTERLACE) {
426391 switch (gif->pass) {
427392 case 0:
460425 }
461426
462427 /*
463 * I apologize for the large size of this routine and the goto error
464 * construct - I almost _never_ do that. I offer the excuse of
465 * optimizing for speed.
466 *
467 * RCM -- busted these down into smaller subroutines... still very hard to
468 * read.
469 */
470
471
472 /*
473428 * Decode the packetized lwz bytes
474429 */
475 static void Gif_literal(DilloGif *gif, guint code)
430 static void Gif_literal(DilloGif *gif, uint_t code)
476431 {
477432 gif->linebuf[gif->line_index++] = code;
478433 if (gif->line_index >= gif->Width) {
488443 * ?
489444 */
490445 /* Profiling reveals over half the GIF time is spent here: */
491 static void Gif_sequence(DilloGif *gif, guint code)
492 {
493 guint o_index, o_size, orig_code;
494 guint sequence_length = gif->length[code];
495 guint line_index = gif->line_index;
496 gint num_spill_lines;
497 gint spill_line_index = gif->spill_line_index;
498 guchar *last_byte_ptr, *obuf;
446 static void Gif_sequence(DilloGif *gif, uint_t code)
447 {
448 uint_t o_index, o_size, orig_code;
449 uint_t sequence_length = gif->length[code];
450 uint_t line_index = gif->line_index;
451 int num_spill_lines;
452 int spill_line_index = gif->spill_line_index;
453 uchar_t *last_byte_ptr, *obuf;
499454
500455 gif->length[gif->last_code + 1] = sequence_length + 1;
501456 gif->code_and_byte[gif->last_code + 1] = (code << 8);
516471 /* Allocate more spill lines. */
517472 spill_line_index = gif->num_spill_lines_max;
518473 gif->num_spill_lines_max = num_spill_lines << 1;
519 gif->spill_lines = g_realloc(gif->spill_lines,
474 gif->spill_lines = dRealloc(gif->spill_lines,
520475 gif->num_spill_lines_max *
521 sizeof(guchar *));
476 sizeof(uchar_t *));
522477
523478 for (; spill_line_index < gif->num_spill_lines_max;
524479 spill_line_index++)
525480 gif->spill_lines[spill_line_index] =
526 g_malloc(gif->Width);
481 dMalloc(gif->Width);
527482 }
528483 spill_line_index = num_spill_lines - 1;
529484 obuf = gif->spill_lines[spill_line_index];
543498 /* Write o_size bytes to
544499 * obuf[o_index - o_size..o_index). */
545500 for (; o_size > 0 && o_index > 0; o_size--) {
546 guint code_and_byte = gif->code_and_byte[code];
547
548 DEBUG_MSG(5, "%d ", gif->code_and_byte[code] & 255);
501 uint_t code_and_byte = gif->code_and_byte[code];
502
503 _MSG("%d ", gif->code_and_byte[code] & 255);
549504
550505 obuf[--o_index] = code_and_byte & 255;
551506 code = code_and_byte >> 8;
565520 }
566521 /* Ok, now we write the first byte of the sequence. */
567522 /* We are sure that the code is literal. */
568 DEBUG_MSG(5, "%d", code);
523 _MSG("%d", code);
569524 obuf[--o_index] = code;
570525 gif->code_and_byte[gif->last_code] |= code;
571526
572527 /* Fix up the output if the original code was last_code. */
573528 if (orig_code == gif->last_code) {
574529 *last_byte_ptr = code;
575 DEBUG_MSG(5, " fixed (%d)!", code);
576 }
577 DEBUG_MSG(5, "\n");
530 _MSG(" fixed (%d)!", code);
531 }
532 _MSG("\n");
578533
579534 /* Output any full lines. */
580535 if (gif->line_index >= gif->Width) {
592547 if (num_spill_lines) {
593548 /* Swap the last spill line with the gif line, using
594549 * linebuf as the swap temporary. */
595 guchar *linebuf = gif->spill_lines[num_spill_lines - 1];
550 uchar_t *linebuf = gif->spill_lines[num_spill_lines - 1];
596551
597552 gif->spill_lines[num_spill_lines - 1] = gif->linebuf;
598553 gif->linebuf = linebuf;
610565 * < 0 on error
611566 * -1 if the decompression code was not in the lookup table
612567 */
613 static gint Gif_process_code(DilloGif *gif, guint code, guint clear_code)
568 static int Gif_process_code(DilloGif *gif, uint_t code, uint_t clear_code)
614569 {
615570
616571 /* A short table describing what to do with the code:
621576 */
622577 if (code < clear_code) {
623578 /* a literal code. */
624 DEBUG_MSG(5, "literal\n");
579 _MSG("literal\n");
625580 Gif_literal(gif, code);
626581 return 1;
627582 } else if (code >= clear_code + 2) {
632587 return 1;
633588 } else if (code == clear_code) {
634589 /* clear code. Resets the whole table */
635 DEBUG_MSG(5, "clear\n");
590 _MSG("clear\n");
636591 return 0;
637592 } else {
638593 /* end code. */
639 DEBUG_MSG(5, "end\n");
594 _MSG("end\n");
640595 return 2;
641596 }
642597 }
644599 /*
645600 * ?
646601 */
647 static gint Gif_decode(DilloGif *gif, const guchar *buf, size_t bsize)
602 static int Gif_decode(DilloGif *gif, const uchar_t *buf, size_t bsize)
648603 {
649604 /*
650605 * Data block processing. The image stuff is a series of data blocks.
652607 * of the data block. 0 == the last data block.
653608 */
654609 size_t bufsize, packet_size;
655 guint clear_code;
656 guint window;
657 gint bits_in_window;
658 guint code;
659 gint code_size;
660 guint code_mask;
610 uint_t clear_code;
611 uint_t window;
612 int bits_in_window;
613 uint_t code;
614 int code_size;
615 uint_t code_mask;
661616
662617 bufsize = bsize;
663618
677632 */
678633 while (bufsize > 0) {
679634 /* lwz_bytes is the number of remaining lwz bytes in the packet. */
680 gint lwz_bytes = MIN(packet_size, bufsize);
635 int lwz_bytes = MIN(packet_size, bufsize);
681636
682637 bufsize -= lwz_bytes;
683638 packet_size -= lwz_bytes;
694649 * at the start of the window */
695650 code = (window >> (32 - bits_in_window)) & code_mask;
696651
697 DEBUG_MSG(5, "code = %d, ", code);
652 _MSG("code = %d, ", code);
698653
699654 bits_in_window -= code_size;
700655 switch (Gif_process_code(gif, code, clear_code)) {
757712 /*
758713 * ?
759714 */
760 static gint Gif_check_sig(DilloGif *gif, const guchar *ibuf, gint ibsize)
715 static int Gif_check_sig(DilloGif *gif, const uchar_t *ibuf, int ibsize)
761716 {
762717 /* at beginning of file - read magic number */
763718 if (ibsize < 6)
764719 return 0;
765 if (strncmp(ibuf, "GIF", 3) != 0) {
766 gif->state = 999;
767 return 6;
768 }
769 if (strncmp(ibuf + 3, "87a", 3) != 0 &&
770 strncmp(ibuf + 3, "89a", 3) != 0) {
720 if (memcmp(ibuf, "GIF87a", 6) != 0 &&
721 memcmp(ibuf, "GIF89a", 6) != 0) {
722 MSG_WARN("\"%s\" is not a GIF file.\n", URL_STR(gif->url));
771723 gif->state = 999;
772724 return 6;
773725 }
783735 */
784736 static inline size_t
785737 Gif_do_color_table(DilloGif *gif, void *Buf,
786 const guchar *buf, size_t bsize, size_t CT_Size)
738 const uchar_t *buf, size_t bsize, size_t CT_Size)
787739 {
788740 size_t Size = 3 * (1 << (1 + CT_Size));
789741
790742 if (Size > bsize)
791743 return 0;
792744
793 gif->ColorMap_ofs = (gulong) buf - (gulong) Buf;
745 gif->ColorMap_ofs = (ulong_t) buf - (ulong_t) Buf;
794746 gif->NumColors = (1 << (1 + CT_Size));
795747 return Size;
796748 }
800752 * <Logical Screen> ::= Logical Screen Descriptor [Global Color Table]
801753 */
802754 static size_t Gif_get_descriptor(DilloGif *gif, void *Buf,
803 const guchar *buf, gint bsize)
755 const uchar_t *buf, int bsize)
804756 {
805757
806758 /* screen descriptor */
807759 size_t Size = 7, /* Size of descriptor */
808760 mysize; /* Size of color table */
809 guchar Flags;
761 uchar_t Flags;
810762
811763 if (bsize < 7)
812764 return 0;
837789 * with the stuff at the header. For now, we punt...
838790 */
839791 static size_t Gif_do_img_desc(DilloGif *gif, void *Buf,
840 const guchar *buf, size_t bsize)
841 {
842 guchar Flags;
792 const uchar_t *buf, size_t bsize)
793 {
794 uchar_t Flags;
843795 size_t Size = 9 + 1; /* image descriptor size + first byte of image data */
844796
845797 if (bsize < 10)
847799
848800 gif->Width = LM_to_uint(buf[4], buf[5]);
849801 gif->Height = LM_to_uint(buf[6], buf[7]);
850 gif->linebuf = g_malloc(gif->Width);
802
803 /* check max image size */
804 if (gif->Width <= 0 || gif->Height <= 0 ||
805 gif->Width > IMAGE_MAX_AREA / gif->Height) {
806 MSG("Gif_do_img_desc: suspicious image size request %u x %u\n",
807 gif->Width, gif->Height);
808 gif->state = 999;
809 return 0;
810 }
851811
852812 a_Dicache_set_parms(gif->url, gif->version, gif->Image,
853813 gif->Width, gif->Height, DILLO_IMG_TYPE_INDEXED);
879839 gif->y = 0;
880840 Gif_lwz_init(gif);
881841 gif->spill_line_index = 0;
842 gif->linebuf = dMalloc(gif->Width);
882843 gif->state = 3; /*Process the lzw data next */
883844 if (gif->Image && gif->ColorMap_ofs) {
884845 a_Dicache_set_cmap(gif->url, gif->version, gif->Image,
885 (guchar *) Buf + gif->ColorMap_ofs,
846 (uchar_t *) Buf + gif->ColorMap_ofs,
886847 gif->NumColors, 256, gif->transparent);
887848 }
888849 return Size;
916877 * "consumed"
917878 */
918879 static size_t GIF_Block(DilloGif * gif, void *Buf,
919 const guchar *buf, size_t bsize)
880 const uchar_t *buf, size_t bsize)
920881 {
921882 size_t Size = 0, mysize;
922 guchar C;
883 uchar_t C;
923884
924885 if (bsize < 1)
925886 return 0;
993954 * State == 3 is special... this is inside of <Data> but all of the stuff in
994955 * there has been gotten and set up. So we stream it outside.
995956 */
996 static size_t Gif_process_bytes(DilloGif *gif, const guchar *ibuf,
997 gint bufsize, void *Buf)
998 {
999 gint tmp_bufsize = bufsize;
957 static size_t Gif_process_bytes(DilloGif *gif, const uchar_t *ibuf,
958 int bufsize, void *Buf)
959 {
960 int tmp_bufsize = bufsize;
1000961 size_t mysize;
1001962
1002963 switch (gif->state) {
10451006 break;
10461007 }
10471008
1048 DEBUG_MSG(5, "Gif_process_bytes: final state %d, %ld bytes consumed\n",
1049 gif->state, (glong)(bufsize - tmp_bufsize));
1009 _MSG("Gif_process_bytes: final state %d, %ld bytes consumed\n",
1010 gif->state, (long)(bufsize - tmp_bufsize));
10501011
10511012 return bufsize - tmp_bufsize;
10521013 }
10531014
1015 #else /* ENABLE_GIF */
1016
1017 void *a_Gif_new() { return 0; }
1018 void a_Gif_callback() { return; }
1019
10541020 #endif /* ENABLE_GIF */
+0
-458
src/gtk_ext_button.c less more
0 /*
1 * File: selection.c
2 *
3 * Copyright 2004 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This Gtk+ widget is a variant of GtkButton, which adds two features:
13 *
14 * 1. the possibility to react on different mouse buttons (not only button
15 * 1), and
16 * 2. a clean way to attach menus.
17 *
18 * To archieve this use one of the following functions:
19 *
20 * a_Gtk_ext_button_set_inactive,
21 * a_Gtk_ext_button_set_command,
22 * a_Gtk_ext_button_attach_menu, or
23 * a_Gtk_ext_button_attach_menu_creator.
24 *
25 * See comments there for more informations.
26 *
27 * About signals: For the command button mode, there are some new signals,
28 * "clicked1", "clicked2", and "clicked3", which are emitted for the respective
29 * mouse button. The signal "clicked" should be connected to as well, since
30 * it is still used for non-mouse event handling, e.g. when then button is
31 * activated by the <Enter> key.
32 */
33
34 #include "gtk_ext_button.h"
35 #include <gtk/gtklabel.h>
36 #include <gtk/gtkmain.h>
37 #include <gtk/gtksignal.h>
38
39 static void Gtk_ext_button_class_init (GtkExtButtonClass *klass);
40 static void Gtk_ext_button_init (GtkExtButton *button);
41
42 static void Gtk_ext_button_destroy (GtkObject *object);
43 static gint Gtk_ext_button_button_press (GtkWidget *widget,
44 GdkEventButton *event);
45 static gint Gtk_ext_button_button_release (GtkWidget *widget,
46 GdkEventButton *event);
47 static gint Gtk_ext_button_enter_notify (GtkWidget *widget,
48 GdkEventCrossing *event);
49 static gint Gtk_ext_button_leave_notify (GtkWidget *widget,
50 GdkEventCrossing *event);
51 static void Gtk_ext_button_menu_hidden (GtkExtButton *ext_button);
52
53 static gint clicked_signals[3];
54
55 /*
56 * Create a new GtkExtButton with no child.
57 */
58 GtkWidget *a_Gtk_ext_button_new ()
59 {
60 return gtk_type_new (a_Gtk_ext_button_get_type ());
61 }
62
63 /*
64 * Create a new GtkExtButton with a label.
65 */
66 GtkWidget *a_Gtk_ext_button_new_with_label (const gchar *label)
67 {
68 GtkWidget *button;
69 GtkWidget *label_widget;
70
71 button = a_Gtk_ext_button_new ();
72 label_widget = gtk_label_new (label);
73 gtk_misc_set_alignment (GTK_MISC (label_widget), 0.5, 0.5);
74
75 gtk_container_add (GTK_CONTAINER (button), label_widget);
76 gtk_widget_show (label_widget);
77
78 return button;
79 }
80
81 /*
82 * Standard Gtk+ function.
83 */
84 guint a_Gtk_ext_button_get_type ()
85 {
86 static gint type = 0;
87
88 if (!type) {
89 GtkTypeInfo info = {
90 "GtkExtButton",
91 sizeof (GtkExtButton),
92 sizeof (GtkExtButtonClass),
93 (GtkClassInitFunc) Gtk_ext_button_class_init,
94 (GtkObjectInitFunc) Gtk_ext_button_init,
95 (GtkArgSetFunc)NULL,
96 (GtkArgGetFunc)NULL,
97 (GtkClassInitFunc)NULL
98 };
99 type = gtk_type_unique (gtk_button_get_type (), &info);
100 }
101
102 return type;
103 }
104
105 /*
106 * Standard Gtk+ function.
107 */
108 static void Gtk_ext_button_class_init(GtkExtButtonClass *klass)
109 {
110 GtkObjectClass *object_class;
111 GtkWidgetClass *widget_class;
112
113 object_class = (GtkObjectClass*) klass;
114 object_class->destroy = Gtk_ext_button_destroy;
115
116 clicked_signals[0] =
117 gtk_signal_new ("clicked1",
118 GTK_RUN_FIRST | GTK_RUN_ACTION,
119 object_class->type,
120 GTK_SIGNAL_OFFSET (GtkExtButtonClass, clicked1),
121 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
122 clicked_signals[1] =
123 gtk_signal_new ("clicked2",
124 GTK_RUN_FIRST | GTK_RUN_ACTION,
125 object_class->type,
126 GTK_SIGNAL_OFFSET (GtkExtButtonClass, clicked2),
127 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
128 clicked_signals[2] =
129 gtk_signal_new ("clicked3",
130 GTK_RUN_FIRST | GTK_RUN_ACTION,
131 object_class->type,
132 GTK_SIGNAL_OFFSET (GtkExtButtonClass, clicked3),
133 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
134 gtk_object_class_add_signals (object_class, clicked_signals, 3);
135
136 widget_class = (GtkWidgetClass*)klass;
137 widget_class->button_press_event = Gtk_ext_button_button_press;
138 widget_class->button_release_event = Gtk_ext_button_button_release;
139 widget_class->enter_notify_event = Gtk_ext_button_enter_notify;
140 widget_class->leave_notify_event = Gtk_ext_button_leave_notify;
141 }
142
143
144 /*
145 * Standard Gtk+ function.
146 */
147 static void Gtk_ext_button_init(GtkExtButton *button)
148 {
149 int i;
150
151 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
152
153 button->pressed_button = 0;
154 button->active_menu = NULL;
155 button->menu_signal_id = -1;
156
157 for(i = 0; i < 5; i++)
158 button->action[i] = GTK_EXT_BUTTON_INACTIVE;
159 }
160
161
162 /*
163 * Standard Gtk+ function.
164 */
165 static void Gtk_ext_button_destroy (GtkObject *object)
166 {
167 GtkExtButton *button;
168
169 button = GTK_EXT_BUTTON (object);
170 if (button->menu_signal_id != -1)
171 gtk_signal_disconnect (GTK_OBJECT (button->active_menu),
172 button->menu_signal_id);
173 }
174
175
176 /*
177 * This is used for gtk_menu_popup, to position the menu.
178 */
179 static void Gtk_ext_button_position_menu (GtkMenu *menu,
180 gint *x,
181 gint *y,
182 gpointer user_data)
183 {
184 GtkWidget *widget;
185 GtkRequisition requisition;
186
187 widget = GTK_WIDGET (user_data);
188 gdk_window_get_origin (widget->window, x, y);
189
190 gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
191 if (*y + widget->allocation.height + requisition.height
192 > gdk_screen_height())
193 /* Show menu above button, since there is not enough space below. */
194 *y -= requisition.height;
195 else
196 /* Show menu below button. */
197 *y += widget->allocation.height;
198
199 /* If the menu does not fit horizontilly, adjust position. */
200 if (*x + requisition.width > gdk_screen_width ())
201 *x = gdk_screen_width () - requisition.width;
202 }
203
204
205 /*
206 * Standard Gtk+ function.
207 */
208 static gint Gtk_ext_button_button_press (GtkWidget *widget,
209 GdkEventButton *event)
210 {
211 GtkButton *button;
212 GtkExtButton *ext_button;
213 GtkStateType new_state;
214 GtkMenu *menu;
215
216 button = GTK_BUTTON (widget);
217 ext_button = GTK_EXT_BUTTON (widget);
218
219 if (ext_button->pressed_button) {
220 /* Already a button pressed. */
221 return FALSE;
222 } else if (event->button >= 1 && event->button <= 3 &&
223 ext_button->action[event->button - 1]
224 != GTK_EXT_BUTTON_INACTIVE) {
225 ext_button->pressed_button = event->button;
226
227 gtk_grab_add (widget);
228 button->button_down = TRUE;
229 new_state = (button->in_button ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL);
230
231 if (GTK_WIDGET_STATE (button) != new_state) {
232 gtk_widget_set_state (GTK_WIDGET (button), new_state);
233 gtk_widget_queue_draw (GTK_WIDGET (button));
234 }
235
236 menu = NULL;
237
238 switch(ext_button->action[event->button - 1]) {
239 case GTK_EXT_BUTTON_INACTIVE:
240 g_assert_not_reached ();
241 break;
242
243 case GTK_EXT_BUTTON_COMMAND:
244 /* Nothing to do anymore. */
245 break;
246
247 case GTK_EXT_BUTTON_MENU:
248 menu = ext_button->action_data[event->button - 1].menu;
249 break;
250
251 case GTK_EXT_BUTTON_MENU_CREATOR:
252 menu =
253 ext_button->action_data[event->button - 1].creator.func (
254 ext_button,
255 ext_button->action_data[event->button - 1].creator.data);
256 break;
257 }
258
259 if (menu) {
260 ext_button->active_menu = menu;
261 gtk_menu_popup (menu, NULL, widget, Gtk_ext_button_position_menu,
262 widget, event->button, event->time);
263 ext_button->menu_signal_id =
264 gtk_signal_connect_object (GTK_OBJECT (menu), "hide",
265 GTK_SIGNAL_FUNC (
266 Gtk_ext_button_menu_hidden),
267 (gpointer) button);
268 }
269
270 return TRUE;
271 } else
272 return FALSE;
273 }
274
275
276 /*
277 * Standard Gtk+ function.
278 */
279 static gint Gtk_ext_button_button_release (GtkWidget *widget,
280 GdkEventButton *event)
281 {
282 GtkButton *button;
283 GtkExtButton *ext_button;
284 GtkStateType new_state;
285
286 ext_button = GTK_EXT_BUTTON (widget);
287
288 /* Only react on the button initially pressed. */
289 if (event->button == ext_button->pressed_button) {
290 button = GTK_BUTTON (widget);
291
292 if (button->in_button)
293 gtk_signal_emit (GTK_OBJECT (widget),
294 clicked_signals[event->button - 1]);
295
296 gtk_grab_remove (widget);
297
298 button->button_down = FALSE;
299 ext_button->pressed_button = 0;
300 new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
301
302 if (GTK_WIDGET_STATE (button) != new_state) {
303 gtk_widget_set_state (GTK_WIDGET (button), new_state);
304 /* We _draw () instead of queue_draw so that if the operation
305 * blocks, the label doesn't vanish.
306 */
307 gtk_widget_draw (GTK_WIDGET (button), NULL);
308 }
309 }
310
311 return TRUE;
312 }
313
314
315 /*
316 * Standard Gtk+ function.
317 */
318 static gint Gtk_ext_button_enter_notify (GtkWidget *widget,
319 GdkEventCrossing *event)
320 {
321 GtkButton *button;
322 GtkWidget *event_widget;
323 GtkStateType new_state;
324
325 button = GTK_BUTTON (widget);
326 event_widget = gtk_get_event_widget ((GdkEvent*) event);
327
328 if ((event_widget == widget) && (event->detail != GDK_NOTIFY_INFERIOR)) {
329 button->in_button = TRUE;
330 new_state =
331 (button->button_down ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT);
332
333 if (GTK_WIDGET_STATE (button) != new_state) {
334 gtk_widget_set_state (GTK_WIDGET (button), new_state);
335 gtk_widget_queue_draw (GTK_WIDGET (button));
336 }
337 }
338
339 return FALSE;
340 }
341
342
343 /*
344 * Standard Gtk+ function.
345 */
346 static gint Gtk_ext_button_leave_notify (GtkWidget *widget,
347 GdkEventCrossing *event)
348 {
349 GtkButton *button;
350 GtkExtButton *ext_button;
351 GtkWidget *event_widget;
352
353 button = GTK_BUTTON (widget);
354 event_widget = gtk_get_event_widget ((GdkEvent*) event);
355
356 if ((event_widget == widget) &&
357 (event->detail != GDK_NOTIFY_INFERIOR)) {
358 button->in_button = FALSE;
359
360 ext_button = GTK_EXT_BUTTON (widget);
361
362 /* If a men is attached for this mouse button, we keep the button widget
363 * inset. */
364 if (ext_button->pressed_button == 0 ||
365 ext_button->action[ext_button->pressed_button -1]
366 == GTK_EXT_BUTTON_COMMAND) {
367 /* Otherwise, normal behavior. */
368 if (GTK_WIDGET_STATE (button) != GTK_STATE_NORMAL) {
369 gtk_widget_set_state (GTK_WIDGET (button), GTK_STATE_NORMAL);
370 gtk_widget_queue_draw (GTK_WIDGET (button));
371 }
372 }
373 }
374
375 return FALSE;
376 }
377
378 /*
379 * This function is called, when a popped up menu is hidden again, to reset
380 * the state of the button.
381 */
382 static void Gtk_ext_button_menu_hidden (GtkExtButton *ext_button)
383 {
384 GtkButton *button;
385
386 g_return_if_fail (ext_button->menu_signal_id != -1);
387 g_return_if_fail (ext_button->active_menu != NULL);
388 gtk_signal_disconnect (GTK_OBJECT (ext_button->active_menu),
389 ext_button->menu_signal_id);
390 ext_button->menu_signal_id = -1;
391 ext_button->active_menu = NULL;
392
393 gtk_grab_remove (GTK_WIDGET (ext_button));
394
395 button = GTK_BUTTON (ext_button);
396 button->in_button = FALSE;
397 button->button_down = FALSE;
398 ext_button->pressed_button = 0;
399
400 gtk_widget_set_state (GTK_WIDGET (button), GTK_STATE_NORMAL);
401 gtk_widget_queue_draw (GTK_WIDGET (button));
402 }
403
404
405 /*
406 * Set no action for the specific mouse button. This is the default.
407 */
408 void a_Gtk_ext_button_set_inactive (GtkExtButton *button,
409 gint button_no)
410 {
411 g_return_if_fail (button_no >= 1 && button_no <= 3);
412 button->action[button_no - 1] = GTK_EXT_BUTTON_INACTIVE;
413 }
414
415
416 /*
417 * Make the button behave like normal command button.
418 */
419 void a_Gtk_ext_button_set_command (GtkExtButton *button,
420 gint button_no)
421 {
422 g_return_if_fail (button_no >= 1 && button_no <= 3);
423 button->action[button_no - 1] = GTK_EXT_BUTTON_COMMAND;
424 }
425
426
427 /*
428 * Attach a fixed menu to the button, which is popped up, when the user
429 * presses the respective button.
430 */
431 void a_Gtk_ext_button_attach_menu (GtkExtButton *button,
432 gint button_no,
433 GtkMenu *menu)
434 {
435 g_return_if_fail (button_no >= 1 && button_no <= 3);
436 button->action[button_no - 1] = GTK_EXT_BUTTON_MENU;
437 button->action_data[button_no - 1].menu = menu;
438 }
439
440 /*
441 * Attach a dynamically created menu to the button, which is popped up, when
442 * the user presses the respective button.
443 *
444 * When the respective button has been pressed, the creator function is called
445 * with the button, and the value of the argument data, the return value must
446 * be the menu, which is then popped up.
447 */
448 void a_Gtk_ext_button_attach_menu_creator (GtkExtButton *button,
449 gint button_no,
450 GtkExtButtonMenuCreator *creator,
451 gpointer data)
452 {
453 g_return_if_fail (button_no >= 1 && button_no <= 3);
454 button->action[button_no - 1] = GTK_EXT_BUTTON_MENU_CREATOR;
455 button->action_data[button_no - 1].creator.func = creator;
456 button->action_data[button_no - 1].creator.data = data;
457 }
+0
-90
src/gtk_ext_button.h less more
0 #ifndef __GTK_EXT_BUTTON_H__
1 #define __GTK_EXT_BUTTON_H__
2
3 #include <gtk/gtkbutton.h>
4 #include <gtk/gtkmenu.h>
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
9
10
11 #define GTK_EXT_BUTTON(obj) (GTK_CHECK_CAST ((obj), \
12 a_Gtk_ext_button_get_type (), \
13 GtkExtButton))
14 #define GTK_EXT_BUTTON_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), \
15 a_Gtk_ext_button_get_type (), \
16 GtkExtButtonClass))
17 #define GTK_IS_EXT_BUTTON(obj) (GTK_CHECK_TYPE ((obj), \
18 a_Gtk_ext_button_get_type ()))
19
20
21 typedef struct _GtkExtButton GtkExtButton;
22 typedef struct _GtkExtButtonClass GtkExtButtonClass;
23
24 typedef GtkMenu* (GtkExtButtonMenuCreator)(GtkExtButton *button,
25 gpointer data);
26
27 struct _GtkExtButton
28 {
29 GtkButton button;
30
31 /*
32 * What actually happens, when the user presses on the specific button.
33 */
34 enum {
35 GTK_EXT_BUTTON_INACTIVE, /* Nothing happens. */
36 GTK_EXT_BUTTON_COMMAND, /* Behaves like a normal command button. */
37 GTK_EXT_BUTTON_MENU, /* Pops up a fixed menu. */
38 GTK_EXT_BUTTON_MENU_CREATOR /* Pops up a menu, which may be created by
39 * a passed function. */
40 } action[3];
41
42 union
43 {
44 GtkMenu *menu;
45 struct
46 {
47 GtkExtButtonMenuCreator *func;
48 gpointer data;
49 } creator;
50 } action_data[3];
51
52 gint menu_signal_id;
53 guint pressed_button;
54 GtkMenu *active_menu;
55 };
56
57 struct _GtkExtButtonClass
58 {
59 GtkButtonClass parent_class;
60
61 void (*clicked1) (GtkButton *button);
62 void (*clicked2) (GtkButton *button);
63 void (*clicked3) (GtkButton *button);
64 };
65
66
67 guint a_Gtk_ext_button_get_type (void);
68 GtkWidget *a_Gtk_ext_button_new (void);
69 GtkWidget *a_Gtk_ext_button_new_with_label (const gchar *label);
70
71 void a_Gtk_ext_button_set_inactive (GtkExtButton *button,
72 gint button_no);
73 void a_Gtk_ext_button_set_command (GtkExtButton *button,
74 gint button_no);
75 void a_Gtk_ext_button_attach_menu (GtkExtButton *button,
76 gint button_no,
77 GtkMenu *menu);
78 void a_Gtk_ext_button_attach_menu_creator (GtkExtButton *button,
79 gint button_no,
80 GtkExtButtonMenuCreator
81 *creator,
82 gpointer data);
83
84 #ifdef __cplusplus
85 }
86 #endif /* __cplusplus */
87
88
89 #endif /* __GTK_EXT_BUTTON_H__ */
+0
-170
src/gtk_ext_menu.c less more
0 /*
1 * File: selection.c
2 *
3 * Copyright 2004 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This widget, which is a variant of GtkMenu, works together with
13 * GtkExtMenuItem. Together, they add the possibility to react on different
14 * mouse buttons, for activating the menu items.
15 *
16 * If an item is activated by the mouse, the signals "activate1", "activate2",
17 * and "activate3" are emitted. The signal "activate" should still be regarded,
18 * since it is emitted (by the base classes), when an item was activated by
19 * other input devices, e.g. the keyboard.
20 *
21 * Some notes about the implementation: Normally, mouse events are handled
22 * by GtkMenu, not by GtkMenuItem. GtkMenu will call gtk_widget_activate(),
23 * which emits the activate signal. For GtkMenuItem, this is "activate".
24 *
25 * GtkExtMenu changes dealing with release events a bit: if the affected
26 * menu item is a GtkExtMenuItem, it is told to handle this event specially.
27 * The GtkExtMenuItem will simply change its activate signal temporally.
28 *
29 * (This is a bit ugly, but still cleaner than listening to the low-level
30 * events, like "button-release-event".)
31 */
32
33 #include "gtk_ext_menu.h"
34 #include "gtk_ext_menu_item.h"
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkmain.h>
37 #include <gtk/gtksignal.h>
38
39 static void Gtk_ext_menu_class_init (GtkExtMenuClass *klass);
40 static void Gtk_ext_menu_init (GtkExtMenu *menu);
41
42 static gint Gtk_ext_menu_button_release (GtkWidget *widget,
43 GdkEventButton *event);
44
45 static GtkMenuClass *parent_class = NULL;
46
47
48 /*
49 * Standard Gtk+ function.
50 */
51 GtkType a_Gtk_ext_menu_get_type (void)
52 {
53 static GtkType type = 0;
54
55 if (!type) {
56 GtkTypeInfo info = {
57 "GtkExtMenu",
58 sizeof (GtkExtMenu),
59 sizeof (GtkExtMenuClass),
60 (GtkClassInitFunc) Gtk_ext_menu_class_init,
61 (GtkObjectInitFunc) Gtk_ext_menu_init,
62 (GtkArgSetFunc) NULL,
63 (GtkArgGetFunc) NULL,
64 (GtkClassInitFunc)NULL
65 };
66
67 type = gtk_type_unique (gtk_menu_get_type (), &info);
68 }
69
70 return type;
71 }
72
73
74 /*
75 * Standard Gtk+ function.
76 */
77 static void Gtk_ext_menu_class_init (GtkExtMenuClass *klass)
78 {
79 GtkWidgetClass *gtk_widget_class;
80
81 parent_class = gtk_type_class (gtk_menu_get_type ());
82
83 gtk_widget_class = (GtkWidgetClass*) klass;
84 gtk_widget_class->button_release_event = Gtk_ext_menu_button_release;
85 }
86
87
88 /*
89 * Standard Gtk+ function.
90 */
91 static void Gtk_ext_menu_init (GtkExtMenu *menu)
92 {
93 }
94
95
96 /*
97 * Create a new GtkExtMenu.
98 */
99 GtkWidget* a_Gtk_ext_menu_new (void)
100 {
101 return gtk_type_new (a_Gtk_ext_menu_get_type ());
102 }
103
104
105 /*
106 * Copied from Gtk+ 1.2.10, file "menu_shell.c", function
107 * gtk_menu_shell_is_item.
108 */
109 static gint Gtk_ext_menu_is_item (GtkMenuShell *menu_shell,
110 GtkWidget *child)
111 {
112 GtkWidget *parent;
113
114 g_return_val_if_fail (menu_shell != NULL, FALSE);
115 g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
116 g_return_val_if_fail (child != NULL, FALSE);
117
118 parent = child->parent;
119 while (parent && GTK_IS_MENU_SHELL (parent)) {
120 if (parent == (GtkWidget*) menu_shell)
121 return TRUE;
122 parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
123 }
124
125 return FALSE;
126 }
127
128 /*
129 * Copied from Gtk+ 1.2.10, file "menu_shell.c", function
130 * gtk_menu_shell_get_item.
131 */
132 static GtkWidget* Gtk_ext_menu_get_item (GtkMenuShell *menu_shell,
133 GdkEvent *event)
134 {
135 GtkWidget *menu_item;
136
137 menu_item = gtk_get_event_widget ((GdkEvent*) event);
138
139 while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
140 menu_item = menu_item->parent;
141
142 if (menu_item && Gtk_ext_menu_is_item (menu_shell, menu_item))
143 return menu_item;
144 else
145 return NULL;
146 }
147
148
149 /*
150 * Standard Gtk+ function.
151 */
152 static gint Gtk_ext_menu_button_release (GtkWidget *widget,
153 GdkEventButton *event)
154 {
155 GtkWidget *menu_item;
156 gint return_value;
157
158 menu_item = Gtk_ext_menu_get_item (GTK_MENU_SHELL (widget),
159 (GdkEvent*) event);
160 if (menu_item != NULL && GTK_IS_EXT_MENU_ITEM (menu_item))
161 p_Gtk_ext_menu_item_prepare_button_release (GTK_EXT_MENU_ITEM(menu_item),
162 event);
163 return_value =
164 GTK_WIDGET_CLASS(parent_class)->button_release_event (widget, event);
165 if (menu_item != NULL && GTK_IS_EXT_MENU_ITEM (menu_item))
166 p_Gtk_ext_menu_item_finish_button_release (GTK_EXT_MENU_ITEM (menu_item),
167 event);
168 return return_value;
169 }
+0
-39
src/gtk_ext_menu.h less more
0 #ifndef __GTK_EXT_MENU_H__
1 #define __GTK_EXT_MENU_H__
2
3 #include <gtk/gtkmenu.h>
4
5 #ifdef __cplusplus
6 extern "C" {
7
8 #endif /* __cplusplus */
9
10 #define GTK_EXT_MENU(obj) (GTK_CHECK_CAST ((obj), \
11 a_Gtk_ext_menu_get_type (), GtkExtMenu))
12 #define GTK_EXT_MENU_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), \
13 a_Gtk_ext_menu_get_type (), \
14 GtkExtMenuClass))
15 #define GTK_IS_EXT_MENU(obj) (GTK_CHECK_TYPE ((obj), \
16 a_Gtk_ext_menu_get_type ()))
17
18 typedef struct _GtkExtMenu GtkExtMenu;
19 typedef struct _GtkExtMenuClass GtkExtMenuClass;
20
21 struct _GtkExtMenu
22 {
23 GtkMenu menu;
24 };
25
26 struct _GtkExtMenuClass
27 {
28 GtkMenuClass parent_class;
29 };
30
31 GtkType a_Gtk_ext_menu_get_type (void);
32 GtkWidget* a_Gtk_ext_menu_new (void);
33
34 #ifdef __cplusplus
35 }
36 #endif /* __cplusplus */
37
38 #endif /* __GTK_EXT_MENU_H__ */
+0
-144
src/gtk_ext_menu_item.c less more
0 /*
1 * File: selection.c
2 *
3 * Copyright 2004 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * See comments at the beginning of "gtk_ext_menu.c".
13 */
14
15 #include "gtk_ext_menu_item.h"
16 #include <gtk/gtksignal.h>
17 #include <gtk/gtkaccellabel.h>
18
19 static void Gtk_ext_menu_item_class_init (GtkExtMenuItemClass *klass);
20 static void Gtk_ext_menu_item_init (GtkExtMenuItem *item);
21
22 static gint std_activate_signal;
23 static gint activate_signals[3];
24
25 /*
26 * Standard Gtk+ function.
27 */
28 GtkType a_Gtk_ext_menu_item_get_type (void)
29 {
30 static GtkType type = 0;
31
32 if (!type) {
33 GtkTypeInfo info = {
34 "GtkExtMenuItem",
35 sizeof (GtkExtMenuItem),
36 sizeof (GtkExtMenuItemClass),
37 (GtkClassInitFunc) Gtk_ext_menu_item_class_init,
38 (GtkObjectInitFunc) Gtk_ext_menu_item_init,
39 (GtkArgSetFunc) NULL,
40 (GtkArgGetFunc) NULL,
41 (GtkClassInitFunc)NULL
42 };
43
44 type = gtk_type_unique (gtk_menu_item_get_type (), &info);
45 }
46
47 return type;
48 }
49
50 /*
51 * Standard Gtk+ function.
52 */
53 static void Gtk_ext_menu_item_class_init (GtkExtMenuItemClass *klass)
54 {
55 GtkObjectClass *object_class;
56
57 object_class = (GtkObjectClass*) klass;
58 activate_signals[0] =
59 gtk_signal_new ("activate1",
60 GTK_RUN_FIRST | GTK_RUN_ACTION,
61 object_class->type,
62 GTK_SIGNAL_OFFSET (GtkExtMenuItemClass, activate1),
63 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
64 activate_signals[1] =
65 gtk_signal_new ("activate2",
66 GTK_RUN_FIRST | GTK_RUN_ACTION,
67 object_class->type,
68 GTK_SIGNAL_OFFSET (GtkExtMenuItemClass, activate2),
69 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
70 activate_signals[2] =
71 gtk_signal_new ("activate3",
72 GTK_RUN_FIRST | GTK_RUN_ACTION,
73 object_class->type,
74 GTK_SIGNAL_OFFSET (GtkExtMenuItemClass, activate3),
75 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
76 gtk_object_class_add_signals (object_class, activate_signals, 3);
77
78 klass->activate1 = NULL;
79 klass->activate2 = NULL;
80 klass->activate3 = NULL;
81
82 std_activate_signal =
83 GTK_WIDGET_CLASS(gtk_type_class (gtk_menu_item_get_type ()))
84 ->activate_signal;
85 }
86
87
88 /*
89 * Standard Gtk+ function.
90 */
91 static void Gtk_ext_menu_item_init (GtkExtMenuItem *item)
92 {
93 }
94
95 /*
96 * Create a new, empty GtkExtMenuItem.
97 */
98 GtkWidget *a_Gtk_ext_menu_item_new (void)
99 {
100 return gtk_type_new (a_Gtk_ext_menu_item_get_type ());
101 }
102
103 /*
104 * Create a GtkExtMenuItem with a label as child.
105 */
106 GtkWidget *a_Gtk_ext_menu_item_new_with_label (const gchar *label)
107 {
108 GtkWidget *menu_item;
109 GtkWidget *accel_label;
110
111 menu_item = a_Gtk_ext_menu_item_new ();
112 accel_label = gtk_accel_label_new (label);
113 gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
114
115 gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
116 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
117 gtk_widget_show (accel_label);
118
119 return menu_item;
120 }
121
122 /*
123 * This method is called by GtkExtMenu, before handling a button release event.
124 * See comments there.
125 */
126 void p_Gtk_ext_menu_item_prepare_button_release (GtkExtMenuItem *item,
127 GdkEventButton *event)
128 {
129 if (event->button >= 1 && event->button <= 3)
130 GTK_WIDGET_CLASS(GTK_OBJECT(item)->klass)->activate_signal =
131 activate_signals[event->button - 1];
132 }
133
134 /*
135 * This method is called by GtkExtMenu, after handling a button release event.
136 * See comments there.
137 */
138 void p_Gtk_ext_menu_item_finish_button_release (GtkExtMenuItem *item,
139 GdkEventButton *event)
140 {
141 GTK_WIDGET_CLASS(GTK_OBJECT(item)->klass)->activate_signal =
142 std_activate_signal;
143 }
+0
-50
src/gtk_ext_menu_item.h less more
0 #ifndef __GTK_EXT_MENU_ITEM_H__
1 #define __GTK_EXT_MENU_ITEM_H__
2
3 #include <gtk/gtkmenuitem.h>
4
5 #ifdef __cplusplus
6 extern "C" {
7
8 #endif /* __cplusplus */
9
10 #define GTK_EXT_MENU_ITEM(obj) (GTK_CHECK_CAST ((obj), \
11 a_Gtk_ext_menu_item_get_type (), \
12 GtkExtMenuItem))
13 #define GTK_EXT_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), \
14 a_Gtk_ext_menu_item_get_type (), \
15 GtkExtMenuItemClass))
16 #define GTK_IS_EXT_MENU_ITEM(obj) (GTK_CHECK_TYPE ((obj), \
17 a_Gtk_ext_menu_item_get_type ()))
18
19 typedef struct _GtkExtMenuItem GtkExtMenuItem;
20 typedef struct _GtkExtMenuItemClass GtkExtMenuItemClass;
21
22 struct _GtkExtMenuItem
23 {
24 GtkMenuItem menu_item;
25 };
26
27 struct _GtkExtMenuItemClass
28 {
29 GtkMenuItemClass parent_class;
30
31 void (*activate1) (GtkExtMenuItem *menu_item);
32 void (*activate2) (GtkExtMenuItem *menu_item);
33 void (*activate3) (GtkExtMenuItem *menu_item);
34 };
35
36 GtkType a_Gtk_ext_menu_item_get_type (void);
37 GtkWidget* a_Gtk_ext_menu_item_new (void);
38 GtkWidget* a_Gtk_ext_menu_item_new_with_label (const gchar *label);
39
40 void p_Gtk_ext_menu_item_prepare_button_release (GtkExtMenuItem *item,
41 GdkEventButton *event);
42 void p_Gtk_ext_menu_item_finish_button_release (GtkExtMenuItem *item,
43 GdkEventButton *event);
44
45 #ifdef __cplusplus
46 }
47 #endif /* __cplusplus */
48
49 #endif /* __GTK_EXT_MENU_ITEM_H__ */
+0
-196
src/gtk_menu_title.c less more
0 /*
1 * File: selection.c
2 *
3 * Copyright 2004 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * This is a widget with a very special purpose, for displaying titles within
13 * menues. It is set to inactive (so it cannot be focussed), and the text is
14 * centered horizontally.
15 *
16 * BTW, it is simple to change the look of menu titles, by putting the
17 * following into your ~/.gtkrc file:
18 *
19 * style "menu-title" = "default" {
20 * font = "-*-helvetica-medium-o-*-*-10-*-*-*-*-*-*-*"
21 * }
22 *
23 * widget "*GtkMenuTitle" style "menu-title"
24 */
25 #include "gtk_menu_title.h"
26
27 #define BORDER_SPACING 3
28
29 static void Gtk_menu_title_class_init (GtkMenuTitleClass *klass);
30 static void Gtk_menu_title_init (GtkMenuTitle *menu_title);
31
32 static void Gtk_menu_title_destroy (GtkObject *object);
33 static void Gtk_menu_title_size_request (GtkWidget *widget,
34 GtkRequisition *requisition);
35 static void Gtk_menu_title_draw (GtkWidget *widget,
36 GdkRectangle *area);
37 static gint Gtk_menu_title_expose_event (GtkWidget *widget,
38 GdkEventExpose *event);
39
40 static GtkMenuItemClass *parent_class = NULL;
41
42
43 /*
44 * Standard Gtk+ function.
45 */
46 GtkType a_Gtk_menu_title_get_type (void)
47 {
48 static GtkType type = 0;
49
50 if (!type) {
51 GtkTypeInfo info = {
52 "GtkMenuTitle",
53 sizeof (GtkMenuTitle),
54 sizeof (GtkMenuTitleClass),
55 (GtkClassInitFunc) Gtk_menu_title_class_init,
56 (GtkObjectInitFunc) Gtk_menu_title_init,
57 (GtkArgSetFunc) NULL,
58 (GtkArgGetFunc) NULL,
59 (GtkClassInitFunc)NULL
60 };
61
62 type = gtk_type_unique (gtk_menu_item_get_type (), &info);
63 }
64
65 return type;
66 }
67
68
69 /*
70 * Standard Gtk+ function.
71 */
72 static void Gtk_menu_title_class_init (GtkMenuTitleClass *klass)
73 {
74 GtkWidgetClass *widget_class;
75 GtkObjectClass *object_class;
76
77 parent_class = (GtkMenuItemClass*) klass;
78
79 object_class = (GtkObjectClass*) klass;
80 object_class->destroy = Gtk_menu_title_destroy;
81
82 widget_class = (GtkWidgetClass*) klass;
83 widget_class->size_request = Gtk_menu_title_size_request;
84 widget_class->draw = Gtk_menu_title_draw;
85 widget_class->expose_event = Gtk_menu_title_expose_event;
86 }
87
88
89 /*
90 * Standard Gtk+ function.
91 */
92 static void Gtk_menu_title_init (GtkMenuTitle *menu_title)
93 {
94 menu_title->label = NULL;
95 }
96
97
98 /*
99 * Return a new GtkMenuTitle.
100 */
101 GtkWidget* a_Gtk_menu_title_new (const char *label)
102 {
103 GtkWidget *widget;
104
105 widget = gtk_type_new (a_Gtk_menu_title_get_type ());
106 GTK_MENU_TITLE(widget)->label = label ? g_strdup (label) : NULL;
107 gtk_widget_set_sensitive (widget, FALSE);
108 return widget;
109 }
110
111
112 /*
113 * Standard Gtk+ function.
114 */
115 static void Gtk_menu_title_destroy (GtkObject *object)
116 {
117 GtkMenuTitle *menu_title;
118
119 menu_title = GTK_MENU_TITLE (object);
120 if (menu_title->label)
121 g_free (menu_title->label);
122 }
123
124
125 /*
126 * Standard Gtk+ function.
127 */
128 static void Gtk_menu_title_size_request (GtkWidget *widget,
129 GtkRequisition *requisition)
130 {
131 GtkMenuTitle *menu_title;
132
133 menu_title = GTK_MENU_TITLE (widget);
134 requisition->width = 2 * (GTK_CONTAINER (widget)->border_width +
135 widget->style->klass->xthickness +
136 BORDER_SPACING);
137 requisition->height = 2 * (GTK_CONTAINER (widget)->border_width +
138 widget->style->klass->ythickness);
139
140 if (menu_title->label) {
141 requisition->width +=
142 gdk_string_width (widget->style->font, GTK_MENU_TITLE(widget)->label);
143 requisition->height +=
144 widget->style->font->ascent + widget->style->font->descent;
145 }
146 }
147
148 /*
149 * Standard Gtk+ function.
150 */
151 static void Gtk_menu_title_paint (GtkWidget *widget,
152 GdkRectangle *area)
153 {
154 GtkMenuTitle *menu_title;
155 gint x, y;
156
157 menu_title = GTK_MENU_TITLE (widget);
158 if (menu_title->label) {
159 x =
160 (widget->allocation.width - gdk_string_width (widget->style->font,
161 menu_title->label)) / 2;
162 y =
163 GTK_CONTAINER (widget)->border_width +
164 widget->style->klass->ythickness
165 + widget->style->font->ascent;
166
167 /* We do not use widget->state, but instead GTK_STATE_NORMAL, since
168 * otherwise, the text would be rendered gray. */
169 gdk_draw_string (widget->window, widget->style->font,
170 widget->style->fg_gc[GTK_STATE_NORMAL], x, y,
171 menu_title->label);
172 }
173 }
174
175 /*
176 * Standard Gtk+ function.
177 */
178 static void Gtk_menu_title_draw (GtkWidget *widget,
179 GdkRectangle *area)
180 {
181 if (GTK_WIDGET_DRAWABLE (widget))
182 Gtk_menu_title_paint (widget, area);
183 }
184
185
186 /*
187 * Standard Gtk+ function.
188 */
189 static gint Gtk_menu_title_expose_event (GtkWidget *widget,
190 GdkEventExpose *event)
191 {
192 if (GTK_WIDGET_DRAWABLE (widget))
193 Gtk_menu_title_paint (widget, &event->area);
194 return TRUE;
195 }
+0
-41
src/gtk_menu_title.h less more
0 #ifndef __GTK_MENU_TITLE_H__
1 #define __GTK_MENU_TITLE_H__
2
3 #include <gtk/gtkmenuitem.h>
4
5 #ifdef __cplusplus
6 extern "C" {
7
8 #endif /* __cplusplus */
9
10 #define GTK_MENU_TITLE(obj) (GTK_CHECK_CAST ((obj), \
11 a_Gtk_menu_title_get_type(), \
12 GtkMenuTitle))
13 #define GTK_MENU_TITLE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), \
14 a_Gtk_menu_title_get_type(), \
15 GtkMenuTitleClass))
16 #define GTK_IS_MENU_TITLE (GTK_CHECK_TYPE ((obj), \
17 a_Gtk_menu_title_get_type()))
18
19 typedef struct _GtkMenuTitle GtkMenuTitle;
20 typedef struct _GtkMenuTitleClass GtkMenuTitleClass;
21
22 struct _GtkMenuTitle
23 {
24 GtkMenuItem gtk_menu_item;
25 char *label;
26 };
27
28 struct _GtkMenuTitleClass
29 {
30 GtkMenuItemClass parent_class;
31 };
32
33 GtkType a_Gtk_menu_title_get_type (void);
34 GtkWidget* a_Gtk_menu_title_new (const char *label);
35
36 #ifdef __cplusplus
37 }
38 #endif /* __cplusplus */
39
40 #endif /* __GTK_MENU_TITLE_H__ */
00 /*
11 * File: history.c
22 *
3 * Copyright (C) 2001, 2002 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 2001-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
1212 * Linear history (it also provides indexes for the navigation stack)
1313 */
1414
15 #include "msg.h"
1516 #include "list.h"
1617 #include "history.h"
1718
1819
1920 typedef struct {
2021 DilloUrl *url;
21 gchar *title;
22 char *title;
2223 } H_Item;
2324
2425
2526 /* Global history list */
2627 static H_Item *history = NULL;
27 static gint history_size = 0; /* [1 based] */
28 static gint history_size_max = 16;
28 static int history_size = 0; /* [1 based] */
29 static int history_size_max = 16;
2930
31
32 /*
33 * Debug procedure.
34 */
35 void History_show()
36 {
37 int i;
38
39 MSG(" {");
40 for (i = 0; i < history_size; ++i)
41 MSG(" %s", URL_STR(history[i].url));
42 MSG(" }\n");
43 }
3044
3145 /*
3246 * Add a new H_Item at the end of the history list
3448 */
3549 int a_History_add_url(DilloUrl *url)
3650 {
37 gint i, idx;
51 int i, idx;
3852
53 _MSG("a_History_add_url: '%s' ", URL_STR(url));
3954 for (i = 0; i < history_size; ++i)
40 if (a_Url_cmp(history[i].url, url) == 0)
41 return i;
55 if (!a_Url_cmp(history[i].url, url) &&
56 !strcmp(URL_FRAGMENT(history[i].url), URL_FRAGMENT(url)))
57 break;
4258
43 idx = history_size;
44 a_List_add(history, history_size, history_size_max);
45 history[idx].url = a_Url_dup(url);
46 history[idx].title = NULL;
47 ++history_size;
59 if (i < history_size) {
60 idx = i;
61 _MSG("FOUND at idx=%d\n", idx);
62 } else {
63 idx = history_size;
64 a_List_add(history, history_size, history_size_max);
65 history[idx].url = a_Url_dup(url);
66 history[idx].title = NULL;
67 ++history_size;
68 _MSG("ADDED at idx=%d\n", idx);
69 }
70
71 /* History_show(); */
72
4873 return idx;
4974 }
5075
5176 /*
52 * Set the page-title for a given URL (by idx)
53 * (this is known when the first chunks of HTML data arrive)
77 * Return the DilloUrl field (by index)
5478 */
55 int a_History_set_title(gint idx, const gchar *title)
79 const DilloUrl *a_History_get_url(int idx)
5680 {
57 g_return_val_if_fail(idx >= 0 && idx < history_size, 0);
81 _MSG("a_History_get_url: ");
82 /* History_show(); */
5883
59 g_free(history[idx].title);
60 history[idx].title = g_strdup(title);
61 return 1;
62 }
63
64 /*
65 * Return the DilloUrl camp (by index)
66 */
67 DilloUrl *a_History_get_url(gint idx)
68 {
69 g_return_val_if_fail(idx >= 0 && idx < history_size, NULL);
84 dReturn_val_if_fail(idx >= 0 && idx < history_size, NULL);
7085
7186 return history[idx].url;
7287 }
7388
7489 /*
75 * Return the title camp (by index)
90 * Return the title field (by index)
91 * ('force' returns URL_STR when there's no title)
7692 */
77 const gchar *a_History_get_title(gint idx)
93 const char *a_History_get_title(int idx, int force)
7894 {
79 g_return_val_if_fail(idx >= 0 && idx < history_size, NULL);
95 dReturn_val_if_fail(idx >= 0 && idx < history_size, NULL);
8096
8197 if (history[idx].title)
8298 return history[idx].title;
99 else if (force)
100 return URL_STR(history[idx].url);
83101 else
84 return URL_STR_(history[idx].url);
102 return NULL;
85103 }
86104
87105 /*
88 * Return the title camp (by url)
106 * Return the title field (by url)
89107 * ('force' returns URL_STR when there's no title)
90108 */
91 const gchar *a_History_get_title_by_url(DilloUrl *url, gint force)
109 const char *a_History_get_title_by_url(const DilloUrl *url, int force)
92110 {
93 gint i;
111 int i;
94112
95 g_return_val_if_fail(url != NULL, NULL);
113 dReturn_val_if_fail(url != NULL, NULL);
96114
97115 for (i = 0; i < history_size; ++i)
98116 if (a_Url_cmp(url, history[i].url) == 0)
105123 return NULL;
106124 }
107125
126 /*
127 * Set the page-title for a given URL
128 */
129 void a_History_set_title_by_url(const DilloUrl *url, const char *title)
130 {
131 int i;
132
133 dReturn_if (url == NULL);
134
135 for (i = history_size - 1; i >= 0; --i)
136 if (a_Url_cmp(url, history[i].url) == 0)
137 break;
138
139 if (i >= 0) {
140 dFree(history[i].title);
141 history[i].title = dStrdup(title);
142 } else {
143 MSG_ERR("a_History_set_title_by_url: %s not found\n", URL_STR(url));
144 }
145 }
146
108147
109148 /*
110149 * Free all the memory used by this module
111150 */
112 void a_History_free()
151 void a_History_freeall()
113152 {
114 gint i;
153 int i;
115154
116155 for (i = 0; i < history_size; ++i) {
117156 a_Url_free(history[i].url);
118 g_free(history[i].title);
157 dFree(history[i].title);
119158 }
120 g_free(history);
159 dFree(history);
121160 }
00
11 #ifndef __DILLO_HISTORY_H__
22 #define __DILLO_HISTORY_H__
3
4 #include <glib.h>
53
64 #include "url.h"
75
119 #endif /* __cplusplus */
1210
1311 int a_History_add_url(DilloUrl *url);
14 int a_History_set_title(gint idx, const gchar *title);
15 DilloUrl *a_History_get_url(gint idx);
16 const gchar *a_History_get_title(gint idx);
17 const gchar *a_History_get_title_by_url(DilloUrl *url, gint force);
18 void a_History_free(void);
12 void a_History_set_title_by_url(const DilloUrl *url, const char *title);
13 const DilloUrl *a_History_get_url(int idx);
14 const char *a_History_get_title(int idx, int force);
15 const char *a_History_get_title_by_url(const DilloUrl *url, int force);
16 void a_History_freeall(void);
1917
2018
2119 #ifdef __cplusplus
+0
-5218
src/html.c less more
0 /*
1 * File: html.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 James McCollough <jamesm@gtwn.net>
5 * Copyright (C) 2000-2004 Jorge Arellano Cid <jcid@dillo.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13 /*
14 * Dillo HTML parsing routines
15 */
16
17 /* Undefine if you want to unroll tables. For instance for PDAs */
18 #define USE_TABLES
19
20 /* Define to 1 to ignore white space immediately after an open tag,
21 * and immediately before a close tag. */
22 #define SGML_SPCDEL 0
23
24
25 #include <ctype.h> /* for isspace and tolower */
26 #include <string.h> /* for memcpy and memmove */
27 #include <stdlib.h>
28 #include <stdio.h> /* for sprintf */
29 #include <math.h> /* for rint */
30 #include <errno.h>
31
32 #include <gtk/gtk.h>
33
34 #include "msg.h"
35 #include "list.h"
36 #include "binaryconst.h"
37 #include "colors.h"
38 #include "dillo.h"
39 #include "history.h"
40 #include "nav.h"
41 #include "menu.h"
42 #include "commands.h"
43 #include "dw.h" /* for Dw_cursor_hand */
44
45 #include "dw_gtk_viewport.h"
46 #include "dw_gtk_scrolled_window.h"
47 #include "dw_widget.h"
48 #include "dw_page.h"
49 #include "dw_bullet.h"
50 #include "dw_button.h"
51 #include "dw_hruler.h"
52 #include "dw_embed_gtk.h"
53 #include "dw_table.h"
54 #include "dw_table_cell.h"
55 #include "dw_list_item.h"
56 #include "dw_style.h"
57 #include "interface.h"
58 #include "progressbar.h"
59 #include "prefs.h"
60 #include "misc.h"
61 #include "capi.h"
62 #include "html.h"
63
64 #define DEBUG_LEVEL 10
65 #include "debug.h"
66
67 typedef void (*TagOpenFunct) (DilloHtml *Html, char *Tag, gint Tagsize);
68 typedef void (*TagCloseFunct) (DilloHtml *Html, gint TagIdx);
69
70 #define TAB_SIZE 8
71
72 /*
73 * Forward declarations
74 */
75 static const char *Html_get_attr(DilloHtml *html,
76 const char *tag,
77 gint tagsize,
78 const char *attrname);
79 static const char *Html_get_attr2(DilloHtml *html,
80 const char *tag,
81 gint tagsize,
82 const char *attrname,
83 DilloHtmlTagParsingFlags flags);
84 static char *Html_get_attr_wdef(DilloHtml *html,
85 const char *tag,
86 gint tagsize,
87 const char *attrname,
88 const char *def);
89 static void Html_add_widget(DilloHtml *html, DwWidget *widget,
90 char *width_str, char *height_str,
91 DwStyle *style_attrs);
92 static gint Html_write_raw(DilloHtml *html, char *buf, gint bufsize, gint Eof);
93 static void Html_write(DilloHtml *html, char *Buf, gint BufSize, gint Eof);
94 static void Html_close(DilloHtml *html, gint ClientKey);
95 static void Html_callback(int Op, CacheClient_t *Client);
96 static DilloHtml *Html_new(BrowserWindow *bw, const DilloUrl *url);
97 static void Html_tag_open_input(DilloHtml *html, char *tag, gint tagsize);
98 static void Html_add_input(DilloHtmlForm *form,
99 DilloHtmlInputType type,
100 GtkWidget *widget,
101 const char *name,
102 const char *init_str,
103 DilloHtmlSelect *select,
104 gboolean init_val);
105 static void Html_submit_form(GtkWidget *submit, DilloHtmlLB *html_lb,
106 gint click_x, gint click_y);
107 static void Html_reset_form(GtkWidget *reset, DilloHtmlLB *html_lb);
108 static gint Html_tag_index(char *tag);
109
110 /* exported function */
111 DwWidget *a_Html_text(const char *Type, void *P, CA_Callback_t *Call,
112 void **Data);
113
114
115 /*
116 * Local Data
117 */
118
119 /* The following array of font sizes has to be _strictly_ crescent */
120 static const gint FontSizes[] = {8, 10, 12, 14, 18, 24};
121 static const gint FontSizesNum = 6;
122 static const gint FontSizesBase = 2;
123
124 /* Parsing table structure */
125 typedef struct {
126 gchar *name; /* element name */
127 unsigned char Flags; /* flags (explained near the table data) */
128 gchar EndTag; /* Is it Required, Optional or Forbidden */
129 guchar TagLevel; /* Used to heuristically parse bad HTML */
130 TagOpenFunct open; /* Open function */
131 TagCloseFunct close; /* Close function */
132 } TagInfo;
133 static const TagInfo Tags[];
134
135 /*
136 * Return the line number of the tag being processed by the parser.
137 */
138 static gint Html_get_line_number(DilloHtml *html)
139 {
140 gint i, ofs, line;
141 const char *p = html->Start_Buf;
142
143 g_return_val_if_fail(p != NULL, -1);
144
145 ofs = html->CurrTagOfs;
146 line = html->OldTagLine;
147 for (i = html->OldTagOfs; i < ofs; ++i)
148 if (p[i] == '\n')
149 ++line;
150 html->OldTagOfs = html->CurrTagOfs;
151 html->OldTagLine = line;
152 return line;
153 }
154
155 /*
156 * Collect HTML error strings inside the linkblock.
157 */
158 static void Html_msg(DilloHtml *html, const char *format, ... )
159 {
160 va_list argp;
161 gchar buf[512];
162
163 g_snprintf(buf, 512, "HTML warning: line %d, ",
164 Html_get_line_number(html));
165 g_string_append(html->linkblock->page_bugs, buf);
166 va_start(argp, format);
167 g_vsnprintf(buf, 512, format, argp);
168 va_end(argp);
169 g_string_append(html->linkblock->page_bugs, buf);
170 a_Interface_bug_meter_update(html->bw,
171 ++html->linkblock->num_page_bugs);
172 }
173
174 /*
175 * Wrapper for a_Url_new that adds an error detection message.
176 * (if use_base_url is TRUE, html->linkblock->base_url is used)
177 */
178 static DilloUrl *Html_url_new(DilloHtml *html,
179 const gchar *url_str, const gchar *base_url,
180 gint flags, gint32 posx, gint32 posy,
181 gint use_base_url)
182 {
183 DilloUrl *url;
184 gint n_ic, n_ic_spc;
185
186 url = a_Url_new(
187 url_str,
188 (use_base_url) ? base_url : URL_STR_(html->linkblock->base_url),
189 flags, posx, posy);
190 if ((n_ic = URL_ILLEGAL_CHARS(url)) != 0) {
191 const char *suffix = (n_ic) > 1 ? "s" : "";
192 n_ic_spc = URL_ILLEGAL_CHARS_SPC(url);
193 if (n_ic == n_ic_spc) {
194 MSG_HTML("URL has %d illegal character%s [%d space%s]\n",
195 n_ic, suffix, n_ic_spc, suffix);
196 } else if (n_ic_spc == 0) {
197 MSG_HTML("URL has %d illegal character%s [%d in (00-1F or 7F)]\n",
198 n_ic, suffix, n_ic);
199 } else {
200 MSG_HTML("URL has %d illegal character%s "
201 "[%d space%s and %d in (00-1F or 7F)]\n",
202 n_ic, suffix, n_ic_spc, n_ic_spc ? "s" : "", n_ic-n_ic_spc);
203 }
204 }
205 return url;
206 }
207
208 /*
209 * Set callback function and callback data for "html/text" MIME type.
210 */
211 DwWidget *a_Html_text(const char *Type, void *P, CA_Callback_t *Call,
212 void **Data)
213 {
214 DilloWeb *web = P;
215 DilloHtml *html = Html_new(web->bw, web->url);
216
217 *Data = (void *) html;
218 *Call = (CA_Callback_t) Html_callback;
219
220 return html->dw;
221 }
222
223 /*
224 * We'll make the linkblock first to get it out of the way.
225 */
226 static DilloHtmlLB *Html_lb_new(BrowserWindow *bw, const DilloUrl *url)
227 {
228 DilloHtmlLB *html_lb = g_new(DilloHtmlLB, 1);
229
230 html_lb->bw = bw;
231 html_lb->base_url = a_Url_dup(url);
232 html_lb->num_forms_max = 1;
233 html_lb->num_forms = 0;
234 html_lb->forms = NULL;
235
236 html_lb->num_links_max = 1;
237 html_lb->num_links = 0;
238 html_lb->links = NULL;
239 a_Dw_image_map_list_init(&html_lb->maps);
240
241 html_lb->link_color = prefs.link_color;
242 html_lb->visited_color = prefs.visited_color;
243
244 html_lb->num_page_bugs = 0;
245 html_lb->page_bugs = g_string_new("");
246
247 return html_lb;
248 }
249
250 /*
251 * Free the memory used by the linkblock
252 */
253 static void Html_lb_free(void *lb)
254 {
255 gint i, j, k;
256 DilloHtmlForm *form;
257 DilloHtmlLB *html_lb = lb;
258
259 DEBUG_MSG(3, "Html_lb_free\n");
260
261 a_Url_free(html_lb->base_url);
262
263 for (i = 0; i < html_lb->num_forms; i++) {
264 form = &html_lb->forms[i];
265 a_Url_free(form->action);
266 for (j = 0; j < form->num_inputs; j++) {
267 g_free(form->inputs[j].name);
268 g_free(form->inputs[j].init_str);
269
270 if (form->inputs[j].type == DILLO_HTML_INPUT_SELECT ||
271 form->inputs[j].type == DILLO_HTML_INPUT_SEL_LIST) {
272 for (k = 0; k < form->inputs[j].select->num_options; k++) {
273 g_free(form->inputs[j].select->options[k].value);
274 }
275 g_free(form->inputs[j].select->options);
276 g_free(form->inputs[j].select);
277 }
278 }
279 g_free(form->inputs);
280 }
281 g_free(html_lb->forms);
282
283 for (i = 0; i < html_lb->num_links; i++)
284 if (html_lb->links[i])
285 a_Url_free(html_lb->links[i]);
286 g_free(html_lb->links);
287
288 a_Dw_image_map_list_free(&html_lb->maps);
289
290 g_string_free(html_lb->page_bugs, TRUE);
291
292 g_free(html_lb);
293 }
294
295
296 /*
297 * Set the URL data for image maps.
298 */
299 static void Html_set_link_coordinates(DilloHtmlLB *lb,
300 gint link, gint x, gint y)
301 {
302 gchar data[64];
303
304 if (x != -1) {
305 g_snprintf(data, 64, "?%d,%d", x, y);
306 a_Url_set_ismap_coords(lb->links[link], data);
307 }
308 }
309
310 /*
311 * Handle the status function generated by the dw scroller,
312 * and show the url in the browser status-bar.
313 */
314 static void Html_handle_status(DwWidget *widget, gint link, gint x, gint y,
315 DilloHtmlLB *lb)
316 {
317 DilloUrl *url;
318
319 url = (link == -1) ? NULL : lb->links[link];
320 if (url) {
321 Html_set_link_coordinates(lb, link, x, y);
322 a_Interface_msg(lb->bw, "%s",
323 URL_ALT_(url) ? URL_ALT_(url) : URL_STR_(url));
324 a_Dw_widget_set_cursor (widget, Dw_cursor_hand);
325 lb->bw->status_is_link = 1;
326
327 } else {
328 if (lb->bw->status_is_link)
329 a_Interface_msg(lb->bw, "");
330 a_Dw_widget_set_cursor (widget, NULL);
331 }
332 }
333
334 /*
335 * Popup the link menu ("link_pressed" callback of the page)
336 */
337 static gboolean Html_link_menu(DwWidget *widget, gint link, gint x, gint y,
338 GdkEventButton *event, DilloHtmlLB *lb)
339 {
340 DwWidget *widget_at_cursor;
341 gboolean show_oi = FALSE;
342
343 if (event->button == 3) {
344 Html_set_link_coordinates(lb, link, x, y);
345 a_Menu_popup_set_url(lb->bw, lb->links[link]);
346
347 /* if we've got an image, prepare the image popup */
348 widget_at_cursor =
349 a_Dw_gtk_scrolled_window_widget_at_viewport_point(
350 GTK_DW_SCROLLED_WINDOW (lb->bw->docwin), event->x, event->y);
351 if (widget_at_cursor && DW_IS_IMAGE (widget_at_cursor)) {
352 DwImage *image = DW_IMAGE (widget_at_cursor);
353 /* test image->url (it may have not started to arrive yet!) */
354 if (image->url) {
355 /* use the second URL for this popup */
356 gtk_object_set_data(GTK_OBJECT (lb->bw->menu_popup.over_image),
357 "url2", GINT_TO_POINTER(2));
358 a_Menu_popup_set_url2(lb->bw, image->url);
359 show_oi = TRUE;
360 }
361 }
362 a_Menu_popup_ol_show_oi(lb->bw, show_oi);
363
364 gtk_menu_popup(GTK_MENU(lb->bw->menu_popup.over_link), NULL, NULL,
365 NULL, NULL, event->button, event->time);
366 return TRUE;
367 }
368
369 return FALSE;
370 }
371
372
373 /*
374 * Activate a link ("link_clicked" callback of the page)
375 */
376 static gboolean Html_link_clicked(DwWidget *widget, gint link, gint x, gint y,
377 GdkEventButton *event, DilloHtmlLB *lb)
378 {
379 Html_set_link_coordinates(lb, link, x, y);
380 if (event->button == 1)
381 a_Nav_push(lb->bw, lb->links[link]);
382 else if (event->button == 2) {
383 a_Nav_push_nw(lb->bw, lb->links[link]);
384 } else {
385 return FALSE;
386 }
387
388 if (DW_IS_PAGE (widget))
389 a_Dw_page_change_link_color (DW_PAGE (widget), link, lb->visited_color);
390
391 return TRUE;
392 }
393
394 /*
395 * Popup the image menu ("button_press_event" callback of image)
396 */
397 static gboolean Html_image_menu(DwWidget *widget,
398 gint32 x, gint32 y, GdkEventButton *event,
399 BrowserWindow *bw)
400 {
401 DwImage *image = DW_IMAGE (widget);
402 if (event->button == 3 && image->url) {
403 a_Menu_popup_set_url(bw, image->url);
404 a_Menu_popup_clear_url2(bw->menu_popup.over_image);
405
406 gtk_menu_popup(GTK_MENU(bw->menu_popup.over_image), NULL, NULL,
407 NULL, NULL, event->button, event->time);
408 return TRUE;
409 }
410
411 return FALSE;
412 }
413
414 /*
415 * Popup the page menu ("button_press_event" callback of the viewport)
416 */
417 static int Html_page_menu(GtkWidget *viewport, GdkEventButton *event,
418 BrowserWindow *bw)
419 {
420 gpointer bug_pix;
421
422 if (event->button == 3) {
423 /* set the working URL */
424 a_Menu_popup_set_url(bw, a_History_get_url(NAV_TOP(bw)));
425 /* set "View page Bugs" sensitivity */
426 bug_pix = gtk_object_get_data(GTK_OBJECT(bw->status_bug_meter), "bug");
427 gtk_widget_set_sensitive(bw->viewbugs_menuitem,
428 GTK_WIDGET_VISIBLE(GTK_WIDGET(bug_pix)));
429 gtk_menu_popup(GTK_MENU(bw->menu_popup.over_page), NULL, NULL,
430 NULL, NULL, event->button, event->time);
431 return TRUE;
432 } else
433 return FALSE;
434 }
435
436 /*
437 * Connect all signals of a page or an image.
438 */
439 static void Html_connect_signals(DilloHtml *html, GtkObject *widget)
440 {
441 gtk_signal_connect (widget, "link_entered",
442 GTK_SIGNAL_FUNC(Html_handle_status),
443 (gpointer)html->linkblock);
444 gtk_signal_connect (widget, "link_pressed", GTK_SIGNAL_FUNC(Html_link_menu),
445 (gpointer)html->linkblock);
446 gtk_signal_connect (widget, "link_clicked",
447 GTK_SIGNAL_FUNC(Html_link_clicked),
448 (gpointer)html->linkblock);
449 }
450
451
452 /*
453 * Create a new link in the linkblock, set it as the url's parent
454 * and return the index.
455 */
456 static gint Html_set_new_link(DilloHtml *html, DilloUrl **url)
457 {
458 gint nl;
459
460 nl = html->linkblock->num_links;
461 a_List_add(html->linkblock->links, nl, html->linkblock->num_links_max);
462 html->linkblock->links[nl] = (*url) ? *url : NULL;
463 return html->linkblock->num_links++;
464 }
465
466
467 /*
468 * Check an integer value to be inside a range.
469 * Return: 'n' if valid, 'def' if not.
470 */
471 static int Html_check_int(int n, int min, int max, int def)
472 {
473 return (n >= min && n <= max) ? n : def;
474 }
475
476 /*
477 * Allocate and insert form information into the Html linkblock
478 */
479 static gint Html_form_new(DilloHtmlLB *html_lb,
480 DilloHtmlMethod method,
481 const DilloUrl *action,
482 DilloHtmlEnc enc)
483 {
484 gint nf;
485
486 a_List_add(html_lb->forms, html_lb->num_forms, html_lb->num_forms_max);
487
488 nf = html_lb->num_forms;
489 html_lb->forms[nf].method = method;
490 html_lb->forms[nf].action = a_Url_dup(action);
491 html_lb->forms[nf].enc = enc;
492 html_lb->forms[nf].num_inputs = 0;
493 html_lb->forms[nf].num_inputs_max = 4;
494 html_lb->forms[nf].inputs = NULL;
495 html_lb->forms[nf].num_entry_fields = 0;
496 html_lb->forms[nf].num_submit_buttons = 0;
497 html_lb->num_forms++;
498
499 _MSG("Html_form_new: action=%s nform=%d\n", action, nf);
500 return nf;
501 }
502
503
504 /*
505 * Change one toplevel attribute. var should be an identifier. val is
506 * only evaluated once, so you can safely use a function call for it.
507 */
508 #define HTML_SET_TOP_ATTR(html, var, val) \
509 do { \
510 DwStyle style_attrs, *old_style; \
511 \
512 old_style = (html)->stack[(html)->stack_top].style; \
513 style_attrs = *old_style; \
514 style_attrs.var = (val); \
515 (html)->stack[(html)->stack_top].style = \
516 a_Dw_style_new (&style_attrs, (html)->bw->main_window->window); \
517 a_Dw_style_unref (old_style); \
518 } while (FALSE)
519
520 /*
521 * Set the font at the top of the stack. BImask specifies which
522 * attributes in BI should be changed.
523 */
524 static void Html_set_top_font(DilloHtml *html, gchar *name, gint size,
525 gint BI, gint BImask)
526 {
527 DwStyleFont font_attrs;
528
529 font_attrs = *html->stack[(html)->stack_top].style->font;
530 if ( name )
531 font_attrs.name = name;
532 if ( size )
533 font_attrs.size = size;
534 if ( BImask & 1 )
535 font_attrs.weight = (BI & 1) ? 700 : 400;
536 if ( BImask & 2 )
537 font_attrs.style = (BI & 2) ?
538 (prefs.use_oblique ?
539 DW_STYLE_FONT_STYLE_OBLIQUE : DW_STYLE_FONT_STYLE_ITALIC) :
540 DW_STYLE_FONT_STYLE_NORMAL;
541
542 HTML_SET_TOP_ATTR (html, font, a_Dw_style_font_new (&font_attrs));
543 }
544
545 /*
546 * Evaluates the ALIGN attribute (left|center|right|justify) and
547 * sets the style at the top of the stack.
548 */
549 static void Html_tag_set_align_attr(DilloHtml *html, char *tag, gint tagsize)
550 {
551 const char *align, *charattr;
552
553 if ((align = Html_get_attr(html, tag, tagsize, "align"))) {
554 if (g_strcasecmp (align, "left") == 0)
555 HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_LEFT);
556 else if (g_strcasecmp (align, "right") == 0)
557 HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_RIGHT);
558 else if (g_strcasecmp (align, "center") == 0)
559 HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_CENTER);
560 else if (g_strcasecmp (align, "justify") == 0)
561 HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_JUSTIFY);
562 else if (g_strcasecmp (align, "char") == 0) {
563 /* todo: Actually not supported for <p> etc. */
564 HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_STRING);
565 if ((charattr = Html_get_attr(html, tag, tagsize, "char"))) {
566 if (charattr[0] == 0)
567 /* todo: ALIGN=" ", and even ALIGN="&32;" will reult in
568 * an empty string (don't know whether the latter is
569 * correct, has to be clarified with the specs), so
570 * that for empty strings, " " is assumed. */
571 HTML_SET_TOP_ATTR (html, text_align_char, ' ');
572 else
573 HTML_SET_TOP_ATTR (html, text_align_char, charattr[0]);
574 } else
575 /* todo: Examine LANG attr of <html>. */
576 HTML_SET_TOP_ATTR (html, text_align_char, '.');
577 }
578 }
579 }
580
581 /*
582 * Evaluates the VALIGN attribute (top|bottom|middle|baseline) and
583 * sets the style in style_attrs. Returns TRUE when set.
584 */
585 static gboolean Html_tag_set_valign_attr(DilloHtml *html, char *tag,
586 gint tagsize, DwStyle *style_attrs)
587 {
588 const char *attr;
589
590 if ((attr = Html_get_attr(html, tag, tagsize, "valign"))) {
591 if (g_strcasecmp (attr, "top") == 0)
592 style_attrs->valign = DW_STYLE_VALIGN_TOP;
593 else if (g_strcasecmp (attr, "bottom") == 0)
594 style_attrs->valign = DW_STYLE_VALIGN_BOTTOM;
595 else if (g_strcasecmp (attr, "baseline") == 0)
596 style_attrs->valign = DW_STYLE_VALIGN_BASELINE;
597 else
598 style_attrs->valign = DW_STYLE_VALIGN_MIDDLE;
599 return TRUE;
600 } else
601 return FALSE;
602 }
603
604
605 /*
606 * Add a new DwPage into the current DwPage, for indentation.
607 * left and right are the horizontal indentation amounts, space is the
608 * vertical space around the block.
609 */
610 static void Html_add_indented_widget(DilloHtml *html, DwWidget *page,
611 int left, int right, int space)
612 {
613 DwStyle style_attrs, *style;
614
615 style_attrs = *html->stack[html->stack_top].style;
616
617 a_Dw_style_box_set_val(&style_attrs.margin, 0);
618 a_Dw_style_box_set_val(&style_attrs.border_width, 0);
619 a_Dw_style_box_set_val(&style_attrs.padding, 0);
620
621 /* Activate this for debugging */
622 #if 0
623 a_Dw_style_box_set_val(&style_attrs.border_width, 1);
624 a_Dw_style_box_set_border_color
625 (&style_attrs,
626 a_Dw_style_shaded_color_new(style_attrs.color->color_val,
627 html->bw->main_window->window));
628 a_Dw_style_box_set_border_style(&style_attrs, DW_STYLE_BORDER_DASHED);
629 #endif
630
631 style_attrs.margin.left = left;
632 style_attrs.margin.right = right;
633 style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
634
635 a_Dw_page_add_parbreak (DW_PAGE (html->dw), space, style);
636 a_Dw_page_add_widget (DW_PAGE (html->dw), page, style);
637 a_Dw_page_add_parbreak (DW_PAGE (html->dw), space, style);
638 html->stack[html->stack_top].page = html->dw = page;
639 html->stack[html->stack_top].hand_over_break = TRUE;
640 a_Dw_style_unref (style);
641
642 /* Handle it when the user clicks on a link */
643 Html_connect_signals(html, GTK_OBJECT(page));
644 }
645
646 /*
647 * Create and add a new indented DwPage to the current DwPage
648 */
649 static void Html_add_indented(DilloHtml *html, int left, int right, int space)
650 {
651 DwWidget *page = a_Dw_page_new ();
652 Html_add_indented_widget (html, page, left, right, space);
653 }
654
655 /*
656 * Given a font_size, this will return the correct 'level'.
657 * (or the closest, if the exact level isn't found).
658 */
659 static gint Html_fontsize_to_level(gint fontsize)
660 {
661 gint i, level;
662 gdouble normalized_size = fontsize / prefs.font_factor,
663 approximation = FontSizes[FontSizesNum-1] + 1;
664
665 for (i = level = 0; i < FontSizesNum; i++)
666 if (approximation >= fabs(normalized_size - FontSizes[i])) {
667 approximation = fabs(normalized_size - FontSizes[i]);
668 level = i;
669 } else {
670 break;
671 }
672
673 return level;
674 }
675
676 /*
677 * Given a level of a font, this will return the correct 'size'.
678 */
679 static gint Html_level_to_fontsize(gint level)
680 {
681 level = MAX(0, level);
682 level = MIN(FontSizesNum - 1, level);
683
684 return rint(FontSizes[level]*prefs.font_factor);
685 }
686
687 /*
688 * Miscelaneous initializations for a DwPage
689 */
690 static void Html_set_dwpage(DilloHtml *html)
691 {
692 DwWidget *widget;
693 DwPage *page;
694 DwStyle style_attrs;
695 DwStyleFont font;
696
697 g_return_if_fail (html->dw == NULL);
698
699 widget = a_Dw_page_new ();
700 page = DW_PAGE (widget);
701 html->dw = html->stack[0].page = widget;
702
703 /* Create a dummy font, attribute, and tag for the bottom of the stack. */
704 font.name = prefs.vw_fontname; /* Helvetica */
705 font.size = Html_level_to_fontsize(FontSizesBase);
706 font.weight = 400;
707 font.style = DW_STYLE_FONT_STYLE_NORMAL;
708
709 a_Dw_style_init_values (&style_attrs, html->bw->main_window->window);
710 style_attrs.font = a_Dw_style_font_new (&font);
711 style_attrs.color = a_Dw_style_color_new (prefs.text_color,
712 html->bw->main_window->window);
713 html->stack[0].style = a_Dw_style_new (&style_attrs,
714 html->bw->main_window->window);
715
716 html->stack[0].table_cell_style = NULL;
717
718 /* Handle it when the user clicks on a link */
719 Html_connect_signals(html, GTK_OBJECT(widget));
720
721 gtk_signal_connect_while_alive (
722 GTK_OBJECT(GTK_BIN(html->bw->docwin)->child), "button_press_event",
723 GTK_SIGNAL_FUNC(Html_page_menu), (gpointer)html->bw, GTK_OBJECT (page));
724
725 /* Connect the "bug meter" button-press to the linkblock */
726 gtk_signal_connect_while_alive(
727 GTK_OBJECT (html->bw->status_bug_meter), "clicked",
728 GTK_SIGNAL_FUNC (a_Commands_view_page_bugs_callback),
729 (gpointer)html->linkblock, GTK_OBJECT (page));
730 gtk_signal_connect_while_alive(
731 GTK_OBJECT (html->bw->status_bug_meter), "clicked1",
732 GTK_SIGNAL_FUNC (a_Commands_view_page_bugs_callback),
733 (gpointer)html->linkblock, GTK_OBJECT (page));
734 /* also connect with the "View page Bugs" menuitem */
735 gtk_signal_connect_while_alive(
736 GTK_OBJECT (html->bw->viewbugs_menuitem), "activate",
737 GTK_SIGNAL_FUNC (a_Commands_view_page_bugs_callback),
738 (gpointer)html->linkblock, GTK_OBJECT (page));
739
740 /* Destroy the linkblock when the DwPage is destroyed */
741 gtk_signal_connect_object(GTK_OBJECT(page), "destroy",
742 GTK_SIGNAL_FUNC(Html_lb_free),
743 (gpointer)html->linkblock);
744 }
745
746 /*
747 * Create and initialize a new DilloHtml structure
748 */
749 static DilloHtml *Html_new(BrowserWindow *bw, const DilloUrl *url)
750 {
751 DilloHtml *html;
752
753 html = g_new(DilloHtml, 1);
754
755 html->Start_Buf = NULL;
756 html->Start_Ofs = 0;
757 html->CurrTagOfs = 0;
758 html->OldTagOfs = 0;
759 html->OldTagLine = 1;
760
761 html->DocType = DT_NONE; /* assume Tag Soup 0.0! :-) */
762 html->DocTypeVersion = 0.0f;
763
764 html->dw = NULL;
765 html->bw = bw;
766 html->linkblock = Html_lb_new(bw, url);
767
768 html->stack_max = 16;
769 html->stack_top = 0;
770 html->stack = g_new(DilloHtmlState, html->stack_max);
771 html->stack[0].tag_name = g_strdup("none");
772 html->stack[0].style = NULL;
773 html->stack[0].table_cell_style = NULL;
774 html->stack[0].parse_mode = DILLO_HTML_PARSE_MODE_INIT;
775 html->stack[0].table_mode = DILLO_HTML_TABLE_MODE_NONE;
776 html->stack[0].cell_text_align_set = FALSE;
777 html->stack[0].list_type = HTML_LIST_NONE; /* no <ul> or <ol> open */
778 html->stack[0].list_number = 0;
779 html->stack[0].tag_idx = -1; /* MUST not be used */
780 html->stack[0].page = NULL;
781 html->stack[0].table = NULL;
782 html->stack[0].ref_list_item = NULL;
783 html->stack[0].current_bg_color = prefs.bg_color;
784 html->stack[0].hand_over_break = FALSE;
785
786 html->Stash = g_string_new("");
787 html->StashSpace = FALSE;
788
789 html->SPCBuf = NULL;
790
791 html->pre_column = 0;
792 html->PreFirstChar = FALSE;
793 html->PrevWasCR = FALSE;
794 html->PrevWasOpenTag = FALSE;
795 html->SPCPending = FALSE;
796 html->InVisitedLink = FALSE;
797 html->ReqTagClose = FALSE;
798 html->CloseOneTag = FALSE;
799 html->TagSoup = TRUE;
800 html->NameVal = NULL;
801
802 html->Num_HTML = html->Num_HEAD = html->Num_BODY = html->Num_TITLE = 0;
803
804 html->InFlags = 0;
805
806 html->attr_data = g_string_sized_new(1024);
807
808 Html_set_dwpage(html);
809
810 return html;
811 }
812
813 /*
814 * Initialize the stash buffer
815 */
816 static void Html_stash_init(DilloHtml *html)
817 {
818 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_STASH;
819 html->StashSpace = FALSE;
820 g_string_truncate(html->Stash, 0);
821 }
822
823 /* Entities list from the HTML 4.01 DTD */
824 typedef struct {
825 char *entity;
826 int isocode;
827 } Ent_t;
828
829 #define NumEnt 252
830 static const Ent_t Entities[NumEnt] = {
831 {"AElig",0306}, {"Aacute",0301}, {"Acirc",0302}, {"Agrave",0300},
832 {"Alpha",01621},{"Aring",0305}, {"Atilde",0303}, {"Auml",0304},
833 {"Beta",01622}, {"Ccedil",0307}, {"Chi",01647}, {"Dagger",020041},
834 {"Delta",01624},{"ETH",0320}, {"Eacute",0311}, {"Ecirc",0312},
835 {"Egrave",0310},{"Epsilon",01625},{"Eta",01627}, {"Euml",0313},
836 {"Gamma",01623},{"Iacute",0315}, {"Icirc",0316}, {"Igrave",0314},
837 {"Iota",01631}, {"Iuml",0317}, {"Kappa",01632}, {"Lambda",01633},
838 {"Mu",01634}, {"Ntilde",0321}, {"Nu",01635}, {"OElig",0522},
839 {"Oacute",0323},{"Ocirc",0324}, {"Ograve",0322}, {"Omega",01651},
840 {"Omicron",01637},{"Oslash",0330},{"Otilde",0325},{"Ouml",0326},
841 {"Phi",01646}, {"Pi",01640}, {"Prime",020063},{"Psi",01650},
842 {"Rho",01641}, {"Scaron",0540}, {"Sigma",01643}, {"THORN",0336},
843 {"Tau",01644}, {"Theta",01630}, {"Uacute",0332}, {"Ucirc",0333},
844 {"Ugrave",0331},{"Upsilon",01645},{"Uuml",0334}, {"Xi",01636},
845 {"Yacute",0335},{"Yuml",0570}, {"Zeta",01626}, {"aacute",0341},
846 {"acirc",0342}, {"acute",0264}, {"aelig",0346}, {"agrave",0340},
847 {"alefsym",020465},{"alpha",01661},{"amp",38}, {"and",021047},
848 {"ang",021040}, {"aring",0345}, {"asymp",021110},{"atilde",0343},
849 {"auml",0344}, {"bdquo",020036},{"beta",01662}, {"brvbar",0246},
850 {"bull",020042},{"cap",021051}, {"ccedil",0347}, {"cedil",0270},
851 {"cent",0242}, {"chi",01707}, {"circ",01306}, {"clubs",023143},
852 {"cong",021105},{"copy",0251}, {"crarr",020665},{"cup",021052},
853 {"curren",0244},{"dArr",020723}, {"dagger",020040},{"darr",020623},
854 {"deg",0260}, {"delta",01664}, {"diams",023146},{"divide",0367},
855 {"eacute",0351},{"ecirc",0352}, {"egrave",0350}, {"empty",021005},
856 {"emsp",020003},{"ensp",020002}, {"epsilon",01665},{"equiv",021141},
857 {"eta",01667}, {"eth",0360}, {"euml",0353}, {"euro",020254},
858 {"exist",021003},{"fnof",0622}, {"forall",021000},{"frac12",0275},
859 {"frac14",0274},{"frac34",0276}, {"frasl",020104},{"gamma",01663},
860 {"ge",021145}, {"gt",62}, {"hArr",020724}, {"harr",020624},
861 {"hearts",023145},{"hellip",020046},{"iacute",0355},{"icirc",0356},
862 {"iexcl",0241}, {"igrave",0354}, {"image",020421},{"infin",021036},
863 {"int",021053}, {"iota",01671}, {"iquest",0277}, {"isin",021010},
864 {"iuml",0357}, {"kappa",01672}, {"lArr",020720}, {"lambda",01673},
865 {"lang",021451},{"laquo",0253}, {"larr",020620}, {"lceil",021410},
866 {"ldquo",020034},{"le",021144}, {"lfloor",021412},{"lowast",021027},
867 {"loz",022712}, {"lrm",020016}, {"lsaquo",020071},{"lsquo",020030},
868 {"lt",60}, {"macr",0257}, {"mdash",020024},{"micro",0265},
869 {"middot",0267},{"minus",021022},{"mu",01674}, {"nabla",021007},
870 {"nbsp",32}, {"ndash",020023},{"ne",021140}, {"ni",021013},
871 {"not",0254}, {"notin",021011},{"nsub",021204}, {"ntilde",0361},
872 {"nu",01675}, {"oacute",0363}, {"ocirc",0364}, {"oelig",0523},
873 {"ograve",0362},{"oline",020076},{"omega",01711}, {"omicron",01677},
874 {"oplus",021225},{"or",021050}, {"ordf",0252}, {"ordm",0272},
875 {"oslash",0370},{"otilde",0365}, {"otimes",021227},{"ouml",0366},
876 {"para",0266}, {"part",021002}, {"permil",020060},{"perp",021245},
877 {"phi",01706}, {"pi",01700}, {"piv",01726}, {"plusmn",0261},
878 {"pound",0243}, {"prime",020062},{"prod",021017}, {"prop",021035},
879 {"psi",01710}, {"quot",34}, {"rArr",020722}, {"radic",021032},
880 {"rang",021452},{"raquo",0273}, {"rarr",020622}, {"rceil",021411},
881 {"rdquo",020035},{"real",020434},{"reg",0256}, {"rfloor",021413},
882 {"rho",01701}, {"rlm",020017}, {"rsaquo",020072},{"rsquo",020031},
883 {"sbquo",020032},{"scaron",0541},{"sdot",021305}, {"sect",0247},
884 {"shy",0255}, {"sigma",01703}, {"sigmaf",01702},{"sim",021074},
885 {"spades",023140},{"sub",021202},{"sube",021206}, {"sum",021021},
886 {"sup",021203}, {"sup1",0271}, {"sup2",0262}, {"sup3",0263},
887 {"supe",021207},{"szlig",0337}, {"tau",01704}, {"there4",021064},
888 {"theta",01670},{"thetasym",01721},{"thinsp",020011},{"thorn",0376},
889 {"tilde",01334},{"times",0327}, {"trade",020442},{"uArr",020721},
890 {"uacute",0372},{"uarr",020621}, {"ucirc",0373}, {"ugrave",0371},
891 {"uml",0250}, {"upsih",01722}, {"upsilon",01705},{"uuml",0374},
892 {"weierp",020430},{"xi",01676}, {"yacute",0375}, {"yen",0245},
893 {"yuml",0377}, {"zeta",01666}, {"zwj",020015}, {"zwnj",020014}
894 };
895
896
897 /*
898 * Comparison function for binary search
899 */
900 static int Html_entity_comp(const void *a, const void *b)
901 {
902 return strcmp(((Ent_t *)a)->entity, ((Ent_t *)b)->entity);
903 }
904
905 /*
906 * Binary search of 'key' in entity list
907 */
908 static int Html_entity_search(char *key)
909 {
910 Ent_t *res, EntKey;
911
912 EntKey.entity = key;
913 res = bsearch(&EntKey, Entities, NumEnt, sizeof(Ent_t), Html_entity_comp);
914 if ( res )
915 return (res - Entities);
916 return -1;
917 }
918
919 /*
920 * Switch a few UCS encodings to latin1.
921 */
922 static gint Html_try_ucs2latin1(gint isocode)
923 {
924 gint ret;
925 switch (isocode) {
926 case 0x2018:
927 case 0x2019: ret = '\''; break;
928 case 0x201c:
929 case 0x201d: ret = '"'; break;
930 case 0x2013:
931 case 0x2014: ret = '-'; break;
932 case 0x2039: ret = '<'; break;
933 case 0x203a: ret = '>'; break;
934 case 0x2022: ret = 176; break;
935 default: ret = -1; break;
936 }
937 return ret;
938 }
939
940 /*
941 * Switch a few 'undefined for HTML' ASCII encodings to latin1.
942 */
943 static gint Html_try_ascii2latin1(gint isocode)
944 {
945 gint ret;
946 switch (isocode) {
947 case 145:
948 case 146: ret = '\''; break;
949 case 147:
950 case 148: ret = '"'; break;
951 case 149: ret = 176; break;
952 case 150:
953 case 151: ret = '-'; break;
954 default: ret = isocode; break;
955 }
956 return ret;
957 }
958
959 /*
960 * Given an entity, return the ISO-Latin1 character code.
961 * Returns a negative value (error code) if not a valid entity.
962 *
963 * The first character *token is assumed to be == '&'
964 *
965 * For valid entities, *entsize is set to the length of the parsed entity.
966 */
967 static gint Html_parse_entity(DilloHtml *html, const gchar *token,
968 gint toksize, gint *entsize)
969 {
970 gint isocode, i;
971 gchar *tok, *s, c;
972
973 token++;
974 tok = s = toksize ? g_strndup(token, (guint)toksize) : g_strdup(token);
975
976 isocode = -1;
977
978 if (*s == '#') {
979 /* numeric character reference */
980 errno = 0;
981 if (*++s == 'x' || *s == 'X') {
982 if (isxdigit(*++s)) {
983 /* strtol with base 16 accepts leading "0x" - we don't */
984 if (*s == '0' && s[1] == 'x') {
985 s++;
986 isocode = 0;
987 } else {
988 isocode = strtol(s, &s, 16);
989 }
990 }
991 } else if (isdigit(*s)) {
992 isocode = strtol(s, &s, 10);
993 }
994
995 if (!isocode || errno || isocode > 0x7fffffffL) {
996 /* this catches null bytes, errors and codes >=2^31 */
997 MSG_HTML("numeric character reference out of range\n");
998 isocode = -2;
999 }
1000
1001 if (isocode != -1) {
1002 if (*s == ';')
1003 s++;
1004 else if (prefs.show_extra_warnings)
1005 MSG_HTML("numeric character reference without trailing ';'\n");
1006 }
1007
1008 } else if (isalpha(*s)) {
1009 /* character entity reference */
1010 while (isalnum(*++s) || strchr(":_.-", *s));
1011 c = *s;
1012 *s = 0;
1013
1014 if (c != ';' || (i = Html_entity_search(tok)) == -1) {
1015 if ((html->DocType == DT_HTML && html->DocTypeVersion == 4.01f) ||
1016 html->DocType == DT_XHTML)
1017 MSG_HTML("undefined character entity '%s'\n", tok);
1018 isocode = -3;
1019 } else
1020 isocode = Entities[i].isocode;
1021
1022 if (c == ';')
1023 s++;
1024 else if (prefs.show_extra_warnings)
1025 MSG_HTML("character entity reference without trailing ';'\n");
1026 }
1027
1028 *entsize = s-tok+1;
1029 g_free(tok);
1030
1031 if (isocode >= 128 && isocode <= 159) {
1032 MSG_HTML("code positions 128-159 are not defined for ISO Latin-1\n");
1033 isocode = Html_try_ascii2latin1(isocode);
1034 } else if (isocode > 255)
1035 /* Try a few UCS translations to Latin1 */
1036 isocode = Html_try_ucs2latin1(isocode);
1037 else if (isocode == -1 && prefs.show_extra_warnings)
1038 MSG_HTML("literal '&'\n");
1039
1040 return isocode;
1041 }
1042
1043 /*
1044 * Convert all the entities in a token to plain ISO character codes. Takes
1045 * a token and its length, and returns a newly allocated string.
1046 */
1047 static char *
1048 Html_parse_entities(DilloHtml *html, gchar *token, gint toksize)
1049 {
1050 gchar *esc_set = "&\xE2\xC2";
1051 gchar *new_str;
1052 gint i, j, isocode, entsize;
1053
1054 new_str = g_strndup(token, toksize);
1055 if (new_str[strcspn(new_str, esc_set)] == 0)
1056 return new_str;
1057
1058 for (i = j = 0; i < toksize; i++) {
1059 if (token[i] == '&' &&
1060 (isocode = Html_parse_entity(html, token+i,
1061 toksize-i, &entsize)) >= 0) {
1062 new_str[j++] = (gchar) isocode;
1063 i += entsize-1;
1064
1065 } else if (token[i] == '\xE2' && token[i+1] == '\x80' && i+2 < toksize){
1066 /* Hack: for parsing some UTF-8 characters into latin1 */
1067 switch (token[i+2]) {
1068 case '\x94':
1069 new_str[j++] = '-';
1070 new_str[j++] = '-';
1071 break;
1072 case '\x98':
1073 case '\x99':
1074 new_str[j++] = '\'';
1075 break;
1076 case '\x9C':
1077 case '\x9D':
1078 new_str[j++] = '"';
1079 break;
1080 case '\xA2':
1081 new_str[j++] = '*';
1082 new_str[j++] = ' ';
1083 break;
1084 default: /* unhandled */
1085 new_str[j++] = '\xE2';
1086 break;
1087 }
1088 i += 2;
1089
1090 } else if (token[i] == '\xC2' && token[i+1] == '\xA0') {
1091 /* Hack: for parsing some UTF-8 characters into latin1 */
1092 new_str[j++] = ' ';
1093 ++i;
1094
1095 } else {
1096 new_str[j++] = token[i];
1097 }
1098 }
1099 new_str[j] = '\0';
1100 return new_str;
1101 }
1102
1103 /*
1104 * Parse spaces
1105 *
1106 */
1107 static void Html_process_space(DilloHtml *html, char *space, gint spacesize)
1108 {
1109 gint i, offset;
1110 DilloHtmlParseMode parse_mode = html->stack[html->stack_top].parse_mode;
1111
1112 if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH ) {
1113 html->StashSpace = (html->Stash->len > 0);
1114 html->SPCPending = FALSE;
1115
1116 } else if ( parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM ) {
1117 char *Pword = g_strndup(space, spacesize);
1118 g_string_append(html->Stash, Pword);
1119 g_free(Pword);
1120 html->SPCPending = FALSE;
1121
1122 } else if ( parse_mode == DILLO_HTML_PARSE_MODE_PRE ) {
1123 /* re-scan the string for characters that cause line breaks */
1124 for (i = 0; i < spacesize; i++) {
1125 /* Support for "\r", "\n" and "\r\n" line breaks (skips the first) */
1126 if (!html->PreFirstChar &&
1127 (space[i] == '\r' || (space[i] == '\n' && !html->PrevWasCR))) {
1128 a_Dw_page_add_linebreak(DW_PAGE (html->dw),
1129 html->stack[(html)->stack_top].style);
1130 html->pre_column = 0;
1131 }
1132 html->PreFirstChar = FALSE;
1133
1134 /* cr and lf should not be rendered -- they appear as a break */
1135 switch (space[i]) {
1136 case '\r':
1137 case '\n':
1138 break;
1139 case '\t':
1140 if (prefs.show_extra_warnings)
1141 MSG_HTML("TAB character inside <PRE>\n");
1142 offset = TAB_SIZE - html->pre_column % TAB_SIZE;
1143 a_Dw_page_add_text(DW_PAGE (html->dw),
1144 g_strnfill(offset, ' '),
1145 html->stack[html->stack_top].style);
1146 html->pre_column += offset;
1147 break;
1148 default:
1149 a_Dw_page_add_text(DW_PAGE (html->dw),
1150 g_strndup(space + i, 1),
1151 html->stack[html->stack_top].style);
1152 html->pre_column++;
1153 break;
1154 }
1155
1156 html->PrevWasCR = (space[i] == '\r');
1157 }
1158 html->SPCPending = FALSE;
1159
1160 } else {
1161 if (SGML_SPCDEL && html->PrevWasOpenTag) {
1162 /* SGML_SPCDEL ignores white space inmediately after an open tag */
1163 html->SPCPending = FALSE;
1164 } else {
1165 g_free(html->SPCBuf);
1166 html->SPCBuf = g_strndup(space, spacesize);
1167 html->SPCPending = TRUE;
1168 }
1169
1170 if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY )
1171 html->StashSpace = (html->Stash->len > 0);
1172 }
1173 }
1174
1175 /*
1176 * Handles putting the word into its proper place
1177 * > STASH and VERBATIM --> html->Stash
1178 * > otherwise it goes through a_Dw_page_add_text()
1179 *
1180 * Entities are parsed (or not) according to parse_mode.
1181 */
1182 static void Html_process_word(DilloHtml *html, char *word, gint size)
1183 {
1184 gint i, start;
1185 gchar *Pword;
1186 DilloHtmlParseMode parse_mode = html->stack[html->stack_top].parse_mode;
1187
1188 if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH ||
1189 parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY ) {
1190 if ( html->StashSpace ) {
1191 g_string_append_c(html->Stash, ' ');
1192 html->StashSpace = FALSE;
1193 }
1194 Pword = Html_parse_entities(html, word, size);
1195 g_string_append(html->Stash, Pword);
1196 g_free(Pword);
1197
1198 } else if ( parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM ) {
1199 /* word goes in untouched, it is not processed here. */
1200 Pword = g_strndup(word, size);
1201 g_string_append(html->Stash, Pword);
1202 g_free(Pword);
1203 }
1204
1205 if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH ||
1206 parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM ) {
1207 /* skip until the closing instructions */
1208
1209 } else if ( parse_mode == DILLO_HTML_PARSE_MODE_PRE ) {
1210 /* all this overhead is to catch white-space entities */
1211 Pword = Html_parse_entities(html, word, size);
1212 for (start = i = 0; Pword[i]; start = i)
1213 if (isspace(Pword[i])) {
1214 while (Pword[++i] && isspace(Pword[i]));
1215 Html_process_space(html, Pword + start, i - start);
1216 } else {
1217 while (Pword[++i] && !isspace(Pword[i]));
1218 a_Dw_page_add_text(DW_PAGE (html->dw),
1219 g_strndup(Pword + start, i - start),
1220 html->stack[html->stack_top].style);
1221 html->pre_column += i - start;
1222 html->PreFirstChar = FALSE;
1223 }
1224 g_free(Pword);
1225
1226 } else {
1227 /* add pending space if present */
1228 if (html->SPCPending && (!SGML_SPCDEL || !html->PrevWasOpenTag))
1229 /* SGML_SPCDEL ignores space after an open tag */
1230 a_Dw_page_add_space(DW_PAGE (html->dw),
1231 html->stack[html->stack_top].style);
1232
1233 /* actually white-space entities inside the word could be
1234 * collapsed (except &nbsp;), but that's too much overhead
1235 * for a very rare case of ill-formed HTML --Jcid */
1236
1237 Pword = Html_parse_entities(html, word, size);
1238 g_strdelimit(Pword, "\t\f\n\r", ' ');
1239 a_Dw_page_add_text(DW_PAGE (html->dw),
1240 Pword,
1241 html->stack[html->stack_top].style);
1242 }
1243
1244 html->PrevWasOpenTag = FALSE;
1245 html->SPCPending = FALSE;
1246 }
1247
1248 /*
1249 * Does the tag in tagstr (e.g. "p") match the tag in the tag, tagsize
1250 * structure, with the initial < skipped over (e.g. "P align=center>")
1251 */
1252 static gboolean Html_match_tag(const char *tagstr, char *tag, gint tagsize)
1253 {
1254 gint i;
1255
1256 for (i = 0; i < tagsize && tagstr[i] != '\0'; i++) {
1257 if (tolower(tagstr[i]) != tolower(tag[i]))
1258 return FALSE;
1259 }
1260 /* The test for '/' is for xml compatibility: "empty/>" will be matched. */
1261 if (i < tagsize && (isspace(tag[i]) || tag[i] == '>' || tag[i] == '/'))
1262 return TRUE;
1263 return FALSE;
1264 }
1265
1266 /*
1267 * This function is called after popping the stack, to
1268 * handle nested DwPage widgets.
1269 */
1270 static void Html_eventually_pop_dw(DilloHtml *html)
1271 {
1272 /* This function is called after popping from the stack, so the
1273 * relevant hand_over_break is at html->stack_top + 1. */
1274 if (html->dw != html->stack[html->stack_top].page) {
1275 if (html->stack[html->stack_top + 1].hand_over_break)
1276 a_Dw_page_hand_over_break(DW_PAGE(html->dw),
1277 html->stack[(html)->stack_top].style);
1278 a_Dw_page_flush(DW_PAGE(html->dw));
1279 html->dw = html->stack[html->stack_top].page;
1280 }
1281 }
1282
1283 /*
1284 * Push the tag (copying attributes from the top of the stack)
1285 */
1286 static void Html_push_tag(DilloHtml *html, gint tag_idx)
1287 {
1288 char *tagstr;
1289 gint n_items;
1290
1291 /* Save the element's name (no parameters) into tagstr. */
1292 tagstr = g_strdup(Tags[tag_idx].name);
1293
1294 n_items = html->stack_top + 1;
1295 a_List_add(html->stack, n_items, html->stack_max);
1296 /* We'll copy the former stack item and just change the tag and its index
1297 * instead of copying all fields except for tag. --Jcid */
1298 html->stack[n_items] = html->stack[n_items - 1];
1299 html->stack[n_items].tag_name = tagstr;
1300 html->stack[n_items].tag_idx = tag_idx;
1301 html->stack_top = n_items;
1302 /* proper memory management, may be unref'd later */
1303 a_Dw_style_ref (html->stack[html->stack_top].style);
1304 if (html->stack[html->stack_top].table_cell_style)
1305 a_Dw_style_ref (html->stack[html->stack_top].table_cell_style);
1306 html->dw = html->stack[html->stack_top].page;
1307 }
1308
1309 /*
1310 * Push the tag (used to force en element with optional open into the stack)
1311 * Note: now it's the same as Html_push_tag(), but things may change...
1312 */
1313 static void Html_force_push_tag(DilloHtml *html, gint tag_idx)
1314 {
1315 Html_push_tag(html, tag_idx);
1316 }
1317
1318 /*
1319 * Pop the top tag in the stack
1320 */
1321 static void Html_real_pop_tag(DilloHtml *html)
1322 {
1323 a_Dw_style_unref (html->stack[html->stack_top].style);
1324 if (html->stack[html->stack_top].table_cell_style)
1325 a_Dw_style_unref (html->stack[html->stack_top].table_cell_style);
1326 g_free(html->stack[html->stack_top--].tag_name);
1327 Html_eventually_pop_dw(html);
1328 }
1329
1330 /*
1331 * Default close function for tags.
1332 * (conditional cleanup of the stack)
1333 * There're several ways of doing it. Considering the HTML 4.01 spec
1334 * which defines optional close tags, and the will to deliver useful diagnose
1335 * messages for bad-formed HTML, it'll go as follows:
1336 * 1.- Search the stack for the first tag that requires a close tag.
1337 * 2.- If it matches, clean all the optional-close tags in between.
1338 * 3.- Cleanup the matching tag. (on error, give a warning message)
1339 *
1340 * If 'w3c_mode' is NOT enabled:
1341 * 1.- Search the stack for a matching tag based on tag level.
1342 * 2.- If it exists, clean all the tags in between.
1343 * 3.- Cleanup the matching tag. (on error, give a warning message)
1344 */
1345 static void Html_tag_cleanup_at_close(DilloHtml *html, gint TagIdx)
1346 {
1347 gint w3c_mode = !prefs.w3c_plus_heuristics;
1348 gint stack_idx, cmp = 1;
1349 gint new_idx = TagIdx;
1350
1351 if (html->CloseOneTag) {
1352 Html_real_pop_tag(html);
1353 html->CloseOneTag = FALSE;
1354 return;
1355 }
1356
1357 /* Look for the candidate tag to close */
1358 stack_idx = html->stack_top;
1359 while (stack_idx &&
1360 (cmp = (new_idx != html->stack[stack_idx].tag_idx)) &&
1361 ((w3c_mode &&
1362 Tags[html->stack[stack_idx].tag_idx].EndTag == 'O') ||
1363 (!w3c_mode &&
1364 Tags[html->stack[stack_idx].tag_idx].TagLevel <
1365 Tags[new_idx].TagLevel))) {
1366 --stack_idx;
1367 }
1368
1369 /* clean, up to the matching tag */
1370 if (cmp == 0 && stack_idx > 0) {
1371 /* There's a valid matching tag in the stack */
1372 while (html->stack_top >= stack_idx) {
1373 gint toptag_idx = html->stack[html->stack_top].tag_idx;
1374 /* Warn when we decide to close an open tag (for !w3c_mode) */
1375 if (html->stack_top > stack_idx &&
1376 Tags[toptag_idx].EndTag != 'O')
1377 MSG_HTML(" - forcing close of open tag: <%s>\n",
1378 Tags[toptag_idx].name);
1379
1380 /* Close this and only this tag */
1381 html->CloseOneTag = TRUE;
1382 Tags[toptag_idx].close (html, toptag_idx);
1383 }
1384
1385 } else {
1386 MSG_HTML("unexpected closing tag: </%s>. -- expected </%s>\n",
1387 Tags[new_idx].name, html->stack[stack_idx].tag_name);
1388 }
1389 }
1390
1391 /*
1392 * Cleanup (conditional), and Pop the tag (if it matches)
1393 */
1394 static void Html_pop_tag(DilloHtml *html, gint TagIdx)
1395 {
1396 Html_tag_cleanup_at_close(html, TagIdx);
1397 }
1398
1399 /*
1400 * Some parsing routines.
1401 */
1402
1403 /*
1404 * Used by Html_parse_length
1405 */
1406 static DwStyleLength Html_parse_length_or_multi_length (const gchar *attr,
1407 gchar **endptr)
1408 {
1409 DwStyleLength l;
1410 double v;
1411 gchar *end;
1412
1413 v = strtod (attr, &end);
1414 switch (*end) {
1415 case '%':
1416 end++;
1417 l = DW_STYLE_CREATE_PER_LENGTH (v / 100);
1418 break;
1419
1420 case '*':
1421 end++;
1422 l = DW_STYLE_CREATE_REL_LENGTH (v);
1423 break;
1424 /*
1425 The "px" suffix seems not allowed by HTML4.01 SPEC.
1426 case 'p':
1427 if (end[1] == 'x')
1428 end += 2;
1429 */
1430 default:
1431 l = DW_STYLE_CREATE_ABS_LENGTH ((gint)v);
1432 break;
1433 }
1434
1435 if (endptr)
1436 *endptr = end;
1437 return l;
1438 }
1439
1440
1441 /*
1442 * Returns a length or a percentage, or DW_STYLE_UNDEF_LENGTH in case
1443 * of an error, or if attr is NULL.
1444 */
1445 static DwStyleLength Html_parse_length (DilloHtml *html, const gchar *attr)
1446 {
1447 DwStyleLength l;
1448 gchar *end;
1449
1450 l = Html_parse_length_or_multi_length (attr, &end);
1451 if (DW_STYLE_IS_REL_LENGTH (l))
1452 /* not allowed as &Length; */
1453 return DW_STYLE_LENGTH_AUTO;
1454 else {
1455 /* allow only whitespaces */
1456 if (*end && !isspace (*end)) {
1457 MSG_HTML("Garbage after length: %s\n", attr);
1458 return DW_STYLE_LENGTH_AUTO;
1459 }
1460 }
1461
1462 return l;
1463 }
1464
1465 /*
1466 * Parse a color attribute.
1467 * Return value: parsed color, or default_color (+ error msg) on error.
1468 */
1469 static gint32
1470 Html_color_parse(DilloHtml *html, const char *subtag, gint32 default_color)
1471 {
1472 gint err = 1;
1473 gint32 color = a_Color_parse(subtag, default_color, &err);
1474
1475 if (err) {
1476 MSG_HTML("color is not in \"#RRGGBB\" format\n");
1477 }
1478 return color;
1479 }
1480
1481 /*
1482 * Check that 'val' is composed of characters inside [A-Za-z0-9:_.-]
1483 * Note: ID can't have entities, but this check is enough (no '&').
1484 * Return value: 1 if OK, 0 otherwise.
1485 */
1486 static gint
1487 Html_check_name_val(DilloHtml *html, const char *val, const char *attrname)
1488 {
1489 gint i;
1490
1491 for (i = 0; val[i]; ++i)
1492 if (!(isalnum(val[i]) || strchr(":_.-", val[i])))
1493 break;
1494
1495 if (val[i] || !isalpha(val[0]))
1496 MSG_HTML("'%s' value is not of the form "
1497 "[A-Za-z][A-Za-z0-9:_.-]*\n", attrname);
1498
1499 return !(val[i]);
1500 }
1501
1502 /*
1503 * Handle DOCTYPE declaration
1504 *
1505 * Follows the convention that HTML 4.01
1506 * doctypes which include a full w3c DTD url are treated as
1507 * standards-compliant, but 4.01 without the url and HTML 4.0 and
1508 * earlier are not. XHTML doctypes are always standards-compliant
1509 * whether or not an url is present.
1510 *
1511 * Note: I'm not sure about this convention. The W3C validator
1512 * recognizes the "HTML Level" with or without the URL. The convention
1513 * comes from mozilla (see URLs below), but Dillo doesn't have the same
1514 * rendering modes, so it may be better to chose another behaviour. --Jcid
1515 *
1516 * http://www.mozilla.org/docs/web-developer/quirks/doctypes.html
1517 * http://lists.auriga.wearlab.de/pipermail/dillo-dev/2004-October/002300.html
1518 *
1519 * This is not a full DOCTYPE parser, just enough for what Dillo uses.
1520 */
1521 static void Html_parse_doctype(DilloHtml *html, char *tag, gint tagsize)
1522 {
1523 char *HTML_sig = "<!DOCTYPE HTML PUBLIC ";
1524 char *HTML20 = "-//IETF//DTD HTML//EN";
1525 char *HTML32 = "-//W3C//DTD HTML 3.2";
1526 char *HTML40 = "-//W3C//DTD HTML 4.0";
1527 char *HTML401 = "-//W3C//DTD HTML 4.01";
1528 char *HTML401_url = "http://www.w3.org/TR/html4/";
1529 char *XHTML1 = "-//W3C//DTD XHTML 1.0";
1530 char *XHTML1_url = "http://www.w3.org/TR/xhtml1/DTD/";
1531 char *XHTML11 = "-//W3C//DTD XHTML 1.1";
1532 char *XHTML11_url = "http://www.w3.org/TR/xhtml11/DTD/";
1533
1534 int i, quote;
1535 char *p, *ntag = g_strndup(tag, tagsize);
1536
1537 /* Tag sanitization: Collapse whitespace between tokens
1538 * and replace '\n' and '\r' with ' ' inside quoted strings. */
1539 for (i = 0, p = ntag; *p; ++p) {
1540 if (isspace(*p)) {
1541 for (ntag[i++] = ' '; isspace(p[1]); ++p);
1542 } else if ((quote = *p) == '"' || *p == '\'') {
1543 for (ntag[i++] = *p++; (ntag[i++] = *p) && *p != quote; ++p) {
1544 if (*p == '\n' || *p == '\r')
1545 ntag[i - 1] = ' ';
1546 p += (p[0] == '\r' && p[1] == '\n') ? 1 : 0;
1547 }
1548 } else {
1549 ntag[i++] = *p;
1550 }
1551 if (!*p)
1552 break;
1553 }
1554 ntag[i] = 0;
1555
1556 _MSG("New: {%s}\n", ntag);
1557
1558 /* The default DT_NONE type is TagSoup */
1559 if (!g_strncasecmp(ntag, HTML_sig, strlen(HTML_sig))) {
1560 p = ntag + strlen(HTML_sig) + 1;
1561 if (!strncmp(p, HTML401, strlen(HTML401)) &&
1562 a_Misc_stristr(p + strlen(HTML401), HTML401_url)) {
1563 html->DocType = DT_HTML;
1564 html->DocTypeVersion = 4.01f;
1565 } else if (!strncmp(p, XHTML1, strlen(XHTML1)) &&
1566 a_Misc_stristr(p + strlen(XHTML1), XHTML1_url)) {
1567 html->DocType = DT_XHTML;
1568 html->DocTypeVersion = 1.0f;
1569 } else if (!strncmp(p, XHTML11, strlen(XHTML11)) &&
1570 a_Misc_stristr(p + strlen(XHTML11), XHTML11_url)) {
1571 html->DocType = DT_XHTML;
1572 html->DocTypeVersion = 1.1f;
1573 } else if (!strncmp(p, HTML40, strlen(HTML40))) {
1574 html->DocType = DT_HTML;
1575 html->DocTypeVersion = 4.0f;
1576 } else if (!strncmp(p, HTML32, strlen(HTML32))) {
1577 html->DocType = DT_HTML;
1578 html->DocTypeVersion = 3.2f;
1579 } else if (!strncmp(p, HTML20, strlen(HTML20))) {
1580 html->DocType = DT_HTML;
1581 html->DocTypeVersion = 2.0f;
1582 }
1583 }
1584
1585 g_free(ntag);
1586 }
1587
1588 /*
1589 * Handle open HTML element
1590 */
1591 static void Html_tag_open_html(DilloHtml *html, char *tag, gint tagsize)
1592 {
1593 if (!(html->InFlags & IN_HTML))
1594 html->InFlags |= IN_HTML;
1595 ++html->Num_HTML;
1596
1597 if (html->Num_HTML > 1) {
1598 MSG_HTML("HTML element was already open\n");
1599 }
1600 }
1601
1602 /*
1603 * Handle close HTML element
1604 */
1605 static void Html_tag_close_html(DilloHtml *html, gint TagIdx)
1606 {
1607 /* todo: may add some checks here */
1608 if (html->Num_HTML == 1) {
1609 /* beware of pages with multiple HTML close tags... :-P */
1610 html->InFlags &= ~IN_HTML;
1611 }
1612 Html_pop_tag(html, TagIdx);
1613 }
1614
1615 /*
1616 * Handle open HEAD element
1617 */
1618 static void Html_tag_open_head(DilloHtml *html, char *tag, gint tagsize)
1619 {
1620 if (html->InFlags & IN_BODY) {
1621 MSG_HTML("HEAD element must go before the BODY section\n");
1622 html->ReqTagClose = TRUE;
1623 return;
1624 }
1625
1626 if (!(html->InFlags & IN_HEAD))
1627 html->InFlags |= IN_HEAD;
1628 ++html->Num_HEAD;
1629
1630 if (html->Num_HEAD > 1) {
1631 MSG_HTML("HEAD element was already open\n");
1632 }
1633 }
1634
1635 /*
1636 * Handle close HEAD element
1637 * Note: as a side effect of Html_test_section() this function is called
1638 * twice when the head element is closed implicitly.
1639 */
1640 static void Html_tag_close_head(DilloHtml *html, gint TagIdx)
1641 {
1642 if (html->InFlags & IN_HEAD) {
1643 if (html->Num_TITLE == 0)
1644 MSG_HTML("HEAD section lacks the TITLE element\n");
1645
1646 html->InFlags &= ~IN_HEAD;
1647 }
1648 Html_pop_tag(html, TagIdx);
1649 }
1650
1651 /*
1652 * Handle open TITLE
1653 * calls stash init, where the title string will be stored
1654 */
1655 static void Html_tag_open_title(DilloHtml *html, char *tag, gint tagsize)
1656 {
1657 ++html->Num_TITLE;
1658 Html_stash_init(html);
1659 }
1660
1661 /*
1662 * Handle close TITLE
1663 * set page-title in the browser window and in the history.
1664 */
1665 static void Html_tag_close_title(DilloHtml *html, gint TagIdx)
1666 {
1667 if (html->InFlags & IN_HEAD) {
1668 /* title is only valid inside HEAD */
1669 a_Interface_set_page_title(html->linkblock->bw, html->Stash->str);
1670 a_History_set_title(NAV_TOP(html->linkblock->bw), html->Stash->str);
1671 } else {
1672 MSG_HTML("the TITLE element must be inside the HEAD section\n");
1673 }
1674 Html_pop_tag(html, TagIdx);
1675 }
1676
1677 /*
1678 * Handle open SCRIPT
1679 * initializes stash, where the embedded code will be stored.
1680 * MODE_VERBATIM is used because MODE_STASH catches entities.
1681 */
1682 static void Html_tag_open_script(DilloHtml *html, char *tag, gint tagsize)
1683 {
1684 Html_stash_init(html);
1685 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
1686 }
1687
1688 /*
1689 * Handle close SCRIPT
1690 */
1691 static void Html_tag_close_script(DilloHtml *html, gint TagIdx)
1692 {
1693 /* eventually the stash will be sent to an interpreter for parsing */
1694 Html_pop_tag(html, TagIdx);
1695 }
1696
1697 /*
1698 * Handle open STYLE
1699 * store the contents to the stash where (in the future) the style
1700 * sheet interpreter can get it.
1701 */
1702 static void Html_tag_open_style(DilloHtml *html, char *tag, gint tagsize)
1703 {
1704 Html_stash_init(html);
1705 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
1706 }
1707
1708 /*
1709 * Handle close STYLE
1710 */
1711 static void Html_tag_close_style(DilloHtml *html, gint TagIdx)
1712 {
1713 /* eventually the stash will be sent to an interpreter for parsing */
1714 Html_pop_tag(html, TagIdx);
1715 }
1716
1717 /*
1718 * <BODY>
1719 */
1720 static void Html_tag_open_body(DilloHtml *html, char *tag, gint tagsize)
1721 {
1722 const char *attrbuf;
1723 DwPage *page;
1724 DwStyle style_attrs, *style;
1725 gint32 color;
1726
1727 if (!(html->InFlags & IN_BODY))
1728 html->InFlags |= IN_BODY;
1729 ++html->Num_BODY;
1730
1731 if (html->Num_BODY > 1) {
1732 MSG_HTML("BODY element was already open\n");
1733 return;
1734 }
1735 if (html->InFlags & IN_HEAD) {
1736 /* if we're here, it's bad XHTML, no need to recover */
1737 MSG_HTML("unclosed HEAD element\n");
1738 }
1739
1740 page = DW_PAGE (html->dw);
1741
1742 if (!prefs.force_my_colors) {
1743 if ((attrbuf = Html_get_attr(html, tag, tagsize, "bgcolor"))) {
1744 color = Html_color_parse(html, attrbuf, prefs.bg_color);
1745 if ( (color == 0xffffff && !prefs.allow_white_bg) ||
1746 prefs.force_my_colors )
1747 color = prefs.bg_color;
1748
1749 style_attrs = *html->dw->style;
1750 style_attrs.background_color =
1751 a_Dw_style_color_new (color, html->bw->main_window->window);
1752 style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
1753 a_Dw_widget_set_style (html->dw, style);
1754 a_Dw_style_unref (style);
1755 html->stack[html->stack_top].current_bg_color = color;
1756 }
1757
1758 if ((attrbuf = Html_get_attr(html, tag, tagsize, "text"))) {
1759 color = Html_color_parse(html, attrbuf, prefs.text_color);
1760 HTML_SET_TOP_ATTR
1761 (html, color,
1762 a_Dw_style_color_new (color, html->bw->main_window->window));
1763 }
1764
1765 if ((attrbuf = Html_get_attr(html, tag, tagsize, "link")))
1766 html->linkblock->link_color = Html_color_parse(html, attrbuf,
1767 prefs.link_color);
1768
1769 if ((attrbuf = Html_get_attr(html, tag, tagsize, "vlink")))
1770 html->linkblock->visited_color =
1771 Html_color_parse(html, attrbuf, prefs.visited_color);
1772
1773 if (prefs.contrast_visited_color) {
1774 /* get a color that has a "safe distance" from text, link and bg */
1775 html->linkblock->visited_color =
1776 a_Color_vc(html->linkblock->visited_color,
1777 html->stack[html->stack_top].style->color->color_val,
1778 html->linkblock->link_color,
1779 html->stack[html->stack_top].current_bg_color);
1780 }
1781 }
1782
1783 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_BODY;
1784 }
1785
1786 /*
1787 * BODY
1788 */
1789 static void Html_tag_close_body(DilloHtml *html, gint TagIdx)
1790 {
1791 if (html->Num_BODY == 1) {
1792 /* some tag soup pages use multiple BODY tags... */
1793 html->InFlags &= ~IN_BODY;
1794 }
1795 Html_pop_tag(html, TagIdx);
1796 }
1797
1798 /*
1799 * <P>
1800 * todo: what's the point between adding the parbreak before and
1801 * after the push?
1802 */
1803 static void Html_tag_open_p(DilloHtml *html, char *tag, gint tagsize)
1804 {
1805 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
1806 html->stack[(html)->stack_top].style);
1807 Html_tag_set_align_attr (html, tag, tagsize);
1808 }
1809
1810 /*
1811 * <TABLE>
1812 */
1813 static void Html_tag_open_table(DilloHtml *html, char *tag, gint tagsize)
1814 {
1815 #ifdef USE_TABLES
1816 DwWidget *table;
1817 DwStyle style_attrs, *tstyle, *old_style;
1818 const char *attrbuf;
1819 gint32 border = 0, cellspacing = 1, cellpadding = 2, bgcolor;
1820 #endif
1821
1822 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
1823 html->stack[(html)->stack_top].style);
1824
1825 #ifdef USE_TABLES
1826 if ((attrbuf = Html_get_attr(html, tag, tagsize, "border")))
1827 border = *attrbuf ? Html_check_int(strtol(attrbuf,NULL,10), 0,100,1) : 1;
1828 if ((attrbuf = Html_get_attr(html, tag, tagsize, "cellspacing")))
1829 cellspacing = Html_check_int(strtol(attrbuf, NULL, 10), 0, 100, 1);
1830 if ((attrbuf = Html_get_attr(html, tag, tagsize, "cellpadding")))
1831 cellpadding = Html_check_int(strtol(attrbuf, NULL, 10), 0, 100, 2);
1832
1833 /* The style for the table */
1834 style_attrs = *html->stack[html->stack_top].style;
1835
1836 /* When dillo was started with the --debug-rendering option, there
1837 * is always a border around the table. */
1838 if (dillo_dbg_rendering)
1839 a_Dw_style_box_set_val (&style_attrs.border_width, MIN (border, 1));
1840 else
1841 a_Dw_style_box_set_val (&style_attrs.border_width, border);
1842
1843 a_Dw_style_box_set_border_color
1844 (&style_attrs,
1845 a_Dw_style_shaded_color_new (
1846 html->stack[html->stack_top].current_bg_color,
1847 html->bw->main_window->window));
1848 a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_OUTSET);
1849 style_attrs.border_spacing = cellspacing;
1850
1851 if ((attrbuf = Html_get_attr(html, tag, tagsize, "width"))) {
1852 int dw_len = Html_parse_length (html, attrbuf);
1853 int len = strtol(attrbuf, NULL, 10);
1854 if ((DW_STYLE_IS_PER_LENGTH(dw_len) &&
1855 Html_check_int(len, 0, 100, -1) != -1) ||
1856 (DW_STYLE_IS_ABS_LENGTH(dw_len) &&
1857 Html_check_int(len, 0, 5000, -1) != -1) ||
1858 (DW_STYLE_IS_REL_LENGTH(dw_len) &&
1859 Html_check_int(len, 0, 100, -1) != -1)) {
1860 style_attrs.width = dw_len;
1861 }
1862 }
1863
1864 if ((attrbuf = Html_get_attr(html, tag, tagsize, "align"))) {
1865 if (g_strcasecmp (attrbuf, "left") == 0)
1866 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_LEFT;
1867 else if (g_strcasecmp (attrbuf, "right") == 0)
1868 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_RIGHT;
1869 else if (g_strcasecmp (attrbuf, "center") == 0)
1870 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_CENTER;
1871 }
1872
1873 if (!prefs.force_my_colors &&
1874 (attrbuf = Html_get_attr(html, tag, tagsize, "bgcolor"))) {
1875 bgcolor = Html_color_parse(html, attrbuf, -1);
1876 if (bgcolor != -1) {
1877 if (bgcolor == 0xffffff && !prefs.allow_white_bg)
1878 bgcolor = prefs.bg_color;
1879 html->stack[html->stack_top].current_bg_color = bgcolor;
1880 style_attrs.background_color =
1881 a_Dw_style_color_new (bgcolor, html->bw->main_window->window);
1882 }
1883 }
1884
1885 tstyle = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
1886
1887 /* The style for the cells */
1888 style_attrs = *html->stack[html->stack_top].style;
1889 /* When dillo was started with the --debug-rendering option, there
1890 * is always a border around the cells. */
1891 if (dillo_dbg_rendering)
1892 a_Dw_style_box_set_val (&style_attrs.border_width, 1);
1893 else
1894 a_Dw_style_box_set_val (&style_attrs.border_width, border ? 1 : 0);
1895
1896 a_Dw_style_box_set_val (&style_attrs.padding, cellpadding);
1897 a_Dw_style_box_set_border_color (&style_attrs, tstyle->border_color.top);
1898 a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_INSET);
1899
1900 old_style = html->stack[html->stack_top].table_cell_style;
1901 html->stack[html->stack_top].table_cell_style =
1902 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
1903 if (old_style)
1904 a_Dw_style_unref (old_style);
1905
1906 table = a_Dw_table_new ();
1907 a_Dw_page_add_widget (DW_PAGE (html->dw), table, tstyle);
1908 a_Dw_style_unref (tstyle);
1909
1910 html->stack[html->stack_top].table_mode = DILLO_HTML_TABLE_MODE_TOP;
1911 html->stack[html->stack_top].cell_text_align_set = FALSE;
1912 html->stack[html->stack_top].table = table;
1913 #endif
1914 }
1915
1916
1917 /*
1918 * used by <TD> and <TH>
1919 */
1920 static void Html_tag_open_table_cell(DilloHtml *html, char *tag, gint tagsize,
1921 DwStyleTextAlignType text_align)
1922 {
1923 #ifdef USE_TABLES
1924 DwWidget *col_page;
1925 gint colspan = 1, rowspan = 1;
1926 const char *attrbuf;
1927 DwStyle style_attrs, *style, *old_style;
1928 gint32 bgcolor;
1929 gboolean new_style;
1930
1931 switch (html->stack[html->stack_top].table_mode) {
1932 case DILLO_HTML_TABLE_MODE_NONE:
1933 MSG_HTML("<td> or <th> outside <table>\n");
1934 return;
1935
1936 case DILLO_HTML_TABLE_MODE_TOP:
1937 MSG_HTML("<td> or <th> outside <tr>\n");
1938 /* a_Dw_table_add_cell takes care that dillo does not crash. */
1939 /* continues */
1940 case DILLO_HTML_TABLE_MODE_TR:
1941 case DILLO_HTML_TABLE_MODE_TD:
1942 /* todo: check errors? */
1943 if ((attrbuf = Html_get_attr(html, tag, tagsize, "colspan")))
1944 colspan = Html_check_int(strtol(attrbuf, NULL, 10), 0, 1000, 1);
1945 if ((attrbuf = Html_get_attr(html, tag, tagsize, "rowspan")))
1946 rowspan = Html_check_int(strtol(attrbuf, NULL, 10), 0, 1000, 1);
1947
1948 /* text style */
1949 old_style = html->stack[html->stack_top].style;
1950 style_attrs = *old_style;
1951 if (!html->stack[html->stack_top].cell_text_align_set)
1952 style_attrs.text_align = text_align;
1953 if (Html_get_attr(html, tag, tagsize, "nowrap"))
1954 style_attrs.white_space = DW_STYLE_WHITE_SPACE_NOWRAP;
1955 else
1956 style_attrs.white_space = DW_STYLE_WHITE_SPACE_NORMAL;
1957
1958 html->stack[html->stack_top].style =
1959 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
1960 a_Dw_style_unref (old_style);
1961 Html_tag_set_align_attr (html, tag, tagsize);
1962
1963 /* cell style */
1964 style_attrs = *html->stack[html->stack_top].table_cell_style;
1965 new_style = FALSE;
1966
1967 if ((attrbuf = Html_get_attr(html, tag, tagsize, "width"))) {
1968 style_attrs.width = Html_parse_length (html, attrbuf);
1969 new_style = TRUE;
1970 }
1971
1972 if (Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs))
1973 new_style = TRUE;
1974
1975 if (!prefs.force_my_colors &&
1976 (attrbuf = Html_get_attr(html, tag, tagsize, "bgcolor"))) {
1977 bgcolor = Html_color_parse(html, attrbuf, -1);
1978 if (bgcolor != -1) {
1979 if (bgcolor == 0xffffff && !prefs.allow_white_bg)
1980 bgcolor = prefs.bg_color;
1981
1982 new_style = TRUE;
1983 style_attrs.background_color =
1984 a_Dw_style_color_new (bgcolor, html->bw->main_window->window);
1985 html->stack[html->stack_top].current_bg_color = bgcolor;
1986 }
1987 }
1988
1989 if (html->stack[html->stack_top].style->text_align
1990 == DW_STYLE_TEXT_ALIGN_STRING)
1991 col_page = a_Dw_table_cell_new
1992 (a_Dw_table_get_cell_ref
1993 (DW_TABLE (html->stack[html->stack_top].table)));
1994 else
1995 col_page = a_Dw_page_new ();
1996
1997 if (new_style) {
1998 style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
1999 a_Dw_widget_set_style (col_page, style);
2000 a_Dw_style_unref (style);
2001 } else
2002 a_Dw_widget_set_style (col_page,
2003 html->stack[html->stack_top].table_cell_style);
2004
2005 a_Dw_table_add_cell (DW_TABLE (html->stack[html->stack_top].table),
2006 col_page, colspan, rowspan);
2007 html->stack[html->stack_top].page = html->dw = col_page;
2008
2009 /* Handle it when the user clicks on a link */
2010 Html_connect_signals(html, GTK_OBJECT(col_page));
2011 break;
2012
2013 default:
2014 /* compiler happiness */
2015 break;
2016 }
2017
2018 html->stack[html->stack_top].table_mode = DILLO_HTML_TABLE_MODE_TD;
2019 #endif
2020 }
2021
2022
2023 /*
2024 * <TD>
2025 */
2026 static void Html_tag_open_td(DilloHtml *html, char *tag, gint tagsize)
2027 {
2028 Html_tag_open_table_cell (html, tag, tagsize, DW_STYLE_TEXT_ALIGN_LEFT);
2029 }
2030
2031
2032 /*
2033 * <TH>
2034 */
2035 static void Html_tag_open_th(DilloHtml *html, char *tag, gint tagsize)
2036 {
2037 Html_set_top_font(html, NULL, 0, 1, 1);
2038 Html_tag_open_table_cell (html, tag, tagsize, DW_STYLE_TEXT_ALIGN_CENTER);
2039 }
2040
2041
2042 /*
2043 * <TR>
2044 */
2045 static void Html_tag_open_tr(DilloHtml *html, char *tag, gint tagsize)
2046 {
2047 const char *attrbuf;
2048 DwStyle style_attrs, *style, *old_style;
2049 gint32 bgcolor;
2050
2051 #ifdef USE_TABLES
2052 switch (html->stack[html->stack_top].table_mode) {
2053 case DILLO_HTML_TABLE_MODE_NONE:
2054 _MSG("Invalid HTML syntax: <tr> outside <table>\n");
2055 return;
2056
2057 case DILLO_HTML_TABLE_MODE_TOP:
2058 case DILLO_HTML_TABLE_MODE_TR:
2059 case DILLO_HTML_TABLE_MODE_TD:
2060 style = NULL;
2061
2062 if (!prefs.force_my_colors &&
2063 (attrbuf = Html_get_attr(html, tag, tagsize, "bgcolor"))) {
2064 bgcolor = Html_color_parse(html, attrbuf, -1);
2065 if (bgcolor != -1) {
2066 if (bgcolor == 0xffffff && !prefs.allow_white_bg)
2067 bgcolor = prefs.bg_color;
2068
2069 style_attrs = *html->stack[html->stack_top].style;
2070 style_attrs.background_color =
2071 a_Dw_style_color_new (bgcolor, html->bw->main_window->window);
2072 style =
2073 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2074 html->stack[html->stack_top].current_bg_color = bgcolor;
2075 }
2076 }
2077
2078 a_Dw_table_add_row (DW_TABLE (html->stack[html->stack_top].table),
2079 style);
2080 if (style)
2081 a_Dw_style_unref (style);
2082
2083 if (Html_get_attr (html, tag, tagsize, "align")) {
2084 html->stack[html->stack_top].cell_text_align_set = TRUE;
2085 Html_tag_set_align_attr (html, tag, tagsize);
2086 }
2087
2088 style_attrs = *html->stack[html->stack_top].table_cell_style;
2089 if (Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs)) {
2090 old_style = html->stack[html->stack_top].table_cell_style;
2091 html->stack[html->stack_top].table_cell_style =
2092 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2093 a_Dw_style_unref (old_style);
2094 } else
2095
2096 break;
2097
2098 default:
2099 break;
2100 }
2101
2102 html->stack[html->stack_top].table_mode = DILLO_HTML_TABLE_MODE_TR;
2103 #else
2104 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
2105 html->stack[(html)->stack_top].style);
2106 #endif
2107 }
2108
2109 /*
2110 * <FRAME>, <IFRAME>
2111 * todo: This is just a temporary fix while real frame support
2112 * isn't finished. Imitates lynx/w3m's frames.
2113 */
2114 static void Html_tag_open_frame (DilloHtml *html, gchar *tag, gint tagsize)
2115 {
2116 const char *attrbuf;
2117 gchar *src, *buf;
2118 DilloUrl *url;
2119 DwPage *page;
2120 DwStyle style_attrs, *link_style;
2121 DwWidget *bullet;
2122 gint buf_size;
2123
2124 page = DW_PAGE(html->dw);
2125
2126 if ( !(attrbuf = Html_get_attr(html, tag, tagsize, "src")) )
2127 return;
2128
2129 if (!(url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0)))
2130 return;
2131
2132 src = g_strdup(attrbuf);
2133
2134 style_attrs = *(html->stack[html->stack_top].style);
2135
2136 if (a_Capi_get_buf(url, &buf, &buf_size)) /* visited frame */
2137 style_attrs.color = a_Dw_style_color_new
2138 (html->linkblock->visited_color, html->bw->main_window->window);
2139 else /* unvisited frame */
2140 style_attrs.color = a_Dw_style_color_new
2141 (html->linkblock->link_color, html->bw->main_window->window);
2142
2143 style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_UNDERLINE;
2144 style_attrs.x_link = Html_set_new_link(html, &url);
2145 link_style = a_Dw_style_new (&style_attrs,
2146 html->bw->main_window->window);
2147
2148 a_Dw_page_add_parbreak(page, 5, html->stack[(html)->stack_top].style);
2149
2150 /* The bullet will be assigned the current list style, which should
2151 * be "disc" by default, but may in very weird pages be different.
2152 * Anyway, there should be no harm. */
2153 bullet = a_Dw_bullet_new();
2154 a_Dw_page_add_widget(page, bullet, html->stack[html->stack_top].style);
2155 a_Dw_page_add_space(page, html->stack[html->stack_top].style);
2156
2157 if (tolower(tag[1]) == 'i') {
2158 /* IFRAME usually comes with very long advertising/spying URLS,
2159 * to not break rendering we will force name="IFRAME" */
2160 a_Dw_page_add_text(page, g_strdup("IFRAME"), link_style);
2161
2162 } else {
2163 /* FRAME:
2164 * If 'name' tag is present use it, if not use 'src' value */
2165 if ( !(attrbuf = Html_get_attr(html, tag, tagsize, "name")) ) {
2166 a_Dw_page_add_text(page, g_strdup(src), link_style);
2167 } else {
2168 a_Dw_page_add_text(page, g_strdup(attrbuf), link_style);
2169 }
2170 }
2171
2172 a_Dw_page_add_parbreak(page, 5, html->stack[(html)->stack_top].style);
2173
2174 a_Dw_style_unref(link_style);
2175 g_free(src);
2176 }
2177
2178 /*
2179 * <FRAMESET>
2180 * todo: This is just a temporary fix while real frame support
2181 * isn't finished. Imitates lynx/w3m's frames.
2182 */
2183 static void Html_tag_open_frameset (DilloHtml *html, gchar *tag, gint tagsize)
2184 {
2185 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2186 html->stack[(html)->stack_top].style);
2187 a_Dw_page_add_text(DW_PAGE(html->dw), g_strdup("--FRAME--"),
2188 html->stack[html->stack_top].style);
2189 Html_add_indented(html, 40, 0, 5);
2190 }
2191
2192 /*
2193 * <H1> | <H2> | <H3> | <H4> | <H5> | <H6>
2194 */
2195 static void Html_tag_open_h(DilloHtml *html, char *tag, gint tagsize)
2196 {
2197 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2198 html->stack[(html)->stack_top].style);
2199
2200 /* todo: combining these two would be slightly faster */
2201 Html_set_top_font(html, prefs.vw_fontname,
2202 Html_level_to_fontsize(FontSizesNum - (tag[2] - '0')),
2203 1, 3);
2204 Html_tag_set_align_attr (html, tag, tagsize);
2205
2206 /* First finalize unclosed H tags (we test if already named anyway) */
2207 a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
2208 a_Menu_pagemarks_add(html->bw, DW_PAGE (html->dw),
2209 html->stack[html->stack_top].style, (tag[2] - '0'));
2210 Html_stash_init(html);
2211 html->stack[html->stack_top].parse_mode =
2212 DILLO_HTML_PARSE_MODE_STASH_AND_BODY;
2213 }
2214
2215 /*
2216 * Handle close: <H1> | <H2> | <H3> | <H4> | <H5> | <H6>
2217 */
2218 static void Html_tag_close_h(DilloHtml *html, gint TagIdx)
2219 {
2220 a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
2221 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2222 html->stack[(html)->stack_top].style);
2223 Html_pop_tag(html, TagIdx);
2224 }
2225
2226 /*
2227 * <BIG> | <SMALL>
2228 */
2229 static void Html_tag_open_big_small(DilloHtml *html, char *tag, gint tagsize)
2230 {
2231 gint level;
2232
2233 level =
2234 Html_fontsize_to_level(html->stack[html->stack_top].style->font->size) +
2235 ((g_strncasecmp(tag+1, "big", 3)) ? -1 : 1);
2236 Html_set_top_font(html, NULL, Html_level_to_fontsize(level), 0, 0);
2237 }
2238
2239 /*
2240 * <BR>
2241 */
2242 static void Html_tag_open_br(DilloHtml *html, char *tag, gint tagsize)
2243 {
2244 a_Dw_page_add_linebreak(DW_PAGE (html->dw),
2245 html->stack[(html)->stack_top].style);
2246 }
2247
2248 /*
2249 * <BUTTON>
2250 */
2251 static void Html_tag_open_button(DilloHtml *html, char *tag, gint tagsize)
2252 {
2253 /*
2254 * Buttons are rendered on one line, this is (at several levels) a
2255 * bit simpler. May be changed in the future.
2256 */
2257 DwStyle style_attrs, *style;
2258 DwWidget *button, *page;
2259 DilloHtmlForm *form;
2260 DilloHtmlLB *html_lb;
2261 DilloHtmlInputType inp_type;
2262 gchar *name, *value, *type;
2263
2264 /* Render the button */
2265 style_attrs = *html->stack[html->stack_top].style;
2266
2267 a_Dw_style_box_set_val(&style_attrs.margin, 0);
2268 a_Dw_style_box_set_val(&style_attrs.border_width, 0);
2269 a_Dw_style_box_set_val(&style_attrs.padding, 0);
2270 style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2271 button = a_Dw_button_new (DW_USES_HINTS, TRUE);
2272
2273 /* The new button is not set button-insensitive, since nested buttons
2274 * (if they are anyway allowed, todo: search in spec) should all be
2275 * activatable. */
2276 a_Dw_widget_set_button_sensitive (button, TRUE);
2277
2278 a_Dw_page_add_parbreak (DW_PAGE (html->dw), 5, style);
2279 a_Dw_page_add_widget (DW_PAGE (html->dw), button, style);
2280 a_Dw_page_add_parbreak (DW_PAGE (html->dw), 5, style);
2281 a_Dw_style_unref (style);
2282
2283 a_Dw_style_box_set_val(&style_attrs.margin, 5);
2284 style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2285 page = a_Dw_page_new ();
2286 a_Dw_widget_set_style (page, style);
2287 a_Dw_style_unref (style);
2288 a_Dw_container_add (DW_CONTAINER (button), page);
2289 a_Dw_widget_set_button_sensitive (DW_WIDGET (page), FALSE);
2290 a_Dw_style_box_set_val(&style_attrs.margin, 0);
2291
2292 a_Dw_button_set_sensitive (DW_BUTTON (button), FALSE);
2293
2294 html->stack[html->stack_top].page = html->dw = page;
2295
2296 /* Handle it when the user clicks on a link */
2297 Html_connect_signals(html, GTK_OBJECT(page));
2298
2299 /* Connect it to the form */
2300 html_lb = html->linkblock;
2301 form = &(html_lb->forms[html_lb->num_forms - 1]);
2302
2303 type = Html_get_attr_wdef(html, tag, tagsize, "type", "");
2304
2305 if (strcmp(type, "submit") == 0) {
2306 inp_type = DILLO_HTML_INPUT_BUTTON_SUBMIT;
2307 gtk_signal_connect(GTK_OBJECT(button), "clicked",
2308 GTK_SIGNAL_FUNC(Html_submit_form), html_lb);
2309 } else if (strcmp(type, "reset") == 0) {
2310 inp_type = DILLO_HTML_INPUT_BUTTON_RESET;
2311 gtk_signal_connect(GTK_OBJECT(button), "clicked",
2312 GTK_SIGNAL_FUNC(Html_reset_form), html_lb);
2313 } else
2314 return;
2315
2316 value = Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
2317 name = Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
2318
2319 Html_add_input(form, inp_type, (GtkWidget*)button, name, value,
2320 NULL, FALSE);
2321
2322 g_free(type);
2323 g_free(name);
2324 g_free(value);
2325 }
2326
2327
2328 /*
2329 * <FONT>
2330 */
2331 static void Html_tag_open_font(DilloHtml *html, char *tag, gint tagsize)
2332 {
2333 #if 1
2334 DwStyle style_attrs, *old_style;
2335 /*DwStyleFont font;*/
2336 const char *attrbuf;
2337 gint32 color;
2338
2339 if (!prefs.force_my_colors) {
2340 old_style = html->stack[html->stack_top].style;
2341 style_attrs = *old_style;
2342
2343 if ((attrbuf = Html_get_attr(html, tag, tagsize, "color"))) {
2344 if (prefs.contrast_visited_color && html->InVisitedLink) {
2345 color = html->linkblock->visited_color;
2346 } else {
2347 /* use the tag-specified color */
2348 color = Html_color_parse(
2349 html, attrbuf, style_attrs.color->color_val);
2350 style_attrs.color = a_Dw_style_color_new
2351 (color, html->bw->main_window->window);
2352 }
2353 }
2354
2355 #if 0
2356 if ((attrbuf = Html_get_attr(html, tag, tagsize, "face"))) {
2357 font = *( style_attrs.font );
2358 font.name = attrbuf;
2359 style_attrs.font = a_Dw_style_font_new_from_list (&font);
2360 }
2361 #endif
2362
2363 html->stack[html->stack_top].style =
2364 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2365 a_Dw_style_unref (old_style);
2366 }
2367
2368 #endif
2369 }
2370
2371 /*
2372 * <ABBR>
2373 */
2374 static void Html_tag_open_abbr(DilloHtml *html, char *tag, gint tagsize)
2375 {
2376 DwTooltip *tooltip;
2377 const char *attrbuf;
2378
2379 if ((attrbuf = Html_get_attr(html, tag, tagsize, "title"))) {
2380 tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
2381 HTML_SET_TOP_ATTR(html, x_tooltip, tooltip);
2382 }
2383 }
2384
2385 /*
2386 * <B>
2387 */
2388 static void Html_tag_open_b(DilloHtml *html, char *tag, gint tagsize)
2389 {
2390 Html_set_top_font(html, NULL, 0, 1, 1);
2391 }
2392
2393 /*
2394 * <STRONG>
2395 */
2396 static void Html_tag_open_strong(DilloHtml *html, char *tag, gint tagsize)
2397 {
2398 Html_set_top_font(html, NULL, 0, 1, 1);
2399 }
2400
2401 /*
2402 * <I>
2403 */
2404 static void Html_tag_open_i(DilloHtml *html, char *tag, gint tagsize)
2405 {
2406 Html_set_top_font(html, NULL, 0, 2, 2);
2407 }
2408
2409 /*
2410 * <EM>
2411 */
2412 static void Html_tag_open_em(DilloHtml *html, char *tag, gint tagsize)
2413 {
2414 Html_set_top_font(html, NULL, 0, 2, 2);
2415 }
2416
2417 /*
2418 * <CITE>
2419 */
2420 static void Html_tag_open_cite(DilloHtml *html, char *tag, gint tagsize)
2421 {
2422 Html_set_top_font(html, NULL, 0, 2, 2);
2423 }
2424
2425 /*
2426 * <CENTER>
2427 */
2428 static void Html_tag_open_center(DilloHtml *html, char *tag, gint tagsize)
2429 {
2430 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
2431 html->stack[(html)->stack_top].style);
2432 HTML_SET_TOP_ATTR(html, text_align, DW_STYLE_TEXT_ALIGN_CENTER);
2433 }
2434
2435 /*
2436 * <ADDRESS>
2437 */
2438 static void Html_tag_open_address(DilloHtml *html, char *tag, gint tagsize)
2439 {
2440 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2441 html->stack[(html)->stack_top].style);
2442 Html_set_top_font(html, NULL, 0, 2, 2);
2443 }
2444
2445 /*
2446 * <TT>
2447 */
2448 static void Html_tag_open_tt(DilloHtml *html, char *tag, gint tagsize)
2449 {
2450 Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
2451 }
2452
2453 /*
2454 * Read image-associated tag attributes,
2455 * create new image and add it to the html page (if add is TRUE).
2456 */
2457 static DilloImage *Html_add_new_image(DilloHtml *html, char *tag,
2458 gint tagsize, DwStyle *style_attrs,
2459 gboolean add)
2460 {
2461 const int MAX_W = 6000, MAX_H = 6000;
2462
2463 DilloImage *Image;
2464 char *width_ptr, *height_ptr, *alt_ptr;
2465 const char *attrbuf;
2466 DwStyleLength s_w, s_h;
2467 int space, w = 0, h = 0;
2468
2469 if (prefs.show_tooltip &&
2470 (attrbuf = Html_get_attr(html, tag, tagsize, "title")))
2471 style_attrs->x_tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
2472
2473 alt_ptr = Html_get_attr_wdef(html, tag, tagsize, "alt", NULL);
2474 width_ptr = Html_get_attr_wdef(html, tag, tagsize, "width", NULL);
2475 height_ptr = Html_get_attr_wdef(html, tag, tagsize, "height", NULL);
2476 // Check for malicious values
2477 // TODO: the same for percentage and relative lengths.
2478 if (width_ptr) {
2479 s_w = Html_parse_length (html, width_ptr);
2480 w = (DW_STYLE_IS_ABS_LENGTH(s_w)) ? DW_STYLE_ABS_LENGTH_VAL(s_w) : 0;
2481 }
2482 if (height_ptr) {
2483 s_h = Html_parse_length (html, height_ptr);
2484 h = (DW_STYLE_IS_ABS_LENGTH(s_h)) ? DW_STYLE_ABS_LENGTH_VAL(s_h) : 0;
2485 }
2486 if (w < 0 || h < 0 || abs(w*h) > MAX_W * MAX_H) {
2487 g_free(width_ptr);
2488 g_free(height_ptr);
2489 width_ptr = height_ptr = NULL;
2490 MSG("Html_add_new_image: suspicious image size request %dx%d\n", w, h);
2491 }
2492
2493 /* todo: we should scale the image respecting its ratio.
2494 * As the image size is not known at this time, maybe a flag
2495 * can be set to scale it later.
2496 if ((width_ptr && !height_ptr) || (height_ptr && !width_ptr))
2497 [...]
2498 */
2499
2500 /* Spacing to the left and right */
2501 if ((attrbuf = Html_get_attr(html, tag, tagsize, "hspace"))) {
2502 space = strtol(attrbuf, NULL, 10);
2503
2504 if (space > 0)
2505 style_attrs->margin.left = style_attrs->margin.right = space;
2506 }
2507
2508 /* Spacing at the top and bottom */
2509 if ((attrbuf = Html_get_attr(html, tag, tagsize, "vspace"))) {
2510 space = strtol(attrbuf, NULL, 10);
2511
2512 if (space > 0)
2513 style_attrs->margin.top = style_attrs->margin.bottom = space;
2514 }
2515
2516 /* Add a new image widget to this page */
2517 if ((Image = a_Image_new(0, 0, alt_ptr,
2518 html->stack[html->stack_top].current_bg_color)))
2519 if (add)
2520 Html_add_widget(html, DW_WIDGET(Image->dw), width_ptr, height_ptr,
2521 style_attrs);
2522
2523 g_free(width_ptr);
2524 g_free(height_ptr);
2525 g_free(alt_ptr);
2526 return Image;
2527 }
2528
2529 /*
2530 * Tell cache to retrieve image
2531 */
2532 static void Html_load_image(DilloHtml *html, DilloUrl *url, DilloImage *Image)
2533 {
2534 DilloWeb *Web;
2535 gint ClientKey;
2536 /* Fill a Web structure for the cache query */
2537 Web = a_Web_new(url);
2538 Web->bw = html->bw;
2539 Web->Image = Image;
2540 Web->flags |= WEB_Image;
2541 /* Request image data from the cache */
2542 if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
2543 a_Interface_add_client(html->bw, ClientKey, 0);
2544 a_Interface_add_url(html->bw, url, WEB_Image);
2545 }
2546 }
2547
2548 /*
2549 * Create a new Image struct and request the image-url to the cache
2550 * (If it either hits or misses, is not relevant here; that's up to the
2551 * cache functions)
2552 */
2553 static void Html_tag_open_img(DilloHtml *html, char *tag, gint tagsize)
2554 {
2555 DilloImage *Image;
2556 DilloUrl *url, *usemap_url;
2557 DwPage *page;
2558 DwStyle style_attrs;
2559 const char *attrbuf;
2560 gint border;
2561
2562 /* This avoids loading images. Useful for viewing suspicious HTML email. */
2563 if (URL_FLAGS(html->linkblock->base_url) & URL_SpamSafe)
2564 return;
2565
2566 if (!(attrbuf = Html_get_attr(html, tag, tagsize, "src")) ||
2567 !(url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0)))
2568 return;
2569
2570 page = DW_PAGE (html->dw);
2571
2572
2573 usemap_url = NULL;
2574 if ((attrbuf = Html_get_attr(html, tag, tagsize, "usemap")))
2575 /* todo: usemap URLs outside of the document are not used. */
2576 usemap_url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0);
2577
2578 style_attrs = *html->stack[html->stack_top].style;
2579
2580 if (html->stack[html->stack_top].style->x_link != -1 ||
2581 usemap_url != NULL) {
2582 /* Images within links */
2583 border = 1;
2584 if ((attrbuf = Html_get_attr(html, tag, tagsize, "border")))
2585 border = strtol (attrbuf, NULL, 10);
2586
2587 if (html->stack[html->stack_top].style->x_link != -1)
2588 /* In this case we can use the text color */
2589 a_Dw_style_box_set_border_color
2590 (&style_attrs,
2591 a_Dw_style_shaded_color_new (style_attrs.color->color_val,
2592 html->bw->main_window->window));
2593 else
2594 a_Dw_style_box_set_border_color
2595 (&style_attrs,
2596 a_Dw_style_shaded_color_new (html->linkblock->link_color,
2597 html->bw->main_window->window));
2598
2599 a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_SOLID);
2600 a_Dw_style_box_set_val (&style_attrs.border_width, border);
2601 }
2602
2603 Image = Html_add_new_image(html, tag, tagsize, &style_attrs, TRUE);
2604 Html_connect_signals(html, GTK_OBJECT(Image->dw));
2605 gtk_signal_connect_after(GTK_OBJECT(Image->dw), "button_press_event",
2606 GTK_SIGNAL_FUNC(Html_image_menu), html->bw);
2607
2608 /* Image maps */
2609 if (Html_get_attr(html, tag, tagsize, "ismap")) {
2610 /* BUG: if several ISMAP images follow each other without
2611 * being separated with a word, only the first one is ISMAPed
2612 */
2613 a_Dw_image_set_ismap (Image->dw);
2614 _MSG(" Html_tag_open_img: server-side map (ISMAP)\n");
2615 } else if (html->stack[html->stack_top].style->x_link != -1 &&
2616 usemap_url == NULL)
2617 /* For simple links, we have to suppress the "image_pressed" signal.
2618 * This is overridden for USEMAP images. */
2619 a_Dw_widget_set_button_sensitive (DW_WIDGET (Image->dw), FALSE);
2620
2621 if (usemap_url) {
2622 a_Dw_image_set_usemap (Image->dw, &html->linkblock->maps, usemap_url);
2623 a_Url_free (usemap_url);
2624 }
2625
2626 Html_load_image(html, url, Image);
2627 a_Url_free(url);
2628 }
2629
2630 /*
2631 * <map>
2632 */
2633 static void Html_tag_open_map(DilloHtml *html, char *tag, gint tagsize)
2634 {
2635 char *hash_name;
2636 const char *attrbuf;
2637 DilloUrl *url;
2638
2639 if (html->InFlags & IN_MAP) {
2640 MSG_HTML("nested <map>\n");
2641 } else {
2642 if ((attrbuf = Html_get_attr(html, tag, tagsize, "name"))) {
2643 hash_name = g_strdup_printf("#%s", attrbuf);
2644 url = Html_url_new(html, hash_name, NULL, 0, 0, 0, 0);
2645 a_Dw_image_map_list_add_map (&html->linkblock->maps, url);
2646 a_Url_free (url);
2647 g_free(hash_name);
2648 }
2649 html->InFlags |= IN_MAP;
2650 }
2651 }
2652
2653 /*
2654 * Handle close <MAP>
2655 */
2656 static void Html_tag_close_map(DilloHtml *html, gint TagIdx)
2657 {
2658 html->InFlags &= ~IN_MAP;
2659 Html_pop_tag(html, TagIdx);
2660 }
2661
2662 /*
2663 * Read coords in a string and fill a GdkPoint array
2664 */
2665 static int Html_read_coords(DilloHtml *html, const char *str, GdkPoint *array)
2666 {
2667 gint i, toggle, pending, coord;
2668 const char *tail = str;
2669 char *newtail = NULL;
2670
2671 i = 0;
2672 toggle = 0;
2673 pending = 1;
2674 while ( pending ) {
2675 coord = strtol(tail, &newtail, 10);
2676 if (toggle) {
2677 array[i].y = coord;
2678 array[++i].x = 0;
2679 toggle = 0;
2680 } else {
2681 array[i].x = coord;
2682 array[i].y = -1;
2683 toggle = 1;
2684 }
2685 if (!*newtail || (coord == 0 && newtail == tail)) {
2686 pending = 0;
2687 } else {
2688 if (*newtail != ',') {
2689 MSG_HTML("usemap coords MUST be separated with ','\n");
2690 }
2691 tail = newtail + 1;
2692 }
2693 }
2694
2695 return i;
2696 }
2697
2698 /*
2699 * <AREA>
2700 */
2701 static void Html_tag_open_area(DilloHtml *html, char *tag, gint tagsize)
2702 {
2703 /* todo: point must be a dynamic array */
2704 GdkPoint point[1024];
2705 DilloUrl* url;
2706 const char *attrbuf;
2707 gint type = DW_IMAGE_MAP_SHAPE_RECT;
2708 gint nbpoints, link = -1;
2709
2710 if ( (attrbuf = Html_get_attr(html, tag, tagsize, "shape")) ) {
2711 if ( g_strcasecmp(attrbuf, "rect") == 0 )
2712 type = DW_IMAGE_MAP_SHAPE_RECT;
2713 else if ( g_strcasecmp(attrbuf, "circle") == 0 )
2714 type = DW_IMAGE_MAP_SHAPE_CIRCLE;
2715 else if ( g_strncasecmp(attrbuf, "poly", 4) == 0 )
2716 type = DW_IMAGE_MAP_SHAPE_POLY;
2717 else
2718 type = DW_IMAGE_MAP_SHAPE_RECT;
2719 }
2720 /* todo: add support for coords in % */
2721 if ( (attrbuf = Html_get_attr(html, tag, tagsize, "coords")) ) {
2722 /* Is this a valid poly ?
2723 * rect = x0,y0,x1,y1 => 2
2724 * circle = x,y,r => 2
2725 * poly = x0,y0,x1,y1,x2,y2 minimum => 3 */
2726 nbpoints = Html_read_coords(html, attrbuf, point);
2727 } else
2728 return;
2729
2730 if ( Html_get_attr(html, tag, tagsize, "nohref") ) {
2731 link = -1;
2732 _MSG("nohref");
2733 }
2734
2735 if ((attrbuf = Html_get_attr(html, tag, tagsize, "href"))) {
2736 url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0);
2737 g_return_if_fail ( url != NULL );
2738 if ((attrbuf = Html_get_attr(html, tag, tagsize, "alt")))
2739 a_Url_set_alt(url, attrbuf);
2740
2741 link = Html_set_new_link(html, &url);
2742 }
2743
2744 a_Dw_image_map_list_add_shape(&html->linkblock->maps, type, link,
2745 point, nbpoints);
2746 }
2747
2748
2749 /*
2750 * Test and extract the link from a javascript instruction.
2751 */
2752 static const char* Html_get_javascript_link(DilloHtml *html)
2753 {
2754 size_t i;
2755 char ch, *p1, *p2;
2756 GString *Buf = html->attr_data;
2757
2758 if (g_strncasecmp("javascript", Buf->str, 10) == 0) {
2759 i = strcspn(Buf->str, "'\"");
2760 ch = Buf->str[i];
2761 if ((ch == '"' || ch == '\'') &&
2762 (p2 = strchr(Buf->str + i + 1 , ch))) {
2763 p1 = Buf->str + i;
2764 MSG_HTML("link depends on javascript()\n");
2765 g_string_truncate(Buf, p2 - Buf->str);
2766 g_string_erase(Buf, 0, p1 - Buf->str + 1);
2767 }
2768 }
2769 return Buf->str;
2770 }
2771
2772 /*
2773 * Register an anchor for this page.
2774 */
2775 static void Html_add_anchor(DilloHtml *html, const char *name)
2776 {
2777 _MSG("Registering ANCHOR: %s\n", name);
2778 if (!a_Dw_page_add_anchor(DW_PAGE(html->dw), name,
2779 html->stack[html->stack_top].style))
2780 MSG_HTML("Anchor names must be unique within the document\n");
2781 /* According to Sec. 12.2.1 of the HTML 4.01 spec, "anchor names that
2782 * differ only in case may not appear in the same document", but
2783 * "comparisons between fragment identifiers and anchor names must be
2784 * done by exact (case-sensitive) match." We ignore the case issue and
2785 * always test for exact matches. Moreover, what does uppercase mean
2786 * for Unicode characters outside the ASCII range? */
2787 }
2788
2789 /*
2790 * <A>
2791 */
2792 static void Html_tag_open_a(DilloHtml *html, char *tag, gint tagsize)
2793 {
2794 DwStyle style_attrs, *old_style;
2795 DilloUrl *url;
2796 const char *attrbuf;
2797 gchar *buf;
2798 gint buf_size;
2799
2800 /* todo: add support for MAP with A HREF */
2801 Html_tag_open_area(html, tag, tagsize);
2802
2803 if ((attrbuf = Html_get_attr(html, tag, tagsize, "href"))) {
2804 /* if it's a javascript link, extract the reference. */
2805 if (tolower(attrbuf[0]) == 'j')
2806 attrbuf = Html_get_javascript_link(html);
2807
2808 url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0);
2809 g_return_if_fail ( url != NULL );
2810
2811 old_style = html->stack[html->stack_top].style;
2812 style_attrs = *old_style;
2813
2814 if (a_Capi_get_buf(url, &buf, &buf_size)) {
2815 html->InVisitedLink = TRUE;
2816 style_attrs.color = a_Dw_style_color_new(
2817 html->linkblock->visited_color,
2818 /*
2819 a_Color_vc(html->linkblock->visited_color,
2820 html->stack[html->stack_top].style->color->color_val,
2821 html->linkblock->link_color,
2822 html->stack[html->stack_top].current_bg_color),
2823 */
2824 html->bw->main_window->window);
2825 } else {
2826 style_attrs.color = a_Dw_style_color_new
2827 (html->linkblock->link_color, html->bw->main_window->window);
2828 }
2829
2830 if ((attrbuf = Html_get_attr(html, tag, tagsize, "title")))
2831 style_attrs.x_tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
2832
2833 style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_UNDERLINE;
2834 style_attrs.x_link = Html_set_new_link(html, &url);
2835
2836 html->stack[html->stack_top].style =
2837 a_Dw_style_new (&style_attrs, html->bw->main_window->window);
2838 a_Dw_style_unref (old_style);
2839 }
2840
2841 if ((attrbuf = Html_get_attr(html, tag, tagsize, "name"))) {
2842 if (prefs.show_extra_warnings)
2843 Html_check_name_val(html, attrbuf, "name");
2844 /* html->NameVal is freed in Html_process_tag */
2845 html->NameVal = a_Url_decode_hex_str(attrbuf);
2846 Html_add_anchor(html, html->NameVal);
2847 }
2848 }
2849
2850 /*
2851 * <A> close function
2852 */
2853 static void Html_tag_close_a(DilloHtml *html, gint TagIdx)
2854 {
2855 html->InVisitedLink = FALSE;
2856 Html_pop_tag(html, TagIdx);
2857 }
2858
2859 /*
2860 * Insert underlined text in the page.
2861 */
2862 static void Html_tag_open_u(DilloHtml *html, char *tag, gint tagsize)
2863 {
2864 DwStyle *style;
2865 DwStyle style_attrs;
2866
2867 style = html->stack[html->stack_top].style;
2868 style_attrs = *style;
2869 style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_UNDERLINE;
2870 html->stack[html->stack_top].style =
2871 a_Dw_style_new(&style_attrs, html->bw->main_window->window);
2872 a_Dw_style_unref(style);
2873 }
2874
2875 /*
2876 * Insert strike-through text. Used by <S>, <STRIKE> and <DEL>.
2877 */
2878 static void Html_tag_open_strike(DilloHtml *html, char *tag, gint tagsize)
2879 {
2880 DwStyle *style;
2881 DwStyle style_attrs;
2882
2883 style = html->stack[html->stack_top].style;
2884 style_attrs = *style;
2885 style_attrs.text_decoration |= DW_STYLE_TEXT_DECORATION_LINE_THROUGH;
2886 html->stack[html->stack_top].style =
2887 a_Dw_style_new(&style_attrs, html->bw->main_window->window);
2888 a_Dw_style_unref(style);
2889 }
2890
2891 /*
2892 * <BLOCKQUOTE>
2893 */
2894 static void Html_tag_open_blockquote(DilloHtml *html, char *tag, gint tagsize)
2895 {
2896 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2897 html->stack[(html)->stack_top].style);
2898 Html_add_indented(html, 40, 40, 9);
2899 }
2900
2901 /*
2902 * Handle the <UL> tag.
2903 */
2904 static void Html_tag_open_ul(DilloHtml *html, char *tag, gint tagsize)
2905 {
2906 const char *attrbuf;
2907 DwStyleListStyleType list_style_type;
2908
2909 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2910 html->stack[(html)->stack_top].style);
2911 Html_add_indented(html, 40, 0, 9);
2912
2913 if ((attrbuf = Html_get_attr(html, tag, tagsize, "type"))) {
2914 /* list_style_type explicitly defined */
2915 if (g_strncasecmp(attrbuf, "disc", 4) == 0)
2916 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
2917 else if (g_strncasecmp(attrbuf, "circle", 6) == 0)
2918 list_style_type = DW_STYLE_LIST_STYLE_TYPE_CIRCLE;
2919 else if (g_strncasecmp(attrbuf, "square", 6) == 0)
2920 list_style_type = DW_STYLE_LIST_STYLE_TYPE_SQUARE;
2921 else
2922 /* invalid value */
2923 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
2924 } else {
2925 if (html->stack[html->stack_top].list_type == HTML_LIST_UNORDERED) {
2926 /* Nested <UL>'s. */
2927 /* --EG :: I changed the behavior here : types are cycling instead of
2928 * being forced to square. It's easier for mixed lists level counting.
2929 */
2930 switch (html->stack[html->stack_top].style->list_style_type) {
2931 case DW_STYLE_LIST_STYLE_TYPE_DISC:
2932 list_style_type = DW_STYLE_LIST_STYLE_TYPE_CIRCLE;
2933 break;
2934 case DW_STYLE_LIST_STYLE_TYPE_CIRCLE:
2935 list_style_type = DW_STYLE_LIST_STYLE_TYPE_SQUARE;
2936 break;
2937 case DW_STYLE_LIST_STYLE_TYPE_SQUARE:
2938 default: /* this is actually a bug */
2939 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
2940 break;
2941 }
2942 } else {
2943 /* Either first <UL>, or a <OL> before. */
2944 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
2945 }
2946 }
2947
2948 HTML_SET_TOP_ATTR(html, list_style_type, list_style_type);
2949 html->stack[html->stack_top].list_type = HTML_LIST_UNORDERED;
2950
2951 html->stack[html->stack_top].list_number = 0;
2952 html->stack[html->stack_top].ref_list_item = NULL;
2953 }
2954
2955 /*
2956 * Handle the <MENU> tag.
2957 * (Deprecated and almost the same as <UL>)
2958 */
2959 static void Html_tag_open_menu(DilloHtml *html, char *tag, gint tagsize)
2960 {
2961 DwStyleListStyleType list_style_type = DW_STYLE_LIST_STYLE_TYPE_DISC;
2962
2963 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2964 html->stack[(html)->stack_top].style);
2965 Html_add_indented(html, 40, 0, 9);
2966 HTML_SET_TOP_ATTR(html, list_style_type, list_style_type);
2967 html->stack[html->stack_top].list_type = HTML_LIST_UNORDERED;
2968 html->stack[html->stack_top].list_number = 0;
2969 html->stack[html->stack_top].ref_list_item = NULL;
2970
2971 if (prefs.show_extra_warnings)
2972 MSG_HTML("it is strongly recommended using <UL> instead of <MENU>\n");
2973 }
2974
2975 /*
2976 * Handle the <OL> tag.
2977 */
2978 static void Html_tag_open_ol(DilloHtml *html, char *tag, gint tagsize)
2979 {
2980 const char *attrbuf;
2981 DwStyleListStyleType list_style_type;
2982 int n = 1;
2983
2984 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
2985 html->stack[(html)->stack_top].style);
2986 Html_add_indented(html, 40, 0, 9);
2987
2988 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DECIMAL;
2989
2990 if ((attrbuf = Html_get_attr(html, tag, tagsize, "type"))) {
2991 if (*attrbuf == '1')
2992 list_style_type = DW_STYLE_LIST_STYLE_TYPE_DECIMAL;
2993 else if (*attrbuf == 'a')
2994 list_style_type = DW_STYLE_LIST_STYLE_TYPE_LOWER_ALPHA;
2995 else if (*attrbuf == 'A')
2996 list_style_type = DW_STYLE_LIST_STYLE_TYPE_UPPER_ALPHA;
2997 else if (*attrbuf == 'i')
2998 list_style_type = DW_STYLE_LIST_STYLE_TYPE_LOWER_ROMAN;
2999 else if (*attrbuf == 'I')
3000 list_style_type = DW_STYLE_LIST_STYLE_TYPE_UPPER_ROMAN;
3001 }
3002
3003 HTML_SET_TOP_ATTR(html, list_style_type, list_style_type);
3004 html->stack[html->stack_top].list_type = HTML_LIST_ORDERED;
3005
3006 if ((attrbuf = Html_get_attr(html, tag, tagsize, "start")) &&
3007 (n = (int) strtol(attrbuf, NULL, 10)) < 0) {
3008 MSG_HTML( "illegal '-' character in START attribute; Starting from 0\n");
3009 n = 0;
3010 }
3011 html->stack[html->stack_top].list_number = n;
3012 html->stack[html->stack_top].ref_list_item = NULL;
3013 }
3014
3015 /*
3016 * Handle the <LI> tag.
3017 */
3018 static void Html_tag_open_li(DilloHtml *html, char *tag, gint tagsize)
3019 {
3020 DwWidget *bullet, *list_item, **ref_list_item;
3021 char str[64];
3022 const char *attrbuf;
3023 gint *list_number;
3024
3025 /* Get our parent tag's variables (used as state storage) */
3026 list_number = &html->stack[html->stack_top - 1].list_number;
3027 ref_list_item = &html->stack[html->stack_top - 1].ref_list_item;
3028
3029 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
3030 html->stack[(html)->stack_top].style);
3031
3032 switch (html->stack[html->stack_top].list_type) {
3033 case HTML_LIST_NONE:
3034 MSG_HTML("<li> outside <ul> or <ol>\n");
3035 list_item = a_Dw_list_item_new(NULL);
3036 Html_add_indented_widget(html, list_item, 0, 0, 0 /* or 1 */);
3037 bullet = a_Dw_bullet_new();
3038 a_Dw_list_item_init_with_widget(DW_LIST_ITEM(html->dw), bullet,
3039 html->stack[html->stack_top].style);
3040 break;
3041
3042 default:
3043 list_item = a_Dw_list_item_new((DwListItem*)*ref_list_item);
3044 Html_add_indented_widget(html, list_item, 0, 0, 0 /* or 1 */);
3045 *ref_list_item = list_item;
3046
3047 switch (html->stack[html->stack_top].list_type) {
3048 case HTML_LIST_UNORDERED:
3049 bullet = a_Dw_bullet_new();
3050 a_Dw_list_item_init_with_widget(DW_LIST_ITEM(html->dw), bullet,
3051 html->stack[html->stack_top].style);
3052 break;
3053
3054 case HTML_LIST_ORDERED:
3055 if ((attrbuf = Html_get_attr(html, tag, tagsize, "value")))
3056 *list_number = strtol(attrbuf, NULL, 10);
3057 if (*list_number < 0) {
3058 MSG_HTML(
3059 "illegal '-' character in VALUE attribute; Starting from 0\n");
3060 *list_number = 0;
3061 }
3062 a_Dw_style_numtostr
3063 (*list_number, str, 64,
3064 html->stack[html->stack_top].style->list_style_type);
3065 (*list_number)++;
3066 a_Dw_list_item_init_with_text(DW_LIST_ITEM (html->dw), g_strdup(str),
3067 html->stack[html->stack_top].style);
3068
3069 case HTML_LIST_NONE:
3070 /* make compiler happy */
3071 break;
3072 }
3073 }
3074 }
3075
3076 /*
3077 * <HR>
3078 */
3079 static void Html_tag_open_hr(DilloHtml *html, char *tag, gint tagsize)
3080 {
3081 DwWidget *hruler;
3082 DwStyle style_attrs;
3083 char *width_ptr;
3084 const char *attrbuf;
3085 gint32 size = 0;
3086
3087 width_ptr = Html_get_attr_wdef(html, tag, tagsize, "width", "100%");
3088
3089 style_attrs = *html->stack[html->stack_top].style;
3090
3091 if ((attrbuf = Html_get_attr(html, tag, tagsize, "size")))
3092 size = strtol(attrbuf, NULL, 10);
3093
3094 if ((attrbuf = Html_get_attr(html, tag, tagsize, "align"))) {
3095 if (g_strcasecmp (attrbuf, "left") == 0)
3096 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_LEFT;
3097 else if (g_strcasecmp (attrbuf, "right") == 0)
3098 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_RIGHT;
3099 else if (g_strcasecmp (attrbuf, "center") == 0)
3100 style_attrs.text_align = DW_STYLE_TEXT_ALIGN_CENTER;
3101 }
3102
3103 /* todo: evaluate attribute */
3104 if (Html_get_attr(html, tag, tagsize, "noshade")) {
3105 a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_SOLID);
3106 a_Dw_style_box_set_border_color
3107 (&style_attrs,
3108 a_Dw_style_shaded_color_new (style_attrs.color->color_val,
3109 html->bw->main_window->window));
3110 if (size < 1)
3111 size = 1;
3112 } else {
3113 a_Dw_style_box_set_border_style (&style_attrs, DW_STYLE_BORDER_INSET);
3114 a_Dw_style_box_set_border_color
3115 (&style_attrs,
3116 a_Dw_style_shaded_color_new
3117 (html->stack[html->stack_top].current_bg_color,
3118 html->bw->main_window->window));
3119 if (size < 2)
3120 size = 2;
3121 }
3122
3123 style_attrs.border_width.top =
3124 style_attrs.border_width.left = (size + 1) / 2;
3125 style_attrs.border_width.bottom =
3126 style_attrs.border_width.right = size / 2;
3127
3128 a_Dw_page_add_parbreak (DW_PAGE (html->dw), 5,
3129 html->stack[(html)->stack_top].style);
3130 hruler = a_Dw_hruler_new ();
3131 Html_add_widget(html, hruler, width_ptr, NULL, &style_attrs);
3132 a_Dw_page_add_parbreak (DW_PAGE (html->dw), 5,
3133 html->stack[(html)->stack_top].style);
3134 g_free(width_ptr);
3135 }
3136
3137 /*
3138 * <DL>
3139 */
3140 static void Html_tag_open_dl(DilloHtml *html, char *tag, gint tagsize)
3141 {
3142 /* may want to actually do some stuff here. */
3143 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3144 html->stack[(html)->stack_top].style);
3145 }
3146
3147 /*
3148 * <DT>
3149 */
3150 static void Html_tag_open_dt(DilloHtml *html, char *tag, gint tagsize)
3151 {
3152 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3153 html->stack[(html)->stack_top].style);
3154 Html_set_top_font(html, NULL, 0, 1, 1);
3155 }
3156
3157 /*
3158 * <DD>
3159 */
3160 static void Html_tag_open_dd(DilloHtml *html, char *tag, gint tagsize)
3161 {
3162 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3163 html->stack[(html)->stack_top].style);
3164 Html_add_indented(html, 40, 40, 9);
3165 }
3166
3167 /*
3168 * <PRE>
3169 */
3170 static void Html_tag_open_pre(DilloHtml *html, char *tag, gint tagsize)
3171 {
3172 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3173 html->stack[(html)->stack_top].style);
3174 Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
3175
3176 /* Is the placement of this statement right? */
3177 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_PRE;
3178 HTML_SET_TOP_ATTR (html, white_space, DW_STYLE_WHITE_SPACE_PRE);
3179 html->pre_column = 0;
3180 html->PreFirstChar = TRUE;
3181 html->InFlags |= IN_PRE;
3182 }
3183
3184 /*
3185 * Custom close for <PRE>
3186 */
3187 static void Html_tag_close_pre(DilloHtml *html, gint TagIdx)
3188 {
3189 html->InFlags &= ~IN_PRE;
3190 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3191 html->stack[(html)->stack_top].style);
3192 Html_pop_tag(html, TagIdx);
3193 }
3194
3195 /*
3196 * Check whether a tag is in the "excluding" element set for PRE
3197 * Excl. Set = {IMG, OBJECT, APPLET, BIG, SMALL, SUB, SUP, FONT, BASEFONT}
3198 */
3199 static gint Html_tag_pre_excludes(gint tag_idx)
3200 {
3201 char *es_set[] = {"img", "object", "applet", "big", "small", "sub", "sup",
3202 "font", "basefont", NULL};
3203 static gint ei_set[10], i;
3204
3205 /* initialize array */
3206 if (!ei_set[0])
3207 for (i = 0; es_set[i]; ++i)
3208 ei_set[i] = Html_tag_index(es_set[i]);
3209
3210 for (i = 0; ei_set[i]; ++i)
3211 if (tag_idx == ei_set[i])
3212 return 1;
3213 return 0;
3214 }
3215
3216 /*
3217 * Handle <FORM> tag
3218 */
3219 static void Html_tag_open_form(DilloHtml *html, char *tag, gint tagsize)
3220 {
3221 DilloUrl *action;
3222 DilloHtmlMethod method;
3223 DilloHtmlEnc enc;
3224 const char *attrbuf;
3225
3226 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3227 html->stack[(html)->stack_top].style);
3228
3229 if (html->InFlags & IN_FORM) {
3230 MSG_HTML("nested forms\n");
3231 return;
3232 }
3233 html->InFlags |= IN_FORM;
3234
3235 method = DILLO_HTML_METHOD_GET;
3236 if ((attrbuf = Html_get_attr(html, tag, tagsize, "method"))) {
3237 if (!g_strcasecmp(attrbuf, "post"))
3238 method = DILLO_HTML_METHOD_POST;
3239 /* todo: maybe deal with unknown methods? */
3240 }
3241 if ((attrbuf = Html_get_attr(html, tag, tagsize, "action")))
3242 action = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0);
3243 else
3244 action = a_Url_dup(html->linkblock->base_url);
3245 enc = DILLO_HTML_ENC_URLENCODING;
3246 if ( (attrbuf = Html_get_attr(html, tag, tagsize, "encoding")) ) {
3247 /* todo: maybe deal with unknown encodings? */
3248 }
3249 Html_form_new(html->linkblock, method, action, enc);
3250 a_Url_free(action);
3251 }
3252
3253 static void Html_tag_close_form(DilloHtml *html, gint TagIdx)
3254 {
3255 static gchar *SubmitTag =
3256 "<input type='submit' value='?Submit?' alt='dillo-generated-button'>";
3257 DilloHtmlForm *form;
3258 gint i;
3259
3260 if (html->InFlags & IN_FORM) {
3261 form = &(html->linkblock->forms[html->linkblock->num_forms - 1]);
3262
3263 /* If we don't have a submit button and the user desires one,
3264 let's add a custom one */
3265 if (form->num_submit_buttons == 0) {
3266 if (prefs.show_extra_warnings || form->num_entry_fields != 1)
3267 MSG_HTML("FORM lacks a Submit button\n");
3268 if (prefs.generate_submit) {
3269 MSG_HTML(" (added a submit button internally)\n");
3270 Html_tag_open_input(html, SubmitTag, strlen(SubmitTag));
3271 form->num_submit_buttons = 0;
3272 }
3273 }
3274
3275 /* Make buttons sensitive again */
3276 for (i = 0; i < form->num_inputs; i++) {
3277 /* Check for tricky HTML (e.g. <input type=image>) */
3278 if (!form->inputs[i].widget)
3279 continue;
3280 if (form->inputs[i].type == DILLO_HTML_INPUT_SUBMIT ||
3281 form->inputs[i].type == DILLO_HTML_INPUT_RESET) {
3282 gtk_widget_set_sensitive(form->inputs[i].widget, TRUE);
3283 } else if (form->inputs[i].type == DILLO_HTML_INPUT_IMAGE ||
3284 form->inputs[i].type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
3285 form->inputs[i].type == DILLO_HTML_INPUT_BUTTON_RESET) {
3286 a_Dw_button_set_sensitive(DW_BUTTON(form->inputs[i].widget), TRUE);
3287 }
3288 }
3289 }
3290 html->InFlags &= ~IN_FORM;
3291 html->InFlags &= ~IN_SELECT;
3292 html->InFlags &= ~IN_TEXTAREA;
3293 Html_pop_tag(html, TagIdx);
3294 }
3295
3296 /*
3297 * Handle <META>
3298 * We do not support http-equiv=refresh because it's non standard,
3299 * (the HTML 4.01 SPEC recommends explicitily to avoid it), and it
3300 * can be easily abused!
3301 *
3302 * More info at:
3303 * http://lists.w3.org/Archives/Public/www-html/2000Feb/thread.html#232
3304 *
3305 * todo: Note that we're sending custom HTML while still IN_HEAD. This
3306 * is a hackish way to put the message. A much cleaner approach is to
3307 * build a custom widget for it.
3308 */
3309 static void Html_tag_open_meta(DilloHtml *html, char *tag, gint tagsize)
3310 {
3311 const gchar *meta_template =
3312 "<table width='100%%'><tr><td bgcolor='#ee0000'>Warning:</td>\n"
3313 " <td bgcolor='#8899aa' width='100%%'>\n"
3314 " This page uses the NON-STANDARD meta refresh tag.<br> The HTML 4.01 SPEC\n"
3315 " (sec 7.4.4) recommends explicitly to avoid it.</td></tr>\n"
3316 " <tr><td bgcolor='#a0a0a0' colspan='2'>The author wanted you to go\n"
3317 " <a href='%s'>here</a>%s</td></tr></table><br>\n";
3318
3319 const gchar *equiv, *content;
3320 gchar *html_msg, delay_str[64];
3321 gint delay;
3322
3323 /* only valid inside HEAD */
3324 if (!(html->InFlags & IN_HEAD)) {
3325 MSG_HTML("META elements must be inside the HEAD section\n");
3326 return;
3327 }
3328
3329 if ((equiv = Html_get_attr(html, tag, tagsize, "http-equiv")) &&
3330 !g_strcasecmp(equiv, "refresh") &&
3331 (content = Html_get_attr(html, tag, tagsize, "content"))) {
3332
3333 /* Get delay, if present, and make a message with it */
3334 if ((delay = strtol(content, NULL, 0)))
3335 g_snprintf(delay_str, 64, " after %d second%s.",
3336 delay, (delay > 1) ? "s" : "");
3337 else
3338 sprintf(delay_str, ".");
3339
3340 /* Skip to anything after "URL=" */
3341 while (*content && *(content++) != '=');
3342
3343 /* Send a custom HTML message
3344 * todo: this is a hairy hack, It'd be much better to build a widget. */
3345 html_msg = g_strdup_printf(meta_template, content, delay_str);
3346 {
3347 DilloHtmlProcessingState SaveFlags = html->InFlags;
3348 html->InFlags = IN_BODY;
3349 html->TagSoup = FALSE;
3350 Html_write_raw(html, html_msg, strlen(html_msg), 0);
3351 html->TagSoup = TRUE;
3352 html->InFlags = SaveFlags;
3353 }
3354 g_free(html_msg);
3355 }
3356 }
3357
3358 /*
3359 * Set the history of the menu to be consistent with the active menuitem.
3360 */
3361 static void Html_select_set_history(DilloHtmlInput *input)
3362 {
3363 gint i;
3364
3365 for (i = 0; i < input->select->num_options; i++) {
3366 if (GTK_CHECK_MENU_ITEM(input->select->options[i].menuitem)->active) {
3367 gtk_option_menu_set_history(GTK_OPTION_MENU(input->widget), i);
3368 break;
3369 }
3370 }
3371 }
3372
3373 /*
3374 * Reset the input widget to the initial value.
3375 */
3376 static void Html_reset_input(DilloHtmlInput *input)
3377 {
3378 gint i;
3379
3380 switch (input->type) {
3381 case DILLO_HTML_INPUT_TEXT:
3382 case DILLO_HTML_INPUT_PASSWORD:
3383 gtk_entry_set_text(GTK_ENTRY(input->widget), input->init_str);
3384 break;
3385 case DILLO_HTML_INPUT_CHECKBOX:
3386 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(input->widget),
3387 input->init_val);
3388 break;
3389 case DILLO_HTML_INPUT_RADIO:
3390 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(input->widget),
3391 input->init_val);
3392 break;
3393 case DILLO_HTML_INPUT_SELECT:
3394 if (input->select != NULL) {
3395 /* this is in reverse order so that, in case more than one was
3396 * selected, we get the last one, which is consistent with handling
3397 * of multiple selected options in the layout code. */
3398 for (i = input->select->num_options - 1; i >= 0; i--) {
3399 if (input->select->options[i].init_val) {
3400 gtk_menu_item_activate(GTK_MENU_ITEM
3401 (input->select->options[i].menuitem));
3402 Html_select_set_history(input);
3403 break;
3404 }
3405 }
3406 }
3407 break;
3408 case DILLO_HTML_INPUT_SEL_LIST:
3409 if (!input->select)
3410 break;
3411 for (i = 0; i < input->select->num_options; i++) {
3412 if (input->select->options[i].init_val) {
3413 if (input->select->options[i].menuitem->state == GTK_STATE_NORMAL)
3414 gtk_list_select_child(GTK_LIST(input->select->menu),
3415 input->select->options[i].menuitem);
3416 } else {
3417 if (input->select->options[i].menuitem->state==GTK_STATE_SELECTED)
3418 gtk_list_unselect_child(GTK_LIST(input->select->menu),
3419 input->select->options[i].menuitem);
3420 }
3421 }
3422 break;
3423 case DILLO_HTML_INPUT_TEXTAREA:
3424 if (input->init_str != NULL) {
3425 int pos = 0;
3426 gtk_editable_delete_text(GTK_EDITABLE(input->widget), 0, -1);
3427 gtk_editable_insert_text(GTK_EDITABLE(input->widget), input->init_str,
3428 strlen(input->init_str), &pos);
3429 }
3430 break;
3431 default:
3432 break;
3433 }
3434 }
3435
3436
3437 /*
3438 * Add a new input to the form data structure, setting the initial
3439 * values.
3440 */
3441 static void Html_add_input(DilloHtmlForm *form,
3442 DilloHtmlInputType type,
3443 GtkWidget *widget,
3444 const char *name,
3445 const char *init_str,
3446 DilloHtmlSelect *select,
3447 gboolean init_val)
3448 {
3449 DilloHtmlInput *input;
3450
3451 _MSG("name=[%s] init_str=[%s] init_val=[%d]\n",
3452 name, init_str, init_val);
3453 a_List_add(form->inputs, form->num_inputs, form->num_inputs_max);
3454 input = &(form->inputs[form->num_inputs]);
3455 input->type = type;
3456 input->widget = widget;
3457 input->name = (name) ? g_strdup(name) : NULL;
3458 input->init_str = (init_str) ? g_strdup(init_str) : NULL;
3459 input->select = select;
3460 input->init_val = init_val;
3461 Html_reset_input(input);
3462
3463 /* some stats */
3464 if (type == DILLO_HTML_INPUT_PASSWORD ||
3465 type == DILLO_HTML_INPUT_TEXT ||
3466 type == DILLO_HTML_INPUT_TEXTAREA) {
3467 form->num_entry_fields++;
3468 } else if (type == DILLO_HTML_INPUT_SUBMIT ||
3469 type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
3470 type == DILLO_HTML_INPUT_IMAGE) {
3471 form->num_submit_buttons++;
3472 }
3473 form->num_inputs++;
3474 }
3475
3476
3477 /*
3478 * Given a GtkWidget, find the form that contains it.
3479 * Return value: form_index if successful, -1 otherwise.
3480 */
3481 static int Html_find_form(GtkWidget *reset, DilloHtmlLB *html_lb)
3482 {
3483 gint form_index;
3484 gint input_index;
3485 DilloHtmlForm *form;
3486
3487 for (form_index = 0; form_index < html_lb->num_forms; form_index++) {
3488 form = &(html_lb->forms[form_index]);
3489 for (input_index = 0; input_index < form->num_inputs; input_index++) {
3490 if (form->inputs[input_index].widget == reset) {
3491 return form_index;
3492 }
3493 }
3494 }
3495 return -1;
3496 }
3497
3498 /*
3499 * Reset all inputs in the form containing reset to their initial values.
3500 * In general, reset is the reset button for the form.
3501 */
3502 static void Html_reset_form(GtkWidget *reset, DilloHtmlLB *html_lb)
3503 {
3504 gint i, j;
3505 DilloHtmlForm *form;
3506
3507 if ( (i = Html_find_form(reset, html_lb)) != -1 ){
3508 form = &html_lb->forms[i];
3509 for ( j = 0; j < form->num_inputs; j++)
3510 Html_reset_input(&(form->inputs[j]));
3511 }
3512 }
3513
3514 /*
3515 * Urlencode 'val' and append it to 'str'
3516 */
3517 static void Html_urlencode_append(GString *str, const char *val)
3518 {
3519 gchar *enc_val = a_Url_encode_hex_str(val);
3520 g_string_append(str, enc_val);
3521 g_free(enc_val);
3522 }
3523
3524 /*
3525 * Append a name-value pair to an existing url.
3526 * (name and value are urlencoded before appending them)
3527 */
3528 static void
3529 Html_append_input(GString *url, const char *name, const char *value)
3530 {
3531 if (name != NULL) {
3532 Html_urlencode_append(url, name);
3533 g_string_append_c(url, '=');
3534 Html_urlencode_append(url, value);
3535 g_string_append_c(url, '&');
3536 }
3537 }
3538
3539 /*
3540 * Append a image button click position to an existing url.
3541 */
3542 static void Html_append_clickpos(GString *url, const char *name, int x, int y)
3543 {
3544 if (name) {
3545 Html_urlencode_append(url, name);
3546 g_string_sprintfa(url, ".x=%d&", x);
3547 Html_urlencode_append(url, name);
3548 g_string_sprintfa(url, ".y=%d&", y);
3549 } else
3550 g_string_sprintfa(url, "x=%d&y=%d&", x, y);
3551 }
3552
3553 /*
3554 * Submit the form containing the submit input by making a new query URL
3555 * and sending it with a_Nav_push.
3556 * (Called by GTK+)
3557 * click_x and click_y are used only by input images and are set only when
3558 * called by Html_image_clicked. GTK+ does NOT give these arguments.
3559 */
3560 static void Html_submit_form(GtkWidget *submit, DilloHtmlLB *html_lb,
3561 gint click_x, gint click_y)
3562 {
3563 gint i, input_index;
3564 DilloHtmlForm *form;
3565 DilloHtmlInput *input;
3566 DilloUrl *new_url;
3567 gchar *url_str, *action_str, *p, *text;
3568
3569 /* Search the form that generated the submit event */
3570 if ( (i = Html_find_form(submit, html_lb)) == -1 )
3571 return;
3572
3573 form = &html_lb->forms[i];
3574 if ((form->method == DILLO_HTML_METHOD_GET) ||
3575 (form->method == DILLO_HTML_METHOD_POST)) {
3576 GString *DataStr = g_string_sized_new(4096);
3577
3578 DEBUG_MSG(3,"Html_submit_form form->action=%s\n",URL_STR_(form->action));
3579
3580 for (input_index = 0; input_index < form->num_inputs; input_index++) {
3581 input = &(form->inputs[input_index]);
3582 switch (input->type) {
3583 case DILLO_HTML_INPUT_TEXT:
3584 case DILLO_HTML_INPUT_PASSWORD:
3585 Html_append_input(DataStr, input->name,
3586 gtk_entry_get_text(GTK_ENTRY(input->widget)));
3587 break;
3588 case DILLO_HTML_INPUT_CHECKBOX:
3589 case DILLO_HTML_INPUT_RADIO:
3590 if (GTK_TOGGLE_BUTTON(input->widget)->active &&
3591 input->name != NULL && input->init_str != NULL) {
3592 Html_append_input(DataStr, input->name, input->init_str);
3593 }
3594 break;
3595 case DILLO_HTML_INPUT_HIDDEN:
3596 Html_append_input(DataStr, input->name, input->init_str);
3597 break;
3598 case DILLO_HTML_INPUT_SELECT:
3599 for (i = 0; i < input->select->num_options; i++) {
3600 if (GTK_CHECK_MENU_ITEM(input->select->options[i].menuitem)->
3601 active) {
3602 Html_append_input(DataStr, input->name,
3603 input->select->options[i].value);
3604 break;
3605 }
3606 }
3607 break;
3608 case DILLO_HTML_INPUT_SEL_LIST:
3609 for (i = 0; i < input->select->num_options; i++) {
3610 if (input->select->options[i].menuitem->state ==
3611 GTK_STATE_SELECTED) {
3612 Html_append_input(DataStr, input->name,
3613 input->select->options[i].value);
3614 }
3615 }
3616 break;
3617 case DILLO_HTML_INPUT_TEXTAREA:
3618 text = gtk_editable_get_chars(GTK_EDITABLE (input->widget),0,-1);
3619 Html_append_input(DataStr, input->name, text);
3620 g_free(text);
3621 break;
3622 case DILLO_HTML_INPUT_INDEX:
3623 Html_urlencode_append(DataStr,
3624 gtk_entry_get_text(GTK_ENTRY(input->widget)));
3625 break;
3626 case DILLO_HTML_INPUT_IMAGE:
3627 if (input->widget == submit) {
3628 Html_append_input(DataStr, input->name, input->init_str);
3629 Html_append_clickpos(DataStr, input->name, click_x, click_y);
3630 }
3631 break;
3632 case DILLO_HTML_INPUT_SUBMIT:
3633 case DILLO_HTML_INPUT_BUTTON_SUBMIT:
3634 /* Only the button that triggered the submit. */
3635 if (input->widget == submit && form->num_submit_buttons > 0)
3636 Html_append_input(DataStr, input->name, input->init_str);
3637 break;
3638 default:
3639 break;
3640 } /* switch */
3641 } /* for (inputs) */
3642
3643 if ( DataStr->str[DataStr->len - 1] == '&' )
3644 g_string_truncate(DataStr, DataStr->len - 1);
3645
3646 /* form->action was previously resolved against base URL */
3647 action_str = g_strdup(URL_STR(form->action));
3648
3649 if (form->method == DILLO_HTML_METHOD_POST) {
3650 new_url = a_Url_new(action_str, NULL, 0, 0, 0);
3651 a_Url_set_data(new_url, DataStr->str);
3652 a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_Post);
3653 } else {
3654 /* remove <fragment> and <query> sections if present */
3655 if ((p = strchr(action_str, '#')))
3656 *p = 0;
3657 if ((p = strchr(action_str, '?')))
3658 *p = 0;
3659
3660 url_str = g_strconcat(action_str, "?", DataStr->str, NULL);
3661 new_url = a_Url_new(url_str, NULL, 0, 0, 0);
3662 a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_Get);
3663 g_free(url_str);
3664 }
3665
3666 a_Nav_push(html_lb->bw, new_url);
3667 g_free(action_str);
3668 g_string_free(DataStr, TRUE);
3669 a_Url_free(new_url);
3670 } else {
3671 MSG("Html_submit_form: Method unknown\n");
3672 }
3673
3674 /* now, make the rendered area have its focus back */
3675 gtk_widget_grab_focus(GTK_BIN(html_lb->bw->docwin)->child);
3676 }
3677
3678
3679 /*
3680 * Submit form if it has no submit button.
3681 * (Called by GTK+ when the user presses enter in a text entry within a form)
3682 */
3683 static void Html_enter_submit_form(GtkWidget *submit, DilloHtmlLB *html_lb)
3684 {
3685 gint i;
3686
3687 /* Search the form that generated the submit event */
3688 if ( (i = Html_find_form(submit, html_lb)) == -1 )
3689 return;
3690
3691 /* Submit on enterpress when there's a single text-entry only,
3692 * or if the user set enter to always submit */
3693 if ((html_lb->forms[i].num_entry_fields == 1) ||
3694 prefs.enterpress_forces_submit)
3695 Html_submit_form(submit, html_lb, 1, 1);
3696 }
3697
3698 /*
3699 * Call submit form, when input image has been clicked
3700 */
3701 static void Html_image_clicked(DwWidget *widget, gint x, gint y,
3702 DilloHtmlLB *lb)
3703 {
3704 _MSG("Hallo! (%d, %d, %p)\n", x, y, lb);
3705 Html_submit_form((GtkWidget*) widget, lb, x, y);
3706 }
3707
3708 /*
3709 * Create input image for the form
3710 */
3711 static DwWidget *Html_input_image(DilloHtml *html, char *tag, gint tagsize,
3712 DilloHtmlLB *html_lb, DilloUrl *action)
3713 {
3714 DilloImage *Image;
3715 DwWidget *button;
3716 DilloUrl *url = NULL;
3717 DwStyle style_attrs;
3718 const char *attrbuf;
3719
3720 if ((attrbuf = Html_get_attr(html, tag, tagsize, "src")) &&
3721 (url = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0))) {
3722 button = a_Dw_button_new (0, FALSE);
3723 a_Dw_page_add_widget (DW_PAGE (html->dw), button,
3724 html->stack[html->stack_top].style);
3725 gtk_signal_connect(GTK_OBJECT(button), "clicked_at",
3726 GTK_SIGNAL_FUNC(Html_image_clicked), html_lb);
3727 a_Dw_button_set_sensitive(DW_BUTTON(button), FALSE);
3728
3729 /* create new image and add it to the button */
3730 if ((Image = Html_add_new_image(html, tag, tagsize, &style_attrs,
3731 FALSE))) {
3732 /* By suppressing the "image_pressed" signal, the events are sent
3733 * to the parent DwButton */
3734 a_Dw_widget_set_button_sensitive (DW_WIDGET (Image->dw), FALSE);
3735 a_Dw_widget_set_style(DW_WIDGET(Image->dw),
3736 html->stack[html->stack_top].style);
3737 a_Dw_container_add(DW_CONTAINER(button), DW_WIDGET(Image->dw));
3738 a_Dw_widget_set_cursor(DW_WIDGET(Image->dw), Dw_cursor_hand);
3739 Html_load_image(html, url, Image);
3740 a_Url_free(url);
3741 return button;
3742 }
3743 }
3744
3745 DEBUG_MSG(10, "Html_input_image: unable to create image submit.\n");
3746 a_Url_free(url);
3747 return NULL;
3748 }
3749
3750 /*
3751 * Add a new input to current form
3752 */
3753 static void Html_tag_open_input(DilloHtml *html, char *tag, gint tagsize)
3754 {
3755 DilloHtmlForm *form;
3756 DilloHtmlInputType inp_type;
3757 DilloHtmlLB *html_lb;
3758 DwWidget *embed_gtk;
3759 GtkWidget *widget = NULL;
3760 GSList *group;
3761 char *value, *name, *type, *init_str;
3762 const char *attrbuf, *label;
3763 gboolean init_val = FALSE;
3764 gint input_index;
3765
3766 if (!(html->InFlags & IN_FORM)) {
3767 MSG_HTML("input camp outside <form>\n");
3768 return;
3769 }
3770
3771 html_lb = html->linkblock;
3772 form = &(html_lb->forms[html_lb->num_forms - 1]);
3773
3774 /* Get 'value', 'name' and 'type' */
3775 value = Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
3776 name = Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
3777 type = Html_get_attr_wdef(html, tag, tagsize, "type", "");
3778
3779 init_str = NULL;
3780 if (!g_strcasecmp(type, "password")) {
3781 inp_type = DILLO_HTML_INPUT_PASSWORD;
3782 widget = gtk_entry_new();
3783 gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
3784 if (value)
3785 init_str = g_strdup(Html_get_attr(html, tag, tagsize, "value"));
3786 } else if (!g_strcasecmp(type, "checkbox")) {
3787 inp_type = DILLO_HTML_INPUT_CHECKBOX;
3788 widget = gtk_check_button_new();
3789 init_val = (Html_get_attr(html, tag, tagsize, "checked") != NULL);
3790 init_str = (value) ? value : g_strdup("on");
3791 } else if (!g_strcasecmp(type, "radio")) {
3792 inp_type = DILLO_HTML_INPUT_RADIO;
3793 group = NULL;
3794 for (input_index = 0; input_index < form->num_inputs; input_index++) {
3795 if (form->inputs[input_index].type == DILLO_HTML_INPUT_RADIO &&
3796 (form->inputs[input_index].name &&
3797 !g_strcasecmp(form->inputs[input_index].name, name)) ){
3798 group = gtk_radio_button_group(GTK_RADIO_BUTTON
3799 (form->inputs[input_index].widget));
3800 form->inputs[input_index].init_val = TRUE;
3801 break;
3802 }
3803 }
3804 widget = gtk_radio_button_new(group);
3805
3806 init_val = (Html_get_attr(html, tag, tagsize, "checked") != NULL);
3807 init_str = (value) ? value : NULL;
3808 } else if (!g_strcasecmp(type, "hidden")) {
3809 inp_type = DILLO_HTML_INPUT_HIDDEN;
3810 if (value)
3811 init_str = g_strdup(Html_get_attr(html, tag, tagsize, "value"));
3812 } else if (!g_strcasecmp(type, "submit")) {
3813 inp_type = DILLO_HTML_INPUT_SUBMIT;
3814 init_str = (value) ? value : g_strdup("submit");
3815 widget = gtk_button_new_with_label(init_str);
3816 gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
3817 gtk_signal_connect(GTK_OBJECT(widget), "clicked",
3818 GTK_SIGNAL_FUNC(Html_submit_form), html_lb);
3819 } else if (!g_strcasecmp(type, "reset")) {
3820 inp_type = DILLO_HTML_INPUT_RESET;
3821 init_str = (value) ? value : g_strdup("Reset");
3822 widget = gtk_button_new_with_label(init_str);
3823 gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
3824 gtk_signal_connect(GTK_OBJECT(widget), "clicked",
3825 GTK_SIGNAL_FUNC(Html_reset_form), html_lb);
3826 } else if (!g_strcasecmp(type, "image")) {
3827 if (URL_FLAGS(html->linkblock->base_url) & URL_SpamSafe) {
3828 /* Don't request the image, make a text submit button instead */
3829 inp_type = DILLO_HTML_INPUT_SUBMIT;
3830 attrbuf = Html_get_attr(html, tag, tagsize, "alt");
3831 label = attrbuf ? attrbuf : value ? value : name ? name : "Submit";
3832 init_str = g_strdup(label);
3833 widget = gtk_button_new_with_label(init_str);
3834 gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
3835 gtk_signal_connect(GTK_OBJECT(widget), "clicked",
3836 GTK_SIGNAL_FUNC(Html_submit_form), html_lb);
3837 } else {
3838 inp_type = DILLO_HTML_INPUT_IMAGE;
3839 /* use a dw_image widget */
3840 widget = (GtkWidget*) Html_input_image(html, tag, tagsize,
3841 html_lb, form->action);
3842 init_str = value;
3843 }
3844 } else if (!g_strcasecmp(type, "file")) {
3845 /* todo: implement it! */
3846 inp_type = DILLO_HTML_INPUT_FILE;
3847 init_str = (value) ? value : NULL;
3848 MSG("An input of the type \"file\" wasn't rendered!\n");
3849 } else if (!g_strcasecmp(type, "button")) {
3850 inp_type = DILLO_HTML_INPUT_BUTTON;
3851 if (value) {
3852 init_str = value;
3853 widget = gtk_button_new_with_label(init_str);
3854 }
3855 } else {
3856 /* Text input, which also is the default */
3857 inp_type = DILLO_HTML_INPUT_TEXT;
3858 widget = gtk_entry_new();
3859
3860 init_str = (value) ? value : NULL;
3861 gtk_signal_connect(GTK_OBJECT(widget), "activate",
3862 GTK_SIGNAL_FUNC(Html_enter_submit_form),
3863 html_lb);
3864 }
3865
3866 Html_add_input(form, inp_type, widget, name,
3867 (init_str) ? init_str : "", NULL, init_val);
3868
3869 if (widget != NULL && inp_type != DILLO_HTML_INPUT_IMAGE) {
3870 if (inp_type == DILLO_HTML_INPUT_TEXT ||
3871 inp_type == DILLO_HTML_INPUT_PASSWORD) {
3872 /*
3873 * The following is necessary, because gtk_entry_button_press
3874 * returns FALSE, so the event would be delivered to the
3875 * GtkDwScrolledFrame, which then would be focused, instead of
3876 * the entry.
3877 */
3878 gtk_signal_connect_after(GTK_OBJECT(widget), "button_press_event",
3879 GTK_SIGNAL_FUNC(gtk_true), NULL);
3880
3881 /* Readonly or not? */
3882 gtk_entry_set_editable(
3883 GTK_ENTRY(widget),
3884 !(Html_get_attr(html, tag, tagsize, "readonly")));
3885
3886 /* Set width of the entry */
3887 if ((attrbuf = Html_get_attr(html, tag, tagsize, "size")))
3888 gtk_widget_set_usize(widget, (strtol(attrbuf, NULL, 10) + 1) *
3889 gdk_char_width(widget->style->font, '0'), 0);
3890
3891 /* Maximum length of the text in the entry */
3892 if ((attrbuf = Html_get_attr(html, tag, tagsize, "maxlength")))
3893 gtk_entry_set_max_length(GTK_ENTRY(widget),
3894 strtol(attrbuf, NULL, 10));
3895 }
3896 gtk_widget_show(widget);
3897
3898 embed_gtk = a_Dw_embed_gtk_new();
3899 a_Dw_embed_gtk_add_gtk (DW_EMBED_GTK (embed_gtk), widget);
3900 a_Dw_page_add_widget(DW_PAGE (html->dw), embed_gtk,
3901 html->stack[html->stack_top].style);
3902 }
3903
3904 g_free(type);
3905 g_free(name);
3906 if (init_str != value)
3907 g_free(init_str);
3908 g_free(value);
3909 }
3910
3911 /*
3912 * The ISINDEX tag is just a deprecated form of <INPUT type=text> with
3913 * implied FORM, afaics.
3914 */
3915 static void Html_tag_open_isindex(DilloHtml *html, char *tag, gint tagsize)
3916 {
3917 DilloHtmlForm *form;
3918 DilloHtmlLB *html_lb;
3919 DilloUrl *action;
3920 GtkWidget *widget;
3921 DwWidget *embed_gtk;
3922 const char *attrbuf;
3923
3924 html_lb = html->linkblock;
3925
3926 if ((attrbuf = Html_get_attr(html, tag, tagsize, "action")))
3927 action = Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0);
3928 else
3929 action = a_Url_dup(html->linkblock->base_url);
3930
3931 Html_form_new(html->linkblock, DILLO_HTML_METHOD_GET, action,
3932 DILLO_HTML_ENC_URLENCODING);
3933
3934 form = &(html_lb->forms[html_lb->num_forms - 1]);
3935
3936 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
3937 html->stack[(html)->stack_top].style);
3938
3939 if ((attrbuf = Html_get_attr(html, tag, tagsize, "prompt")))
3940 a_Dw_page_add_text(DW_PAGE (html->dw), g_strdup(attrbuf),
3941 html->stack[html->stack_top].style);
3942
3943 widget = gtk_entry_new();
3944 Html_add_input(form, DILLO_HTML_INPUT_INDEX,
3945 widget, NULL, NULL, NULL, FALSE);
3946 gtk_signal_connect(GTK_OBJECT(widget), "activate",
3947 GTK_SIGNAL_FUNC(Html_enter_submit_form),
3948 html_lb);
3949 gtk_widget_show(widget);
3950 /* compare <input type=text> */
3951 gtk_signal_connect_after(GTK_OBJECT(widget), "button_press_event",
3952 GTK_SIGNAL_FUNC(gtk_true),
3953 NULL);
3954
3955 embed_gtk = a_Dw_embed_gtk_new();
3956 a_Dw_embed_gtk_add_gtk(DW_EMBED_GTK(embed_gtk), widget);
3957 a_Dw_page_add_widget(DW_PAGE (html->dw), embed_gtk,
3958 html->stack[html->stack_top].style);
3959
3960 a_Url_free(action);
3961 }
3962
3963 /*
3964 * Close textarea
3965 * (TEXTAREA is parsed in VERBATIM mode, and entities are handled here)
3966 */
3967 static void Html_tag_close_textarea(DilloHtml *html, gint TagIdx)
3968 {
3969 DilloHtmlLB *html_lb = html->linkblock;
3970 char *str;
3971 DilloHtmlForm *form;
3972 gint i;
3973
3974 if (html->InFlags & IN_FORM && html->InFlags & IN_TEXTAREA) {
3975 /* Remove the line ending that follows the opening tag */
3976 if (html->Stash->str[0] == '\r')
3977 html->Stash = g_string_erase(html->Stash, 0, 1);
3978 if (html->Stash->str[0] == '\n')
3979 html->Stash = g_string_erase(html->Stash, 0, 1);
3980
3981 /* As the spec recommends to canonicalize line endings, it is safe
3982 * to replace '\r' with '\n'. It will be canonicalized anyway! */
3983 for (i = 0; i < html->Stash->len; ++i) {
3984 if (html->Stash->str[i] == '\r') {
3985 if (html->Stash->str[i + 1] == '\n')
3986 g_string_erase(html->Stash, i, 1);
3987 else
3988 html->Stash->str[i] = '\n';
3989 }
3990 }
3991
3992 /* The HTML3.2 spec says it can have "text and character entities". */
3993 str = Html_parse_entities(html, html->Stash->str, html->Stash->len);
3994
3995 form = &(html_lb->forms[html_lb->num_forms - 1]);
3996 form->inputs[form->num_inputs - 1].init_str = str;
3997 gtk_text_insert(GTK_TEXT(form->inputs[form->num_inputs - 1].widget),
3998 NULL, NULL, NULL, str, -1);
3999
4000 html->InFlags &= ~IN_TEXTAREA;
4001 }
4002 Html_pop_tag(html, TagIdx);
4003 }
4004
4005 /*
4006 * The textarea tag
4007 * (todo: It doesn't support wrapping).
4008 */
4009 static void Html_tag_open_textarea(DilloHtml *html, char *tag, gint tagsize)
4010 {
4011 DilloHtmlLB *html_lb;
4012 DilloHtmlForm *form;
4013 GtkWidget *widget;
4014 GtkWidget *scroll;
4015 DwWidget *embed_gtk;
4016 char *name;
4017 const char *attrbuf;
4018 int cols, rows;
4019
4020 /* We can't push a new <FORM> because the 'action' URL is unknown */
4021 if (!(html->InFlags & IN_FORM)) {
4022 MSG_HTML("<textarea> outside <form>\n");
4023 html->ReqTagClose = TRUE;
4024 return;
4025 }
4026 if (html->InFlags & IN_TEXTAREA) {
4027 MSG_HTML("nested <textarea>\n");
4028 html->ReqTagClose = TRUE;
4029 return;
4030 }
4031
4032 html->InFlags |= IN_TEXTAREA;
4033 html_lb = html->linkblock;
4034 form = &(html_lb->forms[html_lb->num_forms - 1]);
4035 Html_stash_init(html);
4036 html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
4037
4038 cols = 20;
4039 if ((attrbuf = Html_get_attr(html, tag, tagsize, "cols")))
4040 cols = strtol(attrbuf, NULL, 10);
4041 rows = 10;
4042 if ((attrbuf = Html_get_attr(html, tag, tagsize, "rows")))
4043 rows = strtol(attrbuf, NULL, 10);
4044 name = NULL;
4045 if ((attrbuf = Html_get_attr(html, tag, tagsize, "name")))
4046 name = g_strdup(attrbuf);
4047
4048 widget = gtk_text_new(NULL, NULL);
4049 /* compare <input type=text> */
4050 gtk_signal_connect_after(GTK_OBJECT(widget), "button_press_event",
4051 GTK_SIGNAL_FUNC(gtk_true),
4052 NULL);
4053
4054 /* Calculate the width and height based on the cols and rows
4055 * todo: Get it right... Get the metrics from the font that will be used.
4056 */
4057 gtk_widget_set_usize(widget, 6 * cols, 16 * rows);
4058
4059 /* If the attribute readonly isn't specified we make the textarea
4060 * editable. If readonly is set we don't have to do anything.
4061 */
4062 if (!Html_get_attr(html, tag, tagsize, "readonly"))
4063 gtk_text_set_editable(GTK_TEXT(widget), TRUE);
4064
4065 scroll = gtk_scrolled_window_new(NULL, NULL);
4066 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
4067 GTK_POLICY_AUTOMATIC,
4068 GTK_POLICY_AUTOMATIC);
4069 gtk_container_add(GTK_CONTAINER(scroll), widget);
4070 gtk_widget_show(widget);
4071 gtk_widget_show(scroll);
4072
4073 Html_add_input(form, DILLO_HTML_INPUT_TEXTAREA,
4074 widget, name, NULL, NULL, FALSE);
4075 g_free(name);
4076
4077 embed_gtk = a_Dw_embed_gtk_new ();
4078 a_Dw_embed_gtk_add_gtk (DW_EMBED_GTK (embed_gtk), scroll);
4079 a_Dw_page_add_widget(DW_PAGE (html->dw), embed_gtk,
4080 html->stack[html->stack_top].style);
4081 }
4082
4083 /*
4084 * <SELECT>
4085 */
4086 /* The select tag is quite tricky, because of gorpy html syntax. */
4087 static void Html_tag_open_select(DilloHtml *html, char *tag, gint tagsize)
4088 {
4089 DilloHtmlForm *form;
4090 DilloHtmlSelect *Select;
4091 DilloHtmlLB *html_lb;
4092 GtkWidget *widget, *menu;
4093 char *name;
4094 const char *attrbuf;
4095 gint size, type, multi;
4096
4097 if (!(html->InFlags & IN_FORM)) {
4098 MSG_HTML("<select> outside <form>\n");
4099 return;
4100 }
4101 if (html->InFlags & IN_SELECT) {
4102 MSG_HTML("nested <select>\n");
4103 return;
4104 }
4105 html->InFlags |= IN_SELECT;
4106
4107 html_lb = html->linkblock;
4108
4109 form = &(html_lb->forms[html_lb->num_forms - 1]);
4110
4111 name = Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
4112
4113 size = 0;
4114 if ((attrbuf = Html_get_attr(html, tag, tagsize, "size")))
4115 size = strtol(attrbuf, NULL, 10);
4116
4117 multi = (Html_get_attr(html, tag, tagsize, "multiple")) ? 1 : 0;
4118 if (size < 1)
4119 size = multi ? 10 : 1;
4120
4121 if (size == 1) {
4122 menu = gtk_menu_new();
4123 widget = gtk_option_menu_new();
4124 type = DILLO_HTML_INPUT_SELECT;
4125 } else {
4126 menu = gtk_list_new();
4127 widget = menu;
4128 if (multi)
4129 gtk_list_set_selection_mode(GTK_LIST(menu), GTK_SELECTION_MULTIPLE);
4130 type = DILLO_HTML_INPUT_SEL_LIST;
4131 }
4132
4133 Select = g_new(DilloHtmlSelect, 1);
4134 Select->menu = menu;
4135 Select->size = size;
4136 Select->num_options = 0;
4137 Select->num_options_max = 8;
4138 Select->options = g_new(DilloHtmlOption, Select->num_options_max);
4139
4140 Html_add_input(form, type, widget, name, NULL, Select, FALSE);
4141 Html_stash_init(html);
4142 g_free(name);
4143 }
4144
4145 /*
4146 * ?
4147 */
4148 static void Html_option_finish(DilloHtml *html)
4149 {
4150 DilloHtmlForm *form;
4151 DilloHtmlInput *input;
4152 GtkWidget *menuitem;
4153 GSList *group;
4154 DilloHtmlSelect *select;
4155
4156 if (!(html->InFlags & IN_FORM))
4157 return;
4158
4159 form = &(html->linkblock->forms[html->linkblock->num_forms - 1]);
4160 input = &(form->inputs[form->num_inputs - 1]);
4161 if ( input->select->num_options <= 0)
4162 return;
4163
4164 select = input->select;
4165 if (input->type == DILLO_HTML_INPUT_SELECT ) {
4166 if ( select->num_options == 1)
4167 group = NULL;
4168 else
4169 group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM
4170 (select->options[0].menuitem));
4171 menuitem = gtk_radio_menu_item_new_with_label(group, html->Stash->str);
4172 select->options[select->num_options - 1].menuitem = menuitem;
4173 if ( select->options[select->num_options - 1].value == NULL )
4174 select->options[select->num_options - 1].value =
4175 g_strdup(html->Stash->str);
4176 gtk_menu_append(GTK_MENU(select->menu), menuitem);
4177 if ( select->options[select->num_options - 1].init_val )
4178 gtk_menu_item_activate(GTK_MENU_ITEM(menuitem));
4179 gtk_widget_show(menuitem);
4180 gtk_signal_connect (GTK_OBJECT (menuitem), "select",
4181 GTK_SIGNAL_FUNC (a_Interface_scroll_popup),
4182 NULL);
4183 } else if ( input->type == DILLO_HTML_INPUT_SEL_LIST ) {
4184 menuitem = gtk_list_item_new_with_label(html->Stash->str);
4185 select->options[select->num_options - 1].menuitem = menuitem;
4186 if (select->options[select->num_options - 1].value == NULL)
4187 select->options[select->num_options - 1].value =
4188 g_strdup(html->Stash->str);
4189 gtk_container_add(GTK_CONTAINER(select->menu), menuitem);
4190 if ( select->options[select->num_options - 1].init_val )
4191 gtk_list_select_child(GTK_LIST(select->menu), menuitem);
4192 gtk_widget_show(menuitem);
4193 }
4194 }
4195
4196 /*
4197 * <OPTION>
4198 */
4199 static void Html_tag_open_option(DilloHtml *html, char *tag, gint tagsize)
4200 {
4201 DilloHtmlForm *form;
4202 DilloHtmlInput *input;
4203 DilloHtmlLB *html_lb;
4204 gint no;
4205
4206 if (!(html->InFlags & IN_SELECT))
4207 return;
4208
4209 html_lb = html->linkblock;
4210
4211 form = &(html_lb->forms[html_lb->num_forms - 1]);
4212 input = &(form->inputs[form->num_inputs - 1]);
4213 if (input->type == DILLO_HTML_INPUT_SELECT ||
4214 input->type == DILLO_HTML_INPUT_SEL_LIST) {
4215 Html_option_finish(html);
4216 no = input->select->num_options;
4217 a_List_add(input->select->options, no, input->select->num_options_max);
4218 input->select->options[no].menuitem = NULL;
4219 input->select->options[no].value = Html_get_attr_wdef(html, tag, tagsize,
4220 "value", NULL);
4221 input->select->options[no].init_val =
4222 (Html_get_attr(html, tag, tagsize, "selected") != NULL);
4223 input->select->num_options++;
4224 }
4225 Html_stash_init(html);
4226 }
4227
4228 /*
4229 * ?
4230 */
4231 static void Html_tag_close_select(DilloHtml *html, gint TagIdx)
4232 {
4233 DilloHtmlForm *form;
4234 DilloHtmlInput *input;
4235 GtkWidget *scrolledwindow;
4236 DilloHtmlLB *html_lb;
4237 DwWidget *embed_gtk;
4238 GtkRequisition req;
4239 gint height;
4240
4241 if (html->InFlags & IN_SELECT) {
4242 html->InFlags &= ~IN_SELECT;
4243
4244 html_lb = html->linkblock;
4245
4246 form = &(html_lb->forms[html_lb->num_forms - 1]);
4247 input = &(form->inputs[form->num_inputs - 1]);
4248 if (input->type == DILLO_HTML_INPUT_SELECT) {
4249 Html_option_finish(html);
4250
4251 gtk_option_menu_set_menu(GTK_OPTION_MENU(input->widget),
4252 input->select->menu);
4253 Html_select_set_history(input);
4254
4255 /* gtk_option_menu_set_history(GTK_OPTION_MENU(input->widget), 1); */
4256
4257 gtk_widget_show(input->widget);
4258
4259 embed_gtk = a_Dw_embed_gtk_new ();
4260 a_Dw_embed_gtk_add_gtk (DW_EMBED_GTK (embed_gtk), input->widget);
4261 a_Dw_page_add_widget(DW_PAGE (html->dw), embed_gtk,
4262 html->stack[html->stack_top].style);
4263 } else if (input->type == DILLO_HTML_INPUT_SEL_LIST) {
4264 Html_option_finish(html);
4265
4266 if (input->select->size < input->select->num_options) {
4267 scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
4268 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
4269 GTK_POLICY_NEVER,
4270 GTK_POLICY_AUTOMATIC);
4271 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW
4272 (scrolledwindow),
4273 input->widget);
4274
4275 gtk_container_set_focus_vadjustment
4276 (GTK_CONTAINER (input->widget),
4277 gtk_scrolled_window_get_vadjustment
4278 (GTK_SCROLLED_WINDOW(scrolledwindow)));
4279
4280 /* Calculate the height of the scrolled window */
4281 gtk_widget_size_request(input->select->options[0].menuitem, &req);
4282 height = input->select->size * req.height +
4283 2 * scrolledwindow->style->klass->ythickness;
4284 gtk_widget_set_usize(scrolledwindow, -1, height);
4285
4286 gtk_widget_show(input->widget);
4287 input->widget = scrolledwindow;
4288 }
4289 gtk_widget_show(input->widget);
4290
4291 /* note: In this next call, scrolledwindows get a g_warning from
4292 * gdkwindow.c:422. I'm not really going to sweat it now - the
4293 * embedded widget stuff is going to get massively redone anyway. */
4294 embed_gtk = a_Dw_embed_gtk_new ();
4295 a_Dw_embed_gtk_add_gtk (DW_EMBED_GTK (embed_gtk), input->widget);
4296 a_Dw_page_add_widget(DW_PAGE (html->dw), embed_gtk,
4297 html->stack[html->stack_top].style);
4298 }
4299 }
4300 Html_pop_tag(html, TagIdx);
4301 }
4302
4303 /*
4304 * Set the Document Base URI
4305 */
4306 static void Html_tag_open_base(DilloHtml *html, char *tag, gint tagsize)
4307 {
4308 const char *attrbuf;
4309 DilloUrl *BaseUrl;
4310
4311 if (html->InFlags & IN_HEAD) {
4312 if ((attrbuf = Html_get_attr(html, tag, tagsize, "href"))) {
4313 BaseUrl = Html_url_new(html, attrbuf, "", 0, 0, 0, 1);
4314 if (URL_SCHEME_(BaseUrl)) {
4315 /* Pass the URL_SpamSafe flag to the new base url */
4316 a_Url_set_flags(
4317 BaseUrl, URL_FLAGS(html->linkblock->base_url) & URL_SpamSafe);
4318 a_Url_free(html->linkblock->base_url);
4319 html->linkblock->base_url = BaseUrl;
4320 } else {
4321 MSG_HTML("base URI is relative (it MUST be absolute)\n");
4322 a_Url_free(BaseUrl);
4323 }
4324 }
4325 } else {
4326 MSG_HTML("the BASE element must appear in the HEAD section\n");
4327 }
4328 }
4329
4330 /*
4331 * <CODE>
4332 */
4333 static void Html_tag_open_code(DilloHtml *html, char *tag, gint tagsize)
4334 {
4335 Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
4336 }
4337
4338 /*
4339 * <DFN>
4340 */
4341 static void Html_tag_open_dfn(DilloHtml *html, char *tag, gint tagsize)
4342 {
4343 Html_set_top_font(html, NULL, 0, 2, 3);
4344 }
4345
4346 /*
4347 * <KBD>
4348 */
4349 static void Html_tag_open_kbd(DilloHtml *html, char *tag, gint tagsize)
4350 {
4351 Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
4352 }
4353
4354 /*
4355 * <SAMP>
4356 */
4357 static void Html_tag_open_samp(DilloHtml *html, char *tag, gint tagsize)
4358 {
4359 Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
4360 }
4361
4362 /*
4363 * <VAR>
4364 */
4365 static void Html_tag_open_var(DilloHtml *html, char *tag, gint tagsize)
4366 {
4367 Html_set_top_font(html, NULL, 0, 2, 2);
4368 }
4369
4370 /*
4371 * <SUB>
4372 */
4373 static void Html_tag_open_sub(DilloHtml *html, char *tag, gint tagsize)
4374 {
4375 HTML_SET_TOP_ATTR (html, valign, DW_STYLE_VALIGN_SUB);
4376 }
4377
4378 /*
4379 * <SUP>
4380 */
4381 static void Html_tag_open_sup(DilloHtml *html, char *tag, gint tagsize)
4382 {
4383 HTML_SET_TOP_ATTR (html, valign, DW_STYLE_VALIGN_SUPER);
4384 }
4385
4386 /*
4387 * <DIV> (todo: make a complete implementation)
4388 */
4389 static void Html_tag_open_div(DilloHtml *html, char *tag, gint tagsize)
4390 {
4391 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
4392 html->stack[(html)->stack_top].style);
4393 Html_tag_set_align_attr (html, tag, tagsize);
4394 }
4395
4396 /*
4397 * </DIV>, also used for </TABLE> and </CENTER>
4398 */
4399 static void Html_tag_close_div(DilloHtml *html, gint TagIdx)
4400 {
4401 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 0,
4402 html->stack[(html)->stack_top].style);
4403 Html_pop_tag(html, TagIdx);
4404 }
4405
4406 /*
4407 * Default close for most tags - just pop the stack.
4408 */
4409 static void Html_tag_close_default(DilloHtml *html, gint TagIdx)
4410 {
4411 Html_pop_tag(html, TagIdx);
4412 }
4413
4414 /*
4415 * Default close for paragraph tags - pop the stack and break.
4416 */
4417 static void Html_tag_close_par(DilloHtml *html, gint TagIdx)
4418 {
4419 a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
4420 html->stack[(html)->stack_top].style);
4421 Html_pop_tag(html, TagIdx);
4422 }
4423
4424
4425 /*
4426 * Function index for the open and close functions for each tag
4427 * (Alphabetically sorted for a binary search)
4428 *
4429 * Explanation for the 'Flags' camp:
4430 *
4431 * {"address", B8(010110), ...}
4432 * |||||`- inline element
4433 * ||||`-- block element
4434 * |||`--- inline container
4435 * ||`---- block container
4436 * |`----- body element
4437 * `------ head element
4438 *
4439 * Notes:
4440 * - The upper two bits are not used yet.
4441 * - Empty elements have both inline and block container clear.
4442 * (flow have both set)
4443 */
4444 struct _TagInfo{
4445 gchar *name;
4446 unsigned char Flags;
4447 gchar EndTag;
4448 guchar TagLevel;
4449 TagOpenFunct open;
4450 TagCloseFunct close;
4451 };
4452
4453
4454 static const TagInfo Tags[] = {
4455 {"a", B8(010101),'R',2, Html_tag_open_a, Html_tag_close_a},
4456 {"abbr", B8(010101),'R',2, Html_tag_open_abbr, Html_tag_close_default},
4457 /* acronym 010101 */
4458 {"address", B8(010110),'R',2, Html_tag_open_address, Html_tag_close_par},
4459 {"area", B8(010001),'F',0, Html_tag_open_area, Html_tag_close_default},
4460 {"b", B8(010101),'R',2, Html_tag_open_b, Html_tag_close_default},
4461 {"base", B8(100001),'F',0, Html_tag_open_base, Html_tag_close_default},
4462 /* basefont 010001 */
4463 /* bdo 010101 */
4464 {"big", B8(010101),'R',2, Html_tag_open_big_small, Html_tag_close_default},
4465 {"blockquote", B8(011110),'R',2,Html_tag_open_blockquote,Html_tag_close_par},
4466 {"body", B8(011110),'O',1, Html_tag_open_body, Html_tag_close_body},
4467 {"br", B8(010001),'F',0, Html_tag_open_br, Html_tag_close_default},
4468 {"button", B8(011101),'R',2, Html_tag_open_button, Html_tag_close_default},
4469 /* caption */
4470 {"center", B8(011110),'R',2, Html_tag_open_center, Html_tag_close_div},
4471 {"cite", B8(010101),'R',2, Html_tag_open_cite, Html_tag_close_default},
4472 {"code", B8(010101),'R',2, Html_tag_open_code, Html_tag_close_default},
4473 /* col 010010 'F' */
4474 /* colgroup */
4475 {"dd", B8(011110),'O',1, Html_tag_open_dd, Html_tag_close_par},
4476 {"del", B8(011101),'R',2, Html_tag_open_strike, Html_tag_close_default},
4477 {"dfn", B8(010101),'R',2, Html_tag_open_dfn, Html_tag_close_default},
4478 /* dir 011010 */
4479 /* todo: complete <div> support! */
4480 {"div", B8(011110),'R',2, Html_tag_open_div, Html_tag_close_div},
4481 {"dl", B8(011010),'R',2, Html_tag_open_dl, Html_tag_close_par},
4482 {"dt", B8(010110),'O',1, Html_tag_open_dt, Html_tag_close_par},
4483 {"em", B8(010101),'R',2, Html_tag_open_em, Html_tag_close_default},
4484 /* fieldset */
4485 {"font", B8(010101),'R',2, Html_tag_open_font, Html_tag_close_default},
4486 {"form", B8(011110),'R',2, Html_tag_open_form, Html_tag_close_form},
4487 {"frame", B8(010010),'F',0, Html_tag_open_frame, Html_tag_close_default},
4488 {"frameset", B8(011110),'R',2,Html_tag_open_frameset, Html_tag_close_default},
4489 {"h1", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4490 {"h2", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4491 {"h3", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4492 {"h4", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4493 {"h5", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4494 {"h6", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
4495 {"head", B8(101101),'O',1, Html_tag_open_head, Html_tag_close_head},
4496 {"hr", B8(010010),'F',0, Html_tag_open_hr, Html_tag_close_default},
4497 {"html", B8(001110),'O',1, Html_tag_open_html, Html_tag_close_html},
4498 {"i", B8(010101),'R',2, Html_tag_open_i, Html_tag_close_default},
4499 {"iframe", B8(011110),'R',2, Html_tag_open_frame, Html_tag_close_default},
4500 {"img", B8(010001),'F',0, Html_tag_open_img, Html_tag_close_default},
4501 {"input", B8(010001),'F',0, Html_tag_open_input, Html_tag_close_default},
4502 /* ins */
4503 {"isindex", B8(110001),'F',0, Html_tag_open_isindex, Html_tag_close_default},
4504 {"kbd", B8(010101),'R',2, Html_tag_open_kbd, Html_tag_close_default},
4505 /* label 010101 */
4506 /* legend 01?? */
4507 {"li", B8(011110),'O',1, Html_tag_open_li, Html_tag_close_default},
4508 /* link 100000 'F' */
4509 {"map", B8(011001),'R',2, Html_tag_open_map, Html_tag_close_map},
4510 /* menu 1010 -- todo: not exactly 1010, it can contain LI and inline */
4511 {"menu", B8(011010),'R',2, Html_tag_open_menu, Html_tag_close_par},
4512 {"meta", B8(100001),'F',0, Html_tag_open_meta, Html_tag_close_default},
4513 /* noframes 1011 */
4514 /* noscript 1011 */
4515 /* object 11xxxx */
4516 {"ol", B8(011010),'R',2, Html_tag_open_ol, Html_tag_close_par},
4517 /* optgroup */
4518 {"option", B8(010001),'O',1, Html_tag_open_option, Html_tag_close_default},
4519 {"p", B8(010110),'O',1, Html_tag_open_p, Html_tag_close_par},
4520 /* param 010001 'F' */
4521 {"pre", B8(010110),'R',2, Html_tag_open_pre, Html_tag_close_pre},
4522 /* q 010101 */
4523 {"s", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
4524 {"samp", B8(010101),'R',2, Html_tag_open_samp, Html_tag_close_default},
4525 {"script", B8(111001),'R',2, Html_tag_open_script, Html_tag_close_script},
4526 {"select", B8(011001),'R',2, Html_tag_open_select, Html_tag_close_select},
4527 {"small", B8(010101),'R',2, Html_tag_open_big_small, Html_tag_close_default},
4528 /* span 0101 */
4529 {"strike", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
4530 {"strong", B8(010101),'R',2, Html_tag_open_strong, Html_tag_close_default},
4531 {"style", B8(100101),'R',2, Html_tag_open_style, Html_tag_close_style},
4532 {"sub", B8(010101),'R',2, Html_tag_open_sub, Html_tag_close_default},
4533 {"sup", B8(010101),'R',2, Html_tag_open_sup, Html_tag_close_default},
4534 {"table", B8(011010),'R',5, Html_tag_open_table, Html_tag_close_div},
4535 /* tbody */
4536 {"td", B8(011110),'O',3, Html_tag_open_td, Html_tag_close_default},
4537 {"textarea", B8(010101),'R',2,Html_tag_open_textarea,Html_tag_close_textarea},
4538 /* tfoot */
4539 {"th", B8(011110),'O',1, Html_tag_open_th, Html_tag_close_default},
4540 /* thead */
4541 {"title", B8(100101),'R',2, Html_tag_open_title, Html_tag_close_title},
4542 {"tr", B8(011010),'O',4, Html_tag_open_tr, Html_tag_close_default},
4543 {"tt", B8(010101),'R',2, Html_tag_open_tt, Html_tag_close_default},
4544 {"u", B8(010101),'R',2, Html_tag_open_u, Html_tag_close_default},
4545 {"ul", B8(011010),'R',2, Html_tag_open_ul, Html_tag_close_par},
4546 {"var", B8(010101),'R',2, Html_tag_open_var, Html_tag_close_default}
4547
4548 };
4549 #define NTAGS (sizeof(Tags)/sizeof(Tags[0]))
4550
4551
4552 /*
4553 * Compares tag from buffer ('/' or '>' or space-ended string) [p1]
4554 * with tag from taglist (lowercase, zero ended string) [p2]
4555 * Return value: as strcmp()
4556 */
4557 static gint Html_tag_compare(char *p1, char *p2)
4558 {
4559 while ( *p2 ) {
4560 if ( tolower(*p1) != *p2 )
4561 return(tolower(*p1) - *p2);
4562 ++p1;
4563 ++p2;
4564 }
4565 return !strchr(" >/\n\r\t", *p1);
4566 }
4567
4568 /*
4569 * Get 'tag' index
4570 * return -1 if tag is not handled yet
4571 */
4572 static gint Html_tag_index(char *tag)
4573 {
4574 gint low, high, mid, cond;
4575
4576 /* Binary search */
4577 low = 0;
4578 high = NTAGS - 1; /* Last tag index */
4579 while (low <= high) {
4580 mid = (low + high) / 2;
4581 if ((cond = Html_tag_compare(tag, Tags[mid].name)) < 0 )
4582 high = mid - 1;
4583 else if (cond > 0)
4584 low = mid + 1;
4585 else
4586 return mid;
4587 }
4588 return -1;
4589 }
4590
4591 /*
4592 * For elements with optional close, check whether is time to close.
4593 * Return value: (1: Close, 0: Don't close)
4594 * --tuned for speed.
4595 */
4596 static gint Html_needs_optional_close(gint old_idx, gint cur_idx)
4597 {
4598 static gint i_P = -1, i_LI, i_TD, i_TR, i_TH, i_DD, i_DT, i_OPTION;
4599 /* i_THEAD, i_TFOOT, i_COLGROUP; */
4600
4601 if (i_P == -1) {
4602 /* initialize the indexes of elements with optional close */
4603 i_P = Html_tag_index("p"),
4604 i_LI = Html_tag_index("li"),
4605 i_TD = Html_tag_index("td"),
4606 i_TR = Html_tag_index("tr"),
4607 i_TH = Html_tag_index("th"),
4608 i_DD = Html_tag_index("dd"),
4609 i_DT = Html_tag_index("dt"),
4610 i_OPTION = Html_tag_index("option");
4611 /* i_THEAD = Html_tag_index("thead"); */
4612 /* i_TFOOT = Html_tag_index("tfoot"); */
4613 /* i_COLGROUP = Html_tag_index("colgroup"); */
4614 }
4615
4616 if (old_idx == i_P || old_idx == i_DT) {
4617 /* P and DT are closed by block elements */
4618 return (Tags[cur_idx].Flags & 2);
4619 } else if (old_idx == i_LI) {
4620 /* LI closes LI */
4621 return (cur_idx == i_LI);
4622 } else if (old_idx == i_TD || old_idx == i_TH) {
4623 /* TD and TH are closed by TD, TH and TR */
4624 return (cur_idx == i_TD || cur_idx == i_TH || cur_idx == i_TR);
4625 } else if (old_idx == i_TR) {
4626 /* TR closes TR */
4627 return (cur_idx == i_TR);
4628 } else if (old_idx == i_DD) {
4629 /* DD is closed by DD and DT */
4630 return (cur_idx == i_DD || cur_idx == i_DT);
4631 } else if (old_idx == i_OPTION) {
4632 return 1; /* OPTION always needs close */
4633 }
4634
4635 /* HTML, HEAD, BODY are handled by Html_test_section(), not here. */
4636 /* todo: TBODY is pending */
4637 return 0;
4638 }
4639
4640
4641 /*
4642 * Conditional cleanup of the stack (at open time).
4643 * - This helps catching block elements inside inline containers (a BUG).
4644 * - It also closes elements with "optional" close tag.
4645 *
4646 * This function is called when opening a block element or <OPTION>.
4647 *
4648 * It searches the stack closing open inline containers, and closing
4649 * elements with optional close tag when necessary.
4650 *
4651 * Note: OPTION is the only non-block element with an optional close.
4652 */
4653 static void Html_stack_cleanup_at_open(DilloHtml *html, gint new_idx)
4654 {
4655 /* We know that the element we're about to push is a block element.
4656 * (except for OPTION, which is an empty inline, so is closed anyway)
4657 * Notes:
4658 * Its 'tag' is not yet pushed into the stack,
4659 * 'new_idx' is its index inside Tags[].
4660 */
4661
4662 if (!html->TagSoup)
4663 return;
4664
4665 while (html->stack_top) {
4666 gint oldtag_idx = html->stack[html->stack_top].tag_idx;
4667
4668 if (Tags[oldtag_idx].EndTag == 'O') { /* Element with optional close */
4669 if (!Html_needs_optional_close(oldtag_idx, new_idx))
4670 break;
4671 } else if (Tags[oldtag_idx].Flags & 8) { /* Block container */
4672 break;
4673 }
4674
4675 /* we have an inline (or empty) container... */
4676 if (Tags[oldtag_idx].EndTag == 'R') {
4677 MSG_HTML("<%s> is not allowed to contain <%s>. -- closing <%s>\n",
4678 Tags[oldtag_idx].name, Tags[new_idx].name,
4679 Tags[oldtag_idx].name);
4680 }
4681
4682 /* Workaround for Apache and its bad HTML directory listings... */
4683 if ((html->InFlags & IN_PRE) &&
4684 strcmp(Tags[new_idx].name, "hr") == 0)
4685 break;
4686
4687 /* This call closes the top tag only. */
4688 Html_tag_cleanup_at_close(html, oldtag_idx);
4689 }
4690 }
4691
4692 /*
4693 * HTML, HEAD and BODY elements have optional open and close tags.
4694 * Handle this "magic" here.
4695 */
4696 static void Html_test_section(DilloHtml *html, gint new_idx, gint IsCloseTag)
4697 {
4698 gchar *tag;
4699 gint tag_idx;
4700
4701 if (!(html->InFlags & IN_HTML) && html->DocType == DT_NONE)
4702 MSG_HTML("the required DOCTYPE declaration is missing (or invalid)\n");
4703
4704 if (!(html->InFlags & IN_HTML)) {
4705 tag = "<html>";
4706 tag_idx = Html_tag_index(tag + 1);
4707 if (tag_idx != new_idx || IsCloseTag) {
4708 /* implicit open */
4709 Html_force_push_tag(html, tag_idx);
4710 Tags[tag_idx].open (html, tag, strlen(tag));
4711 }
4712 }
4713
4714 if (Tags[new_idx].Flags & 32) {
4715 /* head element */
4716 if (!(html->InFlags & IN_HEAD)) {
4717 tag = "<head>";
4718 tag_idx = Html_tag_index(tag + 1);
4719 if (tag_idx != new_idx || IsCloseTag) {
4720 /* implicit open of the head element */
4721 Html_force_push_tag(html, tag_idx);
4722 Tags[tag_idx].open (html, tag, strlen(tag));
4723 }
4724 }
4725
4726 } else if (Tags[new_idx].Flags & 16) {
4727 /* body element */
4728 if (html->InFlags & IN_HEAD) {
4729 tag = "</head>";
4730 tag_idx = Html_tag_index(tag + 2);
4731 Tags[tag_idx].close (html, tag_idx);
4732 }
4733 tag = "<body>";
4734 tag_idx = Html_tag_index(tag + 1);
4735 if (tag_idx != new_idx || IsCloseTag) {
4736 /* implicit open */
4737 Html_force_push_tag(html, tag_idx);
4738 Tags[tag_idx].open (html, tag, strlen(tag));
4739 }
4740 }
4741 }
4742
4743 /*
4744 * Process a tag, given as 'tag' and 'tagsize'. -- tagsize is [1 based]
4745 * ('tag' must include the enclosing angle brackets)
4746 * This function calls the right open or close function for the tag.
4747 */
4748 static void Html_process_tag(DilloHtml *html, char *tag, gint tagsize)
4749 {
4750 gint ci, ni; /* current and new tag indexes */
4751 const char *attrbuf;
4752 char *start = tag + 1; /* discard the '<' */
4753 gint IsCloseTag = (*start == '/');
4754
4755 ni = Html_tag_index(start + IsCloseTag);
4756
4757 /* todo: doctype parsing is a bit fuzzy, but enough for the time being */
4758 if (ni == -1 && !(html->InFlags & IN_HTML)) {
4759 if (tagsize > 9 && !g_strncasecmp(tag, "<!doctype", 9))
4760 Html_parse_doctype(html, tag, tagsize);
4761 }
4762
4763 if (!(html->InFlags & IN_HTML)) {
4764 _MSG("\nDoctype: %f\n\n", html->DocTypeVersion);
4765 }
4766
4767 /* Handle HTML, HEAD and BODY. Elements with optional open and close */
4768 if (ni != -1 && !(html->InFlags & IN_BODY) /* && parsing HTML */)
4769 Html_test_section(html, ni, IsCloseTag);
4770
4771 /* White space handling */
4772 if (html->SPCPending && (!SGML_SPCDEL || !IsCloseTag))
4773 /* SGML_SPCDEL requires space pending and open tag */
4774 a_Dw_page_add_space(DW_PAGE (html->dw),
4775 html->stack[html->stack_top].style);
4776 html->SPCPending = FALSE;
4777
4778 /* Tag processing */
4779 ci = html->stack[html->stack_top].tag_idx;
4780 if (ni != -1) {
4781
4782 if (!IsCloseTag) {
4783 /* Open function */
4784
4785 /* Cleanup when opening a block element, or
4786 * when openning over an element with optional close */
4787 if (Tags[ni].Flags & 2 || (ci != -1 && Tags[ci].EndTag == 'O'))
4788 Html_stack_cleanup_at_open(html, ni);
4789
4790 /* todo: this is only raising a warning, take some defined action.
4791 * Note: apache uses IMG inside PRE (we could use its "alt"). */
4792 if ((html->InFlags & IN_PRE) && Html_tag_pre_excludes(ni))
4793 MSG_HTML("<pre> is not allowed to contain <%s>\n", Tags[ni].name);
4794
4795 /* Push the tag into the stack */
4796 Html_push_tag(html, ni);
4797
4798 /* Call the open function for this tag */
4799 Tags[ni].open (html, tag, tagsize);
4800
4801 /* Now parse attributes that can appear on any tag */
4802 if (tagsize >= 8 && /* length of "<t id=i>" */
4803 (attrbuf = Html_get_attr2(html, tag, tagsize, "id",
4804 HTML_LeftTrim | HTML_RightTrim))) {
4805 /* According to the SGML declaration of HTML 4, all NAME values
4806 * occuring outside entities must be converted to uppercase
4807 * (this is what "NAMECASE GENERAL YES" says). But the HTML 4
4808 * spec states in Sec. 7.5.2 that anchor ids are case-sensitive.
4809 * So we don't do it and hope for better specs in the future ...
4810 */
4811 Html_check_name_val(html, attrbuf, "id");
4812 /* We compare the "id" value with the url-decoded "name" value */
4813 if (!html->NameVal || strcmp(html->NameVal, attrbuf)) {
4814 if (html->NameVal)
4815 MSG_HTML("'id' and 'name' attribute of <a> tag differ\n");
4816 Html_add_anchor(html, attrbuf);
4817 }
4818 }
4819
4820 /* Reset NameVal */
4821 if (html->NameVal) {
4822 g_free(html->NameVal);
4823 html->NameVal = NULL;
4824 }
4825
4826 /* let the parser know this was an open tag */
4827 html->PrevWasOpenTag = TRUE;
4828
4829 /* Request inmediate close for elements with forbidden close tag. */
4830 /* todo: XHTML always requires close tags. A simple implementation
4831 * of the commented clause below will make it work. */
4832 if (/* parsing HTML && */ Tags[ni].EndTag == 'F')
4833 html->ReqTagClose = TRUE;
4834 }
4835
4836 /* Close function: test for </x>, ReqTagClose, <x /> and <x/> */
4837 if (*start == '/' || /* </x> */
4838 html->ReqTagClose || /* request */
4839 (tag[tagsize - 2] == '/' && /* XML: */
4840 (isspace(tag[tagsize - 3]) || /* <x /> */
4841 (size_t)tagsize == strlen(Tags[ni].name) + 3))) { /* <x/> */
4842
4843 Tags[ni].close (html, ni);
4844 /* This was a close tag */
4845 html->PrevWasOpenTag = FALSE;
4846 html->ReqTagClose = FALSE;
4847 }
4848
4849 } else {
4850 /* tag not working - just ignore it */
4851 }
4852 }
4853
4854 /*
4855 * Get attribute value for 'attrname' and return it.
4856 * Tags start with '<' and end with a '>' (Ex: "<P align=center>")
4857 * tagsize = strlen(tag) from '<' to '>', inclusive.
4858 *
4859 * Returns one of the following:
4860 * * The value of the attribute.
4861 * * An empty string if the attribute exists but has no value.
4862 * * NULL if the attribute doesn't exist.
4863 */
4864 static const char *Html_get_attr2(DilloHtml *html,
4865 const char *tag,
4866 gint tagsize,
4867 const char *attrname,
4868 DilloHtmlTagParsingFlags flags)
4869 {
4870 gint i, isocode, entsize, Found = 0, delimiter = 0, attr_pos = 0;
4871 GString *Buf = html->attr_data;
4872 DilloHtmlTagParsingState state = SEEK_ATTR_START;
4873
4874 g_return_val_if_fail(*attrname, NULL);
4875
4876 g_string_truncate(Buf, 0);
4877
4878 for (i = 1; i < tagsize; ++i) {
4879 switch (state) {
4880 case SEEK_ATTR_START:
4881 if (isspace(tag[i]))
4882 state = SEEK_TOKEN_START;
4883 else if (tag[i] == '=')
4884 state = SEEK_VALUE_START;
4885 break;
4886
4887 case MATCH_ATTR_NAME:
4888 if ((Found = (!(attrname[attr_pos]) &&
4889 (tag[i] == '=' || isspace(tag[i]) || tag[i] == '>')))) {
4890 state = SEEK_TOKEN_START;
4891 --i;
4892 } else if (tolower(tag[i]) != tolower(attrname[attr_pos++]))
4893 state = SEEK_ATTR_START;
4894 break;
4895
4896 case SEEK_TOKEN_START:
4897 if (tag[i] == '=') {
4898 state = SEEK_VALUE_START;
4899 } else if (!isspace(tag[i])) {
4900 attr_pos = 0;
4901 state = (Found) ? FINISHED : MATCH_ATTR_NAME;
4902 --i;
4903 }
4904 break;
4905 case SEEK_VALUE_START:
4906 if (!isspace(tag[i])) {
4907 delimiter = (tag[i] == '"' || tag[i] == '\'') ? tag[i] : ' ';
4908 i -= (delimiter == ' ');
4909 state = (Found) ? GET_VALUE : SKIP_VALUE;
4910 }
4911 break;
4912
4913 case SKIP_VALUE:
4914 if ((delimiter == ' ' && isspace(tag[i])) || tag[i] == delimiter)
4915 state = SEEK_TOKEN_START;
4916 break;
4917 case GET_VALUE:
4918 if ((delimiter == ' ' && (isspace(tag[i]) || tag[i] == '>')) ||
4919 tag[i] == delimiter) {
4920 state = FINISHED;
4921 } else if (tag[i] == '&' && (flags & HTML_ParseEntities)) {
4922 if ((isocode = Html_parse_entity(html, tag+i,
4923 tagsize-i, &entsize)) >= 0) {
4924 g_string_append_c(Buf, (gchar) isocode);
4925 i += entsize-1;
4926 } else {
4927 g_string_append_c(Buf, tag[i]);
4928 }
4929 } else if (tag[i] == '\r' || tag[i] == '\t') {
4930 g_string_append_c(Buf, ' ');
4931 } else if (tag[i] == '\n') {
4932 /* ignore */
4933 } else {
4934 g_string_append_c(Buf, tag[i]);
4935 }
4936 break;
4937
4938 case FINISHED:
4939 i = tagsize;
4940 break;
4941 }
4942 }
4943
4944 if (flags & HTML_LeftTrim)
4945 while (isspace(Buf->str[0]))
4946 g_string_erase(Buf, 0, 1);
4947 if (flags & HTML_RightTrim)
4948 while (Buf->len && isspace(Buf->str[Buf->len - 1]))
4949 g_string_truncate(Buf, Buf->len - 1);
4950
4951 return (Found) ? Buf->str : NULL;
4952 }
4953
4954 /*
4955 * Call Html_get_attr2 telling it to parse entities and strip the result
4956 */
4957 static const char *Html_get_attr(DilloHtml *html,
4958 const char *tag,
4959 gint tagsize,
4960 const char *attrname)
4961 {
4962 return Html_get_attr2(html, tag, tagsize, attrname,
4963 HTML_LeftTrim | HTML_RightTrim | HTML_ParseEntities);
4964 }
4965
4966 /*
4967 * "Html_get_attr with default"
4968 * Call Html_get_attr() and strdup() the returned string.
4969 * If the attribute isn't found a copy of 'def' is returned.
4970 */
4971 static char *Html_get_attr_wdef(DilloHtml *html,
4972 const char *tag,
4973 gint tagsize,
4974 const char *attrname,
4975 const char *def)
4976 {
4977 const char *attrbuf = Html_get_attr(html, tag, tagsize, attrname);
4978
4979 return attrbuf ? g_strdup(attrbuf) : g_strdup(def);
4980 }
4981
4982 /*
4983 * Add a widget to the page.
4984 */
4985 static void Html_add_widget(DilloHtml *html,
4986 DwWidget *widget,
4987 char *width_str,
4988 char *height_str,
4989 DwStyle *style_attrs)
4990 {
4991 DwStyle new_style_attrs, *style;
4992
4993 new_style_attrs = *style_attrs;
4994 new_style_attrs.width = width_str ?
4995 Html_parse_length (html, width_str) : DW_STYLE_LENGTH_AUTO;
4996 new_style_attrs.height = height_str ?
4997 Html_parse_length (html, height_str) : DW_STYLE_LENGTH_AUTO;
4998 style = a_Dw_style_new (&new_style_attrs, (html)->bw->main_window->window);
4999 a_Dw_page_add_widget(DW_PAGE (html->dw), widget, style);
5000 a_Dw_style_unref (style);
5001 }
5002
5003
5004 /*
5005 * Dispatch the apropriate function for 'Op'
5006 * This function is a Cache client and gets called whenever new data arrives
5007 * Op : operation to perform.
5008 * CbData : a pointer to a DilloHtml structure
5009 * Buf : a pointer to new data
5010 * BufSize : new data size (in bytes)
5011 */
5012 static void Html_callback(int Op, CacheClient_t *Client)
5013 {
5014 if ( Op ) {
5015 Html_write(Client->CbData, Client->Buf, Client->BufSize, 1);
5016 Html_close(Client->CbData, Client->Key);
5017 } else
5018 Html_write(Client->CbData, Client->Buf, Client->BufSize, 0);
5019 }
5020
5021 /*
5022 * Here's where we parse the html and put it into the page structure.
5023 * Return value: number of bytes parsed
5024 */
5025 static gint Html_write_raw(DilloHtml *html, char *buf, gint bufsize, gint Eof)
5026 {
5027 char ch = 0, *p, *text;
5028 DwPage *page;
5029 gint token_start, buf_index;
5030
5031 g_return_val_if_fail ((page = DW_PAGE (html->dw)) != NULL, 0);
5032
5033 buf = g_strndup(buf, bufsize);
5034
5035 /* Now, 'buf' and 'bufsize' define a buffer aligned to start at a token
5036 * boundary. Iterate through tokens until end of buffer is reached. */
5037 buf_index = 0;
5038 token_start = buf_index;
5039 while (buf_index < bufsize) {
5040 /* invariant: buf_index == bufsize || token_start == buf_index */
5041
5042 if (html->stack[html->stack_top].parse_mode ==
5043 DILLO_HTML_PARSE_MODE_VERBATIM) {
5044 /* Non HTML code here, let's skip until closing tag */
5045 do {
5046 char *tag = html->stack[html->stack_top].tag_name;
5047 buf_index += strcspn(buf + buf_index, "<");
5048 if (buf_index + (gint)strlen(tag) + 3 > bufsize) {
5049 buf_index = bufsize;
5050 } else if (strncmp(buf + buf_index, "</", 2) == 0 &&
5051 Html_match_tag(tag, buf+buf_index+2, strlen(tag)+1)) {
5052 /* copy VERBATIM text into the stash buffer */
5053 text = g_strndup(buf + token_start, buf_index - token_start);
5054 g_string_append(html->Stash, text);
5055 g_free(text);
5056 token_start = buf_index;
5057 break;
5058 } else
5059 ++buf_index;
5060 } while (buf_index < bufsize);
5061 }
5062
5063 if ( isspace(buf[buf_index]) ) {
5064 /* whitespace: group all available whitespace */
5065 while (++buf_index < bufsize && isspace(buf[buf_index]));
5066 Html_process_space(html, buf + token_start, buf_index - token_start);
5067 token_start = buf_index;
5068
5069 } else if (buf[buf_index] == '<' && (ch = buf[buf_index + 1]) &&
5070 (isalpha(ch) || strchr("/!?", ch)) ) {
5071 /* Tag */
5072 if (buf_index + 3 < bufsize && !strncmp(buf + buf_index, "<!--", 4)) {
5073 /* Comment: search for close of comment, skipping over
5074 * everything except a matching "-->" tag. */
5075 while ( (p = memchr(buf + buf_index, '>', bufsize - buf_index)) ){
5076 buf_index = p - buf + 1;
5077 if ( p[-1] == '-' && p[-2] == '-' ) break;
5078 }
5079 if ( p ) {
5080 /* Got the whole comment. Let's throw it away! :) */
5081 token_start = buf_index;
5082 } else
5083 buf_index = bufsize;
5084 } else {
5085 /* Tag: search end of tag (skipping over quoted strings) */
5086 html->CurrTagOfs = html->Start_Ofs + token_start;
5087
5088 while ( buf_index < bufsize ) {
5089 buf_index++;
5090 buf_index += strcspn(buf + buf_index, ">\"'<");
5091 if ( (ch = buf[buf_index]) == '>' ) {
5092 break;
5093 } else if ( ch == '"' || ch == '\'' ) {
5094 /* Skip over quoted string */
5095 buf_index++;
5096 buf_index += strcspn(buf + buf_index,
5097 (ch == '"') ? "\">" : "'>");
5098 if ( buf[buf_index] == '>' ) {
5099 /* Unterminated string value? Let's look ahead and test:
5100 * (<: unterminated, closing-quote: terminated) */
5101 gint offset = buf_index + 1;
5102 offset += strcspn(buf + offset,
5103 (ch == '"') ? "\"<" : "'<");
5104 if (buf[offset] == ch || !buf[offset]) {
5105 buf_index = offset;
5106 } else {
5107 MSG_HTML("attribute lacks closing quote\n");
5108 break;
5109 }
5110 }
5111 } else if ( ch == '<' ) {
5112 /* unterminated tag detected */
5113 p = g_strndup(buf+token_start+1,
5114 strcspn(buf+token_start+1, " <"));
5115 MSG_HTML("<%s> element lacks its closing '>'\n", p);
5116 g_free(p);
5117 --buf_index;
5118 break;
5119 }
5120 }
5121 if (buf_index < bufsize) {
5122 buf_index++;
5123 Html_process_tag(html, buf + token_start,
5124 buf_index - token_start);
5125 token_start = buf_index;
5126 }
5127 }
5128 } else {
5129 /* A Word: search for whitespace or tag open */
5130 while (++buf_index < bufsize) {
5131 buf_index += strcspn(buf + buf_index, " <\n\r\t\f\v");
5132 if ( buf[buf_index] == '<' && (ch = buf[buf_index + 1]) &&
5133 !isalpha(ch) && !strchr("/!?", ch))
5134 continue;
5135 break;
5136 }
5137 if (buf_index < bufsize || Eof) {
5138 /* successfully found end of token */
5139 Html_process_word(html, buf + token_start,
5140 buf_index - token_start);
5141 token_start = buf_index;
5142 }
5143 }
5144 }/*while*/
5145
5146 a_Dw_page_flush(page);
5147 g_free(buf);
5148
5149 return token_start;
5150 }
5151
5152 /*
5153 * Process the newly arrived html and put it into the page structure.
5154 * (This function is called by Html_callback whenever there's new data)
5155 */
5156 static void Html_write(DilloHtml *html, char *Buf, gint BufSize, gint Eof)
5157 {
5158 DwPage *page;
5159 char completestr[32];
5160 gint token_start;
5161 char *buf = Buf + html->Start_Ofs;
5162 gint bufsize = BufSize - html->Start_Ofs;
5163
5164 g_return_if_fail ( (page = DW_PAGE (html->dw)) != NULL );
5165
5166 html->Start_Buf = Buf;
5167 token_start = Html_write_raw(html, buf, bufsize, Eof);
5168 html->Start_Ofs += token_start;
5169
5170 if ( html->bw ) {
5171 g_snprintf(
5172 completestr, 32, "%s%.1f Kb",
5173 PBAR_PSTR(prefs.panel_size == 1),
5174 (float)html->Start_Ofs/1024);
5175 a_Progressbar_update(html->bw->progress, completestr, 1);
5176 }
5177 }
5178
5179 /*
5180 * Finish parsing a HTML page
5181 * (Free html struct, close the client and update progressbar).
5182 */
5183 static void Html_close(DilloHtml *html, gint ClientKey)
5184 {
5185 gint si;
5186
5187 /*
5188 #if defined (DEBUG_LEVEL) && DEBUG_LEVEL >= 1
5189 a_Dw_widget_print_tree (GTK_DW_VIEWPORT(html->dw->viewport)->child);
5190 #endif
5191 */
5192
5193 /* force the close of elements left open (todo: not for XHTML) */
5194 while ((si = html->stack_top)) {
5195 if (html->stack[si].tag_idx != -1) {
5196 Html_tag_cleanup_at_close(html, html->stack[si].tag_idx);
5197 }
5198 }
5199 g_free(html->stack[0].tag_name); /* "none" */
5200 a_Dw_style_unref(html->stack[0].style); /* template style */
5201
5202 g_free(html->stack);
5203
5204 g_string_free(html->Stash, TRUE);
5205 g_free(html->SPCBuf);
5206 g_string_free(html->attr_data, TRUE);
5207
5208 /* Remove this client from our active list */
5209 a_Interface_close_client(html->bw, ClientKey);
5210
5211 /* Set progress bar insensitive */
5212 a_Progressbar_update(html->bw->progress, NULL, 0);
5213
5214 g_free(html);
5215 }
5216
5217
0 /*
1 * File: html.cc
2 *
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * Dillo HTML parsing routines
13 */
14
15 /*-----------------------------------------------------------------------------
16 * Includes
17 *---------------------------------------------------------------------------*/
18 #include <ctype.h> /* for isspace and tolower */
19 #include <string.h> /* for memcpy and memmove */
20 #include <stdlib.h>
21 #include <stdio.h> /* for sprintf */
22 #include <errno.h>
23
24 #include "bw.h" /* for BrowserWindow */
25 #include "msg.h"
26 #include "binaryconst.h"
27 #include "colors.h"
28 #include "utf8.hh"
29
30 #include "misc.h"
31 #include "uicmd.hh"
32 #include "history.h"
33 #include "menu.hh"
34 #include "prefs.h"
35 #include "capi.h"
36 #include "html.hh"
37 #include "html_common.hh"
38 #include "form.hh"
39 #include "table.hh"
40
41 #include "dw/textblock.hh"
42 #include "dw/bullet.hh"
43 #include "dw/listitem.hh"
44 #include "dw/image.hh"
45 #include "dw/ruler.hh"
46
47 /*-----------------------------------------------------------------------------
48 * Defines
49 *---------------------------------------------------------------------------*/
50
51 /* Define to 1 to ignore white space immediately after an open tag,
52 * and immediately before a close tag. */
53 #define SGML_SPCDEL 0
54
55 #define TAB_SIZE 8
56
57 /*-----------------------------------------------------------------------------
58 * Name spaces
59 *---------------------------------------------------------------------------*/
60 using namespace lout;
61 using namespace dw;
62 using namespace dw::core;
63 using namespace dw::core::ui;
64 using namespace dw::core::style;
65
66 /*-----------------------------------------------------------------------------
67 * Typedefs
68 *---------------------------------------------------------------------------*/
69 class DilloHtml;
70 typedef void (*TagOpenFunct) (DilloHtml *html, const char *tag, int tagsize);
71 typedef void (*TagCloseFunct) (DilloHtml *html, int TagIdx);
72
73 typedef enum {
74 SEEK_ATTR_START,
75 MATCH_ATTR_NAME,
76 SEEK_TOKEN_START,
77 SEEK_VALUE_START,
78 SKIP_VALUE,
79 GET_VALUE,
80 FINISHED
81 } DilloHtmlTagParsingState;
82
83 typedef enum {
84 HTML_LeftTrim = 1 << 0,
85 HTML_RightTrim = 1 << 1,
86 HTML_ParseEntities = 1 << 2
87 } DilloHtmlTagParsingFlags;
88
89
90 /*
91 * Exported function with C linkage.
92 */
93 extern "C" {
94 void *a_Html_text(const char *type, void *P, CA_Callback_t *Call,void **Data);
95 }
96
97 /*-----------------------------------------------------------------------------
98 * Forward declarations
99 *---------------------------------------------------------------------------*/
100 static const char *Html_get_attr2(DilloHtml *html,
101 const char *tag,
102 int tagsize,
103 const char *attrname,
104 int tag_parsing_flags);
105 static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof);
106 static bool Html_load_image(BrowserWindow *bw, DilloUrl *url,
107 const DilloUrl *requester, DilloImage *image);
108 static void Html_callback(int Op, CacheClient_t *Client);
109 static void Html_tag_cleanup_at_close(DilloHtml *html, int TagIdx);
110
111 /*-----------------------------------------------------------------------------
112 * Local Data
113 *---------------------------------------------------------------------------*/
114 /* Parsing table structure */
115 typedef struct {
116 const char *name; /* element name */
117 unsigned char Flags; /* flags (explained near the table data) */
118 char EndTag; /* Is it Required, Optional or Forbidden */
119 uchar_t TagLevel; /* Used to heuristically parse bad HTML */
120 TagOpenFunct open; /* Open function */
121 TagCloseFunct close; /* Close function */
122 } TagInfo;
123 extern const TagInfo Tags[];
124
125 /*-----------------------------------------------------------------------------
126 *-----------------------------------------------------------------------------
127 * Main Code
128 *-----------------------------------------------------------------------------
129 *---------------------------------------------------------------------------*/
130
131 /*
132 * Collect HTML error strings.
133 */
134 void DilloHtml::bugMessage(const char *format, ... )
135 {
136 va_list argp;
137
138 dStr_sprintfa(bw->page_bugs,
139 "HTML warning: line %d, ",
140 getCurTagLineNumber());
141 va_start(argp, format);
142 dStr_vsprintfa(bw->page_bugs, format, argp);
143 va_end(argp);
144 a_UIcmd_set_bug_prog(bw, ++bw->num_page_bugs);
145 }
146
147 /*
148 * Wrapper for a_Url_new that adds an error detection message.
149 * If use_base_url is TRUE, it uses base_url. Otherwise it uses html->base_url.
150 */
151 DilloUrl *a_Html_url_new(DilloHtml *html,
152 const char *url_str, const char *base_url,
153 int use_base_url)
154 {
155 DilloUrl *url;
156 int n_ic, n_ic_spc;
157
158 url = a_Url_new(url_str,
159 (use_base_url) ? base_url : URL_STR_(html->base_url));
160 if ((n_ic = URL_ILLEGAL_CHARS(url)) != 0) {
161 const char *suffix = (n_ic) > 1 ? "s" : "";
162 n_ic_spc = URL_ILLEGAL_CHARS_SPC(url);
163 if (n_ic == n_ic_spc) {
164 BUG_MSG("URL has %d illegal character%s (%d space%s)\n",
165 n_ic, suffix, n_ic_spc, suffix);
166 } else if (n_ic_spc == 0) {
167 BUG_MSG("URL has %d illegal character%s (%d in {00-1F, 7F} range)\n",
168 n_ic, suffix, n_ic);
169 } else {
170 BUG_MSG("URL has %d illegal character%s: "
171 "%d space%s, and %d in {00-1F, 7F} range\n",
172 n_ic, suffix,
173 n_ic_spc, n_ic_spc > 1 ? "s" : "", n_ic-n_ic_spc);
174 }
175 }
176 return url;
177 }
178
179 /*
180 * Set callback function and callback data for the "html/text" MIME type.
181 */
182 void *a_Html_text(const char *Type, void *P, CA_Callback_t *Call, void **Data)
183 {
184 DilloWeb *web = (DilloWeb*)P;
185 DilloHtml *html = new DilloHtml(web->bw, web->url, Type);
186
187 *Data = (void*)html;
188 *Call = (CA_Callback_t)Html_callback;
189
190 return (void*)html->dw;
191 }
192
193 static void Html_free(void *data)
194 {
195 delete ((DilloHtml*)data);
196 }
197
198 /*
199 * Used by the "Load images" page menuitem.
200 */
201 void a_Html_load_images(void *v_html, DilloUrl *pattern)
202 {
203 DilloHtml *html = (DilloHtml*)v_html;
204
205 html->loadImages(pattern);
206 }
207
208 /*
209 * Search for form
210 */
211 static bool Html_contains_form(DilloHtml *html, void *v_form)
212 {
213 for (int i = 0; i < html->forms->size(); i++) {
214 if (html->forms->get(i) == v_form) {
215 return true;
216 }
217 }
218 return false;
219 }
220
221 /*
222 * Used by the "Submit form" form menuitem.
223 */
224 void a_Html_form_submit(void *v_html, void *v_form)
225 {
226 DilloHtml *html = (DilloHtml*)v_html;
227
228 if (Html_contains_form(html, v_form)) {
229 /* it's still valid */
230 a_Html_form_submit2(v_form);
231 }
232 }
233
234 /*
235 * Used by the "Reset form" form menuitem.
236 */
237 void a_Html_form_reset(void *v_html, void *v_form)
238 {
239 DilloHtml *html = (DilloHtml*)v_html;
240
241 if (Html_contains_form(html, v_form)) {
242 /* it's still valid */
243 a_Html_form_reset2(v_form);
244 }
245 }
246
247 /*
248 * Used by the "Show/Hide hiddens" form menuitem.
249 */
250 void a_Html_form_display_hiddens(void *v_html, void *v_form, bool_t display)
251 {
252 DilloHtml *html = (DilloHtml*)v_html;
253
254 if (Html_contains_form(html, v_form)) {
255 /* it's still valid */
256 a_Html_form_display_hiddens2(v_form, (display != 0));
257 }
258 }
259
260 /*
261 * Set the URL data for image maps.
262 */
263 static void Html_set_link_coordinates(DilloHtml *html, int link, int x, int y)
264 {
265 char data[64];
266
267 if (x != -1) {
268 snprintf(data, 64, "?%d,%d", x, y);
269 a_Url_set_ismap_coords(html->links->get(link), data);
270 }
271 }
272
273 /*
274 * Create a new link, set it as the url's parent
275 * and return the index.
276 */
277 static int Html_set_new_link(DilloHtml *html, DilloUrl **url)
278 {
279 int nl = html->links->size();
280 html->links->increase();
281 html->links->set(nl, (*url) ? *url : NULL);
282 return nl;
283 }
284
285 /*
286 * Add a new image to our list.
287 * image is NULL if dillo will try to load the image immediately.
288 */
289 static void Html_add_new_htmlimage(DilloHtml *html,
290 DilloUrl **url, DilloImage *image)
291 {
292 DilloHtmlImage *hi = dNew(DilloHtmlImage, 1);
293 hi->url = *url;
294 hi->image = image;
295 a_Image_ref(image);
296
297 int n = html->images->size();
298 html->images->increase();
299 html->images->set(n, hi);
300 }
301
302 /*
303 * Evaluates the ALIGN attribute (left|center|right|justify) and
304 * sets the style at the top of the stack.
305 */
306 void a_Html_tag_set_align_attr(DilloHtml *html, const char *tag, int tagsize)
307 {
308 const char *align;
309
310 if ((align = a_Html_get_attr(html, tag, tagsize, "align"))) {
311 TextAlignType textAlignType = TEXT_ALIGN_LEFT;
312
313 if (dStrcasecmp (align, "left") == 0)
314 textAlignType = TEXT_ALIGN_LEFT;
315 else if (dStrcasecmp (align, "right") == 0)
316 textAlignType = TEXT_ALIGN_RIGHT;
317 else if (dStrcasecmp (align, "center") == 0)
318 textAlignType = TEXT_ALIGN_CENTER;
319 else if (dStrcasecmp (align, "justify") == 0)
320 textAlignType = TEXT_ALIGN_JUSTIFY;
321 #if 0
322 else if (dStrcasecmp (align, "char") == 0) {
323 /* TODO: Actually not supported for <p> etc. */
324 v.textAlign = TEXT_ALIGN_STRING;
325 if ((charattr = a_Html_get_attr(html, tag, tagsize, "char"))) {
326 if (charattr[0] == 0)
327 /* TODO: ALIGN=" ", and even ALIGN="&32;" will reult in
328 * an empty string (don't know whether the latter is
329 * correct, has to be clarified with the specs), so
330 * that for empty strings, " " is assumed. */
331 style_attrs.textAlignChar = ' ';
332 else
333 style_attrs.textAlignChar = charattr[0];
334 } else
335 /* TODO: Examine LANG attr of <html>. */
336 style_attrs.textAlignChar = '.';
337 }
338 #endif
339 html->styleEngine->setNonCssHint(CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM,
340 textAlignType);
341 }
342 }
343
344 /*
345 * Evaluates the VALIGN attribute (top|bottom|middle|baseline) and
346 * sets the style in style_attrs. Returns true when set.
347 */
348 bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag, int tagsize)
349 {
350 const char *attr;
351 VAlignType valign;
352
353 if ((attr = a_Html_get_attr(html, tag, tagsize, "valign"))) {
354 if (dStrcasecmp (attr, "top") == 0)
355 valign = VALIGN_TOP;
356 else if (dStrcasecmp (attr, "bottom") == 0)
357 valign = VALIGN_BOTTOM;
358 else if (dStrcasecmp (attr, "baseline") == 0)
359 valign = VALIGN_BASELINE;
360 else
361 valign = VALIGN_MIDDLE;
362
363 html->styleEngine->setNonCssHint (CSS_PROPERTY_VERTICAL_ALIGN,
364 CSS_TYPE_ENUM, valign);
365 return true;
366 } else
367 return false;
368 }
369
370
371 /*
372 * Create and add a new Textblock to the current Textblock
373 */
374 static void Html_add_textblock(DilloHtml *html, int space)
375 {
376 Textblock *textblock = new Textblock (prefs.limit_text_width);
377
378 HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ());
379 HT2TB(html)->addWidget (textblock, html->styleEngine->style ());
380 HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ());
381 S_TOP(html)->textblock = html->dw = textblock;
382 S_TOP(html)->hand_over_break = true;
383 }
384
385 /*
386 * Create and initialize a new DilloHtml class
387 */
388 DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
389 const char *content_type)
390 {
391 /* Init main variables */
392 bw = p_bw;
393 page_url = a_Url_dup(url);
394 base_url = a_Url_dup(url);
395 dw = NULL;
396
397 /* Init event receiver */
398 linkReceiver.html = this;
399 HT2LT(this)->connectLink (&linkReceiver);
400
401 a_Bw_add_doc(p_bw, this);
402
403 /* Init for-parsing variables */
404 Start_Buf = NULL;
405 Start_Ofs = 0;
406
407 _MSG("DilloHtml(): content type: %s\n", content_type);
408 this->content_type = dStrdup(content_type);
409
410 /* get charset */
411 a_Misc_parse_content_type(content_type, NULL, NULL, &charset);
412
413 stop_parser = false;
414
415 CurrTagOfs = 0;
416 OldTagOfs = 0;
417 OldTagLine = 1;
418
419 DocType = DT_NONE; /* assume Tag Soup 0.0! :-) */
420 DocTypeVersion = 0.0f;
421
422 styleEngine = new StyleEngine (HT2LT (this));
423
424 cssUrls = new misc::SimpleVector <DilloUrl*> (1);
425
426 stack = new misc::SimpleVector <DilloHtmlState> (16);
427 stack->increase();
428 stack->getRef(0)->parse_mode = DILLO_HTML_PARSE_MODE_INIT;
429 stack->getRef(0)->table_mode = DILLO_HTML_TABLE_MODE_NONE;
430 stack->getRef(0)->table_border_mode = DILLO_HTML_TABLE_BORDER_SEPARATE;
431 stack->getRef(0)->cell_text_align_set = false;
432 stack->getRef(0)->list_type = HTML_LIST_NONE;
433 stack->getRef(0)->list_number = 0;
434 stack->getRef(0)->tag_idx = -1; /* MUST not be used */
435 stack->getRef(0)->textblock = NULL;
436 stack->getRef(0)->table = NULL;
437 stack->getRef(0)->ref_list_item = NULL;
438 stack->getRef(0)->hand_over_break = false;
439
440 InFlags = IN_NONE;
441
442 Stash = dStr_new("");
443 StashSpace = false;
444
445 pre_column = 0;
446 PreFirstChar = false;
447 PrevWasCR = false;
448 InVisitedLink = false;
449 ReqTagClose = false;
450 TagSoup = true;
451 loadCssFromStash = false;
452
453 Num_HTML = Num_HEAD = Num_BODY = Num_TITLE = 0;
454
455 attr_data = dStr_sized_new(1024);
456
457 non_css_link_color = -1;
458 non_css_visited_color = -1;
459 visited_color = -1;
460
461 /* Init page-handling variables */
462 forms = new misc::SimpleVector <DilloHtmlForm*> (1);
463 inputs_outside_form = new misc::SimpleVector <DilloHtmlInput*> (1);
464 links = new misc::SimpleVector <DilloUrl*> (64);
465 images = new misc::SimpleVector <DilloHtmlImage*> (16);
466
467 /* Initialize the main widget */
468 initDw();
469 /* Hook destructor to the dw delete call */
470 dw->setDeleteCallback(Html_free, this);
471 }
472
473 /*
474 * Miscellaneous initializations for Dw
475 */
476 void DilloHtml::initDw()
477 {
478 dReturn_if_fail (dw == NULL);
479
480 /* Create the main widget */
481 dw = stack->getRef(0)->textblock = new Textblock (prefs.limit_text_width);
482
483 bw->num_page_bugs = 0;
484 dStr_truncate(bw->page_bugs, 0);
485 }
486
487 /*
488 * Free memory used by the DilloHtml class.
489 */
490 DilloHtml::~DilloHtml()
491 {
492 _MSG("::~DilloHtml(this=%p)\n", this);
493
494 freeParseData();
495
496 a_Bw_remove_doc(bw, this);
497
498 a_Url_free(page_url);
499 a_Url_free(base_url);
500
501 for (int i = 0; i < cssUrls->size(); i++)
502 a_Url_free(cssUrls->get(i));
503 delete (cssUrls);
504
505 for (int i = 0; i < forms->size(); i++)
506 a_Html_form_delete (forms->get(i));
507 delete(forms);
508
509 for (int i = 0; i < inputs_outside_form->size(); i++)
510 a_Html_input_delete(inputs_outside_form->get(i));
511 delete(inputs_outside_form);
512
513 for (int i = 0; i < links->size(); i++)
514 a_Url_free(links->get(i));
515 delete (links);
516
517 for (int i = 0; i < images->size(); i++) {
518 DilloHtmlImage *img = images->get(i);
519 a_Url_free(img->url);
520 a_Image_unref(img->image);
521 dFree(img);
522 }
523 delete (images);
524
525 delete styleEngine;
526 }
527
528 /*
529 * Process the newly arrived html and put it into the page structure.
530 * (This function is called by Html_callback whenever there's new data)
531 */
532 void DilloHtml::write(char *Buf, int BufSize, int Eof)
533 {
534 int token_start;
535 char *buf = Buf + Start_Ofs;
536 int bufsize = BufSize - Start_Ofs;
537
538 _MSG("DilloHtml::write BufSize=%d Start_Ofs=%d\n", BufSize, Start_Ofs);
539 #if 0
540 char *aux = dStrndup(Buf, BufSize);
541 MSG(" {%s}\n", aux);
542 dFree(aux);
543 #endif
544
545 /* Update Start_Buf. It may be used after the parser is stopped */
546 Start_Buf = Buf;
547
548 dReturn_if (dw == NULL);
549 dReturn_if (stop_parser == true);
550
551 token_start = Html_write_raw(this, buf, bufsize, Eof);
552 Start_Ofs += token_start;
553 }
554
555 /*
556 * Return the line number of the tag being processed by the parser.
557 * Also update the offsets.
558 */
559 int DilloHtml::getCurTagLineNumber()
560 {
561 int i, ofs, line;
562 const char *p = Start_Buf;
563
564 dReturn_val_if_fail(p != NULL, -1);
565
566 ofs = CurrTagOfs;
567 line = OldTagLine;
568 for (i = OldTagOfs; i < ofs; ++i)
569 if (p[i] == '\n')
570 ++line;
571 OldTagOfs = CurrTagOfs;
572 OldTagLine = line;
573 return line;
574 }
575
576 /*
577 * Free parsing data.
578 */
579 void DilloHtml::freeParseData()
580 {
581 delete(stack);
582
583 dStr_free(Stash, TRUE);
584 dStr_free(attr_data, TRUE);
585 dFree(content_type);
586 dFree(charset);
587 }
588
589 /*
590 * Finish parsing a HTML page. Close the parser and close the client.
591 * The class is not deleted here, it remains until the widget is destroyed.
592 */
593 void DilloHtml::finishParsing(int ClientKey)
594 {
595 int si;
596
597 dReturn_if (stop_parser == true);
598
599 /* force the close of elements left open (TODO: not for XHTML) */
600 while ((si = stack->size() - 1)) {
601 if (stack->getRef(si)->tag_idx != -1) {
602 Html_tag_cleanup_at_close(this, stack->getRef(si)->tag_idx);
603 }
604 }
605 /* Remove this client from our active list */
606 a_Bw_close_client(bw, ClientKey);
607 }
608
609 /*
610 * Allocate and insert form information.
611 */
612 int DilloHtml::formNew(DilloHtmlMethod method, const DilloUrl *action,
613 DilloHtmlEnc enc, const char *charset)
614 {
615 // avoid data loss on repush after CSS stylesheets have been loaded
616 bool enabled = bw->NumPendingStyleSheets == 0;
617 DilloHtmlForm *form = a_Html_form_new (this, method, action,
618 enc, charset, enabled);
619 int nf = forms->size ();
620 forms->increase ();
621 forms->set (nf, form);
622 _MSG("Html formNew: action=%s nform=%d\n", action, nf);
623 return forms->size();
624 }
625
626 /*
627 * Get the current form.
628 */
629 DilloHtmlForm *DilloHtml::getCurrentForm ()
630 {
631 return forms->get (forms->size() - 1);
632 }
633
634 bool_t DilloHtml::unloadedImages()
635 {
636 for (int i = 0; i < images->size(); i++) {
637 if (images->get(i)->image != NULL) {
638 return TRUE;
639 }
640 }
641 return FALSE;
642 }
643
644 /*
645 * Load images if they were disabled.
646 */
647 void DilloHtml::loadImages (const DilloUrl *pattern)
648 {
649 dReturn_if_fail (bw->nav_expecting == FALSE);
650
651 /* If the user asked for a specific URL, the user (NULL) is the requester,
652 * but if the user just asked for all URLs, use the page URL as the
653 * requester. If the possible patterns become more complex, it might be
654 * good to have the caller supply the requester instead.
655 */
656 const DilloUrl *requester = pattern ? NULL : this->page_url;
657
658 for (int i = 0; i < images->size(); i++) {
659 if (images->get(i)->image) {
660 if ((!pattern) || (!a_Url_cmp(images->get(i)->url, pattern))) {
661 if (Html_load_image(bw, images->get(i)->url, requester,
662 images->get(i)->image)) {
663 a_Image_unref (images->get(i)->image);
664 images->get(i)->image = NULL; // web owns it now
665 }
666 }
667 }
668 }
669 }
670
671 /*
672 * Save URL in a vector (may be loaded later).
673 */
674 void DilloHtml::addCssUrl(const DilloUrl *url)
675 {
676 int nu = cssUrls->size();
677 cssUrls->increase();
678 cssUrls->set(nu, a_Url_dup(url));
679 }
680
681 bool DilloHtml::HtmlLinkReceiver::enter (Widget *widget, int link, int img,
682 int x, int y)
683 {
684 BrowserWindow *bw = html->bw;
685
686 _MSG(" ** ");
687 if (link == -1) {
688 _MSG(" Link LEAVE notify...\n");
689 a_UIcmd_set_msg(bw, "");
690 a_UIcmd_set_pointer_on_link(bw, FALSE);
691 } else {
692 _MSG(" Link ENTER notify...\n");
693 Html_set_link_coordinates(html, link, x, y);
694 a_UIcmd_set_msg(bw, "%s", URL_STR(html->links->get(link)));
695 a_UIcmd_set_pointer_on_link(bw, TRUE);
696 }
697 return true;
698 }
699
700 /*
701 * Handle the "press" signal.
702 */
703 bool DilloHtml::HtmlLinkReceiver::press (Widget *widget, int link, int img,
704 int x, int y, EventButton *event)
705 {
706 BrowserWindow *bw = html->bw;
707 int ret = false;
708 DilloUrl *linkurl = NULL;
709
710 _MSG("pressed button %d\n", event->button);
711 if (event->button == 3) {
712 // popup menus
713 if (img != -1) {
714 // image menu
715 if (link != -1)
716 linkurl = html->links->get(link);
717 const bool_t loaded_img = (html->images->get(img)->image == NULL);
718 a_UIcmd_image_popup(bw, html->images->get(img)->url, loaded_img,
719 html->page_url, linkurl);
720 ret = true;
721 } else {
722 if (link == -1) {
723 a_UIcmd_page_popup(bw, bw->num_page_bugs != 0, html->cssUrls);
724 ret = true;
725 } else {
726 a_UIcmd_link_popup(bw, html->links->get(link));
727 ret = true;
728 }
729 }
730 }
731 return ret;
732 }
733
734 /*
735 * Handle the "click" signal.
736 */
737 bool DilloHtml::HtmlLinkReceiver::click (Widget *widget, int link, int img,
738 int x, int y, EventButton *event)
739 {
740 BrowserWindow *bw = html->bw;
741
742 if ((img != -1) && (html->images->get(img)->image)) {
743 // clicked an image that has not already been loaded
744 if (event->button == 1){
745 // load all instances of this image
746 DilloUrl *pattern = html->images->get(img)->url;
747 html->loadImages(pattern);
748 return true;
749 }
750 }
751
752 if (link != -1) {
753 DilloUrl *url = html->links->get(link);
754 _MSG("clicked on URL %d: %s\n", link, a_Url_str (url));
755
756 Html_set_link_coordinates(html, link, x, y);
757
758 if (event->button == 1) {
759 a_UIcmd_open_url(bw, url);
760 } else if (event->button == 2) {
761 if (prefs.middle_click_opens_new_tab) {
762 int focus = prefs.focus_new_tab ? 1 : 0;
763 if (event->state == SHIFT_MASK) focus = !focus;
764 a_UIcmd_open_url_nt(bw, url, focus);
765 } else
766 a_UIcmd_open_url_nw(bw, url);
767 } else {
768 return false;
769 }
770
771 /* Change the link color to "visited" as visual feedback */
772 for (Widget *w = widget; w; w = w->getParent()) {
773 _MSG(" ->%s\n", w->getClassName());
774 if (w->instanceOf(dw::Textblock::CLASS_ID)) {
775 ((Textblock*)w)->changeLinkColor (link, html->visited_color);
776 break;
777 }
778 }
779 }
780 return true;
781 }
782
783 /*
784 * Initialize the stash buffer
785 */
786 void a_Html_stash_init(DilloHtml *html)
787 {
788 S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_STASH;
789 html->StashSpace = false;
790 dStr_truncate(html->Stash, 0);
791 }
792
793 /* Entities list from the HTML 4.01 DTD */
794 typedef struct {
795 const char *entity;
796 int isocode;
797 } Ent_t;
798
799 #define NumEnt 252
800 static const Ent_t Entities[NumEnt] = {
801 {"AElig",0306}, {"Aacute",0301}, {"Acirc",0302}, {"Agrave",0300},
802 {"Alpha",01621},{"Aring",0305}, {"Atilde",0303}, {"Auml",0304},
803 {"Beta",01622}, {"Ccedil",0307}, {"Chi",01647}, {"Dagger",020041},
804 {"Delta",01624},{"ETH",0320}, {"Eacute",0311}, {"Ecirc",0312},
805 {"Egrave",0310},{"Epsilon",01625},{"Eta",01627}, {"Euml",0313},
806 {"Gamma",01623},{"Iacute",0315}, {"Icirc",0316}, {"Igrave",0314},
807 {"Iota",01631}, {"Iuml",0317}, {"Kappa",01632}, {"Lambda",01633},
808 {"Mu",01634}, {"Ntilde",0321}, {"Nu",01635}, {"OElig",0522},
809 {"Oacute",0323},{"Ocirc",0324}, {"Ograve",0322}, {"Omega",01651},
810 {"Omicron",01637},{"Oslash",0330},{"Otilde",0325},{"Ouml",0326},
811 {"Phi",01646}, {"Pi",01640}, {"Prime",020063},{"Psi",01650},
812 {"Rho",01641}, {"Scaron",0540}, {"Sigma",01643}, {"THORN",0336},
813 {"Tau",01644}, {"Theta",01630}, {"Uacute",0332}, {"Ucirc",0333},
814 {"Ugrave",0331},{"Upsilon",01645},{"Uuml",0334}, {"Xi",01636},
815 {"Yacute",0335},{"Yuml",0570}, {"Zeta",01626}, {"aacute",0341},
816 {"acirc",0342}, {"acute",0264}, {"aelig",0346}, {"agrave",0340},
817 {"alefsym",020465},{"alpha",01661},{"amp",38}, {"and",021047},
818 {"ang",021040}, {"aring",0345}, {"asymp",021110},{"atilde",0343},
819 {"auml",0344}, {"bdquo",020036},{"beta",01662}, {"brvbar",0246},
820 {"bull",020042},{"cap",021051}, {"ccedil",0347}, {"cedil",0270},
821 {"cent",0242}, {"chi",01707}, {"circ",01306}, {"clubs",023143},
822 {"cong",021105},{"copy",0251}, {"crarr",020665},{"cup",021052},
823 {"curren",0244},{"dArr",020723}, {"dagger",020040},{"darr",020623},
824 {"deg",0260}, {"delta",01664}, {"diams",023146},{"divide",0367},
825 {"eacute",0351},{"ecirc",0352}, {"egrave",0350}, {"empty",021005},
826 {"emsp",020003},{"ensp",020002}, {"epsilon",01665},{"equiv",021141},
827 {"eta",01667}, {"eth",0360}, {"euml",0353}, {"euro",020254},
828 {"exist",021003},{"fnof",0622}, {"forall",021000},{"frac12",0275},
829 {"frac14",0274},{"frac34",0276}, {"frasl",020104},{"gamma",01663},
830 {"ge",021145}, {"gt",62}, {"hArr",020724}, {"harr",020624},
831 {"hearts",023145},{"hellip",020046},{"iacute",0355},{"icirc",0356},
832 {"iexcl",0241}, {"igrave",0354}, {"image",020421},{"infin",021036},
833 {"int",021053}, {"iota",01671}, {"iquest",0277}, {"isin",021010},
834 {"iuml",0357}, {"kappa",01672}, {"lArr",020720}, {"lambda",01673},
835 {"lang",021451},{"laquo",0253}, {"larr",020620}, {"lceil",021410},
836 {"ldquo",020034},{"le",021144}, {"lfloor",021412},{"lowast",021027},
837 {"loz",022712}, {"lrm",020016}, {"lsaquo",020071},{"lsquo",020030},
838 {"lt",60}, {"macr",0257}, {"mdash",020024},{"micro",0265},
839 {"middot",0267},{"minus",021022},{"mu",01674}, {"nabla",021007},
840 {"nbsp",0240}, {"ndash",020023},{"ne",021140}, {"ni",021013},
841 {"not",0254}, {"notin",021011},{"nsub",021204}, {"ntilde",0361},
842 {"nu",01675}, {"oacute",0363}, {"ocirc",0364}, {"oelig",0523},
843 {"ograve",0362},{"oline",020076},{"omega",01711}, {"omicron",01677},
844 {"oplus",021225},{"or",021050}, {"ordf",0252}, {"ordm",0272},
845 {"oslash",0370},{"otilde",0365}, {"otimes",021227},{"ouml",0366},
846 {"para",0266}, {"part",021002}, {"permil",020060},{"perp",021245},
847 {"phi",01706}, {"pi",01700}, {"piv",01726}, {"plusmn",0261},
848 {"pound",0243}, {"prime",020062},{"prod",021017}, {"prop",021035},
849 {"psi",01710}, {"quot",34}, {"rArr",020722}, {"radic",021032},
850 {"rang",021452},{"raquo",0273}, {"rarr",020622}, {"rceil",021411},
851 {"rdquo",020035},{"real",020434},{"reg",0256}, {"rfloor",021413},
852 {"rho",01701}, {"rlm",020017}, {"rsaquo",020072},{"rsquo",020031},
853 {"sbquo",020032},{"scaron",0541},{"sdot",021305}, {"sect",0247},
854 {"shy",0255}, {"sigma",01703}, {"sigmaf",01702},{"sim",021074},
855 {"spades",023140},{"sub",021202},{"sube",021206}, {"sum",021021},
856 {"sup",021203}, {"sup1",0271}, {"sup2",0262}, {"sup3",0263},
857 {"supe",021207},{"szlig",0337}, {"tau",01704}, {"there4",021064},
858 {"theta",01670},{"thetasym",01721},{"thinsp",020011},{"thorn",0376},
859 {"tilde",01334},{"times",0327}, {"trade",020442},{"uArr",020721},
860 {"uacute",0372},{"uarr",020621}, {"ucirc",0373}, {"ugrave",0371},
861 {"uml",0250}, {"upsih",01722}, {"upsilon",01705},{"uuml",0374},
862 {"weierp",020430},{"xi",01676}, {"yacute",0375}, {"yen",0245},
863 {"yuml",0377}, {"zeta",01666}, {"zwj",020015}, {"zwnj",020014}
864 };
865
866
867 /*
868 * Comparison function for binary search
869 */
870 static int Html_entity_comp(const void *a, const void *b)
871 {
872 return strcmp(((Ent_t *)a)->entity, ((Ent_t *)b)->entity);
873 }
874
875 /*
876 * Binary search of 'key' in entity list
877 */
878 static int Html_entity_search(char *key)
879 {
880 Ent_t *res, EntKey;
881
882 EntKey.entity = key;
883 res = (Ent_t*) bsearch(&EntKey, Entities, NumEnt,
884 sizeof(Ent_t), Html_entity_comp);
885 if (res)
886 return (res - Entities);
887 return -1;
888 }
889
890 /*
891 * This is M$ non-standard "smart quotes" (w1252). Now even deprecated by them!
892 *
893 * SGML for HTML4.01 defines c >= 128 and c <= 159 as UNUSED.
894 * TODO: Probably I should remove this hack, and add a HTML warning. --Jcid
895 */
896 static int Html_ms_stupid_quotes_2ucs(int isocode)
897 {
898 int ret;
899 switch (isocode) {
900 case 145:
901 case 146: ret = '\''; break;
902 case 147:
903 case 148: ret = '"'; break;
904 case 149: ret = 176; break;
905 case 150:
906 case 151: ret = '-'; break;
907 default: ret = isocode; break;
908 }
909 return ret;
910 }
911
912 /*
913 * Given an entity, return the UCS character code.
914 * Returns a negative value (error code) if not a valid entity.
915 *
916 * The first character *token is assumed to be == '&'
917 *
918 * For valid entities, *entsize is set to the length of the parsed entity.
919 */
920 static int Html_parse_entity(DilloHtml *html, const char *token,
921 int toksize, int *entsize)
922 {
923 int isocode, i;
924 char *tok, *s, c;
925
926 token++;
927 tok = s = toksize ? dStrndup(token, (uint_t)toksize) : dStrdup(token);
928
929 isocode = -1;
930
931 if (*s == '#') {
932 /* numeric character reference */
933 errno = 0;
934 if (*++s == 'x' || *s == 'X') {
935 if (isxdigit(*++s)) {
936 /* strtol with base 16 accepts leading "0x" - we don't */
937 if (*s == '0' && s[1] == 'x') {
938 s++;
939 isocode = 0;
940 } else {
941 isocode = strtol(s, &s, 16);
942 }
943 }
944 } else if (isdigit(*s)) {
945 isocode = strtol(s, &s, 10);
946 }
947
948 if (!isocode || errno || isocode > 0xffff) {
949 /* this catches null bytes, errors and codes >= 0xFFFF */
950 BUG_MSG("numeric character reference out of range\n");
951 isocode = -2;
952 }
953
954 if (isocode != -1) {
955 if (*s == ';')
956 s++;
957 else if (prefs.show_extra_warnings)
958 BUG_MSG("numeric character reference without trailing ';'\n");
959 }
960
961 } else if (isalpha(*s)) {
962 /* character entity reference */
963 while (*++s && (isalnum(*s) || strchr(":_.-", *s))) ;
964 c = *s;
965 *s = 0;
966
967 if ((i = Html_entity_search(tok)) == -1) {
968 if ((html->DocType == DT_HTML && html->DocTypeVersion == 4.01f) ||
969 html->DocType == DT_XHTML)
970 BUG_MSG("undefined character entity '%s'\n", tok);
971 isocode = -3;
972 } else
973 isocode = Entities[i].isocode;
974
975 if (c == ';')
976 s++;
977 else if (prefs.show_extra_warnings)
978 BUG_MSG("character entity reference without trailing ';'\n");
979 }
980
981 *entsize = s-tok+1;
982 dFree(tok);
983
984 if (isocode >= 145 && isocode <= 151) {
985 /* TODO: remove this hack. */
986 isocode = Html_ms_stupid_quotes_2ucs(isocode);
987 } else if (isocode == -1 && prefs.show_extra_warnings)
988 BUG_MSG("literal '&'\n");
989
990 return isocode;
991 }
992
993 /*
994 * Convert all the entities in a token to utf8 encoding. Takes
995 * a token and its length, and returns a newly allocated string.
996 */
997 char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize)
998 {
999 const char *esc_set = "&";
1000 char *new_str, buf[4];
1001 int i, j, k, n, s, isocode, entsize;
1002
1003 new_str = dStrndup(token, toksize);
1004 s = strcspn(new_str, esc_set);
1005 if (new_str[s] == 0)
1006 return new_str;
1007
1008 for (i = j = s; i < toksize; i++) {
1009 if (token[i] == '&' &&
1010 (isocode = Html_parse_entity(html, token+i,
1011 toksize-i, &entsize)) >= 0) {
1012 if (isocode >= 128) {
1013 /* multibyte encoding */
1014 n = a_Utf8_encode(isocode, buf);
1015 for (k = 0; k < n; ++k)
1016 new_str[j++] = buf[k];
1017 } else {
1018 new_str[j++] = (char) isocode;
1019 }
1020 i += entsize-1;
1021 } else {
1022 new_str[j++] = token[i];
1023 }
1024 }
1025 new_str[j] = '\0';
1026 return new_str;
1027 }
1028
1029 /*
1030 * For white-space: pre-line, we must break the line if encountering a newline.
1031 * Otherwise, collapse whitespace as usual.
1032 */
1033 static void Html_process_space_pre_line(DilloHtml *html, const char *space,
1034 int spacesize)
1035 {
1036 int i, breakCnt = 0;
1037
1038 for (i = 0; i < spacesize; i++) {
1039 /* Support for "\r", "\n" and "\r\n" line breaks */
1040 if (space[i] == '\r' || (space[i] == '\n' && !html->PrevWasCR)) {
1041 breakCnt++;
1042 html->PrevWasCR = (space[i] == '\r');
1043
1044 HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
1045 }
1046 }
1047 if (breakCnt == 0) {
1048 HT2TB(html)->addSpace(html->styleEngine->wordStyle ());
1049 }
1050 }
1051
1052 /*
1053 * Parse spaces
1054 */
1055 static void Html_process_space(DilloHtml *html, const char *space,
1056 int spacesize)
1057 {
1058 char *spc;
1059 int i, offset;
1060 DilloHtmlParseMode parse_mode = S_TOP(html)->parse_mode;
1061
1062 if (parse_mode == DILLO_HTML_PARSE_MODE_STASH) {
1063 html->StashSpace = (html->Stash->len > 0);
1064
1065 } else if (parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM) {
1066 dStr_append_l(html->Stash, space, spacesize);
1067
1068 } else if (parse_mode == DILLO_HTML_PARSE_MODE_PRE) {
1069 int spaceCnt = 0;
1070
1071 /* re-scan the string for characters that cause line breaks */
1072 for (i = 0; i < spacesize; i++) {
1073 /* Support for "\r", "\n" and "\r\n" line breaks (skips the first) */
1074 if (!html->PreFirstChar &&
1075 (space[i] == '\r' || (space[i] == '\n' && !html->PrevWasCR))) {
1076
1077 if (spaceCnt) {
1078 spc = dStrnfill(spaceCnt, ' ');
1079 HT2TB(html)->addText (spc, spaceCnt,
1080 html->styleEngine->wordStyle ());
1081 dFree(spc);
1082 spaceCnt = 0;
1083 }
1084 HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
1085 html->pre_column = 0;
1086 }
1087 html->PreFirstChar = false;
1088
1089 /* cr and lf should not be rendered -- they appear as a break */
1090 switch (space[i]) {
1091 case '\r':
1092 case '\n':
1093 break;
1094 case '\t':
1095 if (prefs.show_extra_warnings)
1096 BUG_MSG("TAB character inside <PRE>\n");
1097 offset = TAB_SIZE - html->pre_column % TAB_SIZE;
1098 spaceCnt += offset;
1099 html->pre_column += offset;
1100 break;
1101 default:
1102 spaceCnt++;
1103 html->pre_column++;
1104 break;
1105 }
1106
1107 html->PrevWasCR = (space[i] == '\r');
1108 }
1109
1110 if (spaceCnt) {
1111 spc = dStrnfill(spaceCnt, ' ');
1112 HT2TB(html)->addText (spc, spaceCnt, html->styleEngine->wordStyle ());
1113 dFree(spc);
1114 }
1115
1116 } else {
1117 if (SGML_SPCDEL) {
1118 /* SGML_SPCDEL ignores white space immediately after an open tag */
1119 } else if (html->styleEngine->wordStyle ()->whiteSpace ==
1120 WHITE_SPACE_PRE_LINE) {
1121 Html_process_space_pre_line(html, space, spacesize);
1122 } else {
1123 HT2TB(html)->addSpace(html->styleEngine->wordStyle ());
1124 }
1125
1126 if (parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY)
1127 html->StashSpace = (html->Stash->len > 0);
1128 }
1129 }
1130
1131 /*
1132 * Handles putting the word into its proper place
1133 * > STASH and VERBATIM --> html->Stash
1134 * > otherwise it goes through addText()
1135 *
1136 * Entities are parsed (or not) according to parse_mode.
1137 * 'word' is a '\0'-terminated string.
1138 */
1139 static void Html_process_word(DilloHtml *html, const char *word, int size)
1140 {
1141 int i, j, start;
1142 char *Pword;
1143 DilloHtmlParseMode parse_mode = S_TOP(html)->parse_mode;
1144
1145 if (parse_mode == DILLO_HTML_PARSE_MODE_STASH ||
1146 parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY) {
1147 if (html->StashSpace) {
1148 dStr_append_c(html->Stash, ' ');
1149 html->StashSpace = false;
1150 }
1151 Pword = a_Html_parse_entities(html, word, size);
1152 dStr_append(html->Stash, Pword);
1153 dFree(Pword);
1154
1155 } else if (parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM) {
1156 /* word goes in untouched, it is not processed here. */
1157 dStr_append_l(html->Stash, word, size);
1158 }
1159
1160 if (parse_mode == DILLO_HTML_PARSE_MODE_STASH ||
1161 parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM) {
1162 /* skip until the closing instructions */
1163
1164 } else if (parse_mode == DILLO_HTML_PARSE_MODE_PRE) {
1165 /* all this overhead is to catch white-space entities */
1166 Pword = a_Html_parse_entities(html, word, size);
1167 for (start = i = 0; Pword[i]; start = i)
1168 if (isspace(Pword[i])) {
1169 while (Pword[++i] && isspace(Pword[i])) ;
1170 Html_process_space(html, Pword + start, i - start);
1171 } else {
1172 while (Pword[++i] && !isspace(Pword[i])) ;
1173 HT2TB(html)->addText(Pword + start, i - start,
1174 html->styleEngine->wordStyle ());
1175 html->pre_column += i - start;
1176 html->PreFirstChar = false;
1177 }
1178 dFree(Pword);
1179
1180 } else {
1181 const char *word2, *beyond_word2;
1182
1183 Pword = NULL;
1184 if (!memchr(word,'&', size)) {
1185 /* No entities */
1186 word2 = word;
1187 beyond_word2 = word + size;
1188 } else {
1189 /* Collapse white-space entities inside the word (except &nbsp;) */
1190 Pword = a_Html_parse_entities(html, word, size);
1191 /* Collapse adjacent " \t\f\n\r" characters into a single space */
1192 for (i = j = 0; (Pword[i] = Pword[j]); ++i, ++j) {
1193 if (strchr(" \t\f\n\r", Pword[i])) {
1194 if (i == 0 || (i > 0 && Pword[i-1] != ' '))
1195 Pword[i] = ' ';
1196 else
1197 for (--i; Pword[j+1] && strchr(" \t\f\n\r", Pword[j+1]); ++j)
1198 ;
1199 }
1200 }
1201 word2 = Pword;
1202 beyond_word2 = word2 + strlen(word2);
1203 }
1204 for (start = i = 0; word2[i]; start = i) {
1205 int len;
1206
1207 if (isspace(word2[i])) {
1208 while (word2[++i] && isspace(word2[i])) ;
1209 Html_process_space(html, word2 + start, i - start);
1210 } else if (!strncmp(word2+i, utf8_zero_width_space, 3)) {
1211 i += 3;
1212 } else if (a_Utf8_ideographic(word2+i, beyond_word2, &len)) {
1213 i += len;
1214 HT2TB(html)->addText(word2 + start, i - start,
1215 html->styleEngine->wordStyle ());
1216 } else {
1217 do {
1218 i += len;
1219 } while (word2[i] && !isspace(word2[i]) &&
1220 strncmp(word2+i, utf8_zero_width_space, 3) &&
1221 (!a_Utf8_ideographic(word2+i, beyond_word2, &len)));
1222 HT2TB(html)->addText(word2 + start, i - start,
1223 html->styleEngine->wordStyle ());
1224 }
1225 }
1226 if (Pword == word2)
1227 dFree(Pword);
1228 }
1229 }
1230
1231 /*
1232 * Does the tag in tagstr (e.g. "p") match the tag in the tag, tagsize
1233 * structure, with the initial < skipped over (e.g. "P align=center>")?
1234 */
1235 static bool Html_match_tag(const char *tagstr, char *tag, int tagsize)
1236 {
1237 int i;
1238
1239 for (i = 0; i < tagsize && tagstr[i] != '\0'; i++) {
1240 if (tolower(tagstr[i]) != tolower(tag[i]))
1241 return false;
1242 }
1243 /* The test for '/' is for xml compatibility: "empty/>" will be matched. */
1244 if (i < tagsize && (isspace(tag[i]) || tag[i] == '>' || tag[i] == '/'))
1245 return true;
1246 return false;
1247 }
1248
1249 /*
1250 * This function is called after popping the stack, to
1251 * handle nested Textblock widgets.
1252 */
1253 static void Html_eventually_pop_dw(DilloHtml *html, bool hand_over_break)
1254 {
1255 if (html->dw != S_TOP(html)->textblock) {
1256 if (hand_over_break)
1257 HT2TB(html)->handOverBreak (html->styleEngine->style ());
1258 HT2TB(html)->flush ();
1259 html->dw = S_TOP(html)->textblock;
1260 }
1261 }
1262
1263 /*
1264 * Push the tag (copying attributes from the top of the stack)
1265 */
1266 static void Html_push_tag(DilloHtml *html, int tag_idx)
1267 {
1268 int n_items;
1269
1270 n_items = html->stack->size ();
1271 html->stack->increase ();
1272 /* We'll copy the former stack item and just change the tag and its index
1273 * instead of copying all fields except for tag. --Jcid */
1274 *html->stack->getRef(n_items) = *html->stack->getRef(n_items - 1);
1275 html->stack->getRef(n_items)->tag_idx = tag_idx;
1276 html->dw = S_TOP(html)->textblock;
1277 }
1278
1279 /*
1280 * Push the tag (used to force en element with optional open into the stack)
1281 * Note: now it's the same as Html_push_tag(), but things may change...
1282 */
1283 static void Html_force_push_tag(DilloHtml *html, int tag_idx)
1284 {
1285 html->styleEngine->startElement (tag_idx);
1286 Html_push_tag(html, tag_idx);
1287 }
1288
1289 /*
1290 * Pop the top tag in the stack
1291 */
1292 static void Html_real_pop_tag(DilloHtml *html)
1293 {
1294 bool hand_over_break;
1295
1296 html->styleEngine->endElement (S_TOP(html)->tag_idx);
1297 hand_over_break = S_TOP(html)->hand_over_break;
1298 html->stack->setSize (html->stack->size() - 1);
1299 Html_eventually_pop_dw(html, hand_over_break);
1300 }
1301
1302 /*
1303 * Cleanup the stack to a given index.
1304 */
1305 static void Html_tag_cleanup_to_idx(DilloHtml *html, int idx)
1306 {
1307 int s_sz;
1308 while ((s_sz = html->stack->size()) > idx) {
1309 int toptag_idx = S_TOP(html)->tag_idx;
1310 TagInfo toptag = Tags[toptag_idx];
1311 if (s_sz > idx + 1 && toptag.EndTag != 'O')
1312 BUG_MSG(" - forcing close of open tag: <%s>\n", toptag.name);
1313 _MSG("Close: %*s%s\n", size," ", toptag.name);
1314 toptag.close(html, toptag_idx);
1315 Html_real_pop_tag(html);
1316 }
1317 }
1318
1319 /*
1320 * Default close function for tags.
1321 * (conditional cleanup of the stack)
1322 * There are several ways of doing it. Considering the HTML 4.01 spec
1323 * which defines optional close tags, and the will to deliver useful diagnose
1324 * messages for bad-formed HTML, it'll go as follows:
1325 * 1.- Search the stack for the first tag that requires a close tag.
1326 * 2.- If it matches, clean all the optional-close tags in between.
1327 * 3.- Cleanup the matching tag. (on error, give a warning message)
1328 *
1329 * If 'w3c_mode' is NOT enabled:
1330 * 1.- Search the stack for a matching tag based on tag level.
1331 * 2.- If it exists, clean all the tags in between.
1332 * 3.- Cleanup the matching tag. (on error, give a warning message)
1333 */
1334 static void Html_tag_cleanup_at_close(DilloHtml *html, int new_idx)
1335 {
1336 int w3c_mode = !prefs.w3c_plus_heuristics;
1337 int stack_idx, tag_idx, matched = 0, expected = 0;
1338 TagInfo new_tag = Tags[new_idx];
1339
1340 /* Look for the candidate tag to close */
1341 stack_idx = html->stack->size();
1342 while (--stack_idx) {
1343 tag_idx = html->stack->getRef(stack_idx)->tag_idx;
1344 if (tag_idx == new_idx) {
1345 /* matching tag found */
1346 matched = 1;
1347 break;
1348 } else if (Tags[tag_idx].EndTag == 'O') {
1349 /* skip an optional tag */
1350 continue;
1351 } else if (w3c_mode || Tags[tag_idx].TagLevel >= new_tag.TagLevel) {
1352 /* this is the tag that should have been closed */
1353 expected = 1;
1354 break;
1355 }
1356 }
1357
1358 if (matched) {
1359 Html_tag_cleanup_to_idx(html, stack_idx);
1360 } else if (expected) {
1361 BUG_MSG("unexpected closing tag: </%s> -- expected </%s>.\n",
1362 new_tag.name, Tags[tag_idx].name);
1363 } else {
1364 BUG_MSG("unexpected closing tag: </%s>.\n", new_tag.name);
1365 }
1366 }
1367
1368 /*
1369 * Some parsing routines.
1370 */
1371
1372 /*
1373 * Used by a_Html_parse_length
1374 */
1375 static CssLength Html_parse_length_or_multi_length (const char *attr,
1376 char **endptr)
1377 {
1378 CssLength l;
1379 double v;
1380 char *end;
1381
1382 v = strtod (attr, &end);
1383 switch (*end) {
1384 case '%':
1385 end++;
1386 l = CSS_CREATE_LENGTH (v / 100, CSS_LENGTH_TYPE_PERCENTAGE);
1387 break;
1388
1389 case '*':
1390 end++;
1391 l = CSS_CREATE_LENGTH (v, CSS_LENGTH_TYPE_RELATIVE);
1392 break;
1393 /*
1394 The "px" suffix seems not allowed by HTML4.01 SPEC.
1395 case 'p':
1396 if (end[1] == 'x')
1397 end += 2;
1398 */
1399 default:
1400 l = CSS_CREATE_LENGTH (v, CSS_LENGTH_TYPE_PX);
1401 break;
1402 }
1403
1404 if (endptr)
1405 *endptr = end;
1406 return l;
1407 }
1408
1409
1410 /*
1411 * Returns a length or a percentage, or UNDEF_LENGTH in case
1412 * of an error, or if attr is NULL.
1413 */
1414 CssLength a_Html_parse_length (DilloHtml *html, const char *attr)
1415 {
1416 CssLength l;
1417 char *end;
1418
1419 l = Html_parse_length_or_multi_length (attr, &end);
1420 if (CSS_LENGTH_TYPE (l) == CSS_LENGTH_TYPE_RELATIVE)
1421 /* not allowed as &Length; */
1422 l = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
1423 else {
1424 /* allow only whitespaces */
1425 if (*end && !isspace (*end)) {
1426 BUG_MSG("Garbage after length: %s\n", attr);
1427 l = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
1428 }
1429 }
1430
1431 _MSG("a_Html_parse_length: \"%s\" %d\n", attr, CSS_LENGTH_VALUE(l));
1432 return l;
1433 }
1434
1435 /*
1436 * Parse a color attribute.
1437 * Return value: parsed color, or default_color (+ error msg) on error.
1438 */
1439 int32_t a_Html_color_parse(DilloHtml *html,
1440 const char *subtag, int32_t default_color)
1441 {
1442 int err = 1;
1443 int32_t color = a_Color_parse(subtag, default_color, &err);
1444
1445 if (err) {
1446 BUG_MSG("color is not in \"#RRGGBB\" format\n");
1447 }
1448 return color;
1449 }
1450
1451 /*
1452 * Check that 'val' is composed of characters inside [A-Za-z0-9:_.-]
1453 * Note: ID can't have entities, but this check is enough (no '&').
1454 * Return value: 1 if OK, 0 otherwise.
1455 */
1456 static int
1457 Html_check_name_val(DilloHtml *html, const char *val, const char *attrname)
1458 {
1459 int i;
1460
1461 for (i = 0; val[i]; ++i)
1462 if (!(isalnum(val[i]) || strchr(":_.-", val[i])))
1463 break;
1464
1465 if (val[i] || !isalpha(val[0]))
1466 BUG_MSG("'%s' value is not of the form "
1467 "[A-Za-z][A-Za-z0-9:_.-]*\n", attrname);
1468
1469 return !(val[i]);
1470 }
1471
1472 /*
1473 * Handle DOCTYPE declaration
1474 *
1475 * Follows the convention that HTML 4.01
1476 * doctypes which include a full w3c DTD url are treated as
1477 * standards-compliant, but 4.01 without the url and HTML 4.0 and
1478 * earlier are not. XHTML doctypes are always standards-compliant
1479 * whether or not an url is present.
1480 *
1481 * Note: I'm not sure about this convention. The W3C validator
1482 * recognizes the "HTML Level" with or without the URL. The convention
1483 * comes from mozilla (see URLs below), but Dillo doesn't have the same
1484 * rendering modes, so it may be better to chose another behaviour. --Jcid
1485 *
1486 * http://www.mozilla.org/docs/web-developer/quirks/doctypes.html
1487 * http://lists.auriga.wearlab.de/pipermail/dillo-dev/2004-October/002300.html
1488 *
1489 * This is not a full DOCTYPE parser, just enough for what Dillo uses.
1490 */
1491 static void Html_parse_doctype(DilloHtml *html, const char *tag, int tagsize)
1492 {
1493 static const char HTML_SGML_sig [] = "<!DOCTYPE HTML PUBLIC ";
1494 static const char HTML5_sig [] = "<!DOCTYPE html>";
1495 static const char HTML20 [] = "-//IETF//DTD HTML 2.0";
1496 static const char HTML32 [] = "-//W3C//DTD HTML 3.2";
1497 static const char HTML40 [] = "-//W3C//DTD HTML 4.0";
1498 static const char HTML401 [] = "-//W3C//DTD HTML 4.01";
1499 static const char HTML401_url[] = "http://www.w3.org/TR/html4/";
1500 static const char XHTML1 [] = "-//W3C//DTD XHTML 1.0";
1501 static const char XHTML1_url [] = "http://www.w3.org/TR/xhtml1/DTD/";
1502 static const char XHTML11 [] = "-//W3C//DTD XHTML 1.1";
1503 static const char XHTML11_url[] = "http://www.w3.org/TR/xhtml11/DTD/";
1504
1505 int i, quote;
1506 char *p, *ntag = dStrndup(tag, tagsize);
1507
1508 /* Tag sanitization: Collapse whitespace between tokens
1509 * and replace '\n' and '\r' with ' ' inside quoted strings. */
1510 for (i = 0, p = ntag; *p; ++p) {
1511 if (isspace(*p)) {
1512 for (ntag[i++] = ' '; isspace(p[1]); ++p) ;
1513 } else if ((quote = *p) == '"' || *p == '\'') {
1514 for (ntag[i++] = *p++; (ntag[i] = *p) && ntag[i++] != quote; ++p) {
1515 if (*p == '\n' || *p == '\r')
1516 ntag[i - 1] = ' ';
1517 p += (p[0] == '\r' && p[1] == '\n') ? 1 : 0;
1518 }
1519 } else {
1520 ntag[i++] = *p;
1521 }
1522 if (!*p)
1523 break;
1524 }
1525 ntag[i] = 0;
1526
1527 _MSG("New: {%s}\n", ntag);
1528
1529 /* The default DT_NONE type is TagSoup */
1530 if (!dStrncasecmp(ntag, HTML_SGML_sig, strlen(HTML_SGML_sig))) {
1531 p = ntag + strlen(HTML_SGML_sig) + 1;
1532 if (!strncmp(p, HTML401, strlen(HTML401)) &&
1533 dStristr(p + strlen(HTML401), HTML401_url)) {
1534 html->DocType = DT_HTML;
1535 html->DocTypeVersion = 4.01f;
1536 } else if (!strncmp(p, XHTML1, strlen(XHTML1)) &&
1537 dStristr(p + strlen(XHTML1), XHTML1_url)) {
1538 html->DocType = DT_XHTML;
1539 html->DocTypeVersion = 1.0f;
1540 } else if (!strncmp(p, XHTML11, strlen(XHTML11)) &&
1541 dStristr(p + strlen(XHTML11), XHTML11_url)) {
1542 html->DocType = DT_XHTML;
1543 html->DocTypeVersion = 1.1f;
1544 } else if (!strncmp(p, HTML40, strlen(HTML40))) {
1545 html->DocType = DT_HTML;
1546 html->DocTypeVersion = 4.0f;
1547 } else if (!strncmp(p, HTML32, strlen(HTML32))) {
1548 html->DocType = DT_HTML;
1549 html->DocTypeVersion = 3.2f;
1550 } else if (!strncmp(p, HTML20, strlen(HTML20))) {
1551 html->DocType = DT_HTML;
1552 html->DocTypeVersion = 2.0f;
1553 }
1554 } else if (!dStrcasecmp(ntag, HTML5_sig)) {
1555 BUG_MSG("Document follows HTML5 working draft; treating as HTML4.\n");
1556 html->DocType = DT_HTML;
1557 html->DocTypeVersion = 5.0f;
1558 }
1559
1560 dFree(ntag);
1561 }
1562
1563 /*
1564 * Handle open HTML element
1565 */
1566 static void Html_tag_open_html(DilloHtml *html, const char *tag, int tagsize)
1567 {
1568 if (!(html->InFlags & IN_HTML))
1569 html->InFlags |= IN_HTML;
1570 ++html->Num_HTML;
1571
1572 if (html->Num_HTML > 1) {
1573 BUG_MSG("HTML element was already open\n");
1574 }
1575 }
1576
1577 /*
1578 * Handle close HTML element
1579 */
1580 static void Html_tag_close_html(DilloHtml *html, int TagIdx)
1581 {
1582 /* TODO: may add some checks here */
1583 if (html->Num_HTML == 1) {
1584 /* beware of pages with multiple HTML close tags... :-P */
1585 html->InFlags &= ~IN_HTML;
1586 }
1587 }
1588
1589 /*
1590 * Handle open HEAD element
1591 */
1592 static void Html_tag_open_head(DilloHtml *html, const char *tag, int tagsize)
1593 {
1594 if (html->InFlags & IN_BODY || html->Num_BODY > 0) {
1595 BUG_MSG("HEAD element must go before the BODY section\n");
1596 html->ReqTagClose = true;
1597 return;
1598 }
1599
1600 if (!(html->InFlags & IN_HEAD))
1601 html->InFlags |= IN_HEAD;
1602 ++html->Num_HEAD;
1603
1604 if (html->Num_HEAD > 1) {
1605 BUG_MSG("HEAD element was already open\n");
1606 }
1607 }
1608
1609 /*
1610 * Handle close HEAD element
1611 * Note: as a side effect of Html_test_section() this function is called
1612 * twice when the head element is closed implicitly.
1613 * Note2: HEAD is parsed once completely got.
1614 */
1615 static void Html_tag_close_head(DilloHtml *html, int TagIdx)
1616 {
1617 if (html->InFlags & IN_HEAD) {
1618 _MSG("Closing HEAD section\n");
1619 if (html->Num_TITLE == 0)
1620 BUG_MSG("HEAD section lacks the TITLE element\n");
1621
1622 html->InFlags &= ~IN_HEAD;
1623
1624 /* charset is already set, load remote stylesheets now */
1625 for (int i = 0; i < html->cssUrls->size(); i++) {
1626 a_Html_load_stylesheet(html, html->cssUrls->get(i));
1627 }
1628 }
1629 }
1630
1631 /*
1632 * Handle open TITLE
1633 * calls stash init, where the title string will be stored
1634 */
1635 static void Html_tag_open_title(DilloHtml *html, const char *tag, int tagsize)
1636 {
1637 ++html->Num_TITLE;
1638 a_Html_stash_init(html);
1639 }
1640
1641 /*
1642 * Handle close TITLE
1643 * set page-title in the browser window and in the history.
1644 */
1645 static void Html_tag_close_title(DilloHtml *html, int TagIdx)
1646 {
1647 if (html->InFlags & IN_HEAD) {
1648 /* title is only valid inside HEAD */
1649 a_UIcmd_set_page_title(html->bw, html->Stash->str);
1650 a_History_set_title_by_url(html->page_url, html->Stash->str);
1651 } else {
1652 BUG_MSG("the TITLE element must be inside the HEAD section\n");
1653 }
1654 }
1655
1656 /*
1657 * Handle open SCRIPT
1658 * initializes stash, where the embedded code will be stored.
1659 * MODE_VERBATIM is used because MODE_STASH catches entities.
1660 */
1661 static void Html_tag_open_script(DilloHtml *html, const char *tag, int tagsize)
1662 {
1663 a_Html_stash_init(html);
1664 S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
1665 }
1666
1667 /*
1668 * Handle close SCRIPT
1669 */
1670 static void Html_tag_close_script(DilloHtml *html, int TagIdx)
1671 {
1672 /* eventually the stash will be sent to an interpreter for parsing */
1673 }
1674
1675 /*
1676 * Handle open STYLE
1677 * Store contents in the stash where the style sheet interpreter can get it.
1678 */
1679 static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize)
1680 {
1681 const char *attrbuf;
1682
1683 html->loadCssFromStash = true;
1684
1685 if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) {
1686 BUG_MSG("type attribute is required for <style>\n");
1687 } else if (dStrcasecmp(attrbuf, "text/css")) {
1688 html->loadCssFromStash = false;
1689 }
1690 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) &&
1691 dStrcasecmp(attrbuf, "all") && !dStristr(attrbuf, "screen")) {
1692 /* HTML 4.01 sec. 6.13 says that media descriptors are case-sensitive,
1693 * but sec. 14.2.3 says that the attribute is case-insensitive.
1694 * TODO can be a comma-separated list.
1695 * TODO handheld.
1696 */
1697 html->loadCssFromStash = false;
1698 }
1699
1700 a_Html_stash_init(html);
1701 S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
1702 }
1703
1704 /*
1705 * Handle close STYLE
1706 */
1707 static void Html_tag_close_style(DilloHtml *html, int TagIdx)
1708 {
1709 if (prefs.parse_embedded_css && html->loadCssFromStash)
1710 html->styleEngine->parse(html, NULL, html->Stash->str, html->Stash->len,
1711 CSS_ORIGIN_AUTHOR);
1712 }
1713
1714 /*
1715 * <BODY>
1716 */
1717 static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
1718 {
1719 const char *attrbuf;
1720 int32_t color;
1721 int tag_index_a = a_Html_tag_index ("a");
1722 style::Color *bgColor;
1723
1724 if (!(html->InFlags & IN_BODY))
1725 html->InFlags |= IN_BODY;
1726 ++html->Num_BODY;
1727
1728 if (html->Num_BODY > 1) {
1729 BUG_MSG("BODY element was already open\n");
1730 return;
1731 }
1732 if (html->InFlags & IN_HEAD) {
1733 /* if we're here, it's bad XHTML, no need to recover */
1734 BUG_MSG("unclosed HEAD element\n");
1735 }
1736
1737 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
1738 color = a_Html_color_parse(html, attrbuf, -1);
1739 if (color != -1)
1740 html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
1741 CSS_TYPE_COLOR, color);
1742 }
1743
1744 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "text"))) {
1745 color = a_Html_color_parse(html, attrbuf, -1);
1746 if (color != -1)
1747 html->styleEngine->setNonCssHint (CSS_PROPERTY_COLOR,
1748 CSS_TYPE_COLOR, color);
1749 }
1750
1751 html->styleEngine->restyle ();
1752
1753 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "link")))
1754 html->non_css_link_color = a_Html_color_parse(html, attrbuf, -1);
1755
1756 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vlink")))
1757 html->non_css_visited_color = a_Html_color_parse(html, attrbuf, -1);
1758
1759 html->dw->setStyle (html->styleEngine->style ());
1760
1761 bgColor = html->styleEngine->backgroundColor ();
1762
1763 if (bgColor)
1764 HT2LT(html)->setBgColor(bgColor);
1765
1766 /* Determine a color for visited links.
1767 * This color is computed once per page and used for immediate feedback
1768 * when clicking a link.
1769 * On reload style including color for visited links is computed properly
1770 * according to CSS.
1771 */
1772 html->styleEngine->startElement (tag_index_a);
1773 html->styleEngine->setPseudoVisited ();
1774 if (html->non_css_visited_color != -1) {
1775 html->styleEngine->setNonCssHint (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR,
1776 html->non_css_visited_color);
1777 }
1778 html->visited_color = html->styleEngine->style ()->color->getColor ();
1779 html->styleEngine->endElement (tag_index_a);
1780
1781 if (prefs.contrast_visited_color) {
1782 /* get a color that has a "safe distance" from text, link and bg */
1783 html->visited_color =
1784 a_Color_vc(html->visited_color,
1785 html->styleEngine->style ()->color->getColor(),
1786 html->non_css_link_color,
1787 html->styleEngine->backgroundStyle()->backgroundColor->getColor());
1788 }
1789
1790
1791 S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_BODY;
1792 }
1793
1794 /*
1795 * BODY
1796 */
1797 static void Html_tag_close_body(DilloHtml *html, int TagIdx)
1798 {
1799 if (html->Num_BODY == 1) {
1800 /* some tag soup pages use multiple BODY tags... */
1801 html->InFlags &= ~IN_BODY;
1802 }
1803 }
1804
1805 /*
1806 * <P>
1807 * TODO: what's the point between adding the parbreak before and
1808 * after the push?
1809 */
1810 static void Html_tag_open_p(DilloHtml *html, const char *tag, int tagsize)
1811 {
1812 CssPropertyList props;
1813
1814 a_Html_tag_set_align_attr (html, tag, tagsize);
1815 html->styleEngine->inheritBackgroundColor ();
1816 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
1817 }
1818
1819 /*
1820 * <FRAME>, <IFRAME>
1821 * TODO: This is just a temporary fix while real frame support
1822 * isn't finished. Imitates lynx/w3m's frames.
1823 */
1824 static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize)
1825 {
1826 const char *attrbuf;
1827 char *src;
1828 DilloUrl *url;
1829 Textblock *textblock;
1830 Widget *bullet;
1831 CssPropertyList props;
1832
1833 textblock = HT2TB(html);
1834
1835 if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "src")))
1836 return;
1837
1838 if (!(url = a_Html_url_new(html, attrbuf, NULL, 0)))
1839 return;
1840
1841 src = dStrdup(attrbuf);
1842
1843 if (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached) {
1844 /* visited frame */
1845 html->styleEngine->setPseudoVisited ();
1846 } else {
1847 /* unvisited frame */
1848 html->styleEngine->setPseudoLink ();
1849 }
1850
1851 html->styleEngine->setNonCssHint (PROPERTY_X_LINK, CSS_TYPE_INTEGER,
1852 Html_set_new_link(html,&url));
1853
1854 textblock->addParbreak (5, html->styleEngine->wordStyle ());
1855
1856 bullet = new Bullet();
1857 textblock->addWidget(bullet, html->styleEngine->wordStyle ());
1858 textblock->addSpace(html->styleEngine->wordStyle ());
1859
1860 if (tolower(tag[1]) == 'i') {
1861 /* IFRAME usually comes with very long advertising/spying URLS,
1862 * to not break rendering we will force name="IFRAME" */
1863 textblock->addText ("IFRAME", html->styleEngine->wordStyle ());
1864
1865 } else {
1866 /* FRAME:
1867 * If 'name' tag is present use it, if not use 'src' value */
1868 if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
1869 textblock->addText (src, html->styleEngine->wordStyle ());
1870 } else {
1871 textblock->addText (attrbuf, html->styleEngine->wordStyle ());
1872 }
1873 }
1874
1875 textblock->addParbreak (5, html->styleEngine->wordStyle ());
1876
1877 dFree(src);
1878 }
1879
1880 /*
1881 * <FRAMESET>
1882 * TODO: This is just a temporary fix while real frame support
1883 * isn't finished. Imitates lynx/w3m's frames.
1884 */
1885 static void Html_tag_open_frameset (DilloHtml *html,
1886 const char *tag, int tagsize)
1887 {
1888 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
1889 HT2TB(html)->addText("--FRAME--", html->styleEngine->wordStyle ());
1890 Html_add_textblock(html, 5);
1891 }
1892
1893 /*
1894 * <H1> | <H2> | <H3> | <H4> | <H5> | <H6>
1895 */
1896 static void Html_tag_open_h(DilloHtml *html, const char *tag, int tagsize)
1897 {
1898 html->styleEngine->inheritBackgroundColor ();
1899 a_Html_tag_set_align_attr (html, tag, tagsize);
1900
1901 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
1902
1903 a_Html_stash_init(html);
1904 S_TOP(html)->parse_mode =
1905 DILLO_HTML_PARSE_MODE_STASH_AND_BODY;
1906 }
1907
1908 /*
1909 * <BR>
1910 */
1911 static void Html_tag_open_br(DilloHtml *html, const char *tag, int tagsize)
1912 {
1913 HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
1914 }
1915
1916 /*
1917 * <FONT>
1918 */
1919 static void Html_tag_open_font(DilloHtml *html, const char *tag, int tagsize)
1920 {
1921 const char *attrbuf;
1922 char *fontFamily = NULL;
1923 int32_t color;
1924
1925 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "color"))) {
1926 if (prefs.contrast_visited_color && html->InVisitedLink) {
1927 color = html->visited_color;
1928 } else {
1929 /* use the tag-specified color */
1930 color = a_Html_color_parse(html, attrbuf, -1);
1931 }
1932 if (color != -1)
1933 html->styleEngine->setNonCssHint (CSS_PROPERTY_COLOR,
1934 CSS_TYPE_COLOR, color);
1935 }
1936
1937 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "face"))) {
1938 fontFamily = dStrdup(attrbuf);
1939 html->styleEngine->setNonCssHint (CSS_PROPERTY_FONT_FAMILY,
1940 CSS_TYPE_SYMBOL, fontFamily);
1941 }
1942
1943 dFree(fontFamily);
1944 }
1945
1946 /*
1947 * <ABBR>
1948 */
1949 static void Html_tag_open_abbr(DilloHtml *html, const char *tag, int tagsize)
1950 {
1951 const char *attrbuf;
1952
1953 html->styleEngine->inheritBackgroundColor ();
1954
1955 if (prefs.show_tooltip &&
1956 (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
1957
1958 html->styleEngine->setNonCssHint (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING,
1959 attrbuf);
1960 }
1961 }
1962
1963 /*
1964 * <CENTER>
1965 */
1966 static void Html_tag_open_center(DilloHtml *html, const char *tag, int tagsize)
1967 {
1968 html->styleEngine->inheritBackgroundColor ();
1969 HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
1970 }
1971
1972 /*
1973 * </CENTER>, also used for </TABLE>
1974 */
1975 static void Html_tag_close_center(DilloHtml *html, int TagIdx)
1976 {
1977 HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
1978 }
1979
1980 /*
1981 * <ADDRESS>
1982 */
1983 static void Html_tag_open_address(DilloHtml *html,
1984 const char *tag, int tagsize)
1985 {
1986 html->styleEngine->inheritBackgroundColor ();
1987 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
1988 }
1989
1990 /*
1991 * Read image-associated tag attributes and create new image.
1992 */
1993 DilloImage *a_Html_image_new(DilloHtml *html, const char *tag,
1994 int tagsize, DilloUrl *url)
1995 {
1996 DilloImage *Image;
1997 char *width_ptr, *height_ptr, *alt_ptr;
1998 const char *attrbuf;
1999 CssLength l_w = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
2000 CssLength l_h = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
2001 int space, border, w = 0, h = 0;
2002 bool load_now;
2003
2004 if (prefs.show_tooltip &&
2005 (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
2006 html->styleEngine->setNonCssHint(PROPERTY_X_TOOLTIP, CSS_TYPE_STRING,
2007 attrbuf);
2008 }
2009 alt_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "alt", NULL);
2010 if ((!alt_ptr || !*alt_ptr) && !prefs.load_images) {
2011 dFree(alt_ptr);
2012 alt_ptr = dStrdup("[IMG]"); // Place holder for img_off mode
2013 }
2014 width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", NULL);
2015 height_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "height", NULL);
2016 // Check for malicious values
2017 // TODO: the same for percentage and relative lengths.
2018 if (width_ptr) {
2019 l_w = a_Html_parse_length (html, width_ptr);
2020 w = (int) (CSS_LENGTH_TYPE(l_w) == CSS_LENGTH_TYPE_PX ?
2021 CSS_LENGTH_VALUE(l_w) : 0);
2022 }
2023 if (height_ptr) {
2024 l_h = a_Html_parse_length (html, height_ptr);
2025 h = (int) (CSS_LENGTH_TYPE(l_h) == CSS_LENGTH_TYPE_PX ?
2026 CSS_LENGTH_VALUE(l_h) : 0);
2027 }
2028 /* Check for suspicious image size request that would cause
2029 * an excessive amount of memory to be allocated for the
2030 * image buffer.
2031 * Be careful to avoid integer overflows during the checks.
2032 * There is an additional check in dw/image.cc to catch cases
2033 * where only one dimension is given and the image is scaled
2034 * preserving its original aspect ratio.
2035 * Size requests passed via CSS are also checked there.
2036 */
2037 if (w < 0 || h < 0 ||
2038 w > IMAGE_MAX_AREA || h > IMAGE_MAX_AREA ||
2039 (h > 0 && w > IMAGE_MAX_AREA / h)) {
2040 dFree(width_ptr);
2041 dFree(height_ptr);
2042 width_ptr = height_ptr = NULL;
2043 MSG("a_Html_image_new: suspicious image size request %d x %d\n", w, h);
2044 } else {
2045 if (CSS_LENGTH_TYPE(l_w) != CSS_LENGTH_TYPE_AUTO)
2046 html->styleEngine->setNonCssHint (CSS_PROPERTY_WIDTH,
2047 CSS_TYPE_LENGTH_PERCENTAGE, l_w);
2048 if (CSS_LENGTH_TYPE(l_h) != CSS_LENGTH_TYPE_AUTO)
2049 html->styleEngine->setNonCssHint (CSS_PROPERTY_HEIGHT,
2050 CSS_TYPE_LENGTH_PERCENTAGE, l_h);
2051 }
2052
2053 /* TODO: we should scale the image respecting its ratio.
2054 * As the image size is not known at this time, maybe a flag
2055 * can be set to scale it later.
2056 if ((width_ptr && !height_ptr) || (height_ptr && !width_ptr))
2057 [...]
2058 */
2059
2060 /* Spacing to the left and right */
2061 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "hspace"))) {
2062 space = strtol(attrbuf, NULL, 10);
2063 if (space > 0) {
2064 space = CSS_CREATE_LENGTH(space, CSS_LENGTH_TYPE_PX);
2065 html->styleEngine->setNonCssHint (CSS_PROPERTY_MARGIN_LEFT,
2066 CSS_TYPE_LENGTH_PERCENTAGE, space);
2067 html->styleEngine->setNonCssHint (CSS_PROPERTY_MARGIN_RIGHT,
2068 CSS_TYPE_LENGTH_PERCENTAGE, space);
2069 }
2070 }
2071
2072 /* Spacing at the top and bottom */
2073 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vspace"))) {
2074 space = strtol(attrbuf, NULL, 10);
2075 if (space > 0) {
2076 space = CSS_CREATE_LENGTH(space, CSS_LENGTH_TYPE_PX);
2077 html->styleEngine->setNonCssHint (CSS_PROPERTY_MARGIN_TOP,
2078 CSS_TYPE_LENGTH_PERCENTAGE, space);
2079 html->styleEngine->setNonCssHint (CSS_PROPERTY_MARGIN_BOTTOM,
2080 CSS_TYPE_LENGTH_PERCENTAGE, space);
2081 }
2082 }
2083
2084 /* Border */
2085 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border"))) {
2086 border = strtol(attrbuf, NULL, 10);
2087 if (border >= 0) {
2088 border = CSS_CREATE_LENGTH(border, CSS_LENGTH_TYPE_PX);
2089 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_WIDTH,
2090 CSS_TYPE_LENGTH_PERCENTAGE, border);
2091 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
2092 CSS_TYPE_LENGTH_PERCENTAGE, border);
2093 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_WIDTH,
2094 CSS_TYPE_LENGTH_PERCENTAGE, border);
2095 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_WIDTH,
2096 CSS_TYPE_LENGTH_PERCENTAGE, border);
2097
2098 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_STYLE,
2099 CSS_TYPE_ENUM, BORDER_SOLID);
2100 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_STYLE,
2101 CSS_TYPE_ENUM, BORDER_SOLID);
2102 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_STYLE,
2103 CSS_TYPE_ENUM, BORDER_SOLID);
2104 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_STYLE,
2105 CSS_TYPE_ENUM, BORDER_SOLID);
2106 }
2107 }
2108
2109 /* x_img is an index to a list of {url,image} pairs.
2110 * We know Html_add_new_htmlimage() will use size() as its next index */
2111 html->styleEngine->setNonCssHint (PROPERTY_X_IMG, CSS_TYPE_INTEGER,
2112 html->images->size());
2113
2114 /* Add a new image widget to this page */
2115 Image = a_Image_new(alt_ptr, 0);
2116 if (HT2TB(html)->getBgColor())
2117 Image->bg_color = HT2TB(html)->getBgColor()->getColor();
2118
2119 load_now = prefs.load_images ||
2120 !dStrcasecmp(URL_SCHEME(url), "data") ||
2121 (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached);
2122 bool loading = false;
2123 if (load_now)
2124 loading = Html_load_image(html->bw, url, html->page_url, Image);
2125 Html_add_new_htmlimage(html, &url, loading ? NULL : Image);
2126
2127 dFree(width_ptr);
2128 dFree(height_ptr);
2129 dFree(alt_ptr);
2130 return Image;
2131 }
2132
2133 /*
2134 * Tell cache to retrieve image
2135 */
2136 static bool Html_load_image(BrowserWindow *bw, DilloUrl *url,
2137 const DilloUrl *requester, DilloImage *Image)
2138 {
2139 DilloWeb *Web;
2140 int ClientKey;
2141 /* Fill a Web structure for the cache query */
2142 Web = a_Web_new(url, requester);
2143 Web->bw = bw;
2144 Web->Image = Image;
2145 a_Image_ref(Image);
2146 Web->flags |= WEB_Image;
2147 /* Request image data from the cache */
2148 if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
2149 a_Bw_add_client(bw, ClientKey, 0);
2150 a_Bw_add_url(bw, url);
2151 }
2152 return ClientKey != 0;
2153 }
2154
2155 /*
2156 * Create a new Image struct and request the image-url to the cache
2157 * (If it either hits or misses, is not relevant here; that's up to the
2158 * cache functions)
2159 */
2160 static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize)
2161 {
2162 DilloImage *Image;
2163 DilloUrl *url, *usemap_url;
2164 const char *attrbuf;
2165
2166 /* This avoids loading images. Useful for viewing suspicious HTML email. */
2167 if (URL_FLAGS(html->base_url) & URL_SpamSafe)
2168 return;
2169
2170 if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "src")) ||
2171 !(url = a_Html_url_new(html, attrbuf, NULL, 0)))
2172 return;
2173
2174 usemap_url = NULL;
2175 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "usemap")))
2176 /* TODO: usemap URLs outside of the document are not used. */
2177 usemap_url = a_Html_url_new(html, attrbuf, NULL, 0);
2178
2179 Image = a_Html_image_new(html, tag, tagsize, url);
2180 HT2TB(html)->addWidget((Widget*)Image->dw, html->styleEngine->style());
2181
2182 /* Image maps */
2183 if (a_Html_get_attr(html, tag, tagsize, "ismap")) {
2184 ((::dw::Image*)Image->dw)->setIsMap();
2185 _MSG(" Html_tag_open_img: server-side map (ISMAP)\n");
2186 } else if (html->styleEngine->style ()->x_link != -1 &&
2187 usemap_url == NULL) {
2188 /* For simple links, we have to suppress the "image_pressed" signal.
2189 * This is overridden for USEMAP images. */
2190 // a_Dw_widget_set_button_sensitive (IM2DW(Image->dw), FALSE);
2191 }
2192
2193 if (usemap_url) {
2194 ((::dw::Image*)Image->dw)->setUseMap(&html->maps,
2195 new ::object::String(URL_STR(usemap_url)));
2196 a_Url_free (usemap_url);
2197 }
2198 }
2199
2200 /*
2201 * <map>
2202 */
2203 static void Html_tag_open_map(DilloHtml *html, const char *tag, int tagsize)
2204 {
2205 char *hash_name;
2206 const char *attrbuf;
2207 DilloUrl *url;
2208
2209 if (html->InFlags & IN_MAP) {
2210 BUG_MSG("nested <map>\n");
2211 } else {
2212 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
2213 html->InFlags |= IN_MAP;
2214 hash_name = dStrconcat("#", attrbuf, NULL);
2215 url = a_Html_url_new(html, hash_name, NULL, 0);
2216 html->maps.startNewMap(new ::object::String(URL_STR(url)));
2217 a_Url_free (url);
2218 dFree(hash_name);
2219 } else {
2220 BUG_MSG("name attribute is required for <map>\n");
2221 }
2222 }
2223 }
2224
2225 /*
2226 * Handle close <MAP>
2227 */
2228 static void Html_tag_close_map(DilloHtml *html, int TagIdx)
2229 {
2230 /* This is a hack for the perhaps frivolous feature of drawing image map
2231 * shapes when there is no image to display. If this map is defined after
2232 * an image that has not been loaded (img != NULL), tell the image to
2233 * redraw. (It will only do so if it uses a map.)
2234 */
2235 for (int i = 0; i < html->images->size(); i++) {
2236 DilloImage *img = html->images->get(i)->image;
2237
2238 if (img)
2239 ((dw::Image*) img->dw)->forceMapRedraw();
2240 }
2241 html->InFlags &= ~IN_MAP;
2242 }
2243
2244 /*
2245 * Read coords in a string, returning a vector of ints.
2246 */
2247 static
2248 misc::SimpleVector<int> *Html_read_coords(DilloHtml *html, const char *str)
2249 {
2250 int coord;
2251 const char *tail = str;
2252 char *newtail = NULL;
2253 misc::SimpleVector<int> *coords = new misc::SimpleVector<int> (4);
2254
2255 while (1) {
2256 coord = strtol(tail, &newtail, 10);
2257 if (coord == 0 && newtail == tail)
2258 break;
2259 coords->increase();
2260 coords->set(coords->size() - 1, coord);
2261 while (isspace(*newtail))
2262 newtail++;
2263 if (!*newtail)
2264 break;
2265 if (*newtail != ',') {
2266 BUG_MSG("area coords must be integers separated by commas.\n");
2267 }
2268 tail = newtail + 1;
2269 }
2270
2271 return coords;
2272 }
2273
2274 /*
2275 * <AREA>
2276 */
2277 static void Html_tag_open_area(DilloHtml *html, const char *tag, int tagsize)
2278 {
2279 enum types {UNKNOWN, RECTANGLE, CIRCLE, POLYGON, BACKGROUND};
2280 types type;
2281 misc::SimpleVector<int> *coords = NULL;
2282 DilloUrl* url;
2283 const char *attrbuf;
2284 int link = -1;
2285 Shape *shape = NULL;
2286
2287 if (!(html->InFlags & IN_MAP)) {
2288 BUG_MSG("<area> element not inside <map>\n");
2289 return;
2290 }
2291 attrbuf = a_Html_get_attr(html, tag, tagsize, "shape");
2292
2293 if (!attrbuf || !*attrbuf || !dStrcasecmp(attrbuf, "rect")) {
2294 /* the default shape is a rectangle */
2295 type = RECTANGLE;
2296 } else if (dStrcasecmp(attrbuf, "default") == 0) {
2297 /* "default" is the background */
2298 type = BACKGROUND;
2299 } else if (dStrcasecmp(attrbuf, "circle") == 0) {
2300 type = CIRCLE;
2301 } else if (dStrncasecmp(attrbuf, "poly", 4) == 0) {
2302 type = POLYGON;
2303 } else {
2304 BUG_MSG("<area> unknown shape: \"%s\"\n", attrbuf);
2305 type = UNKNOWN;
2306 }
2307 if (type == RECTANGLE || type == CIRCLE || type == POLYGON) {
2308 /* TODO: add support for coords in % */
2309 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "coords"))) {
2310 coords = Html_read_coords(html, attrbuf);
2311
2312 if (type == RECTANGLE) {
2313 if (coords->size() != 4)
2314 BUG_MSG("<area> rectangle must have four coordinate values\n");
2315 if (coords->size() >= 4)
2316 shape = new Rectangle(coords->get(0),
2317 coords->get(1),
2318 coords->get(2) - coords->get(0),
2319 coords->get(3) - coords->get(1));
2320 } else if (type == CIRCLE) {
2321 if (coords->size() != 3)
2322 BUG_MSG("<area> circle must have three coordinate values\n");
2323 if (coords->size() >= 3)
2324 shape = new Circle(coords->get(0), coords->get(1),
2325 coords->get(2));
2326 } else if (type == POLYGON) {
2327 Polygon *poly;
2328 int i;
2329 if (coords->size() % 2)
2330 BUG_MSG("<area> polygon with odd number of coordinates\n");
2331 shape = poly = new Polygon();
2332 for (i = 0; i < (coords->size() / 2); i++)
2333 poly->addPoint(coords->get(2*i), coords->get(2*i + 1));
2334 }
2335 delete(coords);
2336 }
2337 }
2338 if (shape != NULL || type == BACKGROUND) {
2339 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "href"))) {
2340 url = a_Html_url_new(html, attrbuf, NULL, 0);
2341 dReturn_if_fail ( url != NULL );
2342 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "alt")))
2343 a_Url_set_alt(url, attrbuf);
2344
2345 link = Html_set_new_link(html, &url);
2346 }
2347 if (type == BACKGROUND)
2348 html->maps.setCurrentMapDefaultLink(link);
2349 else
2350 html->maps.addShapeToCurrentMap(shape, link);
2351 }
2352 }
2353
2354 /*
2355 * <OBJECT>
2356 * Simply provide a link if the object is something downloadable.
2357 */
2358 static void Html_tag_open_object(DilloHtml *html, const char *tag, int tagsize)
2359 {
2360 DilloUrl *url, *base_url = NULL;
2361 const char *attrbuf;
2362
2363 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "codebase"))) {
2364 base_url = a_Html_url_new(html, attrbuf, NULL, 0);
2365 }
2366
2367 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "data"))) {
2368 url = a_Html_url_new(html, attrbuf,
2369 URL_STR(base_url), (base_url != NULL));
2370 dReturn_if_fail ( url != NULL );
2371
2372 if (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached) {
2373 html->styleEngine->setPseudoVisited ();
2374 } else {
2375 html->styleEngine->setPseudoLink ();
2376 }
2377
2378 html->styleEngine->setNonCssHint(PROPERTY_X_LINK, CSS_TYPE_INTEGER,
2379 Html_set_new_link(html, &url));
2380
2381 HT2TB(html)->addText("[OBJECT]", html->styleEngine->wordStyle ());
2382 }
2383 a_Url_free(base_url);
2384 }
2385
2386 /*
2387 * Test and extract the link from a javascript instruction.
2388 */
2389 static const char* Html_get_javascript_link(DilloHtml *html)
2390 {
2391 size_t i;
2392 char ch, *p1, *p2;
2393 Dstr *Buf = html->attr_data;
2394
2395 if (dStrncasecmp("javascript", Buf->str, 10) == 0) {
2396 i = strcspn(Buf->str, "'\"");
2397 ch = Buf->str[i];
2398 if ((ch == '"' || ch == '\'') &&
2399 (p2 = strchr(Buf->str + i + 1 , ch))) {
2400 p1 = Buf->str + i;
2401 BUG_MSG("link depends on javascript()\n");
2402 dStr_truncate(Buf, p2 - Buf->str);
2403 dStr_erase(Buf, 0, p1 - Buf->str + 1);
2404 }
2405 }
2406 return Buf->str;
2407 }
2408
2409 /*
2410 * Register an anchor for this page.
2411 */
2412 static void Html_add_anchor(DilloHtml *html, const char *name)
2413 {
2414 _MSG("Registering ANCHOR: %s\n", name);
2415 if (!HT2TB(html)->addAnchor (name, html->styleEngine->style ()))
2416 BUG_MSG("Anchor names must be unique within the document\n");
2417 /*
2418 * According to Sec. 12.2.1 of the HTML 4.01 spec, "anchor names that
2419 * differ only in case may not appear in the same document", but
2420 * "comparisons between fragment identifiers and anchor names must be
2421 * done by exact (case-sensitive) match." We ignore the case issue and
2422 * always test for exact matches. Moreover, what does uppercase mean
2423 * for Unicode characters outside the ASCII range?
2424 */
2425 }
2426
2427 /*
2428 * <A>
2429 */
2430 static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize)
2431 {
2432 DilloUrl *url;
2433 const char *attrbuf;
2434
2435 /* TODO: add support for MAP with A HREF */
2436 if (html->InFlags & IN_MAP)
2437 Html_tag_open_area(html, tag, tagsize);
2438
2439 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "href"))) {
2440 /* if it's a javascript link, extract the reference. */
2441 if (tolower(attrbuf[0]) == 'j')
2442 attrbuf = Html_get_javascript_link(html);
2443
2444 url = a_Html_url_new(html, attrbuf, NULL, 0);
2445 dReturn_if_fail ( url != NULL );
2446
2447 if (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached) {
2448 html->InVisitedLink = true;
2449 html->styleEngine->setPseudoVisited ();
2450 if (html->non_css_visited_color != -1)
2451 html->styleEngine->setNonCssHint(CSS_PROPERTY_COLOR,
2452 CSS_TYPE_COLOR,
2453 html->non_css_visited_color);
2454 } else {
2455 html->styleEngine->setPseudoLink ();
2456 if (html->non_css_link_color != -1)
2457 html->styleEngine->setNonCssHint(CSS_PROPERTY_COLOR,
2458 CSS_TYPE_COLOR,
2459 html->non_css_link_color);
2460 }
2461
2462 html->styleEngine->setNonCssHint (PROPERTY_X_LINK, CSS_TYPE_INTEGER,
2463 Html_set_new_link(html, &url));
2464 }
2465 if (prefs.show_tooltip &&
2466 (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
2467 html->styleEngine->setNonCssHint (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING,
2468 attrbuf);
2469 }
2470
2471 html->styleEngine->inheritBackgroundColor ();
2472
2473 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
2474 char *nameVal;
2475 const char *id = html->styleEngine->getId ();
2476
2477 if (prefs.show_extra_warnings)
2478 Html_check_name_val(html, attrbuf, "name");
2479
2480 nameVal = a_Url_decode_hex_str(attrbuf);
2481
2482 if (nameVal) {
2483 /* We compare the "id" value with the url-decoded "name" value */
2484 if (!id || strcmp(nameVal, id)) {
2485 if (id)
2486 BUG_MSG("'id' and 'name' attribute of <a> tag differ\n");
2487 Html_add_anchor(html, nameVal);
2488 }
2489
2490 dFree(nameVal);
2491 }
2492 }
2493 }
2494
2495 /*
2496 * <A> close function
2497 */
2498 static void Html_tag_close_a(DilloHtml *html, int TagIdx)
2499 {
2500 html->InVisitedLink = false;
2501 }
2502
2503 /*
2504 * <BLOCKQUOTE>
2505 */
2506 static void Html_tag_open_blockquote(DilloHtml *html,
2507 const char *tag, int tagsize)
2508 {
2509 Html_add_textblock(html, 9);
2510 }
2511
2512 /*
2513 * <Q>
2514 */
2515 static void Html_tag_open_q(DilloHtml *html, const char *tag, int tagsize)
2516 {
2517 /*
2518 * Left Double Quotation Mark, which is wrong in many cases, but
2519 * should at least be widely recognized.
2520 */
2521 const char *U201C = "\xe2\x80\x9c";
2522
2523 html->styleEngine->inheritBackgroundColor ();
2524 HT2TB(html)->addText (U201C, html->styleEngine->wordStyle ());
2525 }
2526
2527 /*
2528 * </Q>
2529 */
2530 static void Html_tag_close_q(DilloHtml *html, int TagIdx)
2531 {
2532 /* Right Double Quotation Mark */
2533 const char *U201D = "\xe2\x80\x9d";
2534
2535 HT2TB(html)->addText (U201D, html->styleEngine->wordStyle ());
2536 }
2537
2538 /*
2539 * Handle the <UL> tag.
2540 */
2541 static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize)
2542 {
2543 const char *attrbuf;
2544 ListStyleType list_style_type;
2545
2546 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) {
2547
2548 /* list_style_type explicitly defined */
2549 if (dStrcasecmp(attrbuf, "disc") == 0)
2550 list_style_type = LIST_STYLE_TYPE_DISC;
2551 else if (dStrcasecmp(attrbuf, "circle") == 0)
2552 list_style_type = LIST_STYLE_TYPE_CIRCLE;
2553 else if (dStrcasecmp(attrbuf, "square") == 0)
2554 list_style_type = LIST_STYLE_TYPE_SQUARE;
2555 else
2556 /* invalid value */
2557 list_style_type = LIST_STYLE_TYPE_DISC;
2558
2559 html->styleEngine->setNonCssHint (CSS_PROPERTY_LIST_STYLE_TYPE,
2560 CSS_TYPE_ENUM, list_style_type);
2561 }
2562
2563 Html_add_textblock(html, 9);
2564
2565 S_TOP(html)->list_type = HTML_LIST_UNORDERED;
2566 S_TOP(html)->list_number = 0;
2567 S_TOP(html)->ref_list_item = NULL;
2568 }
2569
2570 /*
2571 * Handle the <DIR> or <MENU> tag.
2572 * (Deprecated and almost the same as <UL>)
2573 */
2574 static void Html_tag_open_dir(DilloHtml *html, const char *tag, int tagsize)
2575 {
2576 html->styleEngine->inheritBackgroundColor ();
2577 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
2578
2579 S_TOP(html)->list_type = HTML_LIST_UNORDERED;
2580 S_TOP(html)->list_number = 0;
2581 S_TOP(html)->ref_list_item = NULL;
2582
2583 if (prefs.show_extra_warnings)
2584 BUG_MSG("Obsolete list type; use <UL> instead\n");
2585 }
2586
2587 /*
2588 * Handle the <MENU> tag.
2589 */
2590 static void Html_tag_open_menu(DilloHtml *html, const char *tag, int tagsize)
2591 {
2592 Html_tag_open_dir(html, tag, tagsize);
2593 }
2594
2595 /*
2596 * Handle the <OL> tag.
2597 */
2598 static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize)
2599 {
2600 const char *attrbuf;
2601 int n = 1;
2602
2603 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) {
2604 ListStyleType listStyleType = LIST_STYLE_TYPE_DECIMAL;
2605
2606 if (*attrbuf == '1')
2607 listStyleType = LIST_STYLE_TYPE_DECIMAL;
2608 else if (*attrbuf == 'a')
2609 listStyleType = LIST_STYLE_TYPE_LOWER_ALPHA;
2610 else if (*attrbuf == 'A')
2611 listStyleType = LIST_STYLE_TYPE_UPPER_ALPHA;
2612 else if (*attrbuf == 'i')
2613 listStyleType = LIST_STYLE_TYPE_LOWER_ROMAN;
2614 else if (*attrbuf == 'I')
2615 listStyleType = LIST_STYLE_TYPE_UPPER_ROMAN;
2616
2617 html->styleEngine->setNonCssHint (CSS_PROPERTY_LIST_STYLE_TYPE,
2618 CSS_TYPE_ENUM, listStyleType);
2619 }
2620
2621 Html_add_textblock(html, 9);
2622
2623 S_TOP(html)->list_type = HTML_LIST_ORDERED;
2624
2625 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "start")) &&
2626 (n = (int) strtol(attrbuf, NULL, 10)) < 0) {
2627 BUG_MSG( "illegal '-' character in START attribute; Starting from 0\n");
2628 n = 0;
2629 }
2630 S_TOP(html)->list_number = n;
2631 S_TOP(html)->ref_list_item = NULL;
2632 }
2633
2634 /*
2635 * Handle the <LI> tag.
2636 */
2637 static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize)
2638 {
2639 Style *style = html->styleEngine->style ();
2640 Style *wordStyle = html->styleEngine->wordStyle ();
2641 Widget **ref_list_item;
2642 ListItem *list_item;
2643 int *list_number;
2644 const char *attrbuf;
2645 char buf[16];
2646
2647 if (S_TOP(html)->list_type == HTML_LIST_NONE)
2648 BUG_MSG("<li> outside <ul> or <ol>\n");
2649
2650 html->InFlags |= IN_LI;
2651
2652 /* Get our parent tag's variables (used as state storage) */
2653 list_number = &html->stack->getRef(html->stack->size()-2)->list_number;
2654 ref_list_item = &html->stack->getRef(html->stack->size()-2)->ref_list_item;
2655
2656 HT2TB(html)->addParbreak (0, wordStyle);
2657
2658 list_item = new ListItem ((ListItem*)*ref_list_item,prefs.limit_text_width);
2659 HT2TB(html)->addWidget (list_item, style);
2660 HT2TB(html)->addParbreak (0, wordStyle);
2661 *ref_list_item = list_item;
2662 S_TOP(html)->textblock = html->dw = list_item;
2663
2664 if (style->listStyleType == LIST_STYLE_TYPE_NONE) {
2665 // none
2666 } else if (style->listStyleType >= LIST_STYLE_TYPE_DECIMAL) {
2667 // ordered
2668 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "value")) &&
2669 (*list_number = strtol(attrbuf, NULL, 10)) < 0) {
2670 BUG_MSG("illegal negative LIST VALUE attribute; Starting from 0\n");
2671 *list_number = 0;
2672 }
2673 numtostr((*list_number)++, buf, 16, style->listStyleType);
2674 list_item->initWithText (buf, wordStyle);
2675 } else {
2676 // unordered
2677 list_item->initWithWidget (new Bullet(), wordStyle);
2678 }
2679 }
2680
2681 /*
2682 * Close <LI>
2683 */
2684 static void Html_tag_close_li(DilloHtml *html, int TagIdx)
2685 {
2686 html->InFlags &= ~IN_LI;
2687 ((ListItem *)html->dw)->flush ();
2688 }
2689
2690 /*
2691 * <HR>
2692 */
2693 static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize)
2694 {
2695 Widget *hruler;
2696 char *width_ptr;
2697 const char *attrbuf;
2698 int32_t size = 0;
2699
2700 width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", NULL);
2701 if (width_ptr) {
2702 html->styleEngine->setNonCssHint (CSS_PROPERTY_WIDTH,
2703 CSS_TYPE_LENGTH_PERCENTAGE,
2704 a_Html_parse_length (html, width_ptr));
2705 dFree(width_ptr);
2706 }
2707
2708 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size")))
2709 size = strtol(attrbuf, NULL, 10);
2710
2711 a_Html_tag_set_align_attr(html, tag, tagsize);
2712
2713 /* TODO: evaluate attribute */
2714 if (a_Html_get_attr(html, tag, tagsize, "noshade")) {
2715 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_STYLE,
2716 CSS_TYPE_ENUM, BORDER_SOLID);
2717 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_STYLE,
2718 CSS_TYPE_ENUM, BORDER_SOLID);
2719 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_STYLE,
2720 CSS_TYPE_ENUM, BORDER_SOLID);
2721 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_STYLE,
2722 CSS_TYPE_ENUM, BORDER_SOLID);
2723
2724 if (size <= 0)
2725 size = 1;
2726 }
2727
2728 if (size > 0) {
2729 CssLength size_top = CSS_CREATE_LENGTH ((size+1)/2, CSS_LENGTH_TYPE_PX);
2730 CssLength size_bottom = CSS_CREATE_LENGTH (size / 2, CSS_LENGTH_TYPE_PX);
2731 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_WIDTH,
2732 CSS_TYPE_LENGTH_PERCENTAGE, size_top);
2733 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_WIDTH,
2734 CSS_TYPE_LENGTH_PERCENTAGE, size_top);
2735 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
2736 CSS_TYPE_LENGTH_PERCENTAGE,
2737 size_bottom);
2738 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_WIDTH,
2739 CSS_TYPE_LENGTH_PERCENTAGE,
2740 size_bottom);
2741 }
2742
2743 HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
2744
2745 hruler = new Ruler();
2746 hruler->setStyle (html->styleEngine->style ());
2747 HT2TB(html)->addWidget (hruler, html->styleEngine->style ());
2748 HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
2749 }
2750
2751 /*
2752 * <DL>
2753 */
2754 static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize)
2755 {
2756 /* may want to actually do some stuff here. */
2757 html->styleEngine->inheritBackgroundColor ();
2758 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
2759 }
2760
2761 /*
2762 * <DT>
2763 */
2764 static void Html_tag_open_dt(DilloHtml *html, const char *tag, int tagsize)
2765 {
2766 html->styleEngine->inheritBackgroundColor ();
2767 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
2768 }
2769
2770 /*
2771 * <DD>
2772 */
2773 static void Html_tag_open_dd(DilloHtml *html, const char *tag, int tagsize)
2774 {
2775 Html_add_textblock(html, 9);
2776 }
2777
2778 /*
2779 * <PRE>
2780 */
2781 static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize)
2782 {
2783 html->styleEngine->inheritBackgroundColor ();
2784 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
2785
2786 html->InFlags |= IN_PRE;
2787 }
2788
2789 /*
2790 * Custom close for <PRE>
2791 */
2792 static void Html_tag_close_pre(DilloHtml *html, int TagIdx)
2793 {
2794 html->InFlags &= ~IN_PRE;
2795 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
2796 }
2797
2798 /*
2799 * Check whether a tag is in the "excluding" element set for PRE
2800 * Excl. Set = {IMG, OBJECT, APPLET, BIG, SMALL, SUB, SUP, FONT, BASEFONT}
2801 */
2802 static int Html_tag_pre_excludes(int tag_idx)
2803 {
2804 const char *es_set[] = {"img", "object", "applet", "big", "small", "sub",
2805 "sup", "font", "basefont", NULL};
2806 static int ei_set[10], i;
2807
2808 /* initialize array */
2809 if (!ei_set[0])
2810 for (i = 0; es_set[i]; ++i)
2811 ei_set[i] = a_Html_tag_index(es_set[i]);
2812
2813 for (i = 0; ei_set[i]; ++i)
2814 if (tag_idx == ei_set[i])
2815 return 1;
2816 return 0;
2817 }
2818
2819 /*
2820 * Handle <META>
2821 * We do not support http-equiv=refresh with delay>0 because it's
2822 * non standard, (the HTML 4.01 SPEC recommends explicitly to avoid it).
2823 * More info at:
2824 * http://lists.w3.org/Archives/Public/www-html/2000Feb/thread.html#msg232
2825 * Instant client-side redirects (delay=0) are supported:
2826 * http://www.w3.org/TR/2008/NOTE-WCAG20-TECHS-20081211/H76.html
2827 *
2828 * TODO: Note that we're sending custom HTML while still IN_HEAD. This
2829 * is a hackish way to put the message. A much cleaner approach is to
2830 * build a custom widget for it.
2831 */
2832 static void Html_tag_open_meta(DilloHtml *html, const char *tag, int tagsize)
2833 {
2834 const char meta_template[] =
2835 "<table width='100%%'><tr><td bgcolor='#ee0000'>Warning:</td>\n"
2836 " <td bgcolor='#8899aa' width='100%%'>\n"
2837 " This page uses the NON-STANDARD meta refresh tag.<br> The HTML 4.01 SPEC\n"
2838 " (sec 7.4.4) recommends explicitly to avoid it.</td></tr>\n"
2839 " <tr><td bgcolor='#a0a0a0' colspan='2'>The author wanted you to go\n"
2840 " <a href='%s'>here</a>%s</td></tr></table><br>\n";
2841
2842 const char *p, *equiv, *content, *new_content;
2843 char delay_str[64], *mr_url;
2844 DilloUrl *new_url;
2845 int delay;
2846
2847 /* only valid inside HEAD */
2848 if (!(html->InFlags & IN_HEAD)) {
2849 BUG_MSG("META elements must be inside the HEAD section\n");
2850 return;
2851 }
2852
2853 if ((equiv = a_Html_get_attr(html, tag, tagsize, "http-equiv"))) {
2854 if (!dStrcasecmp(equiv, "refresh") &&
2855 (content = a_Html_get_attr(html, tag, tagsize, "content"))) {
2856
2857 /* Get delay, if present, and make a message with it */
2858 if ((delay = strtol(content, NULL, 0))) {
2859 snprintf(delay_str, 64, " after %d second%s.",
2860 delay, (delay > 1) ? "s" : "");
2861 } else {
2862 sprintf(delay_str, ".");
2863 }
2864 /* Skip to anything after "URL=" */
2865 while (*content && *(content++) != '=') ;
2866 /* Handle the case of a quoted URL */
2867 if (*content == '"' || *content == '\'') {
2868 if ((p = strchr(content + 1, *content)))
2869 mr_url = dStrndup(content + 1, p - content - 1);
2870 else
2871 mr_url = dStrdup(content + 1);
2872 } else {
2873 mr_url = dStrdup(content);
2874 }
2875 new_url = a_Url_new(mr_url, URL_STR(html->base_url));
2876
2877 if (a_Url_cmp(html->base_url, new_url) == 0) {
2878 /* redirection loop, or empty url string: ignore */
2879 BUG_MSG("META refresh: %s\n",
2880 *mr_url ? "redirection loop" : "no target URL");
2881 } else if (delay == 0) {
2882 /* zero-delay redirection */
2883 html->stop_parser = true;
2884 if (a_Capi_dpi_verify_request(html->bw, new_url))
2885 a_UIcmd_redirection0((void*)html->bw, new_url);
2886 } else {
2887 /* Send a custom HTML message.
2888 * TODO: This is a hairy hack,
2889 * It'd be much better to build a widget. */
2890 Dstr *ds_msg = dStr_sized_new(256);
2891 dStr_sprintf(ds_msg, meta_template, mr_url, delay_str);
2892 {
2893 int o_InFlags = html->InFlags;
2894 int o_TagSoup = html->TagSoup;
2895 html->InFlags = IN_BODY;
2896 html->TagSoup = false;
2897 Html_write_raw(html, ds_msg->str, ds_msg->len, 0);
2898 html->TagSoup = o_TagSoup;
2899 html->InFlags = o_InFlags;
2900 }
2901 dStr_free(ds_msg, 1);
2902 }
2903 a_Url_free(new_url);
2904 dFree(mr_url);
2905
2906 } else if (!dStrcasecmp(equiv, "content-type") &&
2907 (content = a_Html_get_attr(html, tag, tagsize, "content"))) {
2908 _MSG("Html_tag_open_meta: content={%s}\n", content);
2909 /* Cannot ask cache whether the content type was changed, as
2910 * this code in another bw might have already changed it for us.
2911 */
2912 new_content = a_Capi_set_content_type(html->page_url,content,"meta");
2913 if (a_Misc_content_type_cmp(html->content_type, new_content)) {
2914 html->stop_parser = true; /* The cache buffer is no longer valid */
2915 a_UIcmd_repush(html->bw);
2916 }
2917 }
2918 }
2919 }
2920
2921 /*
2922 * Called by the network engine when a stylesheet has new data.
2923 */
2924 static void Html_css_load_callback(int Op, CacheClient_t *Client)
2925 {
2926 _MSG("Html_css_load_callback: Op=%d\n", Op);
2927 if (Op) { /* EOF */
2928 BrowserWindow *bw = ((DilloWeb *)Client->Web)->bw;
2929 /* Repush when we've got them all */
2930 if (--bw->NumPendingStyleSheets == 0)
2931 a_UIcmd_repush(bw);
2932 }
2933 }
2934
2935 /*
2936 * Tell cache to retrieve a stylesheet
2937 */
2938 void a_Html_load_stylesheet(DilloHtml *html, DilloUrl *url)
2939 {
2940 char *data;
2941 int len;
2942
2943 dReturn_if (url == NULL || ! prefs.load_stylesheets);
2944
2945 _MSG("Html_load_stylesheet: ");
2946 if (a_Capi_get_buf(url, &data, &len)) {
2947 _MSG("cached URL=%s len=%d", URL_STR(url), len);
2948 if (a_Capi_get_flags_with_redirection(url) & CAPI_Completed) {
2949 if (strncmp("@charset \"", data, 10) == 0) {
2950 char *endq = strchr(data+10, '"');
2951
2952 if (endq && (endq - data <= 51)) {
2953 /* IANA limits charset names to 40 characters */
2954 char *content_type;
2955
2956 *endq = '\0';
2957 content_type = dStrconcat("text/css; charset=", data+10, NULL);
2958 *endq = '"';
2959 a_Capi_unref_buf(url);
2960 a_Capi_set_content_type(url, content_type, "meta");
2961 dFree(content_type);
2962 a_Capi_get_buf(url, &data, &len);
2963 }
2964 }
2965 html->styleEngine->parse(html, url, data, len, CSS_ORIGIN_AUTHOR);
2966 }
2967 a_Capi_unref_buf(url);
2968 } else {
2969 /* Fill a Web structure for the cache query */
2970 int ClientKey;
2971 DilloWeb *Web = a_Web_new(url, html->page_url);
2972 Web->bw = html->bw;
2973 if ((ClientKey = a_Capi_open_url(Web, Html_css_load_callback, NULL))) {
2974 ++html->bw->NumPendingStyleSheets;
2975 a_Bw_add_client(html->bw, ClientKey, 0);
2976 a_Bw_add_url(html->bw, url);
2977 MSG("NumPendingStyleSheets=%d", html->bw->NumPendingStyleSheets);
2978 }
2979 }
2980 MSG("\n");
2981 }
2982
2983 /*
2984 * Parse the LINK element (Only CSS stylesheets by now).
2985 * (If it either hits or misses, is not relevant here; that's up to the
2986 * cache functions)
2987 *
2988 * TODO: How will we know when to use "handheld"? Ask the html->bw->ui for
2989 * screen dimensions, or a dillorc preference.
2990 */
2991 static void Html_tag_open_link(DilloHtml *html, const char *tag, int tagsize)
2992 {
2993 DilloUrl *url;
2994 const char *attrbuf;
2995
2996 //char *tag_str = dStrndup(tag, tagsize);
2997 //MSG("Html_tag_open_link(): %s\n", tag_str);
2998 //dFree(tag_str);
2999
3000 /* When viewing suspicious HTML email, don't load LINK */
3001 dReturn_if (URL_FLAGS(html->base_url) & URL_SpamSafe);
3002
3003 /* Ignore LINK outside HEAD */
3004 if (!(html->InFlags & IN_HEAD)) {
3005 BUG_MSG("the LINK element must be inside the HEAD section\n");
3006 return;
3007 }
3008 /* Remote stylesheets enabled? */
3009 dReturn_if_fail (prefs.load_stylesheets);
3010 /* CSS stylesheet link */
3011 if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "rel")) ||
3012 dStrcasecmp(attrbuf, "stylesheet"))
3013 return;
3014
3015 /* IMPLIED attributes? */
3016 if (((attrbuf = a_Html_get_attr(html, tag, tagsize, "type")) &&
3017 dStrcasecmp(attrbuf, "text/css")) ||
3018 ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) &&
3019 !dStristr(attrbuf, "screen") && dStrcasecmp(attrbuf, "all")))
3020 return;
3021
3022 if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "href")) ||
3023 !(url = a_Html_url_new(html, attrbuf, NULL, 0)))
3024 return;
3025
3026 _MSG(" Html_tag_open_link(): addCssUrl %s\n", URL_STR(url));
3027
3028 html->addCssUrl(url);
3029 a_Url_free(url);
3030 }
3031
3032 /*
3033 * Set the history of the menu to be consistent with the active menuitem.
3034 */
3035 //static void Html_select_set_history(DilloHtmlInput *input)
3036 //{
3037 // int i;
3038 //
3039 // for (i = 0; i < input->select->num_options; i++) {
3040 // if (GTK_CHECK_MENU_ITEM(input->select->options[i].menuitem)->active) {
3041 // gtk_option_menu_set_history(GTK_OPTION_MENU(input->widget), i);
3042 // break;
3043 // }
3044 // }
3045 //}
3046
3047
3048 /*
3049 * Set the Document Base URI
3050 */
3051 static void Html_tag_open_base(DilloHtml *html, const char *tag, int tagsize)
3052 {
3053 const char *attrbuf;
3054 DilloUrl *BaseUrl;
3055
3056 if (html->InFlags & IN_HEAD) {
3057 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "href"))) {
3058 BaseUrl = a_Html_url_new(html, attrbuf, "", 1);
3059 if (URL_SCHEME_(BaseUrl)) {
3060 /* Pass the URL_SpamSafe flag to the new base url */
3061 a_Url_set_flags(
3062 BaseUrl, URL_FLAGS(html->base_url) & URL_SpamSafe);
3063 a_Url_free(html->base_url);
3064 html->base_url = BaseUrl;
3065 } else {
3066 BUG_MSG("base URI is relative (it MUST be absolute)\n");
3067 a_Url_free(BaseUrl);
3068 }
3069 }
3070 } else {
3071 BUG_MSG("the BASE element must appear in the HEAD section\n");
3072 }
3073 }
3074
3075 static void Html_tag_open_default(DilloHtml *html,const char *tag,int tagsize)
3076 {
3077 html->styleEngine->inheritBackgroundColor();
3078 }
3079
3080 /*
3081 * <DIV> (TODO: make a complete implementation)
3082 */
3083 static void Html_tag_open_div(DilloHtml *html, const char *tag, int tagsize)
3084 {
3085 a_Html_tag_set_align_attr (html, tag, tagsize);
3086 Html_add_textblock(html, 0);
3087 }
3088
3089 /*
3090 * Default close for most tags.
3091 */
3092 static void Html_tag_close_default(DilloHtml *html, int TagIdx)
3093 {
3094 }
3095
3096 /*
3097 * Default close for paragraph tags - pop the stack and break.
3098 */
3099 static void Html_tag_close_par(DilloHtml *html, int TagIdx)
3100 {
3101 HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
3102 }
3103
3104
3105 /*
3106 * Function index for the open and close functions for each tag
3107 * (Alphabetically sorted for a binary search)
3108 *
3109 * Explanation for the 'Flags' field:
3110 *
3111 * {"address", B8(010110), ...}
3112 * |||||`- inline element
3113 * ||||`-- block element
3114 * |||`--- inline container
3115 * ||`---- block container
3116 * |`----- body element
3117 * `------ head element
3118 *
3119 * Notes:
3120 * - The upper two bits are not used yet.
3121 * - Empty elements have both inline and block container clear.
3122 * (flow have both set)
3123 */
3124
3125
3126 const TagInfo Tags[] = {
3127 {"a", B8(010101),'R',2, Html_tag_open_a, Html_tag_close_a},
3128 {"abbr", B8(010101),'R',2, Html_tag_open_abbr, Html_tag_close_default},
3129 /* acronym 010101 */
3130 {"address", B8(010110),'R',2, Html_tag_open_address, Html_tag_close_par},
3131 {"area", B8(010001),'F',0, Html_tag_open_area, Html_tag_close_default},
3132 {"b", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3133 {"base", B8(100001),'F',0, Html_tag_open_base, Html_tag_close_default},
3134 /* basefont 010001 */
3135 /* bdo 010101 */
3136 {"big", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3137 {"blockquote", B8(011110),'R',2, Html_tag_open_blockquote,
3138 Html_tag_close_default},
3139 {"body", B8(011110),'O',1, Html_tag_open_body, Html_tag_close_body},
3140 {"br", B8(010001),'F',0, Html_tag_open_br, Html_tag_close_default},
3141 {"button", B8(011101),'R',2, Html_tag_open_button, Html_tag_close_button},
3142 /* caption */
3143 {"center", B8(011110),'R',2, Html_tag_open_center, Html_tag_close_center},
3144 {"cite", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3145 {"code", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3146 /* col 010010 'F' */
3147 /* colgroup */
3148 {"dd", B8(011110),'O',1, Html_tag_open_dd, Html_tag_close_default},
3149 {"del", B8(011101),'R',2, Html_tag_open_default, Html_tag_close_default},
3150 {"dfn", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3151 {"dir", B8(011010),'R',2, Html_tag_open_dir, Html_tag_close_par},
3152 /* TODO: complete <div> support! */
3153 {"div", B8(011110),'R',2, Html_tag_open_div, Html_tag_close_default},
3154 {"dl", B8(011010),'R',2, Html_tag_open_dl, Html_tag_close_par},
3155 {"dt", B8(010110),'O',1, Html_tag_open_dt, Html_tag_close_par},
3156 {"em", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3157 /* fieldset */
3158 {"font", B8(010101),'R',2, Html_tag_open_font, Html_tag_close_default},
3159 {"form", B8(011110),'R',2, Html_tag_open_form, Html_tag_close_form},
3160 {"frame", B8(010010),'F',0, Html_tag_open_frame, Html_tag_close_default},
3161 {"frameset", B8(011110),'R',2,Html_tag_open_frameset, Html_tag_close_default},
3162 {"h1", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
3163 {"h2", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
3164 {"h3", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
3165 {"h4", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
3166 {"h5", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
3167 {"h6", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
3168 {"head", B8(101101),'O',1, Html_tag_open_head, Html_tag_close_head},
3169 {"hr", B8(010010),'F',0, Html_tag_open_hr, Html_tag_close_default},
3170 {"html", B8(001110),'O',1, Html_tag_open_html, Html_tag_close_html},
3171 {"i", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3172 {"iframe", B8(011110),'R',2, Html_tag_open_frame, Html_tag_close_default},
3173 {"img", B8(010001),'F',0, Html_tag_open_img, Html_tag_close_default},
3174 {"input", B8(010001),'F',0, Html_tag_open_input, Html_tag_close_default},
3175 /* ins */
3176 {"isindex", B8(110001),'F',0, Html_tag_open_isindex, Html_tag_close_default},
3177 {"kbd", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3178 /* label 010101 */
3179 /* legend 01?? */
3180 {"li", B8(011110),'O',1, Html_tag_open_li, Html_tag_close_li},
3181 {"link", B8(100001),'F',0, Html_tag_open_link, Html_tag_close_default},
3182 {"map", B8(011001),'R',2, Html_tag_open_map, Html_tag_close_map},
3183 /* menu 1010 -- TODO: not exactly 1010, it can contain LI and inline */
3184 {"menu", B8(011010),'R',2, Html_tag_open_menu, Html_tag_close_par},
3185 {"meta", B8(100001),'F',0, Html_tag_open_meta, Html_tag_close_default},
3186 /* noframes 1011 */
3187 /* noscript 1011 */
3188 {"object", B8(111101),'R',2, Html_tag_open_object, Html_tag_close_default},
3189 {"ol", B8(011010),'R',2, Html_tag_open_ol, Html_tag_close_default},
3190 /* optgroup */
3191 {"option", B8(010001),'O',1, Html_tag_open_option, Html_tag_close_default},
3192 {"p", B8(010110),'O',1, Html_tag_open_p, Html_tag_close_par},
3193 /* param 010001 'F' */
3194 {"pre", B8(010110),'R',2, Html_tag_open_pre, Html_tag_close_pre},
3195 {"q", B8(010101),'R',2, Html_tag_open_q, Html_tag_close_q},
3196 {"s", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3197 {"samp", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3198 {"script", B8(111001),'R',2, Html_tag_open_script, Html_tag_close_script},
3199 {"select", B8(010101),'R',2, Html_tag_open_select, Html_tag_close_select},
3200 {"small", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3201 {"span", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3202 {"strike", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3203 {"strong", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3204 {"style", B8(100101),'R',2, Html_tag_open_style, Html_tag_close_style},
3205 {"sub", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3206 {"sup", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3207 {"table", B8(011010),'R',5, Html_tag_open_table, Html_tag_close_center},
3208 /* tbody */
3209 {"td", B8(011110),'O',3, Html_tag_open_td, Html_tag_close_default},
3210 {"textarea", B8(010101),'R',2,Html_tag_open_textarea,Html_tag_close_textarea},
3211 /* tfoot */
3212 {"th", B8(011110),'O',1, Html_tag_open_th, Html_tag_close_default},
3213 /* thead */
3214 {"title", B8(100101),'R',2, Html_tag_open_title, Html_tag_close_title},
3215 {"tr", B8(011010),'O',4, Html_tag_open_tr, Html_tag_close_default},
3216 {"tt", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3217 {"u", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
3218 {"ul", B8(011010),'R',2, Html_tag_open_ul, Html_tag_close_default},
3219 {"var", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}
3220
3221 };
3222 #define NTAGS (sizeof(Tags)/sizeof(Tags[0]))
3223
3224
3225 /*
3226 * Compares tag from buffer ('/' or '>' or space-ended string) [p1]
3227 * with tag from taglist (lowercase, zero ended string) [p2]
3228 * Return value: as strcmp()
3229 */
3230 static int Html_tag_compare(const char *p1, const char *p2)
3231 {
3232 while ( *p2 ) {
3233 if (tolower(*p1) != *p2)
3234 return(tolower(*p1) - *p2);
3235 ++p1;
3236 ++p2;
3237 }
3238 return !strchr(" >/\n\r\t", *p1);
3239 }
3240
3241 /*
3242 * Get 'tag' index
3243 * return -1 if tag is not handled yet
3244 */
3245 int a_Html_tag_index(const char *tag)
3246 {
3247 int low, high, mid, cond;
3248
3249 /* Binary search */
3250 low = 0;
3251 high = NTAGS - 1; /* Last tag index */
3252 while (low <= high) {
3253 mid = (low + high) / 2;
3254 if ((cond = Html_tag_compare(tag, Tags[mid].name)) < 0 )
3255 high = mid - 1;
3256 else if (cond > 0)
3257 low = mid + 1;
3258 else
3259 return mid;
3260 }
3261 return -1;
3262 }
3263
3264 /*
3265 * For elements with optional close, check whether is time to close.
3266 * Return value: (1: Close, 0: Don't close)
3267 * --tuned for speed.
3268 */
3269 static int Html_needs_optional_close(int old_idx, int cur_idx)
3270 {
3271 static int i_P = -1, i_LI, i_TD, i_TR, i_TH, i_DD, i_DT, i_OPTION;
3272 // i_THEAD, i_TFOOT, i_COLGROUP;
3273
3274 if (i_P == -1) {
3275 /* initialize the indexes of elements with optional close */
3276 i_P = a_Html_tag_index("p"),
3277 i_LI = a_Html_tag_index("li"),
3278 i_TD = a_Html_tag_index("td"),
3279 i_TR = a_Html_tag_index("tr"),
3280 i_TH = a_Html_tag_index("th"),
3281 i_DD = a_Html_tag_index("dd"),
3282 i_DT = a_Html_tag_index("dt"),
3283 i_OPTION = a_Html_tag_index("option");
3284 // i_THEAD = a_Html_tag_index("thead");
3285 // i_TFOOT = a_Html_tag_index("tfoot");
3286 // i_COLGROUP = a_Html_tag_index("colgroup");
3287 }
3288
3289 if (old_idx == i_P || old_idx == i_DT) {
3290 /* P and DT are closed by block elements */
3291 return (Tags[cur_idx].Flags & 2);
3292 } else if (old_idx == i_LI) {
3293 /* LI closes LI */
3294 return (cur_idx == i_LI);
3295 } else if (old_idx == i_TD || old_idx == i_TH) {
3296 /* TD and TH are closed by TD, TH and TR */
3297 return (cur_idx == i_TD || cur_idx == i_TH || cur_idx == i_TR);
3298 } else if (old_idx == i_TR) {
3299 /* TR closes TR */
3300 return (cur_idx == i_TR);
3301 } else if (old_idx == i_DD) {
3302 /* DD is closed by DD and DT */
3303 return (cur_idx == i_DD || cur_idx == i_DT);
3304 } else if (old_idx == i_OPTION) {
3305 return 1; // OPTION always needs close
3306 }
3307
3308 /* HTML, HEAD, BODY are handled by Html_test_section(), not here. */
3309 /* TODO: TBODY is pending */
3310 return 0;
3311 }
3312
3313
3314 /*
3315 * Conditional cleanup of the stack (at open time).
3316 * - This helps catching block elements inside inline containers (a BUG).
3317 * - It also closes elements with "optional" close tag.
3318 *
3319 * This function is called when opening a block element or <OPTION>.
3320 *
3321 * It searches the stack closing open inline containers, and closing
3322 * elements with optional close tag when necessary.
3323 *
3324 * Note: OPTION is the only non-block element with an optional close.
3325 */
3326 static void Html_stack_cleanup_at_open(DilloHtml *html, int new_idx)
3327 {
3328 /* We know that the element we're about to push is a block element.
3329 * (except for OPTION, which is an empty inline, so is closed anyway)
3330 * Notes:
3331 * Its 'tag' is not yet pushed into the stack,
3332 * 'new_idx' is its index inside Tags[].
3333 */
3334
3335 if (!html->TagSoup)
3336 return;
3337
3338 while (html->stack->size() > 1) {
3339 int oldtag_idx = S_TOP(html)->tag_idx;
3340
3341 if (Tags[oldtag_idx].EndTag == 'O') { // Element with optional close
3342 if (!Html_needs_optional_close(oldtag_idx, new_idx))
3343 break;
3344 } else if (Tags[oldtag_idx].Flags & 8) { // Block container
3345 break;
3346 }
3347
3348 /* we have an inline (or empty) container... */
3349 if (Tags[oldtag_idx].EndTag == 'R') {
3350 BUG_MSG("<%s> is not allowed to contain <%s>. -- closing <%s>\n",
3351 Tags[oldtag_idx].name, Tags[new_idx].name,
3352 Tags[oldtag_idx].name);
3353 }
3354
3355 /* Workaround for Apache and its bad HTML directory listings... */
3356 if ((html->InFlags & IN_PRE) &&
3357 strcmp(Tags[new_idx].name, "hr") == 0)
3358 break;
3359 /* Avoid OPTION closing SELECT */
3360 if ((html->InFlags & IN_SELECT) &&
3361 strcmp(Tags[new_idx].name,"option") == 0)
3362 break;
3363
3364 /* This call closes the top tag only. */
3365 Html_tag_cleanup_at_close(html, oldtag_idx);
3366 }
3367 }
3368
3369 /*
3370 * HTML, HEAD and BODY elements have optional open and close tags.
3371 * Handle this "magic" here.
3372 */
3373 static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
3374 {
3375 const char *tag;
3376 int tag_idx;
3377
3378 if (!(html->InFlags & IN_HTML) && html->DocType == DT_NONE)
3379 BUG_MSG("the required DOCTYPE declaration is missing (or invalid)\n");
3380
3381 if (!(html->InFlags & IN_HTML)) {
3382 tag = "<html>";
3383 tag_idx = a_Html_tag_index(tag + 1);
3384 if (tag_idx != new_idx || IsCloseTag) {
3385 /* implicit open */
3386 Html_force_push_tag(html, tag_idx);
3387 _MSG("Open : %*s%s\n", html->stack->size()," ",Tags[tag_idx].name);
3388 Tags[tag_idx].open (html, tag, strlen(tag));
3389 }
3390 }
3391
3392 if (Tags[new_idx].Flags & 32) {
3393 /* head element */
3394 if (!(html->InFlags & IN_HEAD)) {
3395 tag = "<head>";
3396 tag_idx = a_Html_tag_index(tag + 1);
3397 if (tag_idx != new_idx || IsCloseTag) {
3398 /* implicit open of the head element */
3399 Html_force_push_tag(html, tag_idx);
3400 _MSG("Open : %*s%s\n", html->stack->size()," ",Tags[tag_idx].name);
3401 Tags[tag_idx].open (html, tag, strlen(tag));
3402 }
3403 }
3404
3405 } else if (Tags[new_idx].Flags & 16) {
3406 /* body element */
3407 if (html->InFlags & IN_HEAD) {
3408 tag = "</head>";
3409 tag_idx = a_Html_tag_index(tag + 2);
3410 Html_tag_cleanup_at_close(html, tag_idx);
3411 }
3412 tag = "<body>";
3413 tag_idx = a_Html_tag_index(tag + 1);
3414 if (tag_idx != new_idx || IsCloseTag) {
3415 /* implicit open */
3416 Html_force_push_tag(html, tag_idx);
3417 _MSG("Open : %*s%s\n", html->stack->size()," ",Tags[tag_idx].name);
3418 Tags[tag_idx].open (html, tag, strlen(tag));
3419 }
3420 }
3421 }
3422
3423 /*
3424 * Parse attributes that can appear on any tag.
3425 */
3426 static void Html_parse_common_attrs(DilloHtml *html, char *tag, int tagsize)
3427 {
3428 const char *attrbuf;
3429
3430 if (tagsize >= 8 && /* length of "<t id=i>" */
3431 (attrbuf = Html_get_attr2(html, tag, tagsize, "id",
3432 HTML_LeftTrim | HTML_RightTrim))) {
3433 /* According to the SGML declaration of HTML 4, all NAME values
3434 * occuring outside entities must be converted to uppercase
3435 * (this is what "NAMECASE GENERAL YES" says). But the HTML 4
3436 * spec states in Sec. 7.5.2 that anchor ids are case-sensitive.
3437 * So we don't do it and hope for better specs in the future ...
3438 */
3439 Html_check_name_val(html, attrbuf, "id");
3440
3441 html->styleEngine->setId(attrbuf);
3442 }
3443
3444 if (tagsize >= 11 && (prefs.parse_embedded_css || prefs.load_stylesheets)) {
3445 /* length of "<t class=i>" or "<t style=i>" */
3446 attrbuf = Html_get_attr2(html, tag, tagsize, "class",
3447 HTML_LeftTrim | HTML_RightTrim);
3448 if (attrbuf)
3449 html->styleEngine->setClass (attrbuf);
3450
3451 attrbuf = Html_get_attr2(html, tag, tagsize, "style",
3452 HTML_LeftTrim | HTML_RightTrim);
3453 if (attrbuf)
3454 html->styleEngine->setStyle (attrbuf);
3455 }
3456
3457 }
3458
3459 /*
3460 * Process a tag, given as 'tag' and 'tagsize'. -- tagsize is [1 based]
3461 * ('tag' must include the enclosing angle brackets)
3462 * This function calls the right open or close function for the tag.
3463 */
3464 static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
3465 {
3466 int ci, ni; /* current and new tag indexes */
3467 char *start = tag + 1; /* discard the '<' */
3468 int IsCloseTag = (*start == '/');
3469
3470 dReturn_if (html->stop_parser == true);
3471
3472 ni = a_Html_tag_index(start + IsCloseTag);
3473 if (ni == -1) {
3474 /* TODO: doctype parsing is a bit fuzzy, but enough for the time being */
3475 if (!(html->InFlags & IN_HTML)) {
3476 if (tagsize > 9 && !dStrncasecmp(tag, "<!doctype", 9))
3477 Html_parse_doctype(html, tag, tagsize);
3478 }
3479 /* Ignore unknown tags */
3480 return;
3481 }
3482
3483 /* Handle HTML, HEAD and BODY. Elements with optional open and close */
3484 if (!(html->InFlags & IN_BODY) /* && parsing HTML */)
3485 Html_test_section(html, ni, IsCloseTag);
3486
3487 /* Tag processing */
3488 ci = S_TOP(html)->tag_idx;
3489 switch (IsCloseTag) {
3490 case 0:
3491 /* Open function */
3492
3493 /* Cleanup when opening a block element, or
3494 * when openning over an element with optional close */
3495 if (Tags[ni].Flags & 2 || (ci != -1 && Tags[ci].EndTag == 'O'))
3496 Html_stack_cleanup_at_open(html, ni);
3497
3498 /* TODO: this is only raising a warning, take some defined action.
3499 * Note: apache uses IMG inside PRE (we could use its "alt"). */
3500 if ((html->InFlags & IN_PRE) && Html_tag_pre_excludes(ni))
3501 BUG_MSG("<pre> is not allowed to contain <%s>\n", Tags[ni].name);
3502
3503 /* Push the tag into the stack */
3504 Html_push_tag(html, ni);
3505
3506 html->styleEngine->startElement (ni);
3507 _MSG("Open : %*s%s\n", html->stack->size(), " ", Tags[ni].name);
3508
3509 /* Parse attributes that can appear on any tag */
3510 Html_parse_common_attrs(html, tag, tagsize);
3511
3512 /* Call the open function for this tag */
3513 _MSG("Open : %s\n", Tags[ni].name);
3514 Tags[ni].open (html, tag, tagsize);
3515 if (html->stop_parser)
3516 break;
3517
3518 if (S_TOP(html)->parse_mode != DILLO_HTML_PARSE_MODE_PRE &&
3519 (html->styleEngine->style ()->whiteSpace == WHITE_SPACE_PRE ||
3520 html->styleEngine->style ()->whiteSpace == WHITE_SPACE_PRE_WRAP)) {
3521 S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_PRE;
3522 html->pre_column = 0;
3523 html->PreFirstChar = true;
3524 }
3525
3526 if (html->styleEngine->getId ())
3527 Html_add_anchor(html, html->styleEngine->getId ());
3528
3529 /* Request immediate close for elements with forbidden close tag. */
3530 /* TODO: XHTML always requires close tags. A simple implementation
3531 * of the commented clause below will make it work. */
3532 if (/* parsing HTML && */ Tags[ni].EndTag == 'F')
3533 html->ReqTagClose = true;
3534
3535 /* Don't break! Open tags may also close themselves */
3536
3537 default:
3538 /* Close function */
3539
3540 /* Test for </x>, ReqTagClose, <x /> and <x/> */
3541 if (*start == '/' || /* </x> */
3542 html->ReqTagClose || /* request */
3543 (tag[tagsize-2] == '/' && /* XML: */
3544 (strchr(" \"'", tag[tagsize-3]) || /* [ "']/> */
3545 (size_t)tagsize == strlen(Tags[ni].name) + 3))) { /* <x/> */
3546
3547 _MSG("Close: %s\n", Tags[ni].name);
3548 Html_tag_cleanup_at_close(html, ni);
3549 /* This was a close tag */
3550 html->ReqTagClose = false;
3551 }
3552 }
3553 }
3554
3555 /*
3556 * Get attribute value for 'attrname' and return it.
3557 * Tags start with '<' and end with a '>' (Ex: "<P align=center>")
3558 * tagsize = strlen(tag) from '<' to '>', inclusive.
3559 *
3560 * Returns one of the following:
3561 * * The value of the attribute.
3562 * * An empty string if the attribute exists but has no value.
3563 * * NULL if the attribute doesn't exist.
3564 */
3565 static const char *Html_get_attr2(DilloHtml *html,
3566 const char *tag,
3567 int tagsize,
3568 const char *attrname,
3569 int tag_parsing_flags)
3570 {
3571 int i, isocode, entsize, Found = 0, delimiter = 0, attr_pos = 0;
3572 Dstr *Buf = html->attr_data;
3573 DilloHtmlTagParsingState state = SEEK_ATTR_START;
3574
3575 dReturn_val_if_fail(*attrname, NULL);
3576
3577 dStr_truncate(Buf, 0);
3578
3579 for (i = 1; i < tagsize; ++i) {
3580 switch (state) {
3581 case SEEK_ATTR_START:
3582 if (isspace(tag[i]))
3583 state = SEEK_TOKEN_START;
3584 else if (tag[i] == '=')
3585 state = SEEK_VALUE_START;
3586 break;
3587
3588 case MATCH_ATTR_NAME:
3589 if ((Found = (!(attrname[attr_pos]) &&
3590 (tag[i] == '=' || isspace(tag[i]) || tag[i] == '>')))) {
3591 state = SEEK_TOKEN_START;
3592 --i;
3593 } else if (tolower(tag[i]) != tolower(attrname[attr_pos++]))
3594 state = SEEK_ATTR_START;
3595 break;
3596
3597 case SEEK_TOKEN_START:
3598 if (tag[i] == '=') {
3599 state = SEEK_VALUE_START;
3600 } else if (!isspace(tag[i])) {
3601 attr_pos = 0;
3602 state = (Found) ? FINISHED : MATCH_ATTR_NAME;
3603 --i;
3604 }
3605 break;
3606 case SEEK_VALUE_START:
3607 if (!isspace(tag[i])) {
3608 delimiter = (tag[i] == '"' || tag[i] == '\'') ? tag[i] : ' ';
3609 i -= (delimiter == ' ');
3610 state = (Found) ? GET_VALUE : SKIP_VALUE;
3611 }
3612 break;
3613
3614 case SKIP_VALUE:
3615 if ((delimiter == ' ' && isspace(tag[i])) || tag[i] == delimiter)
3616 state = SEEK_TOKEN_START;
3617 break;
3618 case GET_VALUE:
3619 if ((delimiter == ' ' && (isspace(tag[i]) || tag[i] == '>')) ||
3620 tag[i] == delimiter) {
3621 state = FINISHED;
3622 } else if (tag[i] == '&' &&
3623 (tag_parsing_flags & HTML_ParseEntities)) {
3624 if ((isocode = Html_parse_entity(html, tag+i,
3625 tagsize-i, &entsize)) >= 0) {
3626 if (isocode >= 128) {
3627 char buf[4];
3628 int k, n = a_Utf8_encode(isocode, buf);
3629 for (k = 0; k < n; ++k)
3630 dStr_append_c(Buf, buf[k]);
3631 } else {
3632 dStr_append_c(Buf, (char) isocode);
3633 }
3634 i += entsize-1;
3635 } else {
3636 dStr_append_c(Buf, tag[i]);
3637 }
3638 } else if (tag[i] == '\r' || tag[i] == '\t') {
3639 dStr_append_c(Buf, ' ');
3640 } else if (tag[i] == '\n') {
3641 /* ignore */
3642 } else {
3643 dStr_append_c(Buf, tag[i]);
3644 }
3645 break;
3646
3647 case FINISHED:
3648 i = tagsize;
3649 break;
3650 }
3651 }
3652
3653 if (tag_parsing_flags & HTML_LeftTrim)
3654 while (isspace(Buf->str[0]))
3655 dStr_erase(Buf, 0, 1);
3656 if (tag_parsing_flags & HTML_RightTrim)
3657 while (Buf->len && isspace(Buf->str[Buf->len - 1]))
3658 dStr_truncate(Buf, Buf->len - 1);
3659
3660 return (Found) ? Buf->str : NULL;
3661 }
3662
3663 /*
3664 * Call Html_get_attr2 telling it to parse entities and strip the result
3665 */
3666 const char *a_Html_get_attr(DilloHtml *html,
3667 const char *tag,
3668 int tagsize,
3669 const char *attrname)
3670 {
3671 return Html_get_attr2(html, tag, tagsize, attrname,
3672 HTML_LeftTrim | HTML_RightTrim | HTML_ParseEntities);
3673 }
3674
3675 /*
3676 * "a_Html_get_attr with default"
3677 * Call a_Html_get_attr() and dStrdup() the returned string.
3678 * If the attribute isn't found a copy of 'def' is returned.
3679 */
3680 char *a_Html_get_attr_wdef(DilloHtml *html,
3681 const char *tag,
3682 int tagsize,
3683 const char *attrname,
3684 const char *def)
3685 {
3686 const char *attrbuf = a_Html_get_attr(html, tag, tagsize, attrname);
3687
3688 return attrbuf ? dStrdup(attrbuf) : dStrdup(def);
3689 }
3690
3691 /*
3692 * Dispatch the apropriate function for 'Op'
3693 * This function is a Cache client and gets called whenever new data arrives
3694 * Op : operation to perform.
3695 * CbData : a pointer to a DilloHtml structure
3696 * Buf : a pointer to new data
3697 * BufSize : new data size (in bytes)
3698 */
3699 static void Html_callback(int Op, CacheClient_t *Client)
3700 {
3701 DilloHtml *html = (DilloHtml*)Client->CbData;
3702
3703 if (Op) { /* EOF */
3704 html->write((char*)Client->Buf, Client->BufSize, 1);
3705 html->finishParsing(Client->Key);
3706 } else {
3707 html->write((char*)Client->Buf, Client->BufSize, 0);
3708 }
3709 }
3710
3711 /*
3712 * Here's where we parse the html and put it into the Textblock structure.
3713 * Return value: number of bytes parsed
3714 */
3715 static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof)
3716 {
3717 char ch = 0, *p, *text;
3718 int token_start, buf_index;
3719
3720 /* Now, 'buf' and 'bufsize' define a buffer aligned to start at a token
3721 * boundary. Iterate through tokens until end of buffer is reached. */
3722 buf_index = 0;
3723 token_start = buf_index;
3724 while ((buf_index < bufsize) && !html->stop_parser) {
3725 /* invariant: buf_index == bufsize || token_start == buf_index */
3726
3727 if (S_TOP(html)->parse_mode ==
3728 DILLO_HTML_PARSE_MODE_VERBATIM) {
3729 /* Non HTML code here, let's skip until closing tag */
3730 do {
3731 const char *tag = Tags[S_TOP(html)->tag_idx].name;
3732 buf_index += strcspn(buf + buf_index, "<");
3733 if (buf_index + (int)strlen(tag) + 3 > bufsize) {
3734 buf_index = bufsize;
3735 } else if (strncmp(buf + buf_index, "</", 2) == 0 &&
3736 Html_match_tag(tag, buf+buf_index+2, strlen(tag)+1)) {
3737 /* copy VERBATIM text into the stash buffer */
3738 text = dStrndup(buf + token_start, buf_index - token_start);
3739 dStr_append(html->Stash, text);
3740 dFree(text);
3741 token_start = buf_index;
3742 break;
3743 } else
3744 ++buf_index;
3745 } while (buf_index < bufsize);
3746
3747 if (buf_index == bufsize)
3748 break;
3749 }
3750
3751 if (isspace(buf[buf_index])) {
3752 /* whitespace: group all available whitespace */
3753 while (++buf_index < bufsize && isspace(buf[buf_index])) ;
3754 Html_process_space(html, buf + token_start, buf_index - token_start);
3755 token_start = buf_index;
3756
3757 } else if (buf[buf_index] == '<' && (ch = buf[buf_index + 1]) &&
3758 (isalpha(ch) || strchr("/!?", ch)) ) {
3759 /* Tag */
3760 if (buf_index + 3 < bufsize && !strncmp(buf + buf_index, "<!--", 4)) {
3761 /* Comment: search for close of comment, skipping over
3762 * everything except a matching "-->" tag. */
3763 while ( (p = (char*) memchr(buf + buf_index, '>',
3764 bufsize - buf_index)) ){
3765 buf_index = p - buf + 1;
3766 if (p[-1] == '-' && p[-2] == '-') break;
3767 }
3768 if (p) {
3769 /* Got the whole comment. Let's throw it away! :) */
3770 token_start = buf_index;
3771 } else
3772 buf_index = bufsize;
3773 } else {
3774 /* Tag: search end of tag (skipping over quoted strings) */
3775 html->CurrTagOfs = html->Start_Ofs + token_start;
3776
3777 while ( buf_index < bufsize ) {
3778 buf_index++;
3779 buf_index += strcspn(buf + buf_index, ">\"'<");
3780 if ((ch = buf[buf_index]) == '>') {
3781 break;
3782 } else if (ch == '"' || ch == '\'') {
3783 /* Skip over quoted string */
3784 buf_index++;
3785 buf_index += strcspn(buf + buf_index,
3786 (ch == '"') ? "\">" : "'>");
3787 if (buf[buf_index] == '>') {
3788 /* Unterminated string value? Let's look ahead and test:
3789 * (<: unterminated, closing-quote: terminated) */
3790 int offset = buf_index + 1;
3791 offset += strcspn(buf + offset,
3792 (ch == '"') ? "\"<" : "'<");
3793 if (buf[offset] == ch || !buf[offset]) {
3794 buf_index = offset;
3795 } else {
3796 BUG_MSG("attribute lacks closing quote\n");
3797 break;
3798 }
3799 }
3800 } else if (ch == '<') {
3801 /* unterminated tag detected */
3802 p = dStrndup(buf+token_start+1,
3803 strcspn(buf+token_start+1, " <"));
3804 BUG_MSG("<%s> element lacks its closing '>'\n", p);
3805 dFree(p);
3806 --buf_index;
3807 break;
3808 }
3809 }
3810 if (buf_index < bufsize) {
3811 buf_index++;
3812 Html_process_tag(html, buf + token_start,
3813 buf_index - token_start);
3814 token_start = buf_index;
3815 }
3816 }
3817 } else {
3818 /* A Word: search for whitespace or tag open */
3819 while (++buf_index < bufsize) {
3820 buf_index += strcspn(buf + buf_index, " <\n\r\t\f\v");
3821 if (buf[buf_index] == '<' && (ch = buf[buf_index + 1]) &&
3822 !isalpha(ch) && !strchr("/!?", ch))
3823 continue;
3824 break;
3825 }
3826 if (buf_index < bufsize || Eof) {
3827 /* successfully found end of token */
3828 ch = buf[buf_index];
3829 buf[buf_index] = 0;
3830 Html_process_word(html, buf + token_start,
3831 buf_index - token_start);
3832 buf[buf_index] = ch;
3833 token_start = buf_index;
3834 }
3835 }
3836 }/*while*/
3837
3838 HT2TB(html)->flush ();
3839
3840 return token_start;
3841 }
3842
3843
+0
-246
src/html.h less more
0 #ifndef __HTML_H__
1 #define __HTML_H__
2
3 #include <gdk/gdk.h>
4 #include <gtk/gtkcontainer.h>
5
6 #include "browser.h" /* for BrowserWindow */
7 #include "dw_widget.h" /* for DwWidget */
8 #include "dw_image.h" /* for DwImageMapList */
9
10 #ifdef __cplusplus
11 extern "C" {
12 #endif /* __cplusplus */
13
14 /* First, the html linkblock. For now, this mostly has forms, although
15 pointers to actual links will go here soon, if for no other reason
16 than to implement history-sensitive link colors. Also, it seems
17 likely that imagemaps will go here. */
18
19 typedef struct _DilloHtmlLB DilloHtmlLB;
20
21 typedef struct _DilloHtml DilloHtml;
22 typedef struct _DilloHtmlClass DilloHtmlClass;
23 typedef struct _DilloHtmlState DilloHtmlState;
24 typedef struct _DilloHtmlForm DilloHtmlForm;
25 typedef struct _DilloHtmlOption DilloHtmlOption;
26 typedef struct _DilloHtmlSelect DilloHtmlSelect;
27 typedef struct _DilloHtmlInput DilloHtmlInput;
28
29
30 struct _DilloHtmlLB {
31 BrowserWindow *bw;
32 DilloUrl *base_url;
33
34 DilloHtmlForm *forms;
35 gint num_forms;
36 gint num_forms_max;
37
38 DilloUrl **links;
39 gint num_links;
40 gint num_links_max;
41
42 DwImageMapList maps;
43
44 gint32 link_color;
45 gint32 visited_color;
46
47 gint num_page_bugs;
48 GString *page_bugs;
49 };
50
51
52 typedef enum {
53 DT_NONE,
54 DT_HTML,
55 DT_XHTML
56 } DilloHtmlDocumentType;
57
58 typedef enum {
59 DILLO_HTML_PARSE_MODE_INIT,
60 DILLO_HTML_PARSE_MODE_STASH,
61 DILLO_HTML_PARSE_MODE_STASH_AND_BODY,
62 DILLO_HTML_PARSE_MODE_VERBATIM,
63 DILLO_HTML_PARSE_MODE_BODY,
64 DILLO_HTML_PARSE_MODE_PRE
65 } DilloHtmlParseMode;
66
67 typedef enum {
68 SEEK_ATTR_START,
69 MATCH_ATTR_NAME,
70 SEEK_TOKEN_START,
71 SEEK_VALUE_START,
72 SKIP_VALUE,
73 GET_VALUE,
74 FINISHED
75 } DilloHtmlTagParsingState;
76
77 typedef enum {
78 HTML_LeftTrim = 1 << 0,
79 HTML_RightTrim = 1 << 1,
80 HTML_ParseEntities = 1 << 2
81 } DilloHtmlTagParsingFlags;
82
83 typedef enum {
84 DILLO_HTML_TABLE_MODE_NONE, /* no table at all */
85 DILLO_HTML_TABLE_MODE_TOP, /* outside of <tr> */
86 DILLO_HTML_TABLE_MODE_TR, /* inside of <tr>, outside of <td> */
87 DILLO_HTML_TABLE_MODE_TD /* inside of <td> */
88 } DilloHtmlTableMode;
89
90
91 typedef enum {
92 IN_HTML = 1 << 0,
93 IN_HEAD = 1 << 1,
94 IN_BODY = 1 << 2,
95 IN_FORM = 1 << 3,
96 IN_SELECT = 1 << 4,
97 IN_TEXTAREA = 1 << 5,
98 IN_MAP = 1 << 6,
99 IN_PRE = 1 << 7,
100 IN_BUTTON = 1 << 8
101 } DilloHtmlProcessingState;
102
103
104 struct _DilloHtmlState {
105 char *tag_name;
106 DwStyle *style, *table_cell_style;
107 DilloHtmlParseMode parse_mode;
108 DilloHtmlTableMode table_mode;
109 gboolean cell_text_align_set;
110 enum { HTML_LIST_NONE, HTML_LIST_UNORDERED, HTML_LIST_ORDERED } list_type;
111 gint list_number;
112
113 /* TagInfo index for the tag that's being processed */
114 gint tag_idx;
115
116 DwWidget *page, *table;
117
118 /* This is used to align list items (especially in enumerated lists) */
119 DwWidget *ref_list_item;
120
121 /* This makes image processing faster than a function
122 a_Dw_widget_get_background_color. */
123 gint32 current_bg_color;
124
125 /* This is used for list items etc; if it is set to TRUE, breaks
126 have to be "handed over" (see Html_add_indented and
127 Html_eventually_pop_dw). */
128 gboolean hand_over_break;
129 };
130
131 typedef enum {
132 DILLO_HTML_METHOD_UNKNOWN,
133 DILLO_HTML_METHOD_GET,
134 DILLO_HTML_METHOD_POST
135 } DilloHtmlMethod;
136
137 typedef enum {
138 DILLO_HTML_ENC_URLENCODING
139 } DilloHtmlEnc;
140
141 struct _DilloHtmlForm {
142 DilloHtmlMethod method;
143 DilloUrl *action;
144 DilloHtmlEnc enc;
145
146 DilloHtmlInput *inputs;
147 gint num_inputs;
148 gint num_inputs_max;
149 gint num_entry_fields;
150 gint num_submit_buttons;
151 };
152
153 struct _DilloHtmlOption {
154 GtkWidget *menuitem;
155 char *value;
156 gboolean init_val;
157 };
158
159 struct _DilloHtmlSelect {
160 GtkWidget *menu;
161 gint size;
162
163 DilloHtmlOption *options;
164 gint num_options;
165 gint num_options_max;
166 };
167
168 typedef enum {
169 DILLO_HTML_INPUT_TEXT,
170 DILLO_HTML_INPUT_PASSWORD,
171 DILLO_HTML_INPUT_CHECKBOX,
172 DILLO_HTML_INPUT_RADIO,
173 DILLO_HTML_INPUT_IMAGE,
174 DILLO_HTML_INPUT_FILE,
175 DILLO_HTML_INPUT_BUTTON,
176 DILLO_HTML_INPUT_HIDDEN,
177 DILLO_HTML_INPUT_SUBMIT,
178 DILLO_HTML_INPUT_RESET,
179 DILLO_HTML_INPUT_BUTTON_SUBMIT,
180 DILLO_HTML_INPUT_BUTTON_RESET,
181 DILLO_HTML_INPUT_SELECT,
182 DILLO_HTML_INPUT_SEL_LIST,
183 DILLO_HTML_INPUT_TEXTAREA,
184 DILLO_HTML_INPUT_INDEX
185 } DilloHtmlInputType;
186
187 struct _DilloHtmlInput {
188 DilloHtmlInputType type;
189 void *widget; /* May be a GtkWidget or a DwWidget. */
190 char *name;
191 char *init_str; /* note: some overloading - for buttons, init_str
192 is simply the value of the button; for text
193 entries, it is the initial value */
194 DilloHtmlSelect *select;
195 gboolean init_val; /* only meaningful for buttons */
196 };
197
198 struct _DilloHtml {
199 DwWidget *dw; /* this is duplicated in the stack (page) */
200
201 DilloHtmlLB *linkblock;
202 gchar *Start_Buf;
203 size_t Start_Ofs;
204 size_t CurrTagOfs;
205 size_t OldTagOfs, OldTagLine;
206
207 DilloHtmlDocumentType DocType; /* as given by DOCTYPE tag */
208 float DocTypeVersion; /* HTML or XHTML version number */
209
210 DilloHtmlState *stack;
211 gint stack_top; /* Index to the top of the stack [0 based] */
212 gint stack_max;
213
214 DilloHtmlProcessingState InFlags; /* tracks which tags we are in */
215
216 GString *Stash;
217 gboolean StashSpace;
218
219 gchar *SPCBuf; /* Buffer for white space */
220
221 gint pre_column; /* current column, used in PRE tags with tabs */
222 gboolean PreFirstChar; /* used to skip the first CR or CRLF in PRE tags */
223 gboolean PrevWasCR; /* Flag to help parsing of "\r\n" in PRE tags */
224 gboolean PrevWasOpenTag; /* Flag to help deferred parsing of white space */
225 gboolean SPCPending; /* Flag to help deferred parsing of white space */
226 gboolean InVisitedLink; /* used to 'contrast_visited_colors' */
227 gboolean ReqTagClose; /* Flag to help handling bad-formed HTML */
228 gboolean CloseOneTag; /* Flag to help Html_tag_cleanup_at_close() */
229 gboolean TagSoup; /* Flag to enable the parser's cleanup functions */
230 gchar *NameVal; /* used for validation of "NAME" and "ID" in <A> */
231
232 /* element counters: used for validation purposes */
233 guchar Num_HTML, Num_HEAD, Num_BODY, Num_TITLE;
234
235 GString *attr_data;
236
237 BrowserWindow *bw;
238 };
239
240 #ifdef __cplusplus
241 }
242 #endif /* __cplusplus */
243
244
245 #endif /* __HTML_H__ */
0 #ifndef __HTML_HH__
1 #define __HTML_HH__
2
3 #include "url.h" // for DilloUrl
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 /*
10 * Exported functions
11 */
12 void a_Html_load_images(void *v_html, DilloUrl *pattern);
13 void a_Html_form_submit(void *v_html, void *v_form);
14 void a_Html_form_reset(void *v_html, void *v_form);
15 void a_Html_form_display_hiddens(void *v_html, void *v_form, bool_t display);
16
17 #ifdef __cplusplus
18 }
19 #endif /* __cplusplus */
20
21
22 #endif /* __HTML_HH__ */
0 #ifndef __HTML_COMMON_HH__
1 #define __HTML_COMMON_HH__
2
3 #include "url.h"
4 #include "bw.h"
5
6 #include "lout/misc.hh"
7 #include "dw/core.hh"
8 #include "dw/image.hh"
9 #include "dw/style.hh"
10
11 #include "image.hh"
12
13 #include "form.hh"
14
15 #include "styleengine.hh"
16
17 /*
18 * Macros
19 */
20
21 // "html struct" to Textblock
22 #define HT2TB(html) ((Textblock*)(html->dw))
23 // "html struct" to "Layout"
24 #define HT2LT(html) ((Layout*)html->bw->render_layout)
25 // "Image" to "Dw Widget"
26 #define IM2DW(Image) ((Widget*)Image->dw)
27 // Top of the parsing stack
28 #define S_TOP(html) (html->stack->getRef(html->stack->size()-1))
29
30 // Add a bug-meter message.
31 #define BUG_MSG(...) \
32 D_STMT_START { \
33 html->bugMessage(__VA_ARGS__); \
34 } D_STMT_END
35
36 /*
37 * Typedefs
38 */
39
40 typedef struct _DilloHtmlImage DilloHtmlImage;
41 typedef struct _DilloHtmlState DilloHtmlState;
42
43 typedef enum {
44 DT_NONE,
45 DT_HTML,
46 DT_XHTML
47 } DilloHtmlDocumentType;
48
49 typedef enum {
50 DILLO_HTML_PARSE_MODE_INIT = 0,
51 DILLO_HTML_PARSE_MODE_STASH,
52 DILLO_HTML_PARSE_MODE_STASH_AND_BODY,
53 DILLO_HTML_PARSE_MODE_VERBATIM,
54 DILLO_HTML_PARSE_MODE_BODY,
55 DILLO_HTML_PARSE_MODE_PRE
56 } DilloHtmlParseMode;
57
58 typedef enum {
59 DILLO_HTML_TABLE_MODE_NONE, /* no table at all */
60 DILLO_HTML_TABLE_MODE_TOP, /* outside of <tr> */
61 DILLO_HTML_TABLE_MODE_TR, /* inside of <tr>, outside of <td> */
62 DILLO_HTML_TABLE_MODE_TD /* inside of <td> */
63 } DilloHtmlTableMode;
64
65 typedef enum {
66 DILLO_HTML_TABLE_BORDER_SEPARATE,
67 DILLO_HTML_TABLE_BORDER_COLLAPSE
68 } DilloHtmlTableBorderMode;
69
70 typedef enum {
71 HTML_LIST_NONE,
72 HTML_LIST_UNORDERED,
73 HTML_LIST_ORDERED
74 } DilloHtmlListMode;
75
76 typedef enum {
77 IN_NONE = 0,
78 IN_HTML = 1 << 0,
79 IN_HEAD = 1 << 1,
80 IN_BODY = 1 << 2,
81 IN_FORM = 1 << 3,
82 IN_SELECT = 1 << 4,
83 IN_OPTION = 1 << 5,
84 IN_TEXTAREA = 1 << 6,
85 IN_MAP = 1 << 7,
86 IN_PRE = 1 << 8,
87 IN_BUTTON = 1 << 9,
88 IN_LI = 1 << 10,
89 } DilloHtmlProcessingState;
90
91 /*
92 * Data Structures
93 */
94
95 struct _DilloHtmlImage {
96 DilloUrl *url;
97 DilloImage *image;
98 };
99
100 struct _DilloHtmlState {
101 DilloHtmlParseMode parse_mode;
102 DilloHtmlTableMode table_mode;
103 DilloHtmlTableBorderMode table_border_mode;
104 bool cell_text_align_set;
105
106 DilloHtmlListMode list_type;
107 int list_number;
108
109 /* TagInfo index for the tag that's being processed */
110 int tag_idx;
111
112 dw::core::Widget *textblock, *table;
113
114 /* This is used to align list items (especially in enumerated lists) */
115 dw::core::Widget *ref_list_item;
116
117 /* This is used for list items etc; if it is set to TRUE, breaks
118 have to be "handed over" (see Html_add_indented and
119 Html_eventually_pop_dw). */
120 bool hand_over_break;
121 };
122
123 /*
124 * Classes
125 */
126
127 class DilloHtml {
128 private:
129 class HtmlLinkReceiver: public dw::core::Layout::LinkReceiver {
130 public:
131 DilloHtml *html;
132
133 bool enter (dw::core::Widget *widget, int link, int img, int x, int y);
134 bool press (dw::core::Widget *widget, int link, int img, int x, int y,
135 dw::core::EventButton *event);
136 bool click (dw::core::Widget *widget, int link, int img, int x, int y,
137 dw::core::EventButton *event);
138 };
139 HtmlLinkReceiver linkReceiver;
140
141 public: //BUG: for now everything is public
142
143 BrowserWindow *bw;
144 DilloUrl *page_url, *base_url;
145 dw::core::Widget *dw; /* this is duplicated in the stack */
146
147 /* -------------------------------------------------------------------*/
148 /* Variables required at parsing time */
149 /* -------------------------------------------------------------------*/
150 char *Start_Buf;
151 int Start_Ofs;
152 char *content_type, *charset;
153 bool stop_parser;
154
155 size_t CurrTagOfs;
156 size_t OldTagOfs, OldTagLine;
157
158 DilloHtmlDocumentType DocType; /* as given by DOCTYPE tag */
159 float DocTypeVersion; /* HTML or XHTML version number */
160
161 /* vector of remote CSS resources, as given by the LINK element */
162 lout::misc::SimpleVector<DilloUrl*> *cssUrls;
163
164 lout::misc::SimpleVector<DilloHtmlState> *stack;
165 StyleEngine *styleEngine;
166
167 int InFlags; /* tracks which elements we are in */
168
169 Dstr *Stash;
170 bool StashSpace;
171
172 int pre_column; /* current column, used in PRE tags with tabs */
173 bool PreFirstChar; /* used to skip the first CR or CRLF in PRE tags */
174 bool PrevWasCR; /* Flag to help parsing of "\r\n" in PRE tags */
175 bool PrevWasOpenTag; /* Flag to help deferred parsing of white space */
176 bool InVisitedLink; /* used to 'contrast_visited_colors' */
177 bool ReqTagClose; /* Flag to help handling bad-formed HTML */
178 bool TagSoup; /* Flag to enable the parser's cleanup functions */
179 bool loadCssFromStash; /* current stash content should be loaded as CSS */
180
181 /* element counters: used for validation purposes */
182 uchar_t Num_HTML, Num_HEAD, Num_BODY, Num_TITLE;
183
184 Dstr *attr_data; /* Buffer for attribute value */
185
186 int32_t non_css_link_color; /* as provided by link attribute in BODY */
187 int32_t non_css_visited_color; /* as provided by vlink attribute in BODY */
188 int32_t visited_color; /* as computed according to CSS */
189
190 /* -------------------------------------------------------------------*/
191 /* Variables required after parsing (for page functionality) */
192 /* -------------------------------------------------------------------*/
193 lout::misc::SimpleVector<DilloHtmlForm*> *forms;
194 lout::misc::SimpleVector<DilloHtmlInput*> *inputs_outside_form;
195 lout::misc::SimpleVector<DilloUrl*> *links;
196 lout::misc::SimpleVector<DilloHtmlImage*> *images;
197 dw::ImageMapsList maps;
198
199 private:
200 void freeParseData();
201 void initDw(); /* Used by the constructor */
202
203 public:
204 DilloHtml(BrowserWindow *bw, const DilloUrl *url, const char *content_type);
205 ~DilloHtml();
206 void bugMessage(const char *format, ... );
207 void connectSignals(dw::core::Widget *dw);
208 void write(char *Buf, int BufSize, int Eof);
209 int getCurTagLineNumber();
210 void finishParsing(int ClientKey);
211 int formNew(DilloHtmlMethod method, const DilloUrl *action,
212 DilloHtmlEnc enc, const char *charset);
213 DilloHtmlForm *getCurrentForm ();
214 bool_t unloadedImages();
215 void loadImages (const DilloUrl *pattern);
216 void addCssUrl(const DilloUrl *url);
217 };
218
219 /*
220 * Parser functions
221 */
222
223 int a_Html_tag_index(const char *tag);
224
225 const char *a_Html_get_attr(DilloHtml *html,
226 const char *tag,
227 int tagsize,
228 const char *attrname);
229
230 char *a_Html_get_attr_wdef(DilloHtml *html,
231 const char *tag,
232 int tagsize,
233 const char *attrname,
234 const char *def);
235
236 DilloUrl *a_Html_url_new(DilloHtml *html,
237 const char *url_str, const char *base_url,
238 int use_base_url);
239
240 DilloImage *a_Html_image_new(DilloHtml *html, const char *tag,
241 int tagsize, DilloUrl *url);
242
243 char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize);
244 void a_Html_pop_tag(DilloHtml *html, int TagIdx);
245 void a_Html_stash_init(DilloHtml *html);
246 int32_t a_Html_color_parse(DilloHtml *html,
247 const char *subtag, int32_t default_color);
248 dw::core::style::Length a_Html_parse_length (DilloHtml *html,
249 const char *attr);
250 void a_Html_tag_set_align_attr(DilloHtml *html, const char *tag, int tagsize);
251 bool a_Html_tag_set_valign_attr(DilloHtml *html,
252 const char *tag, int tagsize);
253
254 void a_Html_load_stylesheet(DilloHtml *html, DilloUrl *url);
255
256 #endif /* __HTML_COMMON_HH__ */
+0
-176
src/image.c less more
0 /*
1 * File: image.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 James McCollough <jamesm@gtwn.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * This file implements image data transfer methods. It handles the transfer
14 * of data from an Image to a DwImage widget.
15 */
16
17 #include <gtk/gtk.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 #include "msg.h"
22 #include "image.h"
23
24 /*
25 * Local data
26 */
27 static size_t linebuf_size = 0;
28 static guchar *linebuf = NULL;
29
30
31 /*
32 * Create and initialize a new image structure.
33 */
34 DilloImage *a_Image_new(gint width,
35 gint height,
36 const char *alt_text,
37 gint32 bg_color)
38 {
39 DilloImage *Image;
40
41 Image = g_new(DilloImage, 1);
42 Image->dw = (DwImage *) a_Dw_image_new(DW_IMAGE_RGB, alt_text);
43 Image->width = 0;
44 Image->height = 0;
45 Image->cmap = NULL;
46 Image->in_type = DILLO_IMG_TYPE_NOTSET;
47 Image->bg_color = bg_color;
48 Image->ProcessedBytes = 0;
49 Image->BitVec = NULL;
50 Image->State = IMG_Empty;
51
52 Image->RefCount = 1;
53
54 return Image;
55 }
56
57 /*
58 * Deallocate an Image structure
59 */
60 static void Image_free(DilloImage *Image)
61 {
62 a_Bitvec_free(Image->BitVec);
63 g_free(Image);
64 }
65
66 /*
67 * Unref and free if necessary
68 */
69 void a_Image_unref(DilloImage *Image)
70 {
71 _MSG(" %d ", Image->RefCount);
72 if (Image && --Image->RefCount == 0)
73 Image_free(Image);
74 }
75
76 /*
77 * Add a reference to an Image struct
78 */
79 void a_Image_ref(DilloImage *Image)
80 {
81 if (Image)
82 ++Image->RefCount;
83 }
84
85 /*
86 * Decode 'buf' (an image line) into RGB format.
87 */
88 static guchar *
89 Image_line(DilloImage *Image, const guchar *buf, const guchar *cmap, gint y)
90 {
91 guint x;
92
93 switch (Image->in_type) {
94 case DILLO_IMG_TYPE_INDEXED:
95 if (cmap) {
96 for (x = 0; x < Image->width; x++)
97 memcpy(linebuf + x * 3, cmap + buf[x] * 3, 3);
98 } else {
99 MSG("Gif:: WARNING, image lacks a color map\n");
100 }
101 break;
102 case DILLO_IMG_TYPE_GRAY:
103 for (x = 0; x < Image->width; x++)
104 memset(linebuf + x * 3, buf[x], 3);
105 break;
106 case DILLO_IMG_TYPE_RGB:
107 /* avoid a memcpy here! --Jcid */
108 return (guchar *)buf;
109 case DILLO_IMG_TYPE_NOTSET:
110 g_warning("ERROR: Image type not set...\n");
111 break;
112 }
113 return linebuf;
114 }
115
116 /*
117 * Set initial parameters of the image
118 */
119 void a_Image_set_parms(DilloImage *Image, guchar *EntryBuf, DilloUrl *url,
120 gint version, guint width, guint height,
121 DilloImgType type)
122 {
123 if ( !Image->dw->buffer )
124 a_Dw_image_set_buffer(Image->dw, EntryBuf, url, version);
125 if ( !Image->BitVec )
126 Image->BitVec = a_Bitvec_new(height);
127 Image->in_type = type;
128 Image->width = width;
129 Image->height = height;
130 if (3 * width > linebuf_size) {
131 linebuf_size = 3 * width;
132 linebuf = g_realloc(linebuf, linebuf_size);
133 }
134 Image->State = IMG_SetParms;
135
136 a_Dw_image_size(Image->dw, width, height);
137 }
138
139 /*
140 * Reference the dicache entry color map
141 */
142 void a_Image_set_cmap(DilloImage *Image, const guchar *cmap)
143 {
144 Image->cmap = cmap;
145 Image->State = IMG_SetCmap;
146 }
147
148 /*
149 * Implement the write method
150 */
151 void a_Image_write(DilloImage *Image, const guchar *buf, guint y, gint decode)
152 {
153 guchar *newbuf;
154
155 g_return_if_fail ( y < Image->height );
156
157 if (decode) {
158 /* Decode 'buf' and copy it into the DicEntry buffer */
159 newbuf = Image_line(Image, buf, Image->cmap, y);
160 memcpy(Image->dw->buffer + y*Image->width*3, newbuf, Image->width*3);
161 }
162 a_Bitvec_set_bit(Image->BitVec, y);
163 Image->State = IMG_Write;
164
165 /* Update the row in DwImage */
166 a_Dw_image_draw_row(Image->dw, Image->width, Image->height, 0, y);
167 }
168
169 /*
170 * Implement the close method
171 */
172 void a_Image_close(DilloImage *Image)
173 {
174 a_Image_unref(Image);
175 }
0 /*
1 * File: image.cc
2 *
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>,
4 * Sebastian Geerken <sgeerken@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * This file implements image data transfer methods. It handles the transfer
14 * of data from an Image to a DwImage widget.
15 */
16
17 #include "msg.h"
18
19 #include "image.hh"
20 #include "dw/core.hh"
21 #include "dw/image.hh"
22
23 using namespace dw::core;
24
25 // Image to Object-Image macro
26 #define I2DW(Image) ((dw::Image*)(Image->dw))
27
28
29 /*
30 * Create and initialize a new image structure.
31 */
32 DilloImage *a_Image_new(const char *alt_text, int32_t bg_color)
33 {
34 DilloImage *Image;
35
36 Image = dNew(DilloImage, 1);
37 Image->dw = (void*) new dw::Image(alt_text);
38 Image->width = 0;
39 Image->height = 0;
40 Image->bg_color = bg_color;
41 Image->ScanNumber = 0;
42 Image->BitVec = NULL;
43 Image->State = IMG_Empty;
44
45 Image->RefCount = 0;
46
47 return Image;
48 }
49
50 /*
51 * Deallocate an Image structure
52 */
53 static void Image_free(DilloImage *Image)
54 {
55 a_Bitvec_free(Image->BitVec);
56 dFree(Image);
57 }
58
59 /*
60 * Unref and free if necessary
61 * Do nothing if the argument is NULL
62 */
63 void a_Image_unref(DilloImage *Image)
64 {
65 _MSG(" %d ", Image->RefCount);
66 if (Image && --Image->RefCount == 0)
67 Image_free(Image);
68 }
69
70 /*
71 * Add a reference to an Image struct
72 * Do nothing if the argument is NULL
73 */
74 void a_Image_ref(DilloImage *Image)
75 {
76 if (Image)
77 ++Image->RefCount;
78 }
79
80 /*
81 * Set initial parameters of the image
82 */
83 void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
84 int version, uint_t width, uint_t height,
85 DilloImgType type)
86 {
87 _MSG("a_Image_set_parms: width=%d height=%d\n", width, height);
88
89 bool resize = (Image->width != width || Image->height != height);
90 I2DW(Image)->setBuffer((Imgbuf*)v_imgbuf, resize);
91
92 if (!Image->BitVec)
93 Image->BitVec = a_Bitvec_new(height);
94 Image->width = width;
95 Image->height = height;
96 Image->State = IMG_SetParms;
97 }
98
99 /*
100 * Implement the write method
101 */
102 void a_Image_write(DilloImage *Image, uint_t y)
103 {
104 _MSG("a_Image_write\n");
105 dReturn_if_fail ( y < Image->height );
106
107 /* Update the row in DwImage */
108 I2DW(Image)->drawRow(y);
109 a_Bitvec_set_bit(Image->BitVec, y);
110 Image->State = IMG_Write;
111 }
112
113 /*
114 * Implement the close method
115 */
116 void a_Image_close(DilloImage *Image)
117 {
118 _MSG("a_Image_close\n");
119 }
120
+0
-66
src/image.h less more
0 #ifndef __IMAGE_H__
1 #define __IMAGE_H__
2
3 /* The DilloImage data-structure and methods */
4
5 #include <gdk/gdk.h>
6 #include <gtk/gtk.h>
7 #include "dw_image.h"
8 #include "bitvec.h"
9
10
11 typedef struct _DilloImage DilloImage;
12
13 typedef enum {
14 DILLO_IMG_TYPE_INDEXED,
15 DILLO_IMG_TYPE_RGB,
16 DILLO_IMG_TYPE_GRAY,
17 DILLO_IMG_TYPE_NOTSET /* Initial value */
18 } DilloImgType;
19
20 /* These will reflect the Image's "state" */
21 typedef enum {
22 IMG_Empty, /* Just created the entry */
23 IMG_SetParms, /* Parameters set */
24 IMG_SetCmap, /* Color map set */
25 IMG_Write, /* Feeding the entry */
26 IMG_Close, /* Whole image got! */
27 IMG_Abort /* Image transfer aborted */
28 } ImageState;
29
30 struct _DilloImage {
31 DwImage *dw;
32
33 /* Parameters as told by image data */
34 guint width;
35 guint height;
36
37 const guchar *cmap; /* Color map (only for indexed) */
38 DilloImgType in_type; /* Image Type */
39 gint32 bg_color; /* Background color */
40
41 gint ProcessedBytes; /* Amount of bytes already decoded */
42 bitvec_t *BitVec; /* Bit vector for decoded rows */
43 ImageState State;
44
45 gint RefCount; /* Reference counter */
46 };
47
48
49 /*
50 * Function prototypes
51 */
52 DilloImage *a_Image_new(gint width, gint height,
53 const char *alt_text, gint32 bg_color);
54 void a_Image_ref(DilloImage *Image);
55 void a_Image_unref(DilloImage *Image);
56
57 void a_Image_set_parms(DilloImage *Image, guchar *EntryBuf, DilloUrl *url,
58 gint version, guint width, guint height,
59 DilloImgType type);
60 void a_Image_set_cmap(DilloImage *Image, const guchar *cmap);
61 void a_Image_write(DilloImage *Image, const guchar *buf, guint y, gint decode);
62 void a_Image_close(DilloImage *Image);
63
64 #endif /* __IMAGE_H__ */
65
0 #ifndef __IMAGE_HH__
1 #define __IMAGE_HH__
2
3 // The DilloImage data-structure and methods
4
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
9
10
11 #include "bitvec.h"
12 #include "url.h"
13
14 /*
15 * Defines
16 */
17
18 /* Arbitrary maximum for image size (to avoid image size-crafting attacks). */
19 #define IMAGE_MAX_AREA (6000 * 6000)
20
21 /*
22 * Types
23 */
24
25 typedef struct _DilloImage DilloImage;
26
27 typedef enum {
28 DILLO_IMG_TYPE_INDEXED,
29 DILLO_IMG_TYPE_RGB,
30 DILLO_IMG_TYPE_GRAY,
31 DILLO_IMG_TYPE_CMYK_INV,
32 DILLO_IMG_TYPE_NOTSET /* Initial value */
33 } DilloImgType;
34
35 /* These will reflect the Image's "state" */
36 typedef enum {
37 IMG_Empty, /* Just created the entry */
38 IMG_SetParms, /* Parameters set */
39 IMG_SetCmap, /* Color map set */
40 IMG_Write, /* Feeding the entry */
41 IMG_Close, /* Whole image got! */
42 IMG_Abort /* Image transfer aborted */
43 } ImageState;
44
45 struct _DilloImage {
46 void *dw;
47
48 /* Parameters as told by image data */
49 uint_t width;
50 uint_t height;
51
52 int32_t bg_color; /* Background color */
53 bitvec_t *BitVec; /* Bit vector for decoded rows */
54 uint_t ScanNumber; /* Current decoding scan */
55 ImageState State; /* Processing status */
56
57 int RefCount; /* Reference counter */
58 };
59
60
61 /*
62 * Function prototypes
63 */
64 DilloImage *a_Image_new(const char *alt_text, int32_t bg_color);
65 void a_Image_ref(DilloImage *Image);
66 void a_Image_unref(DilloImage *Image);
67
68 void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
69 int version, uint_t width, uint_t height,
70 DilloImgType type);
71 void a_Image_write(DilloImage *Image, uint_t y);
72 void a_Image_close(DilloImage *Image);
73
74
75 #ifdef __cplusplus
76 }
77 #endif /* __cplusplus */
78
79 #endif /* __IMAGE_HH__ */
80
0 /*
1 * File: imgbuf.cc
2 *
3 * Copyright (C) 2008 Jorge Arellano Cid <jcid@dillo.org>,
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "msg.h"
12 #include "imgbuf.hh"
13 #include "dw/core.hh"
14 #include "dw/image.hh"
15
16 using namespace dw::core;
17
18 /*
19 * Local data
20 */
21 static size_t linebuf_size = 0;
22 static uchar_t *linebuf = NULL;
23
24
25 /*
26 * Decode 'buf' (an image line) into RGB format.
27 */
28 static uchar_t *Imgbuf_rgb_line(const uchar_t *buf,
29 DilloImgType type, uchar_t *cmap,
30 uint_t width, uint_t y)
31 {
32 uint_t x;
33
34 switch (type) {
35 case DILLO_IMG_TYPE_INDEXED:
36 if (cmap) {
37 for (x = 0; x < width; x++)
38 memcpy(linebuf + x * 3, cmap + buf[x] * 3, 3);
39 } else {
40 MSG_WARN("Gif:: image lacks a color map\n");
41 }
42 break;
43 case DILLO_IMG_TYPE_GRAY:
44 for (x = 0; x < width; x++)
45 memset(linebuf + x * 3, buf[x], 3);
46 break;
47 case DILLO_IMG_TYPE_CMYK_INV:
48 /*
49 * We treat CMYK as if it were "RGBW", and it works. Everyone who is
50 * trying to handle CMYK jpegs is confused by this, and supposedly
51 * the issue is that Adobe CMYK is "wrong" but ubiquitous.
52 */
53 for (x = 0; x < width; x++) {
54 uint_t white = buf[x * 4 + 3];
55 linebuf[x * 3] = buf[x * 4] * white / 0x100;
56 linebuf[x * 3 + 1] = buf[x * 4 + 1] * white / 0x100;
57 linebuf[x * 3 + 2] = buf[x * 4 + 2] * white / 0x100;
58 }
59 break;
60 case DILLO_IMG_TYPE_RGB:
61 /* avoid a memcpy here! --Jcid */
62 return (uchar_t *)buf;
63 case DILLO_IMG_TYPE_NOTSET:
64 MSG_ERR("Imgbuf_rgb_line: type not set...\n");
65 break;
66 }
67 return linebuf;
68 }
69
70 // Wrappers for Imgbuf -------------------------------------------------------
71
72 /*
73 * Increment reference count for an Imgbuf
74 */
75 void a_Imgbuf_ref(void *v_imgbuf)
76 {
77 ((Imgbuf*)v_imgbuf)->ref();
78 }
79
80 /*
81 * Decrement reference count for an Imgbuf
82 */
83 void a_Imgbuf_unref(void *v_imgbuf)
84 {
85 if (v_imgbuf)
86 ((Imgbuf*)v_imgbuf)->unref();
87 }
88
89 /*
90 * Create a new Imgbuf
91 */
92 void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height)
93 {
94 Layout *layout = ((Widget*)v_dw)->getLayout();
95 if (!layout) {
96 MSG_ERR("a_Imgbuf_new: layout is NULL.\n");
97 exit(1);
98 }
99 // Assert linebuf is wide enough.
100 if (3 * width > linebuf_size) {
101 linebuf_size = 3 * width;
102 linebuf = (uchar_t*) dRealloc(linebuf, linebuf_size);
103 }
104
105 return (void*)layout->createImgbuf(Imgbuf::RGB, width, height);
106 }
107
108 /*
109 * Last reference for this Imgbuf?
110 */
111 int a_Imgbuf_last_reference(void *v_imgbuf)
112 {
113 return ((Imgbuf*)v_imgbuf)->lastReference () ? 1 : 0;
114 }
115
116 /*
117 * Update the root buffer of an imgbuf.
118 */
119 void a_Imgbuf_update(void *v_imgbuf, const uchar_t *buf, DilloImgType type,
120 uchar_t *cmap, uint_t width, uint_t height, uint_t y)
121
122 {
123 dReturn_if_fail ( y < height );
124
125 /* Decode 'buf' and copy it into the imgbuf */
126 uchar_t *newbuf = Imgbuf_rgb_line(buf, type, cmap, width, y);
127 ((Imgbuf*)v_imgbuf)->copyRow(y, (byte *)newbuf);
128 }
129
130 /*
131 * Reset for a new scan from a multiple-scan image.
132 */
133 void a_Imgbuf_new_scan(void *v_imgbuf)
134 {
135 ((Imgbuf*)v_imgbuf)->newScan();
136 }
137
0 #ifndef __IMGBUF_HH__
1 #define __IMGBUF_HH__
2
3 // Imgbuf wrappers
4
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
9
10
11 #include "image.hh"
12
13 /*
14 * Function prototypes
15 */
16 void a_Imgbuf_ref(void *v_imgbuf);
17 void a_Imgbuf_unref(void *v_imgbuf);
18 void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height);
19 int a_Imgbuf_last_reference(void *v_imgbuf);
20 void a_Imgbuf_update(void *v_imgbuf, const uchar_t *buf, DilloImgType type,
21 uchar_t *cmap, uint_t width, uint_t height, uint_t y);
22 void a_Imgbuf_new_scan(void *v_imgbuf);
23
24 #ifdef __cplusplus
25 }
26 #endif /* __cplusplus */
27
28 #endif /* __IMGBUF_HH__ */
29
+0
-2158
src/interface.c less more
0 /*
1 * File: interface.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 Sammy Mannaert <nstalkie@tvd.be>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #include <config.h>
13
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <gtk/gtk.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/time.h>
20 #include <fcntl.h>
21
22 #include "msg.h"
23 #include "list.h"
24 #include "misc.h"
25 #include "dillo.h"
26 #include "history.h"
27 #include "nav.h"
28 #include "IO/Url.h"
29 #include "IO/IO.h"
30 #include "interface.h"
31 #include "commands.h"
32 #include "menu.h"
33 #include "bookmark.h"
34 #include "prefs.h"
35 #include "url.h"
36 #include "capi.h"
37 #include "gtk_ext_button.h"
38
39 #include "dw_widget.h"
40 #include "dw_gtk_scrolled_window.h"
41 #include "dw_gtk_viewport.h"
42 #include "dw_gtk_statuslabel.h"
43 #include "dw_container.h"
44 #include "progressbar.h"
45
46 #include "pixmaps.h"
47 #include <gdk/gdkkeysyms.h>
48 #include "../dpip/dpip.h"
49
50 #define DEBUG_LEVEL 0
51 #include "debug.h"
52
53
54 /*
55 * Local Data
56 */
57 /* BrowserWindow holds all the widgets (and perhaps more)
58 * for each new_browser.*/
59 static BrowserWindow **browser_window;
60 static gint num_bw, num_bw_max;
61
62 /* We need only one of them. */
63 static GtkTooltips *tooltips = NULL;
64
65 /* open dialog last dir */
66 static gchar *open_dialog_last_dirname = NULL;
67
68 /* save dialog last dir */
69 static gchar *save_dialog_last_dirname = NULL;
70
71
72 /*
73 * Initialize global data
74 */
75 void a_Interface_init(void)
76 {
77 num_bw = 0;
78 num_bw_max = 16;
79 browser_window = NULL;
80 tooltips = gtk_tooltips_new ();
81 open_dialog_last_dirname = NULL;
82 save_dialog_last_dirname = NULL;
83 }
84
85 /*
86 * Stop all active connections in the browser window (except downloads)
87 */
88 void a_Interface_stop(BrowserWindow *bw)
89 {
90 DEBUG_MSG(3, "a_Interface_stop: hi!\n");
91
92 /* Remove root clients */
93 while ( bw->NumRootClients ) {
94 a_Cache_stop_client(bw->RootClients[0]);
95 a_List_remove(bw->RootClients, 0, bw->NumRootClients);
96 }
97 /* Remove image clients */
98 while ( bw->NumImageClients ) {
99 a_Cache_stop_client(bw->ImageClients[0]);
100 a_List_remove(bw->ImageClients, 0, bw->NumImageClients);
101 }
102 }
103
104 /*
105 * Empty RootClients, ImageClients and PageUrls lists and
106 * reset progress bar data.
107 */
108 void a_Interface_clean(BrowserWindow *bw)
109 {
110 g_return_if_fail ( bw != NULL );
111
112 while ( bw->NumRootClients )
113 a_List_remove(bw->RootClients, 0, bw->NumRootClients);
114
115 while ( bw->NumImageClients )
116 a_List_remove(bw->ImageClients, 0, bw->NumImageClients);
117
118 while ( bw->NumPageUrls ) {
119 a_Url_free(bw->PageUrls[0].Url);
120 a_List_remove(bw->PageUrls, 0, bw->NumPageUrls);
121 }
122
123 /* Zero image-progressbar data */
124 bw->NumImages = 0;
125 bw->NumImagesGot = 0;
126 }
127
128 /*=== Browser Window Interface Updating =====================================*/
129 /*
130 * Remove the cache-client from the bw list
131 * (client can be a image or a html page)
132 */
133 void a_Interface_remove_client(BrowserWindow *bw, gint ClientKey)
134 {
135 gint i;
136 gboolean Found = FALSE;
137
138 for ( i = 0; !Found && i < bw->NumRootClients; ++i)
139 if ( bw->RootClients[i] == ClientKey ) {
140 a_List_remove(bw->RootClients, i, bw->NumRootClients);
141 Found = TRUE;
142 }
143
144 for ( i = 0; !Found && i < bw->NumImageClients; ++i)
145 if ( bw->ImageClients[i] == ClientKey ) {
146 a_List_remove(bw->ImageClients, i, bw->NumImageClients);
147 bw->NumImagesGot++;
148 Found = TRUE;
149 }
150
151 a_Interface_set_button_sens(bw);
152 }
153
154 /*
155 * Remove the cache-client from the bw list
156 * (client can be a image or a html page)
157 */
158 void a_Interface_close_client(BrowserWindow *bw, gint ClientKey)
159 {
160 gchar numstr[32];
161
162 a_Interface_remove_client(bw, ClientKey);
163
164 /* --Progress bars stuff-- */
165 g_snprintf(numstr, 32, "%s%d of %d", PBAR_ISTR(prefs.panel_size == 1),
166 bw->NumImagesGot, bw->NumImages);
167 a_Progressbar_update(bw->imgprogress, numstr,
168 (bw->NumImagesGot == bw->NumImages) ? 0 : 1 );
169 }
170
171 /*
172 * Set the sensitivity on back/forw buttons and menu entries.
173 */
174 static gint Interface_sens_idle_func(BrowserWindow *bw)
175 {
176 gboolean back_sensitive, forw_sensitive, stop_sensitive;
177
178 /* Stop button */
179 stop_sensitive = (bw->NumRootClients > 0);
180 gtk_widget_set_sensitive(bw->stop_button, stop_sensitive);
181
182 /* Back and Forward buttons */
183 back_sensitive = a_Nav_stack_ptr(bw) > 0;
184 gtk_widget_set_sensitive(bw->back_button, back_sensitive);
185 forw_sensitive = (a_Nav_stack_ptr(bw) < a_Nav_stack_size(bw) - 1 &&
186 !bw->nav_expecting);
187 gtk_widget_set_sensitive(bw->forw_button, forw_sensitive);
188
189 bw->sens_idle_id = 0;
190 return FALSE;
191 }
192
193 /*
194 * Set the sensitivity on back/forw buttons and menu entries.
195 */
196 void a_Interface_set_button_sens(BrowserWindow *bw)
197 {
198 if (bw->sens_idle_id == 0)
199 bw->sens_idle_id = gtk_idle_add(
200 (GtkFunction)Interface_sens_idle_func, bw);
201 }
202
203 /*
204 * Add a reference to the cache-client in the browser window's list.
205 * This helps us keep track of which are active in the window so that it's
206 * possible to abort them.
207 * (Root: Flag, whether a Root URL or not)
208 */
209 void a_Interface_add_client(BrowserWindow *bw, gint Key, gint Root)
210 {
211 gint nc;
212 char numstr[32];
213
214 g_return_if_fail ( bw != NULL );
215
216 if ( Root ) {
217 nc = bw->NumRootClients;
218 a_List_add(bw->RootClients, nc, bw->MaxRootClients);
219 bw->RootClients[nc] = Key;
220 bw->NumRootClients++;
221 a_Interface_set_button_sens(bw);
222 } else {
223 nc = bw->NumImageClients;
224 a_List_add(bw->ImageClients, nc, bw->MaxImageClients);
225 bw->ImageClients[nc] = Key;
226 bw->NumImageClients++;
227 bw->NumImages++;
228 a_Interface_set_button_sens(bw);
229
230 /* --Progress bar stuff-- */
231 g_snprintf(numstr, 32, "%s%d of %d", PBAR_ISTR(prefs.panel_size == 1),
232 bw->NumImagesGot, bw->NumImages);
233 a_Progressbar_update(bw->imgprogress, numstr, 1);
234 }
235 }
236
237 /*
238 * Add an URL to the browser window's list.
239 * This helps us keep track of page requested URLs so that it's
240 * possible to stop, abort and reload them.)
241 * Flags: Chosen from {BW_Root, BW_Image, BW_Download}
242 */
243 void a_Interface_add_url(BrowserWindow *bw, const DilloUrl *Url, gint Flags)
244 {
245 gint nu, i;
246 gboolean found = FALSE;
247
248 g_return_if_fail ( bw != NULL && Url != NULL );
249
250 nu = bw->NumPageUrls;
251 for ( i = 0; i < nu; i++ ) {
252 if ( !a_Url_cmp(Url, bw->PageUrls[i].Url) ) {
253 found = TRUE;
254 break;
255 }
256 }
257 if ( !found ) {
258 a_List_add(bw->PageUrls, nu, bw->MaxPageUrls);
259 bw->PageUrls[nu].Url = a_Url_dup(Url);
260 bw->PageUrls[nu].Flags = Flags;
261 bw->NumPageUrls++;
262 }
263
264 /* test:
265 MSG("Urls:\n");
266 for (i = 0; i < bw->NumPageUrls; i++)
267 MSG("%s\n", bw->PageUrls[i].Url);
268 MSG("---\n");
269 */
270 }
271
272 /*
273 * Remove a single browser window. This includes all its open childs,
274 * freeing all resources associated with them, and exiting gtk
275 * if no browser windows are left.
276 */
277 static gboolean Interface_quit(GtkWidget *widget, BrowserWindow *bw)
278 {
279 gint i;
280
281 /* stop/abort open connections. */
282 a_Interface_stop(bw);
283
284 g_slist_free(bw->PanelHandles);
285
286 if (bw->open_dialog_window != NULL)
287 gtk_widget_destroy(bw->open_dialog_window);
288 if (bw->openfile_dialog_window != NULL)
289 gtk_widget_destroy(bw->openfile_dialog_window);
290 if (bw->quit_dialog_window != NULL)
291 gtk_widget_destroy(bw->quit_dialog_window);
292 if (bw->findtext_dialog_window != NULL)
293 gtk_widget_destroy(bw->findtext_dialog_window);
294 if (bw->search_dialog_window != NULL)
295 gtk_widget_destroy(bw->search_dialog_window);
296 if (bw->proxy_passwd_dialog_window != NULL)
297 gtk_widget_destroy(bw->proxy_passwd_dialog_window);
298 if (bw->question_dialog_window != NULL)
299 gtk_widget_destroy(bw->question_dialog_window);
300
301 if (bw->menu_popup.over_page)
302 gtk_widget_destroy(bw->menu_popup.over_page);
303 if (bw->menu_popup.over_link)
304 /* this also destroys menu_popup.over_image */
305 gtk_widget_destroy(bw->menu_popup.over_link);
306 if (bw->menu_popup.over_back)
307 gtk_widget_destroy(bw->menu_popup.over_back);
308 if (bw->menu_popup.over_forw)
309 gtk_widget_destroy(bw->menu_popup.over_forw);
310 if (bw->menu_popup.over_bug)
311 gtk_widget_destroy(bw->menu_popup.over_bug);
312 if (bw->menu_popup.url)
313 a_Url_free(bw->menu_popup.url);
314 if (bw->menu_popup.url2)
315 a_Url_free(bw->menu_popup.url2);
316
317 if (bw->sens_idle_id)
318 gtk_idle_remove(bw->sens_idle_id);
319
320 for (i = 0; i < num_bw; i++)
321 if (browser_window[i] == bw) {
322 browser_window[i] = browser_window[--num_bw];
323 break;
324 }
325
326 /* free nav_stack and nav_expect stuff */
327 a_Nav_free(bw);
328
329 g_free(bw->RootClients);
330 g_free(bw->ImageClients);
331
332 for (i = 0; i < bw->NumPageUrls; i++)
333 a_Url_free(bw->PageUrls[i].Url);
334 g_free(bw->PageUrls);
335 g_free(bw);
336
337 if (num_bw == 0)
338 gtk_main_quit();
339
340 return FALSE;
341 }
342
343
344 /*=== Browser Window Interface Construction =================================*/
345
346 /*
347 * Clear a text entry
348 */
349 static void Interface_entry_clear(GtkEntry *entry)
350 {
351 gtk_entry_set_text(entry, "");
352 gtk_widget_grab_focus(GTK_WIDGET(entry));
353 }
354
355 /*
356 * Get the selection into the clear url button.
357 * (cub = clear url button)
358 */
359 static void Interface_cub_get_selection(GtkWidget *widget, gpointer data)
360 {
361 /* Request the the primary selection as a string */
362 gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
363 GDK_SELECTION_TYPE_STRING,
364 GDK_CURRENT_TIME);
365 }
366
367 /*
368 * Receive the selection (from a paste event)
369 * (cub = clear url button)
370 */
371 static void Interface_cub_selection_received(GtkWidget *widget,
372 GtkSelectionData *selection_data,
373 guint32 time,
374 gpointer data)
375 {
376 BrowserWindow *bw = data;
377 gchar *damn_string;
378
379 _MSG("Interface_cub_selection_received:\n");
380
381 if (selection_data->length < 0) {
382 DEBUG_MSG (1, "Selection retrieval failed\n");
383 return;
384 }
385
386 damn_string = g_strndup((gchar *)selection_data->data,
387 selection_data->length);
388 gtk_entry_set_text(GTK_ENTRY(bw->location), damn_string);
389 gtk_widget_activate(GTK_WIDGET(bw->location));
390 g_free(damn_string);
391
392 return;
393 }
394
395 /*
396 * Create a pixmap and return it.
397 */
398 static GtkWidget *Interface_pixmap_new(GtkWidget *parent, gchar **data)
399 {
400 GtkWidget *pixmapwid;
401 GdkPixmap *pixmap;
402 GdkBitmap *mask;
403 GtkStyle *style;
404
405 style = gtk_widget_get_style(parent);
406
407 pixmap = gdk_pixmap_create_from_xpm_d(parent->window, &mask,
408 &style->bg[GTK_STATE_NORMAL], data);
409
410 pixmapwid = gtk_pixmap_new(pixmap, mask);
411
412 return (pixmapwid);
413 }
414
415 /*
416 * Create an extended button for the toolbar. "label_text" may be NULL.
417 * "icon_ret" and "label_ret " may be used to return the child widgets, but
418 * may also be 0.
419 */
420 static GtkWidget *Interface_toolbox_ext_button_new(GtkWidget *parent,
421 gchar *label_text,
422 gchar **image_data,
423 GtkWidget **icon_ret,
424 GtkWidget **label_ret)
425 {
426 GtkWidget *button, *pixmap, *vbox, *label;
427
428 button = a_Gtk_ext_button_new ();
429 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
430 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
431 pixmap = Interface_pixmap_new (parent, image_data);
432
433 if (label_text) {
434 vbox = gtk_vbox_new(FALSE, 0);
435 gtk_container_add(GTK_CONTAINER (button), vbox);
436 gtk_box_pack_start(GTK_BOX(vbox), pixmap, FALSE, FALSE, 0);
437 label = gtk_label_new(label_text);
438 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
439 } else {
440 gtk_container_add(GTK_CONTAINER (button), pixmap);
441 label = NULL;
442 }
443
444 gtk_widget_show_all (button);
445
446 if (icon_ret)
447 *icon_ret = pixmap;
448 if (label_ret)
449 *label_ret = label;
450
451 return button;
452 }
453
454 /*
455 * Set the bw's cursor type
456 */
457 void a_Interface_set_cursor(BrowserWindow *bw, GdkCursorType CursorType)
458 {
459 GdkCursor *cursor;
460
461 if ( bw->CursorType != CursorType ) {
462 cursor = gdk_cursor_new(CursorType);
463 gdk_window_set_cursor(bw->docwin->window, cursor);
464 gdk_cursor_destroy(cursor);
465 bw->CursorType = CursorType;
466 }
467 }
468
469 /*
470 * Connect button's "clicked" event with (key, key_mod) pair.
471 */
472 static void Interface_set_button_accel(GtkButton *button,
473 gint key,
474 gint key_mod,
475 GtkAccelGroup *accel_group)
476 {
477 gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel_group,
478 key, key_mod, GTK_ACCEL_LOCKED);
479 }
480
481 /*
482 * Create the "NEW" button, its location-entry and the search button.
483 */
484 static GtkWidget *Interface_locbar_new(BrowserWindow *bw)
485 {
486 GtkWidget *hbox, *toolbar_l, *toolbar_r;
487
488 hbox = gtk_hbox_new(FALSE, 0);
489
490 /* location entry */
491 bw->location = gtk_entry_new();
492 gtk_signal_connect(GTK_OBJECT(bw->location), "activate",
493 (GtkSignalFunc) a_Interface_entry_open_url, bw);
494 gtk_widget_add_accelerator(GTK_WIDGET(bw->location), "grab_focus",
495 bw->accel_group, GDK_u,
496 GDK_CONTROL_MASK, GTK_ACCEL_LOCKED);
497
498 /* left toolbar (Clear url) */
499 toolbar_l = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
500 gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar_l), GTK_RELIEF_NONE);
501 GTK_WIDGET_UNSET_FLAGS (toolbar_l, GTK_CAN_FOCUS);
502
503 bw->clear_url_button =
504 Interface_toolbox_ext_button_new(bw->main_window, NULL, s_new_xpm,
505 NULL, NULL);
506 a_Gtk_ext_button_set_command(GTK_EXT_BUTTON(bw->clear_url_button), 1);
507 a_Gtk_ext_button_set_command(GTK_EXT_BUTTON(bw->clear_url_button), 2);
508
509 gtk_toolbar_append_widget(GTK_TOOLBAR(toolbar_l), bw->clear_url_button,
510 "Clear the URL box (middle-click to paste an "
511 "URL).", "Toolbar/Clear");
512 gtk_signal_connect_object(GTK_OBJECT(bw->clear_url_button), "clicked",
513 GTK_SIGNAL_FUNC (Interface_entry_clear),
514 GTK_OBJECT(bw->location));
515 gtk_signal_connect_object(GTK_OBJECT(bw->clear_url_button), "clicked1",
516 GTK_SIGNAL_FUNC (Interface_entry_clear),
517 GTK_OBJECT(bw->location));
518 gtk_signal_connect(GTK_OBJECT(bw->clear_url_button), "clicked2",
519 GTK_SIGNAL_FUNC (Interface_cub_get_selection),
520 NULL);
521 gtk_signal_connect(GTK_OBJECT(bw->clear_url_button), "selection_received",
522 GTK_SIGNAL_FUNC (Interface_cub_selection_received),
523 bw);
524
525 /* right toolbar (Search) */
526 toolbar_r = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
527 gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar_r), GTK_RELIEF_NONE);
528 GTK_WIDGET_UNSET_FLAGS (toolbar_r, GTK_CAN_FOCUS);
529
530 bw->search_button = gtk_toolbar_append_item(
531 GTK_TOOLBAR(toolbar_r),
532 NULL, "Search the Web", "Toolbar/New",
533 Interface_pixmap_new(bw->main_window, search_xpm),
534 GTK_SIGNAL_FUNC(a_Interface_search_dialog),
535 bw);
536 gtk_widget_add_accelerator(GTK_WIDGET(bw->search_button), "clicked",
537 bw->accel_group, GDK_s,
538 GDK_CONTROL_MASK, GTK_ACCEL_LOCKED);
539 gtk_box_pack_start(GTK_BOX(hbox), toolbar_l, FALSE, FALSE, 0);
540 gtk_widget_show(toolbar_l);
541 gtk_box_pack_start(GTK_BOX(hbox), bw->location, TRUE, TRUE, 0);
542 gtk_widget_show(bw->location);
543 gtk_box_pack_start(GTK_BOX(hbox), toolbar_r, FALSE, FALSE, 0);
544 gtk_widget_show(toolbar_r);
545 gtk_widget_show(hbox);
546
547 return (hbox);
548 }
549
550 /*
551 * Create a new toolbar (Back, Forward, Home, Reload, Save and Stop buttons)
552 */
553 static GtkWidget *Interface_toolbar_new(BrowserWindow *bw, gint label)
554 {
555 GtkWidget *toolbar, *label_widget, *icon_widget;
556 GtkToolbarChild *toolbar_child;
557 gboolean s = prefs.small_icons;
558
559 toolbar = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
560 gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar), GTK_RELIEF_NONE);
561
562 /* back button */
563 bw->back_button =
564 Interface_toolbox_ext_button_new(bw->main_window, label ? "Back" : NULL,
565 s ? s_left_xpm : left_xpm,
566 &label_widget, &icon_widget);
567 gtk_toolbar_append_widget(GTK_TOOLBAR(toolbar), bw->back_button,
568 "Go to previous page (right-click for menu).",
569 "Toolbar/Back");
570 gtk_widget_set_sensitive(bw->back_button, FALSE);
571 Interface_set_button_accel(GTK_BUTTON(bw->back_button), GDK_comma,
572 0, bw->accel_group);
573 a_Gtk_ext_button_set_command(GTK_EXT_BUTTON(bw->back_button), 1);
574 a_Gtk_ext_button_attach_menu_creator(
575 GTK_EXT_BUTTON(bw->back_button), 3,
576 a_Commands_back_button_menu_creator_callback, (gpointer)bw);
577 gtk_signal_connect(GTK_OBJECT(bw->back_button), "clicked",
578 GTK_SIGNAL_FUNC(a_Commands_back_callback), (gpointer)bw);
579 gtk_signal_connect(GTK_OBJECT(bw->back_button), "clicked1",
580 GTK_SIGNAL_FUNC(a_Commands_back_callback), (gpointer)bw);
581
582 /*
583 * HACK: We have added GtkExtButton's as widgets
584 * (gtk_toolbar_append_widget), but they should behave like buttons etc.
585 * (gtk_toolbar_append_item), especially, they should have the same size.
586 * So we change some parts of the internal representation.
587 */
588 toolbar_child = ((GtkToolbarChild*)GTK_TOOLBAR(toolbar)->children->data);
589 toolbar_child->type = GTK_TOOLBAR_CHILD_BUTTON;
590 toolbar_child->widget = bw->back_button;
591 toolbar_child->icon = icon_widget;
592 toolbar_child->label = label_widget;
593
594 /* forward button */
595 bw->forw_button =
596 Interface_toolbox_ext_button_new(bw->main_window,
597 label ? "Forward" : NULL,
598 s ? s_right_xpm : right_xpm,
599 &label_widget, &icon_widget);
600 gtk_toolbar_append_widget(GTK_TOOLBAR(toolbar), bw->forw_button,
601 "Go to next page (right-click for menu).",
602 "Toolbar/Forward");
603 gtk_widget_set_sensitive(bw->forw_button, FALSE);
604 Interface_set_button_accel(GTK_BUTTON(bw->forw_button), GDK_period,
605 0, bw->accel_group);
606 a_Gtk_ext_button_set_command(GTK_EXT_BUTTON(bw->forw_button), 1);
607 a_Gtk_ext_button_attach_menu_creator(
608 GTK_EXT_BUTTON(bw->forw_button), 3,
609 a_Commands_forw_button_menu_creator_callback, (gpointer)bw);
610 gtk_signal_connect(GTK_OBJECT(bw->forw_button), "clicked",
611 GTK_SIGNAL_FUNC(a_Commands_forw_callback), (gpointer)bw);
612 gtk_signal_connect(GTK_OBJECT(bw->forw_button), "clicked1",
613 GTK_SIGNAL_FUNC(a_Commands_forw_callback), (gpointer)bw);
614
615 /*
616 * HACK: See above.
617 */
618 toolbar_child =
619 ((GtkToolbarChild*)GTK_TOOLBAR(toolbar)->children->next->data);
620 toolbar_child->type = GTK_TOOLBAR_CHILD_BUTTON;
621 toolbar_child->widget = bw->forw_button;
622 toolbar_child->icon = icon_widget;
623 toolbar_child->label = label_widget;
624
625 /* home button */
626 bw->home_button = gtk_toolbar_append_item(
627 GTK_TOOLBAR(toolbar),
628 label ? "Home" : NULL,
629 "Go to the Home page", "Toolbar/Home",
630 Interface_pixmap_new(bw->main_window,
631 s ? s_home_xpm : home_xpm),
632 (GtkSignalFunc) a_Commands_home_callback, bw);
633 /*
634 * SG: This had never any effect, was it there for future extensions?
635 * gtk_signal_connect(GTK_OBJECT(bw->home_button), "button-press-event",
636 * GTK_SIGNAL_FUNC(a_Commands_navpress_callback), bw);
637 */
638
639 /* reload button */
640 bw->reload_button = gtk_toolbar_append_item(
641 GTK_TOOLBAR(toolbar),
642 label ? "Reload" : NULL,
643 "Reload this page", "Toolbar/Reload",
644 Interface_pixmap_new(bw->main_window,
645 s ? s_reload_xpm : reload_xpm),
646 (GtkSignalFunc) a_Commands_reload_callback, bw);
647 Interface_set_button_accel(GTK_BUTTON(bw->reload_button), GDK_r,
648 GDK_CONTROL_MASK, bw->accel_group);
649
650 /* save button */
651 bw->save_button = gtk_toolbar_append_item(
652 GTK_TOOLBAR(toolbar),
653 label ? "Save" : NULL,
654 "Save this page", "Toolbar/Save",
655 Interface_pixmap_new(bw->main_window,
656 s ? s_save_xpm : save_xpm),
657 (GtkSignalFunc) a_Commands_save_callback, bw);
658 /* stop button */
659 bw->stop_button = gtk_toolbar_append_item(
660 GTK_TOOLBAR(toolbar),
661 label ? "Stop" : NULL,
662 "Stop the current transfer", "Toolbar/Stop",
663 Interface_pixmap_new(bw->main_window,
664 s ? s_stop_xpm : stop_xpm),
665 (GtkSignalFunc) a_Commands_stop_callback, bw);
666 gtk_widget_set_sensitive(bw->stop_button, FALSE);
667
668 /* bookmarks button */
669 bw->bookmarks_button = gtk_toolbar_append_item(
670 GTK_TOOLBAR(toolbar),
671 label ? "Book" : NULL,
672 "View bookmarks", "Toolbar/Bookmarks",
673 Interface_pixmap_new(bw->main_window,
674 s ? s_bm_xpm : bm_xpm),
675 (GtkSignalFunc) a_Commands_viewbm_callback, bw);
676 gtk_widget_set_sensitive(bw->bookmarks_button, TRUE);
677 Interface_set_button_accel(GTK_BUTTON(bw->bookmarks_button), GDK_semicolon,
678 0, bw->accel_group);
679 Interface_set_button_accel(GTK_BUTTON(bw->bookmarks_button), GDK_b,
680 GDK_CONTROL_MASK, bw->accel_group);
681 /*
682 * SG: This had never any effect, was it there for future extensions?
683 * gtk_signal_connect(GTK_OBJECT(bw->bookmarks_button),
684 * "button-press-event",
685 * GTK_SIGNAL_FUNC(a_Commands_navpress_callback), bw);
686 */
687
688 gtk_widget_show(toolbar);
689 return toolbar;
690 }
691
692 /*
693 * Create the progressbar's box
694 */
695 static GtkWidget *Interface_progressbox_new(BrowserWindow *bw, gint vertical)
696 {
697 GtkWidget *progbox;
698
699 progbox = vertical ? gtk_vbox_new(FALSE, 0) : gtk_hbox_new(FALSE, 0);
700 bw->progress_box = progbox;
701 bw->imgprogress = a_Progressbar_new();
702 bw->progress = a_Progressbar_new();
703 gtk_box_pack_start(GTK_BOX(progbox), bw->imgprogress, TRUE, TRUE, 0);
704 gtk_widget_show(bw->imgprogress);
705 gtk_box_pack_start(GTK_BOX(progbox), bw->progress, TRUE, TRUE, 0);
706 gtk_widget_show(bw->progress);
707 return (progbox);
708 }
709
710 /*
711 * Hide/Unhide this bw's control panels.
712 * toggle: Flag [toggle or set].
713 */
714 static void Interface_toggle_panel(BrowserWindow *bw, gint toggle)
715 {
716 if (toggle)
717 bw->fullwindow = !bw->fullwindow;
718
719 if (bw->fullwindow) {
720 g_slist_foreach(bw->PanelHandles, (GFunc)gtk_widget_hide, NULL);
721 gtk_widget_hide(bw->status_box);
722 gtk_widget_show (bw->full_screen_off_button);
723 gtk_widget_grab_focus(GTK_BIN(bw->docwin)->child);
724 } else {
725 g_slist_foreach(bw->PanelHandles, (GFunc)gtk_widget_show, NULL);
726 gtk_widget_show(bw->status_box);
727 gtk_widget_hide (bw->full_screen_off_button);
728 }
729 }
730
731 /*
732 * Customize the appearance of the bw.
733 */
734 static void Interface_browser_window_customize(BrowserWindow *bw)
735 {
736 if ( !prefs.show_back )
737 gtk_widget_hide(bw->back_button);
738 if ( !prefs.show_forw )
739 gtk_widget_hide(bw->forw_button);
740 if ( !prefs.show_home )
741 gtk_widget_hide(bw->home_button);
742 if ( !prefs.show_reload )
743 gtk_widget_hide(bw->reload_button);
744 if ( !prefs.show_save )
745 gtk_widget_hide(bw->save_button);
746 if ( !prefs.show_stop )
747 gtk_widget_hide(bw->stop_button);
748 if ( !prefs.show_bookmarks )
749 gtk_widget_hide(bw->bookmarks_button);
750 if ( !prefs.show_menubar )
751 gtk_widget_hide(bw->menubar);
752 if ( !prefs.show_clear_url)
753 gtk_widget_hide(bw->clear_url_button);
754 if ( !prefs.show_url )
755 gtk_widget_hide(bw->location);
756 if ( !prefs.show_search )
757 gtk_widget_hide(bw->search_button);
758 if ( !prefs.show_progress_box )
759 gtk_widget_hide(bw->progress_box);
760
761 bw->fullwindow = prefs.fullwindow_start;
762 Interface_toggle_panel(bw, FALSE);
763 }
764
765 static void Interface_full_screen_callback (BrowserWindow *bw)
766 {
767 Interface_toggle_panel(bw, TRUE);
768 }
769
770 /*
771 * Handler for double-mouse-clicks that don't belong to the viewport.
772 */
773 static gint Interface_click_callback(BrowserWindow *bw, GdkEventButton *event)
774 {
775 if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
776 Interface_toggle_panel(bw, TRUE);
777 return TRUE;
778 }
779
780 /*
781 * Handler for key presses that don't belong to the viewport.
782 * (Used to customize the interface a bit)
783 */
784 static void Interface_key_press_handler(GtkWidget *widget,
785 GdkEventKey *event,
786 gpointer client_data)
787 {
788 BrowserWindow *bw = client_data;
789
790 switch (event->keyval) {
791 case GDK_BackSpace:
792 /* This key is handled here because GTK accel group ignores it */
793 if (event->state & GDK_SHIFT_MASK)
794 a_Commands_forw_callback(NULL, bw);
795 else
796 a_Commands_back_callback(NULL, bw);
797 break;
798 case GDK_slash:
799 /* This key is handled here because GTK accel group ignores it */
800 a_Commands_findtext_callback(NULL, bw);
801 break;
802 default:
803 _MSG(">> Key pressed!\n");
804 break;
805 }
806 }
807
808 /*
809 * Add the button for switching into fullscreen mode
810 */
811 static void Interface_add_full_screen_button (BrowserWindow *bw, GtkBox *box)
812 {
813 /* The button is put into a vbox, so that it is not enlarged vertically. */
814 GtkWidget *vbox, *dummy, *button, *pixmap;
815
816 vbox = gtk_vbox_new (FALSE, 0);
817 gtk_box_pack_start (GTK_BOX (box), vbox, FALSE, FALSE, 0);
818 gtk_widget_show (vbox);
819
820 /* The dummy will make the button align at the bottom.
821 * (Important only when using large text fonts.) */
822 dummy = gtk_vbox_new (FALSE, 0);
823 gtk_box_pack_start (GTK_BOX (vbox), dummy, TRUE, TRUE, 0);
824 gtk_widget_show (dummy);
825
826 button = gtk_button_new ();
827 gtk_tooltips_set_tip (tooltips, button, "Hide Controls", "Show Controls");
828 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
829 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
830 gtk_widget_show (button);
831
832 pixmap = Interface_pixmap_new(bw->main_window, full_screen_on_xpm);
833 gtk_container_add (GTK_CONTAINER (button), pixmap);
834 gtk_widget_show (pixmap);
835
836 gtk_signal_connect_object
837 (GTK_OBJECT (button), "clicked",
838 GTK_SIGNAL_FUNC (Interface_full_screen_callback), (gpointer)bw);
839 }
840
841 /*
842 * Create a new browser window and return it.
843 * (the new window is stored in browser_window[])
844 */
845 BrowserWindow *
846 a_Interface_browser_window_new(gint width, gint height, guint32 xid)
847 {
848 GtkWidget *box1, *hbox, *button, *label,
849 *progbox, *toolbar, *handlebox, *menubar, *locbox, *pixmap;
850 BrowserWindow *bw;
851 char buf[64];
852
853 /* We use g_new0() to zero the memory */
854 bw = g_new0(BrowserWindow, 1);
855 a_List_add(browser_window, num_bw, num_bw_max);
856 browser_window[num_bw++] = bw;
857
858 /* initialize nav_stack struct in browser_window struct */
859 a_Nav_init(bw);
860
861 if (!xid)
862 bw->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
863 else
864 bw->main_window = gtk_plug_new(xid);
865
866 gtk_window_set_policy(GTK_WINDOW(bw->main_window), TRUE, TRUE, FALSE);
867 gtk_signal_connect(GTK_OBJECT(bw->main_window), "delete_event",
868 GTK_SIGNAL_FUNC(gtk_object_destroy), bw);
869 gtk_signal_connect(GTK_OBJECT(bw->main_window), "destroy",
870 GTK_SIGNAL_FUNC(Interface_quit), bw);
871 gtk_container_border_width(GTK_CONTAINER(bw->main_window), 0);
872
873 gtk_window_set_wmclass(GTK_WINDOW(bw->main_window), "dillo", "Dillo");
874
875 /* -RL :: I must realize the window to see it correctly */
876 gtk_widget_realize(bw->main_window);
877
878 /* Create and attach an accel group to the main window */
879 bw->accel_group = gtk_accel_group_new();
880 gtk_window_add_accel_group(GTK_WINDOW(bw->main_window), bw->accel_group);
881
882 /* set window title */
883 g_snprintf(buf, 64, "Version %s", VERSION);
884 a_Interface_set_page_title(bw, buf);
885
886 box1 = gtk_vbox_new(FALSE, 0);
887
888 /* setup the control panel */
889 if (prefs.panel_size == 1) {
890 handlebox = gtk_handle_box_new();
891 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
892 hbox = gtk_hbox_new(FALSE, 0);
893 /* Control Buttons */
894 toolbar = Interface_toolbar_new(bw, 0);
895 /* Menus */
896 menubar = a_Menu_mainbar_new(bw, 1);
897 /* Location entry */
898 locbox = Interface_locbar_new(bw);
899 /* progress bars */
900 progbox = Interface_progressbox_new(bw, 0);
901
902 gtk_box_pack_start(GTK_BOX(hbox), toolbar, FALSE, FALSE, 0);
903 gtk_widget_show(toolbar);
904 gtk_box_pack_start(GTK_BOX(hbox), menubar, FALSE, FALSE, 0);
905 gtk_widget_show(menubar);
906 gtk_box_pack_start(GTK_BOX(hbox), locbox, TRUE, TRUE, 0);
907 gtk_widget_show(locbox);
908 gtk_box_pack_start(GTK_BOX(hbox), progbox, FALSE, FALSE, 0);
909 gtk_widget_show(progbox);
910 gtk_container_add(GTK_CONTAINER(handlebox), hbox);
911 gtk_widget_show(hbox);
912 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
913 gtk_widget_show(handlebox);
914
915 } else if (prefs.panel_size == 2 || prefs.panel_size == 3) {
916 handlebox = gtk_handle_box_new();
917 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
918 hbox = gtk_hbox_new(FALSE, 0);
919 menubar = a_Menu_mainbar_new(bw, 0);
920 locbox = Interface_locbar_new(bw);
921 gtk_box_pack_start(GTK_BOX(hbox), menubar, FALSE, FALSE, 0);
922 gtk_widget_show(menubar);
923 gtk_box_pack_start(GTK_BOX(hbox), locbox, TRUE, TRUE, 0);
924 gtk_widget_show(locbox);
925 gtk_container_add(GTK_CONTAINER(handlebox), hbox);
926 gtk_widget_show(hbox);
927 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
928 gtk_widget_show(handlebox);
929
930 handlebox = gtk_handle_box_new();
931 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
932 gtk_container_border_width(GTK_CONTAINER(handlebox), 4);
933 hbox = gtk_hbox_new(FALSE, 0);
934 toolbar = Interface_toolbar_new(bw, (prefs.panel_size == 3));
935 progbox = Interface_progressbox_new(bw, (prefs.panel_size == 3));
936 gtk_box_pack_start(GTK_BOX(hbox), toolbar, TRUE, TRUE, 0);
937 gtk_widget_show(toolbar);
938 gtk_box_pack_start(GTK_BOX(hbox), progbox, FALSE, FALSE, 0);
939 gtk_widget_show(progbox);
940 gtk_container_add(GTK_CONTAINER(handlebox), hbox);
941 gtk_widget_show(hbox);
942 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
943 gtk_widget_show(handlebox);
944
945 } else {
946 handlebox = gtk_handle_box_new();
947 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
948 menubar = a_Menu_mainbar_new(bw, 0);
949 gtk_container_add(GTK_CONTAINER(handlebox), menubar);
950 gtk_widget_show(menubar);
951 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
952 gtk_widget_show(handlebox);
953
954 handlebox = gtk_handle_box_new();
955 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
956 gtk_container_border_width(GTK_CONTAINER(handlebox), 4);
957 hbox = gtk_hbox_new(FALSE, 0);
958 toolbar = Interface_toolbar_new(bw, 1);
959 progbox = Interface_progressbox_new(bw, 1);
960 gtk_box_pack_start(GTK_BOX(hbox), toolbar, TRUE, TRUE, 0);
961 gtk_widget_show(toolbar);
962 gtk_box_pack_start(GTK_BOX(hbox), progbox, FALSE, FALSE, 0);
963 gtk_widget_show(progbox);
964 gtk_container_add(GTK_CONTAINER(handlebox), hbox);
965 gtk_widget_show(hbox);
966 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
967 gtk_widget_show(handlebox);
968
969 handlebox = gtk_handle_box_new();
970 bw->PanelHandles = g_slist_append(bw->PanelHandles, handlebox);
971 locbox = Interface_locbar_new(bw);
972 gtk_container_add(GTK_CONTAINER(handlebox), locbox);
973 gtk_widget_show(locbox);
974 gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
975 gtk_widget_show(handlebox);
976 }
977
978 /* Add box1 */
979 gtk_container_add(GTK_CONTAINER(bw->main_window), box1);
980
981 /* Now the main document window */
982 bw->docwin = a_Dw_gtk_scrolled_window_new();
983 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(bw->docwin),
984 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
985 gtk_box_pack_start(GTK_BOX(box1), bw->docwin, TRUE, TRUE, 0);
986 gtk_widget_show(bw->docwin);
987
988 /* todo (GTK2): this call is a bit of a workaround to missing focus
989 * between the location box and the docwin. It's fixed in GTK2:
990 * http://mail.gnome.org/archives/gtk-list/2003-June/msg00307.html */
991 gtk_widget_grab_focus(GTK_BIN(bw->docwin)->child);
992
993 /* Full screen mode via double click is done in two ways: First,
994 * a feature of the selection is used, since in complex pages,
995 * getting events back to the viewport is quite difficult. Second,
996 * a simple callback, called e.g. when viewing image resources. */
997 a_Selection_set_dclick_callback(
998 GTK_DW_VIEWPORT(GTK_BIN(GTK_BIN(bw->docwin)->child)->child)->selection,
999 (void(*)(gpointer))Interface_full_screen_callback, bw);
1000 /* Selection requires an owner widget */
1001 a_Selection_set_owner(
1002 GTK_DW_VIEWPORT(GTK_BIN(GTK_BIN(bw->docwin)->child)->child)->selection,
1003 GTK_BIN(bw->docwin)->child);
1004
1005 gtk_signal_connect_object_after(GTK_OBJECT(GTK_BIN(bw->docwin)->child),
1006 "button_press_event",
1007 GTK_SIGNAL_FUNC(Interface_click_callback),
1008 (gpointer)bw);
1009
1010 /* full screen button: actually there're two buttons, one in the scrolled
1011 * window (fs off) and one in the status bar (fs on). They look as one. */
1012 bw->full_screen_off_button = gtk_button_new ();
1013 gtk_tooltips_set_tip (tooltips, bw->full_screen_off_button,
1014 "Show Controls", "Hide Controls");
1015 GTK_WIDGET_UNSET_FLAGS (bw->full_screen_off_button, GTK_CAN_FOCUS);
1016 a_Dw_gtk_scrolled_window_add_gadget (GTK_DW_SCROLLED_WINDOW (bw->docwin),
1017 bw->full_screen_off_button);
1018 pixmap = Interface_pixmap_new(bw->main_window, full_screen_off_xpm);
1019 gtk_container_add (GTK_CONTAINER (bw->full_screen_off_button), pixmap);
1020 gtk_widget_show (pixmap);
1021 gtk_signal_connect_object
1022 (GTK_OBJECT (bw->full_screen_off_button), "clicked",
1023 GTK_SIGNAL_FUNC (Interface_full_screen_callback), (gpointer)bw);
1024 Interface_set_button_accel(GTK_BUTTON(bw->full_screen_off_button), GDK_h,
1025 GDK_CONTROL_MASK, bw->accel_group);
1026
1027 /* Catch key_press event */
1028 gtk_signal_connect(GTK_OBJECT(GTK_BIN(bw->docwin)->child),
1029 "key_press_event",
1030 GTK_SIGNAL_FUNC(Interface_key_press_handler), bw);
1031
1032 gtk_widget_set_usize(bw->main_window, width, height);
1033
1034 /* status widget */
1035 /* create the over-bug-meter menu */
1036 bw->menu_popup.over_bug = a_Menu_popup_ob_new(bw);
1037
1038 bw->status = a_Dw_gtk_statuslabel_new("");
1039 gtk_misc_set_alignment(GTK_MISC(bw->status), 0.0, 0.5);
1040 /* status widget for HTML errors.
1041 * Note: the "clicked" signal is hooked with the linkblock later */
1042 button = bw->status_bug_meter = a_Gtk_ext_button_new();
1043 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
1044 a_Gtk_ext_button_set_command (GTK_EXT_BUTTON (button), 1);
1045 a_Gtk_ext_button_attach_menu (GTK_EXT_BUTTON (button), 3,
1046 GTK_MENU(bw->menu_popup.over_bug));
1047
1048 hbox = gtk_hbox_new(FALSE, 0);
1049 pixmap = Interface_pixmap_new(bw->main_window, mini_bug_xpm);
1050 gtk_object_set_data(GTK_OBJECT (button), "bug", pixmap);
1051 gtk_box_pack_start(GTK_BOX(hbox), pixmap, FALSE, FALSE, 0);
1052 pixmap = Interface_pixmap_new(bw->main_window, mini_ok_xpm);
1053 gtk_object_set_data(GTK_OBJECT (button), "ok", pixmap);
1054 gtk_box_pack_start(GTK_BOX(hbox), pixmap, FALSE, FALSE, 0);
1055 gtk_widget_show (pixmap);
1056 label = gtk_label_new("");
1057 gtk_object_set_data(GTK_OBJECT (button), "label", label);
1058 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1059 gtk_container_add (GTK_CONTAINER (button), hbox);
1060 gtk_widget_show(hbox);
1061 gtk_tooltips_set_tip (tooltips, button,
1062 "Show HTML bugs (right-click for menu).", "");
1063
1064 bw->status_box = gtk_hbox_new(FALSE, 0);
1065 gtk_box_pack_start(GTK_BOX(bw->status_box), bw->status, TRUE, TRUE, 2);
1066 gtk_widget_show(bw->status);
1067 gtk_box_pack_start(GTK_BOX(bw->status_box), button, FALSE, FALSE, 0);
1068 gtk_widget_show(button);
1069
1070 Interface_add_full_screen_button (bw, GTK_BOX (bw->status_box));
1071
1072 gtk_box_pack_start(GTK_BOX(box1), bw->status_box, FALSE, FALSE, 0);
1073 gtk_widget_show(bw->status_box);
1074
1075 gtk_widget_show(bw->main_window);
1076 gtk_widget_show(box1);
1077
1078 /* initialize the rest of the bw's data. */
1079 bw->pagemarks_menuitem = NULL;
1080 bw->pagemarks_menu = NULL;
1081 bw->pagemarks_last = NULL;
1082 bw->viewbugs_menuitem = NULL;
1083
1084 /* the image menu is created first because it is used by the link menu */
1085 bw->menu_popup.over_image = a_Menu_popup_oi_new(bw);
1086 bw->menu_popup.over_link = a_Menu_popup_ol_new(bw);
1087 bw->menu_popup.over_page = a_Menu_popup_op_new(bw);
1088 bw->menu_popup.over_back = NULL;
1089 bw->menu_popup.over_forw = NULL;
1090 bw->menu_popup.url = NULL;
1091 bw->menu_popup.url2 = NULL;
1092
1093 bw->redirect_level = 0;
1094 bw->sens_idle_id = 0;
1095
1096 bw->CursorType = -1;
1097
1098 bw->RootClients = NULL;
1099 bw->NumRootClients = 0;
1100 bw->MaxRootClients = 8;
1101
1102 bw->ImageClients = NULL;
1103 bw->NumImageClients = 0;
1104 bw->MaxImageClients = 8;
1105 bw->NumImages = 0;
1106 bw->NumImagesGot = 0;
1107
1108 bw->PageUrls = NULL;
1109 bw->NumPageUrls = 0;
1110 bw->MaxPageUrls = 8;
1111
1112 bw->open_dialog_window = NULL;
1113 bw->open_dialog_entry = NULL;
1114 bw->openfile_dialog_window = NULL;
1115 bw->quit_dialog_window = NULL;
1116 bw->save_dialog_window = NULL;
1117 bw->save_link_dialog_window = NULL;
1118 bw->findtext_dialog_window = NULL;
1119 bw->findtext_dialog_check = NULL;
1120 bw->findtext_dialog_entry = NULL;
1121 bw->search_dialog_window = NULL;
1122 bw->search_dialog_entry = NULL;
1123 bw->proxy_passwd_dialog_window = NULL;
1124 bw->proxy_passwd_dialog_entry = NULL;
1125 bw->question_dialog_window = NULL;
1126 bw->question_dialog_data = NULL;
1127 bw->viewsource_window = NULL;
1128 bw->pagebugs_window = NULL;
1129
1130 /* now that the bw is made, let's customize it.. */
1131 Interface_browser_window_customize(bw);
1132
1133 return bw;
1134 }
1135
1136 /*
1137 * Set the title of the browser window to start with "Dillo: "
1138 * prepended to it.
1139 */
1140 void a_Interface_set_page_title(BrowserWindow *bw, char *title)
1141 {
1142 GString *buf;
1143
1144 g_return_if_fail (bw != NULL && title != NULL);
1145
1146 buf = g_string_new("");
1147 g_string_sprintfa(buf, "Dillo: %s", title);
1148 gtk_window_set_title(GTK_WINDOW(bw->main_window), buf->str);
1149 g_string_free(buf, TRUE);
1150 }
1151
1152 /*
1153 * Set location entry's text
1154 */
1155 void a_Interface_set_location_text(BrowserWindow *bw, char *text)
1156 {
1157 gtk_entry_set_text(GTK_ENTRY(bw->location), text);
1158 }
1159
1160 /*
1161 * Get location entry's text
1162 */
1163 gchar *a_Interface_get_location_text(BrowserWindow *bw)
1164 {
1165 return gtk_entry_get_text(GTK_ENTRY(bw->location));
1166 }
1167
1168 /*
1169 * Reset images and text progress bars
1170 */
1171 void a_Interface_reset_progress_bars(BrowserWindow *bw)
1172 {
1173 a_Progressbar_update(bw->progress, "", 0);
1174 a_Progressbar_update(bw->imgprogress, "", 0);
1175 }
1176
1177 /*
1178 * Set the status string on the bottom of the dillo window.
1179 */
1180 void a_Interface_msg(BrowserWindow *bw, const char *format, ... )
1181 {
1182 static char msg[1024];
1183 va_list argp;
1184
1185 if ( bw ) {
1186 va_start(argp, format);
1187 g_vsnprintf(msg, 1024, format, argp);
1188 va_end(argp);
1189 gtk_label_set_text(GTK_LABEL(bw->status), msg);
1190 bw->status_is_link = 0;
1191 }
1192 }
1193
1194 /*
1195 * Update the bug-meter button for detected page errors.
1196 */
1197 void a_Interface_bug_meter_update(BrowserWindow *bw, gint num_err)
1198 {
1199 static char msg[64];
1200 gpointer label, bug, ok;
1201
1202 if ( bw ) {
1203 label = gtk_object_get_data(GTK_OBJECT(bw->status_bug_meter), "label");
1204 if (num_err < 2) {
1205 bug = gtk_object_get_data(GTK_OBJECT(bw->status_bug_meter), "bug");
1206 ok = gtk_object_get_data(GTK_OBJECT(bw->status_bug_meter), "ok");
1207 if (num_err == 0) {
1208 gtk_widget_hide(bug);
1209 gtk_widget_hide(label);
1210 gtk_widget_show(ok);
1211 } else {
1212 gtk_widget_show(bug);
1213 gtk_widget_show(label);
1214 gtk_widget_hide(ok);
1215 }
1216 gtk_widget_queue_resize (GTK_WIDGET (bw->status_bug_meter));
1217 }
1218 g_snprintf(msg, 64, " %d", num_err);
1219 gtk_label_set_text(GTK_LABEL(label), msg);
1220 }
1221 }
1222
1223 /*
1224 * Called from `destroy' callback in Interface_make_*_dialog
1225 */
1226 static void Interface_destroy_window(GtkWidget *widget, GtkWidget **window)
1227 {
1228 /* todo: sometimes this function is called twice with dialog windows */
1229 _MSG("Interface_destroy_window %p\n", *window);
1230 if (*window) {
1231 gtk_widget_destroy(*window);
1232 *window = NULL;
1233 }
1234 }
1235
1236
1237 /*
1238 * Close and free every single browser_window (called at exit time)
1239 */
1240 void a_Interface_quit_all(void)
1241 {
1242 BrowserWindow **bws;
1243 gint i, n_bw;
1244
1245 n_bw = num_bw;
1246 bws = g_malloc(sizeof(BrowserWindow *) * n_bw);
1247
1248 /* we copy into a new list because destroying the main window can
1249 * modify the browser_window array. */
1250 for (i = 0; i < n_bw; i++)
1251 bws[i] = browser_window[i];
1252
1253 for (i = 0; i < n_bw; i++)
1254 gtk_widget_destroy(bws[i]->main_window);
1255
1256 g_free(bws);
1257
1258 g_free(open_dialog_last_dirname);
1259 g_free(save_dialog_last_dirname);
1260 }
1261
1262 /*
1263 * Make a dialog for choosing files (by calling
1264 * gtk_file_selection_*() calls)
1265 * This can be used for saving, opening, or whatever,
1266 * just set the correct callbacks
1267 */
1268 static void
1269 Interface_make_choose_file_dialog(GtkWidget **DialogWindow,
1270 char *WmName, char *WmClass, char *WTitle,
1271 GtkSignalFunc B1CallBack, void *B1CbData)
1272 {
1273 GtkAccelGroup *accel_group;
1274
1275 *DialogWindow = gtk_file_selection_new(WTitle);
1276 gtk_window_set_modal(GTK_WINDOW(*DialogWindow), FALSE);
1277 gtk_window_set_wmclass(GTK_WINDOW(*DialogWindow), WmName, WmClass);
1278
1279 gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(*DialogWindow));
1280 gtk_signal_connect(
1281 GTK_OBJECT(*DialogWindow),
1282 "destroy", (GtkSignalFunc) Interface_destroy_window, DialogWindow);
1283 gtk_signal_connect(
1284 GTK_OBJECT(GTK_FILE_SELECTION(*DialogWindow)->ok_button),
1285 "clicked", (GtkSignalFunc) B1CallBack, B1CbData);
1286 gtk_signal_connect(
1287 GTK_OBJECT(GTK_FILE_SELECTION (*DialogWindow)->cancel_button),
1288 "clicked", (GtkSignalFunc) Interface_destroy_window, DialogWindow);
1289
1290 /* Make GDK_Escape close the dialog */
1291 accel_group = gtk_accel_group_new();
1292 gtk_window_add_accel_group(GTK_WINDOW(*DialogWindow), accel_group);
1293 gtk_widget_add_accelerator(
1294 GTK_FILE_SELECTION(*DialogWindow)->cancel_button, "clicked",
1295 accel_group, GDK_Escape, 0, GTK_ACCEL_LOCKED);
1296 }
1297
1298 /*
1299 * Get the file URL from the widget and push it to the browser window.
1300 */
1301 static void
1302 Interface_openfile_ok_callback(GtkWidget *widget, BrowserWindow *bw)
1303 {
1304 char *fn, *Cfn, *s;
1305 DilloUrl *url;
1306 GString *UrlStr = g_string_sized_new(1024);
1307
1308 fn = gtk_file_selection_get_filename(
1309 GTK_FILE_SELECTION(bw->openfile_dialog_window));
1310
1311 Cfn = (s = a_Misc_escape_chars(fn, "% ")) ? s : fn;
1312 g_string_sprintf(UrlStr, "file:%s", Cfn);
1313 url = a_Url_new(UrlStr->str, NULL, 0, 0, 0);
1314 a_Nav_push(bw, url);
1315 a_Url_free(url);
1316 g_string_free(UrlStr, TRUE);
1317 g_free(s);
1318
1319 g_free(open_dialog_last_dirname);
1320 open_dialog_last_dirname = g_strdup(fn);
1321
1322 gtk_widget_destroy(bw->openfile_dialog_window);
1323 }
1324
1325 /*
1326 * Returns a newly allocated string holding a search url generated from
1327 * a string of keywords (separarated by blanks) and prefs.search_url.
1328 * The search string is urlencoded.
1329 */
1330 static gchar *Interface_make_search_url(const gchar *str)
1331 {
1332 gchar *keys = a_Url_encode_hex_str(str), *c = prefs.search_url;
1333 GString *newstr = g_string_sized_new(strlen(c)+strlen(keys));
1334 gchar *search_url;
1335
1336 for (; *c; c++)
1337 if (*c == '%')
1338 switch(*++c) {
1339 case 's':
1340 g_string_append(newstr, keys); break;;
1341 case '%':
1342 g_string_append_c(newstr, '%'); break;;
1343 case 0:
1344 MSG("Warning: search_url ends with '%%'\n"); c--; break;;
1345 default:
1346 MSG("Warning: illegal specifier '%%%c' in search_url\n", *c);
1347 }
1348 else
1349 g_string_append_c(newstr, *c);
1350
1351 g_free(keys);
1352
1353 search_url = newstr->str;
1354 g_string_free(newstr, FALSE);
1355 return search_url;
1356 }
1357
1358 /*
1359 * Open an url string.
1360 * The URL is not sent "as is", illegal chars are ripped out,
1361 * then it's fully parsed by a_Url_new().
1362 */
1363 static void Interface_open_url_string(gchar *text, BrowserWindow *bw)
1364 {
1365 gchar *new_text;
1366 DilloUrl *url;
1367
1368 if (text && *text) {
1369 /* Filter URL string */
1370 new_text = a_Url_string_strip_delimiters(text);
1371
1372 url = a_Url_new(new_text, NULL, 0, 0, 0);
1373 if (url) {
1374 a_Nav_push(bw, url);
1375 a_Url_free(url);
1376 }
1377 g_free(new_text);
1378 }
1379
1380 /* let the rendered area have focus */
1381 gtk_widget_grab_focus(GTK_BIN(bw->docwin)->child);
1382 }
1383
1384 /*
1385 * Open an URL specified in the location entry, or in the open URL dialog.
1386 */
1387 void a_Interface_entry_open_url(GtkWidget *widget, BrowserWindow *bw)
1388 {
1389 gchar *text;
1390 GtkEntry *entry;
1391
1392 /* entry = { bw->location | bw->open_dialog_entry } */
1393 entry = GTK_ENTRY(widget == bw->location ? widget : bw->open_dialog_entry);
1394 text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
1395 DEBUG_MSG(1, "entry_open_url %s\n", text);
1396 Interface_open_url_string(text, bw);
1397 g_free(text);
1398
1399 if (bw->open_dialog_window != NULL)
1400 gtk_widget_hide(bw->open_dialog_window);
1401 }
1402
1403 /*
1404 * Create and show the "Open File" dialog
1405 */
1406 void a_Interface_openfile_dialog(BrowserWindow *bw)
1407 {
1408 if (!bw->openfile_dialog_window) {
1409 Interface_make_choose_file_dialog(
1410 &(bw->openfile_dialog_window),
1411 "openfile_dialog", "Dillo", "Dillo: Open File",
1412 (GtkSignalFunc) Interface_openfile_ok_callback, (void *)bw);
1413 }
1414
1415 if (open_dialog_last_dirname)
1416 gtk_file_selection_set_filename(
1417 GTK_FILE_SELECTION(bw->openfile_dialog_window),
1418 open_dialog_last_dirname);
1419
1420 if (!GTK_WIDGET_VISIBLE(bw->openfile_dialog_window))
1421 gtk_widget_show(bw->openfile_dialog_window);
1422 else
1423 gdk_window_raise(bw->openfile_dialog_window->window);
1424 }
1425
1426 /*
1427 * Make a dialog interface with three buttons, a text entry, and an optional
1428 * check button.
1429 */
1430 static void
1431 Interface_make_dialog(GtkWidget **DialogWindow, char *WmName, char *WmClass,
1432 char *WTitle, GtkWidget **DialogEntry, char *EntryStr, gint VisibleEntry,
1433 GtkWidget **CheckButton, char *CheckButtonText,
1434 char *B1Label, GtkSignalFunc B1CallBack, void *B1CbData)
1435 {
1436 GtkWidget *button, *box1, *box2, *entry;
1437 GtkAccelGroup *accel_group;
1438
1439 *DialogWindow = gtk_window_new(GTK_WINDOW_DIALOG);
1440 gtk_window_set_wmclass(GTK_WINDOW(*DialogWindow), WmName, WmClass);
1441 gtk_window_set_position(GTK_WINDOW(*DialogWindow), GTK_WIN_POS_CENTER);
1442 gtk_window_set_title(GTK_WINDOW(*DialogWindow), WTitle);
1443 gtk_signal_connect(GTK_OBJECT(*DialogWindow), "destroy",
1444 (GtkSignalFunc) Interface_destroy_window, DialogWindow);
1445
1446 /* Create and attach an accel group to the dialog window */
1447 accel_group = gtk_accel_group_new();
1448 gtk_window_add_accel_group(GTK_WINDOW(*DialogWindow), accel_group);
1449
1450 gtk_container_border_width(GTK_CONTAINER(*DialogWindow), 5);
1451
1452 box1 = gtk_vbox_new(FALSE, 5);
1453 gtk_container_add(GTK_CONTAINER(*DialogWindow), box1);
1454 gtk_widget_show(box1);
1455
1456 entry = gtk_entry_new();
1457 GTK_WIDGET_SET_FLAGS(entry, GTK_HAS_FOCUS);
1458 gtk_widget_set_usize(entry, 250, 0);
1459 gtk_entry_set_text(GTK_ENTRY(entry), EntryStr);
1460 gtk_box_pack_start(GTK_BOX(box1), entry, FALSE, FALSE, 0);
1461 *DialogEntry = GTK_WIDGET(entry);
1462 gtk_entry_set_visibility(GTK_ENTRY(entry), VisibleEntry ? TRUE : FALSE);
1463 gtk_widget_show(entry);
1464
1465 if (CheckButton && CheckButtonText) {
1466 *CheckButton = gtk_check_button_new_with_label(CheckButtonText);
1467 gtk_box_pack_start(GTK_BOX(box1), *CheckButton, FALSE, FALSE, 0);
1468 gtk_widget_show(*CheckButton);
1469 }
1470
1471 gtk_signal_connect(GTK_OBJECT(entry), "activate", B1CallBack, B1CbData);
1472
1473 box2 = gtk_hbox_new(TRUE, 5);
1474 gtk_box_pack_start(GTK_BOX(box1), box2, FALSE, FALSE, 0);
1475 gtk_widget_show(box2);
1476
1477 button = gtk_button_new_with_label(B1Label);
1478 gtk_signal_connect(GTK_OBJECT(button), "clicked", B1CallBack, B1CbData);
1479 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1480 gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 0);
1481 gtk_widget_grab_default(button);
1482 gtk_widget_show(button);
1483 gtk_signal_connect_object(GTK_OBJECT(entry), "focus_in_event",
1484 (GtkSignalFunc) gtk_widget_grab_default,
1485 GTK_OBJECT(button));
1486
1487 button = gtk_button_new_with_label("Clear");
1488 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1489 (GtkSignalFunc) Interface_entry_clear,
1490 GTK_OBJECT(entry));
1491 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1492 gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 0);
1493 gtk_widget_show(button);
1494
1495 button = gtk_button_new_with_label("Cancel");
1496 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1497 (GtkSignalFunc) gtk_widget_destroy,
1498 GTK_OBJECT(*DialogWindow));
1499 gtk_widget_add_accelerator(button, "clicked", accel_group,
1500 GDK_Escape, 0, GTK_ACCEL_LOCKED);
1501 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1502 gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 0);
1503 gtk_widget_show(button);
1504
1505 gtk_widget_grab_focus(entry);
1506 }
1507
1508 /*
1509 * Set bw->question_dialog_answer
1510 */
1511 static void Interface_question_dialog_set_answer_cb(DialogAnswer *answer)
1512 {
1513 _MSG("Interface_question_dialog_set_answer_cb\n");
1514 answer->bw->question_dialog_answer = answer;
1515 }
1516
1517 /*
1518 * Make a question-dialog with a question and some alternatives.
1519 * The selected choices are left in bw->question_dialog_answer in a structure.
1520 * A generic callback function can decide afterwards based on that information.
1521 * (0 means the window was cancelled, and 1 to 5 the respective alternatives)
1522 */
1523 static void Interface_make_question_dialog(
1524 BrowserWindow *bw,
1525 GtkWidget **DialogWindow, char *WmName, char *WmClass,
1526 char *WTitle, char *Question, gint modal_flag,
1527 char *alt1, char *alt2, char *alt3, char *alt4, char *alt5,
1528 GtkSignalFunc AnswerCallback)
1529 {
1530 GtkWidget *frame, *label, *button = NULL, *box1, *box2;
1531 DialogAnswer *answer;
1532 int i;
1533
1534 *DialogWindow = gtk_window_new(GTK_WINDOW_DIALOG);
1535 gtk_window_set_wmclass(GTK_WINDOW(*DialogWindow), WmName, WmClass);
1536 gtk_window_set_title(GTK_WINDOW(*DialogWindow), WTitle);
1537 gtk_window_set_position(GTK_WINDOW(*DialogWindow), GTK_WIN_POS_CENTER);
1538 gtk_container_border_width(GTK_CONTAINER(*DialogWindow), 10);
1539 gtk_window_set_modal(GTK_WINDOW(*DialogWindow), modal_flag);
1540 /* attach AnswerCallback to "destroy" so appropriate actions can be taken */
1541 gtk_signal_connect_object(GTK_OBJECT(*DialogWindow), "destroy",
1542 (GtkSignalFunc) AnswerCallback, (gpointer)bw);
1543 gtk_signal_connect(GTK_OBJECT(*DialogWindow), "destroy",
1544 (GtkSignalFunc) Interface_destroy_window, DialogWindow);
1545
1546 box1 = gtk_vbox_new(FALSE, 5);
1547 frame = gtk_frame_new(NULL);
1548 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
1549 label = gtk_label_new(Question);
1550 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_FILL);
1551 gtk_misc_set_padding(GTK_MISC(label), 20, 20);
1552 gtk_container_add(GTK_CONTAINER(frame), label);
1553 gtk_widget_show(label);
1554 gtk_widget_show(frame);
1555 gtk_box_pack_start(GTK_BOX(box1), frame, TRUE, TRUE, 0);
1556
1557 /* This struct will save us some code and lots of callback functions.
1558 * (it looks clumsy, but provides a handy interface for the caller) */
1559 answer = g_new(DialogAnswer, 6);
1560 for (i = 0; i < 6; ++i) {
1561 answer[i].bw = bw;
1562 answer[i].alt_num = i;
1563 answer[i].this = answer;
1564 }
1565 answer[0].alt_str = NULL; answer[1].alt_str = alt1;
1566 answer[2].alt_str = alt2; answer[3].alt_str = alt3;
1567 answer[4].alt_str = alt4; answer[5].alt_str = alt5;
1568 /* Set the default answer */
1569 bw->question_dialog_answer = &answer[0];
1570
1571 /* pack the alternatives */
1572 box2 = gtk_hbox_new(TRUE, 5);
1573 for (i = 1; i < 6 && answer[i].alt_str; ++i) {
1574 button = gtk_button_new_with_label(answer[i].alt_str);
1575 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1576 (GtkSignalFunc) Interface_question_dialog_set_answer_cb,
1577 (gpointer)&answer[i]);
1578 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1579 (GtkSignalFunc) Interface_destroy_window, DialogWindow);
1580 gtk_widget_show(button);
1581 gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 0);
1582 }
1583
1584 gtk_box_pack_start(GTK_BOX(box1), box2, FALSE, FALSE, 0);
1585 gtk_container_add(GTK_CONTAINER(*DialogWindow), box1);
1586
1587 gtk_widget_show(box2);
1588 gtk_widget_show(box1);
1589 gtk_widget_grab_focus(button);
1590 gtk_widget_show(*DialogWindow);
1591 }
1592
1593 /*
1594 * Create and show an [OK|Cancel] question dialog
1595 */
1596 void a_Interface_question_dialog(
1597 BrowserWindow *bw, gchar *QuestionTxt, gint modal_flag,
1598 char *alt1, char *alt2, char *alt3, char *alt4, char *alt5,
1599 GtkSignalFunc AnswerCallback)
1600 {
1601 if (!bw->question_dialog_window) {
1602 Interface_make_question_dialog(
1603 bw,
1604 &(bw->question_dialog_window), "question_dialog", "Dillo",
1605 "Dillo: Question", QuestionTxt, modal_flag,
1606 alt1, alt2, alt3, alt4, alt5,
1607 AnswerCallback);
1608 } else {
1609 /* should not reach here */
1610 gtk_widget_destroy(bw->question_dialog_window);
1611 }
1612 }
1613
1614 /*
1615 * Create and show the open URL dialog
1616 */
1617 void a_Interface_open_dialog(GtkWidget *widget, BrowserWindow *bw)
1618 {
1619 if (!bw->open_dialog_window) {
1620 Interface_make_dialog(&(bw->open_dialog_window),
1621 "open_dialog", "Dillo", "Dillo: Open URL",
1622 &(bw->open_dialog_entry), "", 1, NULL, NULL,
1623 "OK", (GtkSignalFunc) a_Interface_entry_open_url, (void *)bw);
1624 if (prefs.transient_dialogs)
1625 gtk_window_set_transient_for(GTK_WINDOW(bw->open_dialog_window),
1626 GTK_WINDOW(bw->main_window));
1627 }
1628
1629 if (!GTK_WIDGET_VISIBLE(bw->open_dialog_window))
1630 gtk_widget_show(bw->open_dialog_window);
1631 else
1632 gdk_window_raise(bw->open_dialog_window->window);
1633 }
1634
1635 /*
1636 * Receive data from the cache and save it to a local file
1637 */
1638 static void Interface_save_callback(int Op, CacheClient_t *Client)
1639 {
1640 DilloWeb *Web = Client->Web;
1641 gint Bytes;
1642
1643 if ( Op ){
1644 struct stat st;
1645
1646 fflush(Web->stream);
1647 fstat(fileno(Web->stream), &st);
1648 fclose(Web->stream);
1649 a_Interface_msg(Web->bw, "File saved (%d Bytes)", st.st_size);
1650 } else {
1651 if ( (Bytes = Client->BufSize - Web->SavedBytes) > 0 ) {
1652 Bytes = fwrite(Client->Buf + Web->SavedBytes, 1, Bytes, Web->stream);
1653 Web->SavedBytes += Bytes;
1654 }
1655 }
1656 }
1657
1658 /*
1659 * Save current page to a local file
1660 */
1661 static void Interface_file_save_url(GtkWidget *widget, BrowserWindow *bw)
1662 {
1663 const char *name;
1664 GtkFileSelection *choosefile;
1665 GtkEntry *entry_url;
1666 DilloUrl *url;
1667 FILE *out;
1668
1669 choosefile = GTK_FILE_SELECTION(bw->save_dialog_window);
1670 entry_url = GTK_ENTRY(bw->location);
1671 name = gtk_file_selection_get_filename(choosefile);
1672 url = a_Url_dup(a_History_get_url(NAV_TOP(bw)));
1673
1674 if ( strlen(name) && (out = fopen(name, "w")) != NULL ) {
1675 DilloWeb *Web = a_Web_new(url);
1676 Web->bw = bw;
1677 Web->stream = out;
1678 Web->flags |= WEB_Download;
1679 /* todo: keep track of this client */
1680 a_Capi_open_url(Web, Interface_save_callback, Web);
1681
1682 g_free(save_dialog_last_dirname);
1683 save_dialog_last_dirname = g_strdup(name);
1684 }
1685 a_Url_free(url);
1686
1687 gtk_widget_destroy(bw->save_dialog_window);
1688 }
1689
1690 /*
1691 * Save the link-URL to a local file
1692 */
1693 static void Interface_file_save_link(GtkWidget *widget, BrowserWindow *bw)
1694 {
1695 const gchar *name;
1696 const DilloUrl *url;
1697 gchar *cmd, *buf;
1698 FILE *out;
1699 gint buf_size;
1700
1701 name = gtk_file_selection_get_filename(
1702 GTK_FILE_SELECTION(bw->save_link_dialog_window));
1703 url = a_Menu_popup_get_url(bw);
1704
1705 g_free(save_dialog_last_dirname);
1706 save_dialog_last_dirname = g_strdup(name);
1707
1708 if (!a_Capi_get_buf(url, &buf, &buf_size)) {
1709 /* Not cached, ask the downloads server to get it */
1710 cmd = a_Dpip_build_cmd("cmd=%s url=%s destination=%s",
1711 "download", URL_STR(url), name);
1712 a_Capi_dpi_send_cmd(NULL, bw, cmd, "downloads", 1);
1713 g_free(cmd);
1714
1715 } else {
1716 /* Cached! Save from the cache */
1717 if ( strlen(name) && (out = fopen(name, "w")) != NULL ) {
1718 DilloWeb *Web = a_Web_new(url);
1719 Web->bw = bw;
1720 Web->stream = out;
1721 Web->flags |= WEB_Download;
1722 /* todo: keep track of this client */
1723 a_Capi_open_url(Web, Interface_save_callback, Web);
1724 } else
1725 g_printerr("Error trying to save: %s\n", name);
1726 }
1727
1728 gtk_widget_destroy(bw->save_link_dialog_window);
1729 }
1730
1731 /*
1732 * Scan Url and return a local-filename suggestion for saving
1733 */
1734 static char *Interface_make_save_name(const DilloUrl *url)
1735 {
1736 int i;
1737 gchar *FileName, *FilenameWithDir, *o, *n;
1738
1739 if ((FileName = strrchr(URL_PATH(url), '/')))
1740 FileName = g_strndup(FileName + 1, MIN(strlen(FileName + 1), 80));
1741 else
1742 FileName = g_strdup("");
1743
1744 /* Replace %20 and ' ' with '_' in Filename */
1745 o = n = FileName;
1746 for (i = 0; o[i]; i++) {
1747 *n++ = (o[i] == ' ' || (o[i] == '%' && o[i+1] == '2' && o[i+2] == '0')) ?
1748 i+=2, '_' : o[i];
1749 }
1750 *n = 0;
1751
1752 if (save_dialog_last_dirname) {
1753 if (*FileName) {
1754 gchar *dirpart = g_dirname(save_dialog_last_dirname);
1755 FilenameWithDir =
1756 g_strconcat(dirpart, G_DIR_SEPARATOR_S, FileName, NULL);
1757 g_free(dirpart);
1758 } else {
1759 FilenameWithDir = g_strdup(save_dialog_last_dirname);
1760 }
1761 g_free(FileName);
1762 return FilenameWithDir;
1763 } else
1764 return FileName;
1765 }
1766
1767 /*
1768 * Show the dialog interface for saving an URL
1769 */
1770 void a_Interface_save_dialog(GtkWidget *widget, BrowserWindow *bw)
1771 {
1772 gchar *SuggestedName; /* Suggested save name */
1773 DilloUrl* url;
1774
1775 if (!bw->save_dialog_window) {
1776 Interface_make_choose_file_dialog(
1777 &bw->save_dialog_window,
1778 "save_dialog", "Dillo", "Dillo: Save URL as File...",
1779 (GtkSignalFunc) Interface_file_save_url, (void *)bw );
1780 }
1781 url = a_Url_new(a_Interface_get_location_text(bw), NULL, 0, 0, 0);
1782 SuggestedName = Interface_make_save_name(url);
1783 gtk_file_selection_set_filename(
1784 GTK_FILE_SELECTION(bw->save_dialog_window), SuggestedName);
1785 g_free(SuggestedName);
1786 a_Url_free(url);
1787
1788 if (!GTK_WIDGET_VISIBLE(bw->save_dialog_window))
1789 gtk_widget_show(bw->save_dialog_window);
1790 else
1791 gdk_window_raise(bw->save_dialog_window->window);
1792 }
1793
1794 /*
1795 * Show the dialog interface for saving a link
1796 */
1797 void a_Interface_save_link_dialog(GtkWidget *widget, BrowserWindow *bw)
1798 {
1799 char *SuggestedName; /* Suggested save name */
1800
1801 if (!bw->save_link_dialog_window) {
1802 Interface_make_choose_file_dialog(
1803 &bw->save_link_dialog_window,
1804 "save_link_dialog", "Dillo",
1805 "Dillo: Save link as File...",
1806 (GtkSignalFunc) Interface_file_save_link,
1807 (void *)bw);
1808 }
1809 SuggestedName = Interface_make_save_name(a_Menu_popup_get_url(bw));
1810 gtk_file_selection_set_filename(
1811 GTK_FILE_SELECTION(bw->save_link_dialog_window), SuggestedName);
1812 g_free(SuggestedName);
1813
1814 if (!GTK_WIDGET_VISIBLE(bw->save_link_dialog_window))
1815 gtk_widget_show(bw->save_link_dialog_window);
1816 else
1817 gdk_window_raise(bw->save_link_dialog_window->window);
1818 }
1819
1820 /*
1821 * Offer the "Save Link As..." dialog for an unhandled MIME type URL.
1822 */
1823 void a_Interface_offer_link_download(BrowserWindow *bw, const DilloUrl *url)
1824 {
1825 a_Menu_popup_set_url(bw, url);
1826 a_Interface_save_link_dialog(NULL, bw);
1827 }
1828
1829 /*
1830 * Scroll to an occurence of a string in the open page
1831 */
1832 static void Interface_entry_search(GtkWidget *widget, BrowserWindow* bw)
1833 {
1834 char *string;
1835 gboolean case_sens;
1836
1837 string = gtk_editable_get_chars(GTK_EDITABLE(bw->findtext_dialog_entry),
1838 0, -1);
1839 case_sens = gtk_toggle_button_get_active
1840 (GTK_TOGGLE_BUTTON(bw->findtext_dialog_check));
1841 switch(a_Dw_gtk_scrolled_window_search(GTK_DW_SCROLLED_WINDOW(bw->docwin),
1842 string, case_sens)) {
1843 case FINDTEXT_RESTART:
1844 a_Interface_message_window("Dillo: Find text",
1845 "No further occurence of \"%s\". "
1846 "Restarting from the beginning.", string);
1847 break;
1848 case FINDTEXT_NOT_FOUND:
1849 a_Interface_message_window("Dillo: Find text",
1850 "Cannot find \"%s\".", string);
1851 break;
1852 }
1853 g_free(string);
1854 }
1855
1856 /*
1857 * Show the dialog interface for finding text in a page
1858 */
1859 void a_Interface_findtext_dialog(BrowserWindow *bw)
1860 {
1861 if (!bw->findtext_dialog_window) {
1862 Interface_make_dialog(&(bw->findtext_dialog_window),
1863 "findtext_dialog", "Dillo", "Dillo: Find text in page",
1864 &(bw->findtext_dialog_entry), "", 1,
1865 &(bw->findtext_dialog_check), "Case sensitive",
1866 "Find", (GtkSignalFunc) Interface_entry_search, (void *)bw);
1867 if (prefs.transient_dialogs)
1868 gtk_window_set_transient_for(GTK_WINDOW(bw->findtext_dialog_window),
1869 GTK_WINDOW(bw->main_window));
1870 gtk_signal_connect_object
1871 (GTK_OBJECT(bw->findtext_dialog_window), "destroy",
1872 (GtkSignalFunc) a_Dw_gtk_scrolled_window_reset_search,
1873 (void*)bw->docwin);
1874 gtk_window_set_position(GTK_WINDOW(bw->findtext_dialog_window),
1875 GTK_WIN_POS_NONE);
1876 }
1877
1878 a_Interface_set_nice_window_pos(bw->findtext_dialog_window,
1879 bw->main_window);
1880
1881 if (!GTK_WIDGET_VISIBLE(bw->findtext_dialog_window))
1882 gtk_widget_show(bw->findtext_dialog_window);
1883 else
1884 gdk_window_raise(bw->findtext_dialog_window->window);
1885 }
1886
1887 /*
1888 * Use the search dialog's entry to feed a web search engine.
1889 */
1890 static void Interface_search_callback(GtkWidget *widget, BrowserWindow *bw)
1891 {
1892 gchar *keyw, *url_str;
1893
1894 keyw = gtk_editable_get_chars(GTK_EDITABLE(bw->search_dialog_entry), 0, -1);
1895 if (keyw) {
1896 url_str = Interface_make_search_url(keyw);
1897 Interface_open_url_string(url_str, bw);
1898 g_free(url_str);
1899 g_free(keyw);
1900 }
1901 if (bw->search_dialog_window != NULL)
1902 gtk_widget_hide(bw->search_dialog_window);
1903 }
1904
1905 /*
1906 * Show the dialog interface for web search engine.
1907 */
1908 void a_Interface_search_dialog(GtkWidget *widget, BrowserWindow *bw)
1909 {
1910 if (!bw->search_dialog_window) {
1911 Interface_make_dialog(&(bw->search_dialog_window),
1912 "search_dialog", "Dillo", "Dillo: Search the Web",
1913 &(bw->search_dialog_entry), "", 1, NULL, NULL,
1914 "Search", (GtkSignalFunc)Interface_search_callback, (void *)bw);
1915 if (prefs.transient_dialogs)
1916 gtk_window_set_transient_for(GTK_WINDOW(bw->search_dialog_window),
1917 GTK_WINDOW(bw->main_window));
1918 }
1919
1920 if (!GTK_WIDGET_VISIBLE(bw->search_dialog_window))
1921 gtk_widget_show(bw->search_dialog_window);
1922 else
1923 gdk_window_raise(bw->search_dialog_window->window);
1924 }
1925
1926 /*
1927 * Get and activate a proxy password.
1928 */
1929 static void Interface_entry_proxy_passwd(GtkWidget *widget, BrowserWindow *bw)
1930 {
1931 gchar *text;
1932
1933 text = gtk_editable_get_chars(GTK_EDITABLE(bw->proxy_passwd_dialog_entry),
1934 0, -1);
1935 a_Http_set_proxy_passwd(text);
1936 g_free(text);
1937
1938 if (bw->proxy_passwd_dialog_window != NULL)
1939 gtk_widget_destroy(bw->proxy_passwd_dialog_window);
1940 }
1941
1942 /*
1943 * Show the dialog interface for asking proxy password.
1944 */
1945 void a_Interface_proxy_passwd_dialog(BrowserWindow *bw)
1946 {
1947 GString *buf = g_string_new("");
1948
1949 g_string_sprintf(buf, "Dillo: Enter proxy password for '%s'",
1950 prefs.http_proxyuser);
1951
1952 if (!bw->proxy_passwd_dialog_window) {
1953 Interface_make_dialog(&(bw->proxy_passwd_dialog_window),
1954 "proxy_passwd_dialog", "Dillo", buf->str,
1955 &(bw->proxy_passwd_dialog_entry), "", 0, NULL, NULL,
1956 "OK", (GtkSignalFunc)Interface_entry_proxy_passwd, (void *)bw);
1957 if (prefs.transient_dialogs)
1958 gtk_window_set_transient_for(
1959 GTK_WINDOW(bw->proxy_passwd_dialog_window),
1960 GTK_WINDOW(bw->main_window));
1961 gtk_window_set_modal(GTK_WINDOW(bw->proxy_passwd_dialog_window), TRUE);
1962 }
1963
1964 if (!GTK_WIDGET_VISIBLE(bw->proxy_passwd_dialog_window))
1965 gtk_widget_show(bw->proxy_passwd_dialog_window);
1966
1967 g_string_free(buf, TRUE);
1968 }
1969
1970 /*
1971 * This signal callback adjusts the position of a menu.
1972 * It's useful for very long menus.
1973 */
1974 void a_Interface_scroll_popup(GtkWidget *widget)
1975 {
1976 /*
1977 * todo:
1978 * 1) Scrolling menues should rather be the task of Gtk+. This is
1979 * a hack, and I don't know if it does not break anything.
1980 * 2) It could be improved, e.g. a timeout could be added for
1981 * better mouse navigation.
1982 */
1983 int y, h, mx, my, sh;
1984
1985 y = widget->allocation.y;
1986 h = widget->allocation.height;
1987 gdk_window_get_geometry (widget->parent->parent->window,
1988 &mx, &my, NULL, NULL, NULL);
1989 sh = gdk_screen_height ();
1990
1991 if (y + my < 0)
1992 gdk_window_move (widget->parent->parent->window, mx, - y + 1);
1993 else if (y + my > sh - h)
1994 gdk_window_move (widget->parent->parent->window, mx, sh - h - y - 1);
1995 }
1996
1997 /*
1998 * A general purpose message window.
1999 */
2000 void a_Interface_message_window(const char *title, const char *format, ... )
2001 {
2002 GtkAccelGroup *accel_group;
2003 GtkWidget *window, *frame, *label, *button, *box;
2004 static char msg[1024];
2005 va_list argp;
2006
2007 va_start(argp, format);
2008 g_vsnprintf(msg, 1024, format, argp);
2009 va_end(argp);
2010
2011 window = gtk_window_new(GTK_WINDOW_DIALOG);
2012 gtk_window_set_wmclass(GTK_WINDOW(window), "question_dialog", "dillo");
2013 gtk_window_set_title(GTK_WINDOW(window), title);
2014 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
2015 gtk_container_border_width(GTK_CONTAINER(window), 10);
2016 gtk_signal_connect_object(GTK_OBJECT(window), "delete_event",
2017 (GtkSignalFunc)gtk_widget_destroy, (void*)window);
2018
2019 /* accel_group for the message window */
2020 accel_group = gtk_accel_group_new();
2021 gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
2022
2023 box = gtk_vbox_new(FALSE, 5);
2024 frame = gtk_frame_new(NULL);
2025 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
2026 label = gtk_label_new(msg);
2027 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_FILL);
2028 gtk_misc_set_padding(GTK_MISC(label), 20, 20);
2029 gtk_container_add(GTK_CONTAINER(frame), label);
2030 gtk_widget_show(label);
2031 gtk_widget_show(frame);
2032 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
2033
2034 button = gtk_button_new_with_label("Close");
2035 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
2036 (GtkSignalFunc)gtk_widget_destroy, (void*)window);
2037 gtk_widget_add_accelerator(button, "clicked",
2038 accel_group, GDK_Escape, 0, GTK_ACCEL_LOCKED);
2039 gtk_widget_show(button);
2040 gtk_box_pack_start(GTK_BOX(box), button, FALSE, TRUE, 0);
2041 gtk_widget_show(button);
2042 gtk_container_add(GTK_CONTAINER(window), box);
2043
2044 gtk_widget_show(box);
2045 gtk_widget_grab_focus(button);
2046 gtk_widget_show(window);
2047 }
2048
2049 /*
2050 * A general purpose window for long text display.
2051 */
2052 void a_Interface_text_window (GtkWidget **text_widget,
2053 gchar *title, gchar *wm_class,
2054 gchar *buf, gint buf_size,
2055 gint xsize_max, gint ysize_max)
2056 {
2057 gint xsize, ysize;
2058 GtkWidget *window, *box1, *button, *scrolled_window, *text;
2059
2060 if (*text_widget)
2061 gtk_widget_destroy (*text_widget);
2062
2063 /* -RL :: This code is adapted from testgtk. */
2064 if ( !*text_widget ) {
2065 window = *text_widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2066 gtk_window_set_wmclass(GTK_WINDOW(window), wm_class, "Dillo");
2067 gtk_widget_set_name (window, "text window");
2068 xsize = (prefs.width < xsize_max) ? prefs.width : xsize_max;
2069 ysize = (prefs.height < ysize_max) ? prefs.height : ysize_max;
2070 gtk_widget_set_usize (window, xsize, ysize);
2071 gtk_window_set_policy (GTK_WINDOW(window), TRUE, TRUE, FALSE);
2072
2073 gtk_signal_connect (GTK_OBJECT (window), "destroy",
2074 GTK_SIGNAL_FUNC(gtk_widget_destroyed),
2075 text_widget);
2076
2077 gtk_window_set_title (GTK_WINDOW (window), title);
2078 gtk_container_border_width (GTK_CONTAINER (window), 0);
2079
2080 box1 = gtk_vbox_new (FALSE, 0);
2081 gtk_container_add (GTK_CONTAINER (window), box1);
2082 gtk_widget_show (box1);
2083
2084 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2085 gtk_box_pack_start (GTK_BOX (box1), scrolled_window, TRUE, TRUE, 0);
2086 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
2087 GTK_POLICY_NEVER,
2088 GTK_POLICY_ALWAYS);
2089 gtk_widget_show (scrolled_window);
2090
2091 text = gtk_text_new (NULL, NULL);
2092 gtk_text_set_editable (GTK_TEXT (text), FALSE);
2093 gtk_container_add (GTK_CONTAINER (scrolled_window), text);
2094 gtk_widget_show (text);
2095
2096 gtk_text_freeze (GTK_TEXT (text));
2097 gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, buf, buf_size);
2098 gtk_text_thaw (GTK_TEXT (text));
2099
2100 button = gtk_button_new_with_label ("close");
2101 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
2102 GTK_SIGNAL_FUNC(gtk_widget_destroy),
2103 GTK_OBJECT (window));
2104 gtk_box_pack_start (GTK_BOX (box1), button, FALSE, FALSE, 0);
2105 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
2106 gtk_widget_grab_default (button);
2107 gtk_widget_show (button);
2108 }
2109
2110 if (!GTK_WIDGET_VISIBLE (*text_widget))
2111 gtk_widget_show (*text_widget);
2112 }
2113
2114 /*
2115 * Places win1 in a way that it does not, or as less as possible, cover win2.
2116 */
2117 void a_Interface_set_nice_window_pos(GtkWidget *win1, GtkWidget *win2)
2118 {
2119 gint w1, h1, x2, y2, w2, h2, sw, sh, sr, sl, sb, st, max;
2120
2121 gtk_widget_realize(win1);
2122 gdk_window_get_geometry(win1->window, NULL, NULL, &w1, &h1, NULL);
2123 gdk_window_get_origin(win2->window, &x2, &y2);
2124 gdk_window_get_geometry(win2->window, NULL, NULL, &w2, &h2, NULL);
2125 sw = gdk_screen_width();
2126 sh = gdk_screen_height();
2127
2128 /* space (excluding win1 space) at right, left, bottom and top */
2129 sr = sw - (x2 + w2 + w1);
2130 sl = x2 - w1;
2131 sb = sh - (y2 + h2 + h1);
2132 st = y2 - h1;
2133
2134 /* First, we test, whether win1 can be placed so that it does not
2135 * covor win2. */
2136 if (sr >= 0)
2137 gtk_widget_set_uposition(win1, x2 + w2, (sh - h1) / 2);
2138 else if (sl >= 0)
2139 gtk_widget_set_uposition(win1, x2 - w1, (sh - h1) / 2);
2140 else if (sb >= 0)
2141 gtk_widget_set_uposition(win1, (sh - h1) / 2, y2 + h2);
2142 else if (st >= 0)
2143 gtk_widget_set_uposition(win1, (sh - h1) / 2, y2 - h1);
2144 else {
2145 /* Second, we search for the position where the covered space
2146 * is (more or less) minimized. */
2147 max = MAX(MAX(sr, sl), MAX(sb, st));
2148 if (sr == max)
2149 gtk_widget_set_uposition(win1, sw - w1, (sh - h1) / 2);
2150 else if (sl == max)
2151 gtk_widget_set_uposition(win1, 0, (sh - h1) / 2);
2152 else if (sb == max)
2153 gtk_widget_set_uposition(win1, (sh - h1) / 2, sh - h1);
2154 else
2155 gtk_widget_set_uposition(win1, (sh - h1) / 2, 0);
2156 }
2157 }
+0
-69
src/interface.h less more
0 #ifndef __INTERFACE_H__
1 #define __INTERFACE_H__
2
3 #include "browser.h"
4
5 /*
6 * This struct is used with custom dialogs.
7 * It can be extended to provide extra widgets (e.g. check buttons).
8 *
9 */
10 typedef struct _DialogAnswer DialogAnswer;
11 struct _DialogAnswer {
12 BrowserWindow *bw;
13 gint alt_num;
14 char *alt_str;
15 DialogAnswer *this;
16 };
17
18
19 void a_Interface_init(void);
20 void a_Interface_stop(BrowserWindow *bw);
21 void a_Interface_clean(BrowserWindow *bw);
22 void a_Interface_quit_all(void);
23
24 void a_Interface_add_client(BrowserWindow *bw, gint Key, gint Root);
25 void a_Interface_remove_client(BrowserWindow *bw, gint ClientKey);
26 void a_Interface_add_url(BrowserWindow *bw, const DilloUrl *Url, gint Flags);
27 void a_Interface_close_client(BrowserWindow *bw, gint ClientKey);
28
29 void a_Interface_msg(BrowserWindow *bw, const char *format, ... );
30 void a_Interface_bug_meter_update(BrowserWindow *bw, gint num_err);
31
32 void a_Interface_openfile_dialog(BrowserWindow *bw);
33 void a_Interface_open_dialog(GtkWidget *widget, BrowserWindow *bw);
34 void a_Interface_save_dialog(GtkWidget *widget, BrowserWindow *bw);
35 void a_Interface_save_link_dialog(GtkWidget *widget, BrowserWindow *bw);
36 void a_Interface_offer_link_download(BrowserWindow *bw, const DilloUrl *url);
37 void a_Interface_search_dialog(GtkWidget *widget, BrowserWindow *bw);
38 void a_Interface_findtext_dialog(BrowserWindow *bw);
39 void a_Interface_proxy_passwd_dialog(BrowserWindow *bw);
40 void a_Interface_quit_dialog(BrowserWindow *bw);
41
42 void a_Interface_set_page_title(BrowserWindow *bw, char *title);
43 void a_Interface_set_location_text(BrowserWindow *bw, char *text);
44 gchar *a_Interface_get_location_text(BrowserWindow *bw);
45 void a_Interface_reset_progress_bars(BrowserWindow *bw);
46 void a_Interface_entry_open_url(GtkWidget *widget, BrowserWindow *bw);
47 void a_Interface_set_cursor(BrowserWindow *bw, GdkCursorType CursorType);
48 BrowserWindow *
49 a_Interface_browser_window_new(gint width, gint height, guint32 xid);
50
51 void a_Interface_set_button_sens(BrowserWindow *bw);
52
53 void a_Interface_scroll_popup(GtkWidget *widget);
54
55 void a_Interface_question_dialog(
56 BrowserWindow *bw, gchar *QuestionTxt, gint modal_flag,
57 char *alt1, char *alt2, char *alt3, char *alt4, char *alt5,
58 GtkSignalFunc AnswerCallback);
59 void a_Interface_message_window(const char *title, const char *format, ... );
60 void a_Interface_text_window (GtkWidget **text_widget,
61 gchar *title, gchar *wm_class,
62 gchar *buf, gint buf_size,
63 gint xsize_max, gint ysize_max);
64
65 void a_Interface_set_nice_window_pos(GtkWidget *win1, GtkWidget *win2);
66
67
68 #endif /* __INTERFACE_H__ */
00 /*
11 * File: jpeg.c
22 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 James McCollough <jamesm@gtwn.net>
5 * 2000 Jorge Arellano Cid <jcid@users.sourceforge.net>
3 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
64 *
75 * This program is free software; you can redistribute it and/or modify
86 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
108 * (at your option) any later version.
119 */
1210
2018 #ifdef ENABLE_JPEG
2119
2220 #include <stdio.h>
23 #include <gtk/gtk.h>
2421 #include <setjmp.h>
2522
2623 /* avoid a redefinition of HAVE_STDLIB_H with old jpeglib.h */
2825 # undef HAVE_STDLIB_H
2926 #endif
3027 #include <jpeglib.h>
31
32 #include "image.h"
33 #include "web.h"
28 #ifdef HAVE_STDLIB_H
29 # undef HAVE_STDLIB_H
30 #endif
31
32 #include "image.hh"
3433 #include "cache.h"
3534 #include "dicache.h"
36
37 #define DEBUG_LEVEL 6
38 #include "debug.h"
35 #include "capi.h" /* get cache entry status */
36 #include "msg.h"
3937
4038 typedef enum {
4139 DILLO_JPEG_INIT,
4240 DILLO_JPEG_STARTING,
43 DILLO_JPEG_READING,
41 DILLO_JPEG_READ_BEGIN_SCAN,
42 DILLO_JPEG_READ_IN_SCAN,
43 DILLO_JPEG_READ_END_SCAN,
4444 DILLO_JPEG_DONE,
4545 DILLO_JPEG_ERROR
4646 } DilloJpegState;
6161 typedef struct DilloJpeg {
6262 DilloImage *Image;
6363 DilloUrl *url;
64 gint version;
64 int version;
6565
6666 my_source_mgr Src;
6767
6969 size_t Start_Ofs, Skip, NewStart;
7070 char *Data;
7171
72 guint y;
72 uint_t y;
7373
7474 struct jpeg_decompress_struct cinfo;
7575 struct my_error_mgr jerr;
7878 /*
7979 * Forward declarations
8080 */
81 static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, gint version);
82 static void Jpeg_callback(int Op, CacheClient_t *Client);
83 static void Jpeg_write(DilloJpeg *jpeg, void *Buf, guint BufSize);
84 static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client);
81 static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize);
8582 METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo);
86
87 /* exported function */
88 DwWidget *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call,
89 void **Data);
9083
9184
9285 /* this is the routine called by libjpeg when it detects an error. */
9487 {
9588 /* display message and return to setjmp buffer */
9689 my_error_ptr myerr = (my_error_ptr) cinfo->err;
97 (*cinfo->err->output_message) (cinfo);
90 if (prefs.show_msg) {
91 DilloJpeg *jpeg =
92 ((my_source_mgr *) ((j_decompress_ptr) cinfo)->src)->jpeg;
93 MSG_WARN("\"%s\": ", URL_STR(jpeg->url));
94 (*cinfo->err->output_message) (cinfo);
95 }
9896 longjmp(myerr->setjmp_buffer, 1);
9997 }
10098
10199 /*
102 * MIME handler for "image/jpeg" type
103 * (Sets Jpeg_callback or a_Dicache_callback as the cache-client)
104 */
105 DwWidget *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call,
106 void **Data)
107 {
108 DilloWeb *web = P;
109 DICacheEntry *DicEntry;
110
111 if ( !web->Image )
112 web->Image = a_Image_new(0, 0, NULL, 0);
113
114 /* Add an extra reference to the Image (for dicache usage) */
115 a_Image_ref(web->Image);
116
117 DicEntry = a_Dicache_get_entry(web->url);
118 if ( !DicEntry ) {
119 /* Let's create an entry for this image... */
120 DicEntry = a_Dicache_add_entry(web->url);
121
122 /* ... and let the decoder feed it! */
123 *Data = Jpeg_new(web->Image, DicEntry->url, DicEntry->version);
124 *Call = (CA_Callback_t) Jpeg_callback;
125 } else {
126 /* Let's feed our client from the dicache */
127 a_Dicache_ref(DicEntry->url, DicEntry->version);
128 *Data = web->Image;
129 *Call = (CA_Callback_t) a_Dicache_callback;
130 }
131 return DW_WIDGET (web->Image->dw);
100 * Free the jpeg-decoding data structure.
101 */
102 static void Jpeg_free(DilloJpeg *jpeg)
103 {
104 _MSG("Jpeg_free: jpeg=%p\n", jpeg);
105 jpeg_destroy_decompress(&(jpeg->cinfo));
106 dFree(jpeg);
132107 }
133108
134109 /*
136111 */
137112 static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client)
138113 {
114 _MSG("Jpeg_close\n");
139115 a_Dicache_close(jpeg->url, jpeg->version, Client);
140
141 if (jpeg->state != DILLO_JPEG_DONE) {
142 jpeg_destroy_decompress(&(jpeg->cinfo));
143 }
144 g_free(jpeg);
145 }
146
147 static void init_source(j_decompress_ptr cinfo)
116 Jpeg_free(jpeg);
117 }
118
119 /*
120 * The proper signature is:
121 * static void init_source(j_decompress_ptr cinfo)
122 * (declaring it with no parameter avoids a compiler warning)
123 */
124 static void init_source()
148125 {
149126 }
150127
152129 {
153130 DilloJpeg *jpeg = ((my_source_mgr *) cinfo->src)->jpeg;
154131
155 DEBUG_MSG(5, "fill_input_buffer\n");
132 _MSG("fill_input_buffer\n");
156133 #if 0
157134 if (!cinfo->src->bytes_in_buffer) {
158 DEBUG_MSG(5, "fill_input_buffer: %ld bytes in buffer\n",
159 (glong)cinfo->src->bytes_in_buffer);
160
161 jpeg->Start_Ofs = (gulong) jpeg->cinfo.src->next_input_byte -
162 (gulong) jpeg->Data;
135 _MSG("fill_input_buffer: %ld bytes in buffer\n",
136 (long)cinfo->src->bytes_in_buffer);
137
138 jpeg->Start_Ofs = (ulong_t) jpeg->cinfo.src->next_input_byte -
139 (ulong_t) jpeg->Data;
163140 #endif
164141 if (jpeg->Skip) {
165142 jpeg->Start_Ofs = jpeg->NewStart + jpeg->Skip - 1;
166143 jpeg->Skip = 0;
167144 } else {
168 jpeg->Start_Ofs = (gulong) jpeg->cinfo.src->next_input_byte -
169 (gulong) jpeg->Data;
145 jpeg->Start_Ofs = (ulong_t) jpeg->cinfo.src->next_input_byte -
146 (ulong_t) jpeg->Data;
170147 }
171148 return FALSE;
172149 #if 0
175152 #endif
176153 }
177154
178 static void skip_input_data(j_decompress_ptr cinfo, glong num_bytes)
155 static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
179156 {
180157 DilloJpeg *jpeg;
181158
183160 return;
184161 jpeg = ((my_source_mgr *) cinfo->src)->jpeg;
185162
186 DEBUG_MSG(5, "skip_input_data: Start_Ofs = %lu, num_bytes = %ld,"
187 " %ld bytes in buffer\n",
188 (gulong)jpeg->Start_Ofs, num_bytes,
189 (glong)cinfo->src->bytes_in_buffer);
163 _MSG("skip_input_data: Start_Ofs = %lu, num_bytes = %ld,"
164 " %ld bytes in buffer\n",
165 (ulong_t)jpeg->Start_Ofs, num_bytes,(long)cinfo->src->bytes_in_buffer);
190166
191167 cinfo->src->next_input_byte += num_bytes;
192 if (num_bytes < (glong)cinfo->src->bytes_in_buffer) {
168 if (num_bytes < (long)cinfo->src->bytes_in_buffer) {
193169 cinfo->src->bytes_in_buffer -= num_bytes;
194170 } else {
195171 jpeg->Skip += num_bytes - cinfo->src->bytes_in_buffer + 1;
197173 }
198174 }
199175
200 static void term_source(j_decompress_ptr cinfo)
201 {
202 }
203
204 static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, gint version)
176 /*
177 * The proper signature is:
178 * static void term_source(j_decompress_ptr cinfo)
179 * (declaring it with no parameter avoids a compiler warning)
180 */
181 static void term_source()
182 {
183 }
184
185 void *a_Jpeg_new(DilloImage *Image, DilloUrl *url, int version)
205186 {
206187 my_source_mgr *src;
207 DilloJpeg *jpeg = g_malloc(sizeof(*jpeg));
188 DilloJpeg *jpeg = dMalloc(sizeof(*jpeg));
189 _MSG("a_Jpeg_new: jpeg=%p\n", jpeg);
208190
209191 jpeg->Image = Image;
210192 jpeg->url = url;
237219 return jpeg;
238220 }
239221
240 static void Jpeg_callback(int Op, CacheClient_t *Client)
241 {
242 if (Op)
222 void a_Jpeg_callback(int Op, void *data)
223 {
224 if (Op == CA_Send) {
225 CacheClient_t *Client = data;
226 Jpeg_write(Client->CbData, Client->Buf, Client->BufSize);
227 } else if (Op == CA_Close) {
228 CacheClient_t *Client = data;
243229 Jpeg_close(Client->CbData, Client);
244 else
245 Jpeg_write(Client->CbData, Client->Buf, Client->BufSize);
230 } else if (Op == CA_Abort) {
231 Jpeg_free(data);
232 }
246233 }
247234
248235 /*
249236 * Receive and process new chunks of JPEG image data
250237 */
251 static void Jpeg_write(DilloJpeg *jpeg, void *Buf, guint BufSize)
238 static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
252239 {
253240 DilloImgType type;
254 guchar *linebuf;
241 uchar_t *linebuf;
255242 JSAMPLE *array[1];
256 gint num_read;
257
258 DEBUG_MSG(5, "Jpeg_write: (%p) Bytes in buff: %ld Ofs: %lu\n", jpeg,
259 (glong) BufSize, (gulong)jpeg->Start_Ofs);
243 int num_read;
244
245 _MSG("Jpeg_write: (%p) Bytes in buff: %ld Ofs: %lu\n", jpeg,
246 (long) BufSize, (ulong_t)jpeg->Start_Ofs);
260247
261248 /* See if we are supposed to skip ahead. */
262249 if (BufSize <= jpeg->Start_Ofs)
263250 return;
264251
265252 /* Concatenate with the partial input, if any. */
266 jpeg->cinfo.src->next_input_byte = (guchar *)Buf + jpeg->Start_Ofs;
253 jpeg->cinfo.src->next_input_byte = (uchar_t *)Buf + jpeg->Start_Ofs;
267254 jpeg->cinfo.src->bytes_in_buffer = BufSize - jpeg->Start_Ofs;
268255 jpeg->NewStart = BufSize;
269256 jpeg->Data = Buf;
279266 /* decompression step 3 (see libjpeg.doc) */
280267 if (jpeg_read_header(&(jpeg->cinfo), TRUE) != JPEG_SUSPENDED) {
281268 type = DILLO_IMG_TYPE_GRAY;
282 if (jpeg->cinfo.num_components == 1)
269 if (jpeg->cinfo.num_components == 1) {
283270 type = DILLO_IMG_TYPE_GRAY;
284 else if (jpeg->cinfo.num_components == 3)
271 } else if (jpeg->cinfo.num_components == 3) {
285272 type = DILLO_IMG_TYPE_RGB;
286 else
287 DEBUG_MSG(5, "jpeg: can't handle %d component images\n",
288 jpeg->cinfo.num_components);
273 } else {
274 MSG("4-component JPEG!\n");
275 if (jpeg->cinfo.jpeg_color_space == JCS_YCCK)
276 MSG("YCCK. Are the colors wrong?\n");
277 if (!jpeg->cinfo.saw_Adobe_marker)
278 MSG("No adobe marker! Is the image shown in reverse video?\n");
279 type = DILLO_IMG_TYPE_CMYK_INV;
280 }
281 /*
282 * If a multiple-scan image is not completely in cache,
283 * use progressive display, updating as it arrives.
284 */
285 if (jpeg_has_multiple_scans(&jpeg->cinfo) &&
286 !(a_Capi_get_flags(jpeg->url) & CAPI_Completed))
287 jpeg->cinfo.buffered_image = TRUE;
288
289 /* check max image size */
290 if (jpeg->cinfo.image_width <= 0 || jpeg->cinfo.image_height <= 0 ||
291 jpeg->cinfo.image_width >
292 IMAGE_MAX_AREA / jpeg->cinfo.image_height) {
293 MSG("Jpeg_write: suspicious image size request %u x %u\n",
294 (uint_t)jpeg->cinfo.image_width,
295 (uint_t)jpeg->cinfo.image_height);
296 jpeg->state = DILLO_JPEG_ERROR;
297 return;
298 }
299
289300 a_Dicache_set_parms(jpeg->url, jpeg->version, jpeg->Image,
290 (guint)jpeg->cinfo.image_width,
291 (guint)jpeg->cinfo.image_height,
301 (uint_t)jpeg->cinfo.image_width,
302 (uint_t)jpeg->cinfo.image_height,
292303 type);
293304
294305 /* decompression step 4 (see libjpeg.doc) */
299310 /* decompression step 5 (see libjpeg.doc) */
300311 if (jpeg_start_decompress(&(jpeg->cinfo))) {
301312 jpeg->y = 0;
302 jpeg->state = DILLO_JPEG_READING;
303 }
304 }
305 if (jpeg->state == DILLO_JPEG_READING) {
306 linebuf = g_malloc(jpeg->cinfo.image_width *
313 jpeg->state = jpeg->cinfo.buffered_image ?
314 DILLO_JPEG_READ_BEGIN_SCAN : DILLO_JPEG_READ_IN_SCAN;
315 }
316 }
317
318 /*
319 * A progressive jpeg contains multiple scans that can be used to display
320 * an increasingly sharp image as it is being received. The reading of each
321 * scan must be surrounded by jpeg_start_output()/jpeg_finish_output().
322 */
323
324 if (jpeg->state == DILLO_JPEG_READ_END_SCAN) {
325 if (jpeg_finish_output(&jpeg->cinfo)) {
326 if (jpeg_input_complete(&jpeg->cinfo)) {
327 jpeg->state = DILLO_JPEG_DONE;
328 } else {
329 jpeg->state = DILLO_JPEG_READ_BEGIN_SCAN;
330 }
331 }
332 }
333
334 if (jpeg->state == DILLO_JPEG_READ_BEGIN_SCAN) {
335 if (jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number)) {
336 a_Dicache_new_scan(jpeg->url, jpeg->version);
337 jpeg->state = DILLO_JPEG_READ_IN_SCAN;
338 }
339 }
340
341 if (jpeg->state == DILLO_JPEG_READ_IN_SCAN) {
342 linebuf = dMalloc(jpeg->cinfo.image_width *
307343 jpeg->cinfo.num_components);
308344 array[0] = linebuf;
309 while (jpeg->y < jpeg->cinfo.image_height) {
345
346 while (1) {
310347 num_read = jpeg_read_scanlines(&(jpeg->cinfo), array, 1);
311 if (num_read == 0)
348 if (num_read == 0) {
349 /* out of input */
312350 break;
313 a_Dicache_write(jpeg->Image, jpeg->url, jpeg->version,
314 linebuf, 0, jpeg->y);
351 }
352 a_Dicache_write(jpeg->url, jpeg->version, linebuf, jpeg->y);
315353
316354 jpeg->y++;
317 }
318 if (jpeg->y == jpeg->cinfo.image_height) {
319 DEBUG_MSG(5, "height achieved\n");
320
321 jpeg_destroy_decompress(&(jpeg->cinfo));
322 jpeg->state = DILLO_JPEG_DONE;
323 }
324 g_free(linebuf);
325 }
326 if (jpeg->state == DILLO_JPEG_ERROR) {
327 jpeg_destroy_decompress(&(jpeg->cinfo));
328 jpeg->state = DILLO_JPEG_DONE;
329 }
330 }
355
356 if (jpeg->y == jpeg->cinfo.image_height) {
357 /* end of scan */
358 if (!jpeg->cinfo.buffered_image) {
359 /* single scan */
360 jpeg->state = DILLO_JPEG_DONE;
361 break;
362 } else {
363 jpeg->y = 0;
364 if (jpeg_input_complete(&jpeg->cinfo)) {
365 if (jpeg->cinfo.input_scan_number ==
366 jpeg->cinfo.output_scan_number) {
367 jpeg->state = DILLO_JPEG_DONE;
368 break;
369 } else {
370 /* one final loop through the scanlines */
371 jpeg_finish_output(&jpeg->cinfo);
372 jpeg_start_output(&jpeg->cinfo,
373 jpeg->cinfo.input_scan_number);
374 continue;
375 }
376 }
377 jpeg->state = DILLO_JPEG_READ_END_SCAN;
378 if (!jpeg_finish_output(&jpeg->cinfo)) {
379 /* out of input */
380 break;
381 } else {
382 if (jpeg_input_complete(&jpeg->cinfo)) {
383 jpeg->state = DILLO_JPEG_DONE;
384 break;
385 } else {
386 jpeg->state = DILLO_JPEG_READ_BEGIN_SCAN;
387 }
388 }
389 if (!jpeg_start_output(&jpeg->cinfo,
390 jpeg->cinfo.input_scan_number)) {
391 /* out of input */
392 break;
393 }
394 a_Dicache_new_scan(jpeg->url, jpeg->version);
395 jpeg->state = DILLO_JPEG_READ_IN_SCAN;
396 }
397 }
398 }
399 dFree(linebuf);
400 }
401 }
402
403 #else /* ENABLE_JPEG */
404
405 void *a_Jpeg_new() { return 0; }
406 void a_Jpeg_callback() { return; }
331407
332408 #endif /* ENABLE_JPEG */
0 /*
1 * Key parser
2 *
3 * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <FL/Fl.H>
12 #include <stdio.h>
13 #include <stdlib.h> /* strtol */
14 #include <string.h>
15 #include <ctype.h>
16
17 #include "dlib/dlib.h"
18 #include "keys.hh"
19 #include "utf8.hh"
20 #include "msg.h"
21
22 /*
23 * Local data types
24 */
25 typedef struct {
26 const char *name;
27 KeysCommand_t cmd;
28 int modifier, key;
29 } KeyBinding_t;
30
31 typedef struct {
32 const char *name;
33 const int value;
34 } Mapping_t;
35
36
37 /*
38 * Local data
39 */
40 static const Mapping_t keyNames[] = {
41 { "Backspace", FL_BackSpace },
42 { "Delete", FL_Delete },
43 { "Down", FL_Down },
44 { "End", FL_End },
45 { "Esc", FL_Escape },
46 { "F1", FL_F + 1 },
47 { "F2", FL_F + 2 },
48 { "F3", FL_F + 3 },
49 { "F4", FL_F + 4 },
50 { "F5", FL_F + 5 },
51 { "F6", FL_F + 6 },
52 { "F7", FL_F + 7 },
53 { "F8", FL_F + 8 },
54 { "F9", FL_F + 9 },
55 { "F10", FL_F + 10 },
56 { "F11", FL_F + 11 },
57 { "F12", FL_F + 12 },
58 { "Home", FL_Home },
59 { "Insert", FL_Insert },
60 { "Left", FL_Left },
61 { "PageDown", FL_Page_Down },
62 { "PageUp", FL_Page_Up },
63 { "Print", FL_Print },
64 { "Return", FL_Enter },
65 { "Right", FL_Right },
66 { "Space", ' ' },
67 { "Tab", FL_Tab },
68 { "Up", FL_Up },
69 /* multimedia keys */
70 { "Back", FL_Back },
71 { "Favorites", FL_Favorites },
72 { "Forward", FL_Forward },
73 { "HomePage", FL_Home_Page },
74 { "Mail", FL_Mail },
75 { "MediaNext", FL_Media_Next },
76 { "MediaPlay", FL_Media_Play },
77 { "MediaPrev", FL_Media_Prev },
78 { "MediaStop", FL_Media_Stop },
79 { "Refresh", FL_Refresh },
80 { "Search", FL_Search },
81 { "Sleep", FL_Sleep },
82 { "Stop", FL_Stop },
83 { "VolumeDown", FL_Volume_Down },
84 { "VolumeMute", FL_Volume_Mute },
85 { "VolumeUp", FL_Volume_Up },
86 };
87
88 static const Mapping_t modifierNames[] = {
89 { "Shift", FL_SHIFT },
90 { "Ctrl", FL_CTRL },
91 { "Alt", FL_ALT },
92 { "Meta", FL_META },
93 { "Button1", FL_BUTTON1 },
94 { "Button2", FL_BUTTON2 },
95 { "Button3", FL_BUTTON3 }
96 };
97
98 static const KeyBinding_t default_keys[] = {
99 { "nop" , KEYS_NOP , 0 , 0 },
100 { "open" , KEYS_OPEN , FL_CTRL , 'o' },
101 { "new-window" , KEYS_NEW_WINDOW , FL_CTRL , 'n' },
102 { "new-tab" , KEYS_NEW_TAB , FL_CTRL , 't' },
103 { "left-tab" , KEYS_LEFT_TAB , FL_SHIFT , FL_Tab },
104 { "right-tab" , KEYS_RIGHT_TAB , FL_CTRL , FL_Tab },
105 { "close-tab" , KEYS_CLOSE_TAB , FL_CTRL , 'w' },
106 { "find" , KEYS_FIND , FL_CTRL , 'f' },
107 { "websearch" , KEYS_WEBSEARCH , FL_CTRL , 's' },
108 { "bookmarks" , KEYS_BOOKMARKS , FL_CTRL , 'b' },
109 { "reload" , KEYS_RELOAD , FL_CTRL , 'r' },
110 { "stop" , KEYS_STOP , 0 , 0 },
111 { "save" , KEYS_SAVE , 0 , 0 },
112 { "hide-panels" , KEYS_HIDE_PANELS , 0 , FL_Escape },
113 { "file-menu" , KEYS_FILE_MENU , FL_ALT , 'f' },
114 { "close-all" , KEYS_CLOSE_ALL , FL_CTRL , 'q' },
115 { "back" , KEYS_BACK , 0 , FL_BackSpace },
116 { "back" , KEYS_BACK , 0 , ',' },
117 { "forward" , KEYS_FORWARD , FL_SHIFT , FL_BackSpace },
118 { "forward" , KEYS_FORWARD , 0 , '.' },
119 { "goto" , KEYS_GOTO , FL_CTRL , 'l' },
120 { "home" , KEYS_HOME , FL_CTRL , 'h' },
121 { "screen-up" , KEYS_SCREEN_UP , 0 , FL_Page_Up },
122 { "screen-up" , KEYS_SCREEN_UP , 0 , 'b' },
123 { "screen-down" , KEYS_SCREEN_DOWN , 0 , FL_Page_Down },
124 { "screen-down" , KEYS_SCREEN_DOWN , 0 , ' ' },
125 { "line-up" , KEYS_LINE_UP , 0 , FL_Up },
126 { "line-down" , KEYS_LINE_DOWN , 0 , FL_Down },
127 { "left" , KEYS_LEFT , 0 , FL_Left },
128 { "right" , KEYS_RIGHT , 0 , FL_Right },
129 { "top" , KEYS_TOP , 0 , FL_Home },
130 { "bottom" , KEYS_BOTTOM , 0 , FL_End },
131 };
132
133 static Dlist *bindings;
134
135
136
137 /*
138 * Initialize the bindings list
139 */
140 void Keys::init()
141 {
142 KeyBinding_t *node;
143
144 // Fill our key bindings list
145 bindings = dList_new(32);
146 for (uint_t i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) {
147 if (default_keys[i].key) {
148 node = dNew(KeyBinding_t, 1);
149 node->name = dStrdup(default_keys[i].name);
150 node->cmd = default_keys[i].cmd;
151 node->modifier = default_keys[i].modifier;
152 node->key = default_keys[i].key;
153 dList_insert_sorted(bindings, node, nodeByKeyCmp);
154 }
155 }
156 }
157
158 /*
159 * Free data
160 */
161 void Keys::free()
162 {
163 KeyBinding_t *node;
164
165 while ((node = (KeyBinding_t*)dList_nth_data(bindings, 0))) {
166 dFree((char*)node->name);
167 dList_remove_fast(bindings, node);
168 dFree(node);
169 }
170 dList_free(bindings);
171 }
172
173 /*
174 * Compare function by {key,modifier} pairs.
175 */
176 int Keys::nodeByKeyCmp(const void *node, const void *key)
177 {
178 KeyBinding_t *n = (KeyBinding_t*)node, *k = (KeyBinding_t*)key;
179 _MSG("Keys::nodeByKeyCmp modifier=%d\n", k->modifier);
180 return (n->key != k->key) ? (n->key - k->key) : (n->modifier - k->modifier);
181 }
182
183 /*
184 * Look if the just pressed key is bound to a command.
185 * Return value: The command if found, KEYS_NOP otherwise.
186 */
187 KeysCommand_t Keys::getKeyCmd()
188 {
189 KeysCommand_t ret = KEYS_NOP;
190 KeyBinding_t keyNode;
191
192 keyNode.modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL |FL_ALT|FL_META);
193 if (iscntrl(Fl::event_text()[0])) {
194 keyNode.key = Fl::event_key();
195 } else {
196 const char *beyond = Fl::event_text() + Fl::event_length();
197 keyNode.key = a_Utf8_decode(Fl::event_text(), beyond, NULL);
198
199 /* BUG: The idea is to drop the modifiers if their use results in a
200 * different character (e.g., if shift-8 gives '*', drop the shift,
201 * but if ctrl-6 gives '6', keep the ctrl), but we have to compare a
202 * keysym with a Unicode codepoint, which only works for characters
203 * below U+0100 (those known to latin-1).
204 */
205 if (keyNode.key != Fl::event_key())
206 keyNode.modifier = 0;
207 }
208 _MSG("getKeyCmd: evkey=0x%x evtext=\'%s\' key=0x%x, mod=0x%x\n",
209 Fl::event_key(), Fl::event_text(), keyNode.key, keyNode.modifier);
210 void *data = dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
211 if (data)
212 ret = ((KeyBinding_t*)data)->cmd;
213 return ret;
214 }
215
216 /*
217 * Remove a key binding from the table.
218 */
219 void Keys::delKeyCmd(int key, int mod)
220 {
221 KeyBinding_t keyNode, *node;
222 keyNode.key = key;
223 keyNode.modifier = mod;
224
225 node = (KeyBinding_t*) dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
226 if (node) {
227 dList_remove(bindings, node);
228 dFree((char*)node->name);
229 dFree(node);
230 }
231 }
232
233 /*
234 * Takes a key name and looks it up in the mapping table. If
235 * found, its key code is returned. Otherwise -1 is given
236 * back.
237 */
238 int Keys::getKeyCode(char *keyName)
239 {
240 uint_t i;
241 for (i = 0; i < sizeof(keyNames) / sizeof(Mapping_t); i++) {
242 if (!dStrcasecmp(keyNames[i].name, keyName)) {
243 return keyNames[i].value;
244 }
245 }
246
247 return -1;
248 }
249
250 /*
251 * Takes a command name and searches it in the mapping table.
252 * Return value: command code if found, -1 otherwise
253 */
254 KeysCommand_t Keys::getCmdCode(const char *commandName)
255 {
256 uint_t i;
257
258 for (i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) {
259 if (!dStrcasecmp(default_keys[i].name, commandName))
260 return default_keys[i].cmd;
261 }
262 return KEYS_INVALID;
263 }
264
265 /*
266 * Takes a modifier name and looks it up in the mapping table. If
267 * found, its key code is returned. Otherwise -1 is given back.
268 */
269 int Keys::getModifier(char *modifierName)
270 {
271 uint_t i;
272 for (i = 0; i < sizeof(modifierNames) / sizeof(Mapping_t); i++) {
273 if (!dStrcasecmp(modifierNames[i].name, modifierName)) {
274 return modifierNames[i].value;
275 }
276 }
277
278 return -1;
279 }
280
281 /*
282 * Given a keys command, return a shortcut for it, or 0 if there is none
283 * (e.g., for KEYS_NEW_WINDOW, return CTRL+'n').
284 */
285 int Keys::getShortcut(KeysCommand_t cmd)
286 {
287 int len = dList_length(bindings);
288
289 for (int i = 0; i < len; i++) {
290 KeyBinding_t *node = (KeyBinding_t*)dList_nth_data(bindings, i);
291 if (cmd == node->cmd)
292 return node->modifier + node->key;
293 }
294 return 0;
295 }
296
297 /*
298 * Parse a key-combination/command-name pair, and
299 * insert it into the bindings list.
300 */
301 void Keys::parseKey(char *key, char *commandName)
302 {
303 char *p, *modstr, *keystr;
304 KeysCommand_t symcode;
305 int st, keymod = 0, keycode = 0;
306
307 _MSG("Keys::parseKey key='%s' commandName='%s'\n", key, commandName);
308
309 // Get command code
310 if ((symcode = getCmdCode(commandName)) == KEYS_INVALID) {
311 MSG("Keys::parseKey: Invalid command name: '%s'\n", commandName);
312 return;
313 }
314
315 // Skip space
316 for ( ; isspace(*key); ++key) ;
317 // Get modifiers
318 while(*key == '<' && (p = strchr(key, '>'))) {
319 ++key;
320 modstr = dStrndup(key, p - key);
321 if ((st = getModifier(modstr)) == -1) {
322 MSG("Keys::parseKey unknown modifier: %s\n", modstr);
323 } else {
324 keymod |= st;
325 }
326 dFree(modstr);
327 key = p + 1;
328 }
329 // Allow trailing space after keyname
330 keystr = (*key && (p = strchr(key + 1, ' '))) ? dStrndup(key, p - key - 1) :
331 dStrdup(key);
332 // Get key code
333 if (!key[1]) {
334 keycode = *key;
335 } else if (a_Utf8_char_count(keystr, strlen(keystr)) == 1) {
336 const char *beyond = keystr + strlen(keystr);
337 keycode = a_Utf8_decode(keystr, beyond, NULL);
338 } else if (key[0] == '0' && key[1] == 'x') {
339 /* keysym */
340 keycode = strtol(key, NULL, 0x10);
341 } else if ((st = getKeyCode(keystr)) == -1) {
342 MSG("Keys::parseKey unknown keyname: %s\n", keystr);
343 } else {
344 keycode = st;
345 }
346 dFree(keystr);
347
348 // Set binding
349 if (keycode) {
350 delKeyCmd(keycode, keymod);
351 if (symcode != KEYS_NOP) {
352 KeyBinding_t *node = dNew(KeyBinding_t, 1);
353 node->name = dStrdup(commandName);
354 node->cmd = symcode;
355 node->modifier = keymod;
356 node->key = keycode;
357 dList_insert_sorted(bindings, node, nodeByKeyCmp);
358 _MSG("parseKey: Adding key=%d, mod=%d\n", node->key, node->modifier);
359 }
360 }
361 }
362
363 /*
364 * Parse the keysrc.
365 */
366 void Keys::parse(FILE *fp)
367 {
368 char *line, *keycomb, *command;
369 int st, lineno = 1;
370
371 // scan the file line by line
372 while ((line = dGetline(fp)) != NULL) {
373 st = dParser_parse_rc_line(&line, &keycomb, &command);
374
375 if (st == 0) {
376 _MSG("Keys::parse: keycomb=%s, command=%s\n", keycomb, command);
377 parseKey(keycomb, command);
378 } else if (st < 0) {
379 MSG("Keys::parse: Syntax error in keysrc line %d: "
380 "keycomb=\"%s\" command=\"%s\"\n", lineno, keycomb, command);
381 }
382
383 dFree(line);
384 ++lineno;
385 }
386 fclose(fp);
387 }
0 /*
1 * Key parser
2 *
3 * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #ifndef __KEYS_HH__
12 #define __KEYS_HH__
13
14
15 typedef enum {
16 KEYS_INVALID = -1,
17 KEYS_NOP, /* No operation bound */
18 KEYS_OPEN,
19 KEYS_NEW_WINDOW,
20 KEYS_NEW_TAB,
21 KEYS_LEFT_TAB,
22 KEYS_RIGHT_TAB,
23 KEYS_CLOSE_TAB,
24 KEYS_FIRST_TAB,
25 KEYS_LAST_TAB,
26 KEYS_FIND,
27 KEYS_WEBSEARCH,
28 KEYS_BOOKMARKS,
29 KEYS_RELOAD,
30 KEYS_STOP,
31 KEYS_SAVE,
32 KEYS_HIDE_PANELS,
33 KEYS_FILE_MENU,
34 KEYS_CLOSE_ALL,
35 KEYS_BACK,
36 KEYS_FORWARD,
37 KEYS_GOTO,
38 KEYS_HOME,
39 KEYS_SCREEN_UP,
40 KEYS_SCREEN_DOWN,
41 KEYS_LINE_UP,
42 KEYS_LINE_DOWN,
43 KEYS_LEFT,
44 KEYS_RIGHT,
45 KEYS_TOP,
46 KEYS_BOTTOM
47 } KeysCommand_t;
48
49 class Keys {
50 private:
51 static int nodeByKeyCmp(const void *node, const void *key);
52 static void delKeyCmd(int key, int mod);
53 static KeysCommand_t getCmdCode(const char *symbolName);
54 static int getKeyCode(char *keyName);
55 static int getModifier(char *modifierName);
56 static void parseKey(char *key, char *symbol);
57 public:
58 static void init();
59 static void free();
60 static void parse(FILE *fp);
61 static KeysCommand_t getKeyCmd(void);
62 static int getShortcut(KeysCommand_t cmd);
63 };
64
65
66 #endif /* __KEYS_HH__ */
0 # keysrc
1 # Sample dillo key bindings file.
2 #
3 # The format is: "key = action" or "<modifier>key = action".
4 # Lines that begin with a '#' are comments.
5 # The commented-out bindings below show the defaults built into Dillo.
6 #
7 # Modifiers recognized: "Shift", "Ctrl", "Alt", "Meta".
8 #
9 # Key names recognized: "Backspace", "Delete", "Down", "End", "Esc",
10 # "F1" through "F12", "Home", "Insert", "Left", "PageDown", "PageUp",
11 # "Print", "Return", "Right", "Space", "Tab", "Up".
12 #
13 # Multimedia keys: "Back", "Favorites", "Forward", "HomePage", "Mail",
14 # "MediaNext", "MediaPlay", "MediaPrev", "MediaStop", "Refresh", "Search",
15 # "Sleep", "Stop", "VolumeDown", "VolumeMute", VolumeUp".
16 #
17 # If Dillo is running under X11, keys whose names are not recognized can
18 # be specified using their keysym value in hexadecimal. Use xev to get
19 # the keysym. Example rule: "0x1008ff27 = forward".
20 #
21 # The action "nop" (no operation) can be used to remove a binding.
22
23 # "open" lets you browse your local files for one to open.
24 #<ctrl>o = open
25
26 # "new-window" opens a new browser window.
27 #<ctrl>n = new-window
28
29 # "new-tab" opens a new tab in the current browser window.
30 #<ctrl>t = new-tab
31
32 # "close-tab" closes the current tab.
33 # Note that this closes the browser window if there is only one tab.
34 #<ctrl>w = close-tab
35
36 # "close-all" closes all tabs/windows and exits.
37 #<ctrl>q = close-all
38
39 # "left-tab" and "right-tab" switch to the left/right of the current tab.
40 # <shift>tab = left-tab
41 # <ctrl>tab = right-tab
42
43 # "back" and "forward" move back/forward through the browser history.
44 #backspace = back
45 #<shift>backspace = forward
46 #, = back
47 #. = forward
48
49 # "reload" the current page.
50 #<ctrl>r = reload
51
52 # "home" goes to the homepage that you set in your dillorc.
53 #<ctrl>h = home
54
55 # "find" lets you search for a text string on the current page.
56 #<ctrl>f = find
57
58 # "hide-panels" hides the findbar if present, control panels if not.
59 #esc = hide-panels
60
61 # "websearch" lets you send a text string to the search engine that you
62 # set in your dillorc.
63 #<ctrl>s = websearch
64
65 # go to your "bookmarks".
66 #<ctrl>b = bookmarks
67
68 # "file-menu" pops up the file menu.
69 #<alt>f = file-menu
70
71 # "goto" goes to the location bar at the top of the window.
72 #<ctrl>l = goto
73
74 # "stop" loading the page.
75 #(stop has no default binding)
76
77 # "save" the current page.
78 #(save has no default binding)
79
80 #--------------------------------------------------------------------
81 # MOTION COMMANDS
82 #--------------------------------------------------------------------
83
84 #pageup = screen-up
85 #b = screen-up
86
87 #pagedown = screen-down
88 #space = screen-down
89
90 #up = line-up
91
92 #down = line-down
93
94 #left = left
95
96 #right = right
97
98 #home = top
99
100 #end = bottom
101
00 /*
11 * File: klist.c
22 *
3 * Copyright 2001 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright 2001-2007 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
99 */
1010
2323 /*
2424 * Compare function for searching data by its key
2525 */
26 static gint Klist_key_cmp(gconstpointer Node, gconstpointer key)
26 static int Klist_node_by_key_cmp(const void *Node, const void *key)
2727 {
28 return ( GPOINTER_TO_INT(key) != ((KlistNode_t *)Node)->Key );
28 return ((KlistNode_t *)Node)->Key - VOIDP2INT(key);
29 }
30
31 /*
32 * Compare function for searching data by node
33 */
34 static int Klist_node_by_node_cmp(const void *Node1, const void *Node2)
35 {
36 return ((KlistNode_t *)Node1)->Key - ((KlistNode_t *)Node2)->Key;
2937 }
3038
3139 /*
3240 * Return the data pointer for a given Key (or NULL if not found)
3341 */
34 gpointer a_Klist_get_data(Klist_t *Klist, gint Key)
42 void *a_Klist_get_data(Klist_t *Klist, int Key)
3543 {
36 GSList *list;
44 void *aux;
3745
3846 if (!Klist)
3947 return NULL;
40 list= g_slist_find_custom(Klist->List, GINT_TO_POINTER(Key), Klist_key_cmp);
41 return (list) ? ((KlistNode_t *)list->data)->Data : NULL;
48 aux = dList_find_sorted(Klist->List, INT2VOIDP(Key), Klist_node_by_key_cmp);
49 return (aux) ? ((KlistNode_t*)aux)->Data : NULL;
4250 }
4351
4452 /*
4553 * Insert a data pointer and return a key for it.
4654 */
47 gint a_Klist_insert(Klist_t **Klist, gpointer Data)
55 int a_Klist_insert(Klist_t **Klist, void *Data)
4856 {
4957 KlistNode_t *Node;
5058
5159 if (!*Klist) {
52 (*Klist) = g_new(Klist_t, 1);
53 (*Klist)->List = NULL;
60 (*Klist) = dNew(Klist_t, 1);
61 (*Klist)->List = dList_new(32);
5462 (*Klist)->Clean = 1;
5563 (*Klist)->Counter = 0;
5664 }
5765
5866 /* This avoids repeated keys at the same time */
5967 do {
60 if ( ++((*Klist)->Counter) == 0 ) {
68 if (++((*Klist)->Counter) == 0) {
6169 (*Klist)->Counter = 1;
6270 (*Klist)->Clean = 0;
6371 }
6472 } while (!((*Klist)->Clean) &&
6573 a_Klist_get_data((*Klist), (*Klist)->Counter));
6674
67 Node = g_new(KlistNode_t, 1);
75 Node = dNew(KlistNode_t, 1);
6876 Node->Key = (*Klist)->Counter;
6977 Node->Data = Data;
70 (*Klist)->List = g_slist_prepend((*Klist)->List, Node);
71
78 dList_insert_sorted((*Klist)->List, Node, Klist_node_by_node_cmp);
7279 return (*Klist)->Counter;
7380 }
7481
7582 /*
7683 * Remove data by Key
7784 */
78 void a_Klist_remove(Klist_t *Klist, gint Key)
85 void a_Klist_remove(Klist_t *Klist, int Key)
7986 {
80 GSList *list;
87 void *data;
8188
82 list= g_slist_find_custom(Klist->List, GINT_TO_POINTER(Key), Klist_key_cmp);
83 if (list) {
84 g_free(list->data);
85 Klist->List = g_slist_remove(Klist->List, list->data);
89 data = dList_find_sorted(Klist->List, INT2VOIDP(Key),Klist_node_by_key_cmp);
90 if (data) {
91 dList_remove(Klist->List, data);
92 dFree(data);
8693 }
87 if (Klist->List == NULL)
94 if (dList_length(Klist->List) == 0)
8895 Klist->Clean = 1;
96 }
97
98 /*
99 * Return the number of elements in the Klist
100 */
101 int a_Klist_length(Klist_t *Klist)
102 {
103 return dList_length(Klist->List);
89104 }
90105
91106 /*
93108 */
94109 void a_Klist_free(Klist_t **KlistPtr)
95110 {
96 gpointer node;
111 void *node;
97112 Klist_t *Klist = *KlistPtr;
98113
99114 if (!Klist)
100115 return;
101116
102 while (Klist && Klist->List) {
103 node = Klist->List->data;
104 Klist->List = g_slist_remove(Klist->List, node);
105 g_free(node);
117 while (dList_length(Klist->List) > 0) {
118 node = dList_nth_data(Klist->List, 0);
119 dList_remove_fast(Klist->List, node);
120 dFree(node);
106121 }
107 g_free(Klist);
122 dList_free(Klist->List);
123 dFree(Klist);
108124 *KlistPtr = NULL;
109125 }
110126
00 #ifndef __KLIST_H__
11 #define __KLIST_H__
22
3 #include <glib.h>
3 #include "../dlib/dlib.h"
44
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
59
610 typedef struct _KlistNode KlistNode_t;
711 typedef struct _Klist Klist_t;
812
913 struct _KlistNode {
10 gint Key; /* primary key */
11 gpointer *Data; /* data reference */
14 int Key; /* primary key */
15 void *Data; /* data reference */
1216 };
1317
1418 struct _Klist {
15 GSList *List;
16 gint Clean; /* check flag */
17 gint Counter; /* counter (for making keys) */
19 Dlist *List;
20 int Clean; /* check flag */
21 int Counter; /* counter (for making keys) */
1822 };
1923
2024
2125 /*
2226 * Function prototypes
2327 */
24 gpointer a_Klist_get_data(Klist_t *Klist, gint Key);
25 gint a_Klist_insert(Klist_t **Klist, gpointer Data);
26 void a_Klist_remove(Klist_t *Klist, gint Key);
28 void* a_Klist_get_data(Klist_t *Klist, int Key);
29 int a_Klist_insert(Klist_t **Klist, void *Data);
30 void a_Klist_remove(Klist_t *Klist, int Key);
31 int a_Klist_length(Klist_t *Klist);
2732 void a_Klist_free(Klist_t **Klist);
2833
2934
35 #ifdef __cplusplus
36 }
37 #endif /* __cplusplus */
38
3039 #endif /* __KLIST_H__ */
1414 * list size --to make it faster)
1515 */
1616 #define a_List_resize(list,num_items,alloc_step) \
17 if ( !list ) { \
18 list = g_malloc(alloc_step * sizeof((*list))); \
17 if (!list) { \
18 list = dMalloc(alloc_step * sizeof((*list))); \
1919 } \
20 if ( num_items >= alloc_step ){ \
20 if (num_items >= alloc_step){ \
2121 while ( num_items >= alloc_step ) \
2222 alloc_step <<= 1; \
23 list = g_realloc(list, alloc_step * sizeof((*list))); \
23 list = dRealloc(list, alloc_step * sizeof((*list))); \
2424 }
2525
2626
4040 * ==> We preserve relative position, but not the element index <==
4141 */
4242 #define a_List_remove(list, item, num_items) \
43 if ( list && item < num_items ) { \
43 if (list && item < num_items) { \
4444 list[item] = list[--num_items]; \
4545 }
4646
+0
-526
src/menu.c less more
0 /*
1 * Dillo
2 *
3 * Some code copied from:
4 * The GIMP -- an image manipulation program
5 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <gtk/gtk.h>
26 #include <gdk/gdkkeysyms.h>
27 #include "history.h"
28 #include "nav.h"
29 #include "commands.h"
30 #include "dw_gtk_scrolled_window.h"
31 #include "dw_page.h" /* for a_Dw_page_add_anchor */
32 #include "bookmark.h"
33 #include "interface.h"
34 #include "menu.h"
35 #include "selection.h"
36 #include "gtk_ext_menu.h"
37 #include "gtk_ext_menu_item.h"
38 #include "gtk_menu_title.h"
39
40 /*
41 * Forward declarations
42 */
43 static void Menu_pagemarks_goto_pagemark(GtkWidget *widget,
44 gpointer client_data);
45
46 /*
47 * Make a new menu, insert it into the menu bar, and return it.
48 */
49 static GtkWidget* Menu_new(GtkWidget *menubar, const char *name,
50 gboolean right_justify, BrowserWindow *bw)
51 {
52 GtkWidget *menu;
53 GtkWidget *menuitem;
54 guint tmp_key;
55
56 menu = gtk_menu_new();
57 menuitem = gtk_menu_item_new_with_label((char *) name);
58 tmp_key = gtk_label_parse_uline(GTK_LABEL(GTK_BIN(menuitem)->child), name);
59 gtk_widget_add_accelerator(menuitem, "activate_item", bw->accel_group,
60 tmp_key, GDK_MOD1_MASK, 0);
61
62 if ( right_justify )
63 gtk_menu_item_right_justify(GTK_MENU_ITEM(menuitem));
64 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
65 gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem);
66 gtk_widget_show(menuitem);
67 gtk_menu_set_accel_group(GTK_MENU(menu), bw->accel_group);
68 return menu;
69 }
70
71 /*
72 * Pick the right URL for this popup.
73 */
74 static void Menu_pick_url (GtkWidget *widget, gpointer client_data)
75 {
76 BrowserWindow *bw = (BrowserWindow *)client_data;
77
78 if (gtk_object_get_data(GTK_OBJECT (GTK_MENU (widget->parent)), "url2")) {
79 a_Menu_popup_set_url(bw, bw->menu_popup.url2);
80 }
81 /* clean the url2 tag (currently "over_image" is the only one using it) */
82 gtk_object_remove_data(GTK_OBJECT (bw->menu_popup.over_image), "url2");
83 }
84
85 /*
86 * Add an item to a menu, including the name, an accelerator (not yet
87 * implemented), and a callback function for activation.
88 */
89 static GtkWidget *
90 Menu_add (GtkWidget *menu, const char *name, const char *accel,
91 BrowserWindow *bw,
92 void (*callback) (GtkWidget *widget, void *data), void *data)
93 {
94 GtkWidget *menuitem;
95 GtkAccelGroup *menu_accels;
96 GdkModifierType accel_mods;
97 guint accel_key;
98 guint tmp_key;
99
100 menuitem = gtk_menu_item_new_with_label((char *) name);
101 gtk_menu_append(GTK_MENU(menu), menuitem);
102 menu_accels = gtk_menu_ensure_uline_accel_group(GTK_MENU(menu));
103 tmp_key = gtk_label_parse_uline(GTK_LABEL(GTK_BIN(menuitem)->child), name);
104 gtk_widget_add_accelerator(menuitem, "activate_item",
105 menu_accels, tmp_key, 0, 0);
106 gtk_widget_show(menuitem);
107
108 if (accel != NULL) {
109 gtk_accelerator_parse(accel, &accel_key, &accel_mods);
110 gtk_widget_add_accelerator(menuitem, "activate", bw->accel_group,
111 accel_key, (guint)accel_mods, GTK_ACCEL_VISIBLE);
112 }
113 if (callback != NULL) {
114 gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
115 (GtkSignalFunc) Menu_pick_url, bw);
116 gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
117 (GtkSignalFunc) callback, data);
118 }
119 return menuitem;
120 }
121
122 /*
123 * Add a menu title.
124 */
125 static GtkWidget *Menu_add_title (GtkWidget *menu, const char *name)
126 {
127 GtkWidget *menuitem;
128
129 menuitem = a_Gtk_menu_title_new(name);
130 gtk_menu_append(GTK_MENU(menu), menuitem);
131 gtk_widget_show(menuitem);
132 return menuitem;
133 }
134
135 /*
136 * Add a separator into the menu.
137 */
138 static void Menu_sep(GtkWidget *menu)
139 {
140 GtkWidget *widget;
141
142 widget = gtk_menu_item_new();
143 gtk_menu_append(GTK_MENU(menu), widget);
144 gtk_widget_set_sensitive(widget, FALSE);
145 gtk_widget_show(widget);
146 }
147
148 /*
149 * Make up a new menubar for a main browser window. The accelerator table
150 * is stored in bw->accel_group.
151 * Currently does not deal with dynamic menu items (bookmarks and history).
152 * CP: It *seems* to handle dynamic menu items...
153 */
154 GtkWidget *a_Menu_mainbar_new(BrowserWindow *bw, gint tiny)
155 {
156 GtkWidget *menubar;
157 GtkWidget *file_menu;
158 /* GtkWidget *help_menu; */
159
160 bw->menubar = menubar = gtk_menu_bar_new();
161
162 /* FILE MENU */
163 file_menu = Menu_new(menubar, tiny ? "_F" : "_File", FALSE, bw);
164 Menu_add(file_menu, "_New Browser", "<ctrl>N", bw,
165 a_Commands_new_callback, bw);
166 Menu_add(file_menu, "_Open File...", "<ctrl>O", bw,
167 a_Commands_openfile_callback, bw);
168 Menu_add(file_menu, "Open _URL...", "<ctrl>L", bw,
169 a_Commands_openurl_callback, bw);
170 /*
171 Menu_add(file_menu, "_Preferences", "<ctrl>E", bw,
172 a_Commands_prefs_callback, bw);
173 */
174 Menu_add(file_menu, "Close Window", "<ctrl>Q", bw,
175 a_Commands_close_callback, bw);
176 Menu_sep(file_menu);
177 Menu_add(file_menu, "Exit Dillo", "<alt>Q", bw,
178 a_Commands_exit_callback, bw);
179
180 /* HELP MENU
181 help_menu = Menu_new(menubar, "_Help", TRUE, bw);
182 Menu_add(help_menu, "Dillo _Home", NULL, bw,
183 a_Commands_helphome_callback, bw);
184 */
185 return menubar;
186 }
187
188 /*
189 * Make a new popup menu and return it.
190 */
191 GtkWidget *a_Menu_popup_op_new(BrowserWindow *bw)
192 {
193 GtkWidget *menu;
194
195 menu = gtk_menu_new();
196 Menu_sep(menu);
197 Menu_add_title(menu, "PAGE OPTIONS");
198 Menu_sep(menu);
199 Menu_add(menu, "View page Source", NULL, bw,
200 a_Commands_viewsource_callback, bw);
201 bw->viewbugs_menuitem = Menu_add(menu, "View page Bugs", NULL,bw, NULL,bw);
202
203 Menu_add(menu, "Bookmark this page", NULL, bw,
204 a_Commands_addbm_callback, bw);
205 Menu_sep(menu);
206 Menu_add(menu, "_Find Text", "<ctrl>F", bw,
207 a_Commands_findtext_callback, bw);
208
209 bw->pagemarks_menuitem = Menu_add(menu, "Jump to...", NULL, bw, NULL, bw);
210
211 Menu_sep(menu);
212 Menu_add(menu, "Save page As...", NULL, bw,
213 a_Commands_save_callback, bw);
214
215 return menu;
216 }
217
218 /*
219 * Set the popup's primary DilloUrl
220 */
221 void a_Menu_popup_set_url(BrowserWindow *bw, const DilloUrl *url)
222 {
223 if (bw->menu_popup.url)
224 a_Url_free(bw->menu_popup.url);
225 bw->menu_popup.url = a_Url_dup(url);
226 }
227
228 /*
229 * Set the popup's secondary DilloUrl
230 * Note: this URL is used by the image submenu of the link popup.
231 * (If the need arises, this can be extended using an URL array in the popup
232 * structure, and by setting the to-be-picked-URL-index in the popup)
233 */
234 void a_Menu_popup_set_url2(BrowserWindow *bw, const DilloUrl *url)
235 {
236 if (bw->menu_popup.url2)
237 a_Url_free(bw->menu_popup.url2);
238 bw->menu_popup.url2 = a_Url_dup(url);
239 }
240
241 /*
242 * Remove the popup's secondary DilloUrl.
243 * (This is done at popup time, so and old "url2" can't linger)
244 */
245 void a_Menu_popup_clear_url2(GtkWidget *menu_popup)
246 {
247 /* clear the url2 tag (currently "over_image" is the only one using it) */
248 gtk_object_remove_data(GTK_OBJECT (menu_popup), "url2");
249 }
250
251 /*
252 * Get the popup's DilloUrl
253 */
254 DilloUrl *a_Menu_popup_get_url(BrowserWindow *bw)
255 {
256 return bw->menu_popup.url;
257 }
258
259 /*
260 * Put the url in the status bar when moving cursor over menuitem.
261 */
262 static void
263 Menu_popup_history_select_callback(GtkWidget *widget, gpointer data)
264 {
265 gint idx;
266 BrowserWindow *bw = data;
267
268 idx = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT (widget), "nav_idx"));
269 if (idx >= 0 && idx < a_Nav_stack_size(bw)) {
270 a_Interface_msg(bw, "%s", URL_STR_(a_History_get_url(NAV_IDX(bw,idx))));
271 bw->status_is_link = 1;
272 }
273 }
274
275 /*
276 * Remove the url in the status bar when removing cursor from menuitem.
277 */
278 static void
279 Menu_popup_history_deselect_callback(GtkWidget *widget, gpointer data) {
280 BrowserWindow *bw = data;
281
282 a_Interface_msg(bw, "");
283 }
284
285 /*
286 * Make a new popup menu for history browsing
287 * direction: {backward = -1, forward = 1}
288 */
289 GtkWidget *a_Menu_popup_history_new(BrowserWindow *bw, gint direction)
290 {
291 int i;
292 GtkWidget *menu, *menu_item;
293 GString *text = g_string_sized_new(128);
294
295 menu = a_Gtk_ext_menu_new();
296 Menu_sep(menu);
297 if (direction>0) {
298 Menu_add_title(menu, "FOLLOWING PAGES");
299 i = a_Nav_stack_ptr(bw) + 1;
300 } else {
301 Menu_add_title(menu, "PREVIOUS PAGES");
302 i = a_Nav_stack_ptr(bw) - 1;
303 }
304 Menu_sep(menu);
305
306 for ( ; i >= 0 && i < a_Nav_stack_size(bw); i+=direction ) {
307 if (a_History_get_title(NAV_IDX(bw,i))) {
308 g_string_assign(text, a_History_get_title(NAV_IDX(bw,i)));
309 } else {
310 g_string_assign(text, URL_STR(a_History_get_url(NAV_IDX(bw,i))));
311 }
312 if ( text->len > 64 ) {
313 g_string_truncate(text, 64);
314 g_string_append(text, "...");
315 }
316 menu_item = a_Gtk_ext_menu_item_new_with_label(text->str);
317 gtk_menu_append(GTK_MENU(menu), menu_item);
318 gtk_widget_show(menu_item);
319 /* attach the nav_stack index to the menu item */
320 gtk_object_set_data(GTK_OBJECT (menu_item), "nav_idx",
321 GINT_TO_POINTER(i));
322
323 gtk_signal_connect (
324 GTK_OBJECT (menu_item), "select",
325 GTK_SIGNAL_FUNC (a_Interface_scroll_popup), NULL);
326 gtk_signal_connect (
327 GTK_OBJECT (menu_item), "select",
328 GTK_SIGNAL_FUNC (Menu_popup_history_select_callback), bw);
329 gtk_signal_connect (
330 GTK_OBJECT (menu_item), "deselect",
331 GTK_SIGNAL_FUNC (Menu_popup_history_deselect_callback), bw);
332
333 gtk_signal_connect (
334 GTK_OBJECT (menu_item), "activate",
335 GTK_SIGNAL_FUNC (a_Commands_history_callback_same_bw), bw);
336 gtk_signal_connect (
337 GTK_OBJECT (menu_item), "activate1",
338 GTK_SIGNAL_FUNC (a_Commands_history_callback_same_bw), bw);
339 gtk_signal_connect (
340 GTK_OBJECT (menu_item), "activate2",
341 GTK_SIGNAL_FUNC (a_Commands_history_callback_new_bw), bw);
342 gtk_signal_connect (
343 GTK_OBJECT (menu_item), "activate3",
344 GTK_SIGNAL_FUNC (a_Commands_history_callback_same_bw), bw);
345 }
346
347 g_string_free(text, TRUE);
348 return menu;
349 }
350
351 /*
352 * Make a new popup menu for when the mouse is over a link.
353 */
354 GtkWidget *a_Menu_popup_ol_new(BrowserWindow *bw)
355 {
356 GtkWidget *menu;
357 GtkWidget *copy;
358 GtkWidget *menuitem;
359
360 menu = gtk_menu_new();
361 Menu_sep(menu);
362 Menu_add_title(menu, "LINK OPTIONS");
363 Menu_sep(menu);
364 Menu_add(menu, "Open Link in New Window", NULL, bw,
365 a_Commands_open_link_nw_callback, bw);
366 Menu_add(menu, "Bookmark this Link", NULL, bw,
367 a_Commands_addbm_callback, bw);
368
369 copy = Menu_add(menu, "Copy Link location", NULL, bw,
370 a_Commands_select_popup_url_callback, bw);
371 a_Selection_init_selection(copy);
372
373 Menu_sep(menu);
374 Menu_add(menu, "Save Link As...", NULL, bw,
375 a_Commands_save_link_callback, bw);
376
377 Menu_sep(menu);
378 menuitem = Menu_add(menu, "Image Menu...", NULL, bw, NULL, bw);
379 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem),
380 bw->menu_popup.over_image);
381 bw->menu_popup.ol_oi_submenu = menuitem;
382
383 return menu;
384 }
385
386 /*
387 * Make a new popup menu for when the mouse is over an image
388 */
389 GtkWidget *a_Menu_popup_oi_new (BrowserWindow *bw)
390 {
391 GtkWidget *menu;
392 GtkWidget *copy;
393
394 menu = gtk_menu_new();
395 Menu_sep(menu);
396 Menu_add_title(menu, "IMAGE OPTIONS");
397 Menu_sep(menu);
398 Menu_add(menu, "Isolate Image", NULL, bw,
399 a_Commands_open_link_callback, bw);
400 Menu_add(menu, "Open Image in New Window", NULL, bw,
401 a_Commands_open_link_nw_callback, bw);
402 Menu_add(menu, "Bookmark this Image", NULL, bw,
403 a_Commands_addbm_callback, bw);
404 copy = Menu_add(menu, "Copy Image location", NULL, bw,
405 a_Commands_select_popup_url_callback, bw);
406 a_Selection_init_selection(copy);
407
408 Menu_sep(menu);
409 Menu_add(menu, "Save Image As...", NULL, bw,
410 a_Commands_save_link_callback, bw);
411 return menu;
412 }
413
414 /*
415 * Make a new popup menu for right click over the bug meter.
416 */
417 GtkWidget *a_Menu_popup_ob_new (BrowserWindow *bw)
418 {
419 GtkWidget *menu;
420
421 menu = gtk_menu_new();
422 Menu_sep(menu);
423 Menu_add_title(menu, "BUG METER OPTIONS");
424 Menu_sep(menu);
425 Menu_add(menu, "Validate URL with W3C", NULL, bw,
426 a_Commands_ob_w3c_callback, bw);
427 Menu_add(menu, "Validate URL with WDG", NULL, bw,
428 a_Commands_ob_wdg_callback, bw);
429 Menu_sep(menu);
430 Menu_add(menu, "About Bug Meter...", NULL, bw,
431 a_Commands_ob_info_callback, bw);
432 return menu;
433 }
434
435 /*
436 * Show or Hide the image submenu.
437 */
438 void a_Menu_popup_ol_show_oi(BrowserWindow *bw, gboolean show)
439 {
440 if (show)
441 gtk_widget_show(bw->menu_popup.ol_oi_submenu);
442 else
443 gtk_widget_hide(bw->menu_popup.ol_oi_submenu);
444 }
445
446 /*
447 * Functions to manipulate Pagemarks menu.
448 * Should this be in a separate "pagemark.c" ?
449 */
450
451 /*
452 * make the page scroll to the pagemark
453 */
454 static void Menu_pagemarks_goto_pagemark(GtkWidget *widget,
455 gpointer client_data)
456 {
457 BrowserWindow *bw = (BrowserWindow *) client_data;
458 char anchor[32];
459
460 g_snprintf (anchor, 32, "#%ld", (long int)widget);
461 a_Dw_gtk_scrolled_window_set_anchor(GTK_DW_SCROLLED_WINDOW(bw->docwin),
462 anchor);
463 }
464
465 /*
466 * Deallocate the memory used by a pagemarks menu and create a new one
467 */
468 void a_Menu_pagemarks_new(BrowserWindow *bw)
469 {
470 gtk_widget_set_sensitive(bw->pagemarks_menuitem, FALSE);
471
472 if (bw->pagemarks_menu)
473 gtk_widget_destroy(bw->pagemarks_menu);
474 bw->pagemarks_menu = gtk_menu_new();
475 bw->pagemarks_last = NULL;
476
477 gtk_menu_item_set_submenu(GTK_MENU_ITEM(bw->pagemarks_menuitem),
478 bw->pagemarks_menu);
479 }
480
481 /*
482 * Add a new pagemark (opening H tag).
483 * The text can be set only at the closing H tag.
484 * level is the level of the heading (1-6).
485 */
486 void a_Menu_pagemarks_add(BrowserWindow *bw, void *page, void *style,
487 gint level)
488 {
489 char anchor[32], spaces[32], name[32];
490
491 gtk_widget_set_sensitive(bw->pagemarks_menuitem, TRUE);
492 g_snprintf(spaces, 32, "%*s", 3 * (level - 1), "");
493 bw->pagemarks_last = Menu_add(bw->pagemarks_menu, spaces, NULL, bw,
494 Menu_pagemarks_goto_pagemark, bw);
495 g_snprintf(anchor, 32, "#%ld", (glong)(bw->pagemarks_last));
496 a_Dw_page_add_anchor(page, anchor, style);
497 gtk_signal_connect (GTK_OBJECT (bw->pagemarks_last), "select",
498 GTK_SIGNAL_FUNC (a_Interface_scroll_popup), NULL);
499 g_snprintf(name, 32, "dilloHeading%d", level);
500 gtk_widget_set_name(bw->pagemarks_last, name);
501 }
502
503 /*
504 * Set the text for the last created pagemark.
505 */
506 void a_Menu_pagemarks_set_text(BrowserWindow *bw, const char *str)
507 {
508 GString *text;
509 GtkWidget *child;
510
511 /* Avoid problems with lonely closing tags and nested headings */
512 if ( bw->pagemarks_last &&
513 (child = GTK_BIN (bw->pagemarks_last)->child) &&
514 GTK_IS_LABEL (child) ) {
515 text = g_string_new(GTK_LABEL(child)->label);
516 g_string_append(text, str);
517 if ( text->len > 64 ) {
518 g_string_truncate(text, 64);
519 g_string_append(text, "...");
520 }
521 gtk_label_set_text(GTK_LABEL (child), text->str);
522 g_string_free(text, 1);
523 bw->pagemarks_last = NULL;
524 }
525 }
0 /*
1 * File: menu.cc
2 *
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 // Functions/Methods for menus
12
13 #include <FL/Fl.H>
14 #include <FL/Fl_Menu_Item.H>
15
16 #include "lout/misc.hh" /* SimpleVector */
17 #include "msg.h"
18 #include "menu.hh"
19 #include "uicmd.hh"
20 #include "history.h"
21 #include "html.hh"
22 #include "ui.hh" // for (UI *)
23 #include "keys.hh"
24 #include "timeout.hh"
25
26 /*
27 * Local data
28 */
29
30 // (This data can be encapsulated inside a class for each popup, but
31 // as popups are modal, there's no need).
32 static DilloUrl *popup_url = NULL;
33 // Weak reference to the popup's bw
34 static BrowserWindow *popup_bw = NULL;
35 static void *popup_form = NULL;
36 // Where to place the popup
37 static int popup_x, popup_y;
38 // History popup direction (-1 = back, 1 = forward).
39 static int history_direction = -1;
40 // History popup, list of URL-indexes.
41 static int *history_list = NULL;
42
43
44 //--------------------------------------------------------------------------
45 static void Menu_nop_cb(Fl_Widget*, void*)
46 {
47 }
48
49 /*
50 * Static function for File menu callbacks.
51 */
52 static void filemenu_cb(Fl_Widget*, void *data)
53 {
54 if (strcmp((char*)data, "nw") == 0) {
55 a_UIcmd_open_url_nw(popup_bw, NULL);
56 } else if (strcmp((char*)data, "nt") == 0) {
57 a_UIcmd_open_url_nt(popup_bw, NULL, 1);
58 } else if (strcmp((char*)data, "of") == 0) {
59 a_UIcmd_open_file(popup_bw);
60 } else if (strcmp((char*)data, "ou") == 0) {
61 a_UIcmd_focus_location(popup_bw);
62 } else if (strcmp((char*)data, "cw") == 0) {
63 a_Timeout_add(0.0, a_UIcmd_close_bw, popup_bw);
64 } else if (strcmp((char*)data, "ed") == 0) {
65 a_Timeout_add(0.0, a_UIcmd_close_all_bw, NULL);
66 }
67 }
68
69
70 static void Menu_copy_urlstr_cb(Fl_Widget*, void*)
71 {
72 if (popup_url)
73 a_UIcmd_copy_urlstr(popup_bw, URL_STR(popup_url));
74 }
75
76 static void Menu_link_cb(Fl_Widget*, void *user_data)
77 {
78 DilloUrl *url = (DilloUrl *) user_data ;
79 _MSG("Menu_link_cb: click! :-)\n");
80
81 if (url)
82 a_Menu_link_popup(popup_bw, url);
83 }
84
85 /*
86 * Open URL
87 */
88 static void Menu_open_url_cb(Fl_Widget*, void*)
89 {
90 _MSG("Open URL cb: click! :-)\n");
91 a_UIcmd_open_url(popup_bw, popup_url);
92 }
93
94 /*
95 * Open URL in new window
96 */
97 static void Menu_open_url_nw_cb(Fl_Widget*, void*)
98 {
99 _MSG("Open URL in new window cb: click! :-)\n");
100 a_UIcmd_open_url_nw(popup_bw, popup_url);
101 }
102
103 /*
104 * Open URL in new Tab
105 */
106 static void Menu_open_url_nt_cb(Fl_Widget*, void*)
107 {
108 int focus = prefs.focus_new_tab ? 1 : 0;
109 if (Fl::event_state(FL_SHIFT)) focus = !focus;
110 a_UIcmd_open_url_nt(popup_bw, popup_url, focus);
111 }
112
113 /*
114 * Add bookmark
115 */
116 static void Menu_add_bookmark_cb(Fl_Widget*, void*)
117 {
118 a_UIcmd_add_bookmark(popup_bw, popup_url);
119 }
120
121 /*
122 * Find text
123 */
124 static void Menu_find_text_cb(Fl_Widget*, void*)
125 {
126 ((UI *)popup_bw->ui)->findbar_toggle(1);
127 }
128
129 /*
130 * Save link
131 */
132 static void Menu_save_link_cb(Fl_Widget*, void*)
133 {
134 a_UIcmd_save_link(popup_bw, popup_url);
135 }
136
137 /*
138 * Save current page
139 */
140 static void Menu_save_page_cb(Fl_Widget*, void*)
141 {
142 a_UIcmd_save(popup_bw);
143 }
144
145 /*
146 * View current page source
147 */
148 static void Menu_view_page_source_cb(Fl_Widget*, void*)
149 {
150 a_UIcmd_view_page_source(popup_bw, popup_url);
151 }
152
153 /*
154 * View current page's bugs
155 */
156 static void Menu_view_page_bugs_cb(Fl_Widget*, void*)
157 {
158 a_UIcmd_view_page_bugs(popup_bw);
159 }
160
161 /*
162 * Load images on current page that match URL pattern
163 */
164 static void Menu_load_images_cb(Fl_Widget*, void *user_data)
165 {
166 DilloUrl *page_url = (DilloUrl *) user_data;
167 void *doc = a_Bw_get_url_doc(popup_bw, page_url);
168
169 if (doc)
170 a_Html_load_images(doc, popup_url);
171 }
172
173 /*
174 * Submit form
175 */
176 static void Menu_form_submit_cb(Fl_Widget*, void*)
177 {
178 void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
179
180 if (doc)
181 a_Html_form_submit(doc, popup_form);
182 }
183
184 /*
185 * Reset form
186 */
187 static void Menu_form_reset_cb(Fl_Widget*, void*)
188 {
189 void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
190
191 if (doc)
192 a_Html_form_reset(doc, popup_form);
193 }
194
195 /*
196 * Toggle display of 'hidden' form controls.
197 */
198 static void Menu_form_hiddens_cb(Fl_Widget*, void *user_data)
199 {
200 bool visible = *((bool *) user_data);
201 void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
202
203 if (doc)
204 a_Html_form_display_hiddens(doc, popup_form, !visible);
205 }
206
207 static void Menu_stylesheet_cb(Fl_Widget*, void *vUrl)
208 {
209 const DilloUrl *url = (const DilloUrl *) vUrl;
210 a_UIcmd_open_url(popup_bw, url);
211 }
212
213 /*
214 * Validate URL with the W3C
215 */
216 static void Menu_bugmeter_validate_w3c_cb(Fl_Widget*, void*)
217 {
218 Dstr *dstr = dStr_sized_new(128);
219
220 dStr_sprintf(dstr, "http://validator.w3.org/check?uri=%s",
221 URL_STR(popup_url));
222 a_UIcmd_open_urlstr(popup_bw, dstr->str);
223 dStr_free(dstr, 1);
224 }
225
226 /*
227 * Validate URL with the WDG
228 */
229 static void Menu_bugmeter_validate_wdg_cb(Fl_Widget*, void*)
230 {
231 Dstr *dstr = dStr_sized_new(128);
232
233 dStr_sprintf(dstr,
234 "http://www.htmlhelp.org/cgi-bin/validate.cgi?url=%s&warnings=yes",
235 URL_STR(popup_url));
236 a_UIcmd_open_urlstr(popup_bw, dstr->str);
237 dStr_free(dstr, 1);
238 }
239
240 /*
241 * Show info page for the bug meter
242 */
243 static void Menu_bugmeter_about_cb(Fl_Widget*, void*)
244 {
245 a_UIcmd_open_urlstr(popup_bw, "http://www.dillo.org/help/bug_meter.html");
246 }
247
248 /*
249 * Navigation History callback.
250 * Go to selected URL.
251 */
252 static void Menu_history_cb(Fl_Widget*, void *data)
253 {
254 int mb = Fl::event_button();
255 int offset = history_direction * VOIDP2INT(data);
256 const DilloUrl *url = a_History_get_url(history_list[VOIDP2INT(data)-1]);
257
258 if (mb == 1) {
259 a_UIcmd_nav_jump(popup_bw, offset, 0);
260 } else if (mb == 2) {
261 // Middle button, open in a new window/tab
262 if (prefs.middle_click_opens_new_tab) {
263 int focus = prefs.focus_new_tab ? 1 : 0;
264 if (Fl::event_state(FL_SHIFT)) focus = !focus;
265 a_UIcmd_open_url_nt(popup_bw, url, focus);
266 } else {
267 a_UIcmd_open_url_nw(popup_bw, url);
268 }
269 }
270 }
271
272 /*
273 * Menus are popped-up from this timeout callback so the events
274 * associated with the button are gone when it pops. This way we
275 * avoid a segfault when a new page replaces the page that issued
276 * the popup menu.
277 */
278 static void Menu_popup_cb(void *data)
279 {
280 const Fl_Menu_Item *m = ((Fl_Menu_Item *)data)->popup(popup_x, popup_y);
281
282 if (m && m->callback())
283 m->do_callback((Fl_Widget *)data);
284 a_Timeout_remove();
285 }
286
287 /*
288 * Page popup menu (construction & popup)
289 */
290 void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
291 bool_t has_bugs, void *v_cssUrls)
292 {
293 lout::misc::SimpleVector <DilloUrl*> *cssUrls =
294 (lout::misc::SimpleVector <DilloUrl*> *) v_cssUrls;
295 int j = 0;
296
297 static Fl_Menu_Item *stylesheets = NULL;
298 static Fl_Menu_Item pm[] = {
299 {"View page source", 0, Menu_view_page_source_cb,0,0,0,0,0,0},
300 {"View page bugs", 0, Menu_view_page_bugs_cb,0,0,0,0,0,0},
301 {"View stylesheets", 0, Menu_nop_cb,0,FL_SUBMENU_POINTER|FL_MENU_DIVIDER,
302 0,0,0,0},
303 {"Bookmark this page", 0,Menu_add_bookmark_cb,0,FL_MENU_DIVIDER,0,0,0,0},
304 {"Find text", 0, Menu_find_text_cb,0,0,0,0,0,0},
305 {"Save page as...", 0, Menu_save_page_cb,0,0,0,0,0,0},
306 {0,0,0,0,0,0,0,0,0}
307 };
308
309 popup_x = Fl::event_x();
310 popup_y = Fl::event_y();
311 popup_bw = bw;
312 a_Url_free(popup_url);
313 popup_url = a_Url_dup(url);
314
315 has_bugs == TRUE ? pm[1].activate() : pm[1].deactivate();
316
317 if (strncmp(URL_STR(url), "dpi:/vsource/", 13) == 0)
318 pm[0].deactivate();
319 else
320 pm[0].activate();
321
322 if (stylesheets) {
323 while (stylesheets[j].text) {
324 dFree((char *) stylesheets[j].label());
325 a_Url_free((DilloUrl *) stylesheets[j].user_data());
326 j++;
327 }
328 delete [] stylesheets;
329 stylesheets = NULL;
330 }
331
332 if (cssUrls && cssUrls->size () > 0) {
333 stylesheets = new Fl_Menu_Item[cssUrls->size() + 1];
334 memset(stylesheets, '\0', sizeof(Fl_Menu_Item[cssUrls->size() + 1]));
335
336 for (j = 0; j < cssUrls->size(); j++) {
337
338 /* may want ability to Load individual unloaded stylesheets as well */
339 const char *action = "View ";
340 DilloUrl *url = cssUrls->get(j);
341 const char *url_str = URL_STR(url);
342 const uint_t head_length = 30, tail_length = 40,
343 url_len = strlen(url_str);;
344 char *label;
345
346 if (url_len > head_length + tail_length + 3) {
347 /* trim long URLs when making the label */
348 char *url_head = dStrndup(url_str, head_length);
349 const char *url_tail = url_str + (url_len - tail_length);
350 label = dStrconcat(action, url_head, "...", url_tail, NULL);
351 dFree(url_head);
352 } else {
353 label = dStrconcat(action, url_str, NULL);
354 }
355
356 stylesheets[j].label(FL_NORMAL_LABEL, label);
357 stylesheets[j].callback(Menu_stylesheet_cb, a_Url_dup(url));
358 }
359
360 pm[2].user_data(stylesheets);
361 pm[2].activate();
362 } else {
363 pm[2].deactivate();
364 }
365
366 a_Timeout_add(0.0, Menu_popup_cb, (void*)pm);
367 }
368
369 /*
370 * Link popup menu (construction & popup)
371 */
372 void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url)
373 {
374 static Fl_Menu_Item pm[] = {
375 {"Open link in new tab", 0, Menu_open_url_nt_cb,0,0,0,0,0,0},
376 {"Open link in new window", 0, Menu_open_url_nw_cb,0,FL_MENU_DIVIDER,0,0,
377 0,0},
378 {"Bookmark this link", 0, Menu_add_bookmark_cb,0,0,0,0,0,0},
379 {"Copy link location", 0, Menu_copy_urlstr_cb,0,FL_MENU_DIVIDER,0,0,0,0},
380 {"Save link as...", 0, Menu_save_link_cb,0,0,0,0,0,0},
381 {0,0,0,0,0,0,0,0,0}
382 };
383
384 popup_x = Fl::event_x();
385 popup_y = Fl::event_y();
386 popup_bw = bw;
387 a_Url_free(popup_url);
388 popup_url = a_Url_dup(url);
389
390 a_Timeout_add(0.0, Menu_popup_cb, (void*)pm);
391 }
392
393 /*
394 * Image popup menu (construction & popup)
395 */
396 void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
397 bool_t loaded_img, DilloUrl *page_url,
398 DilloUrl *link_url)
399 {
400 static DilloUrl *popup_page_url = NULL;
401 static DilloUrl *popup_link_url = NULL;
402 static Fl_Menu_Item pm[] = {
403 {"Isolate image", 0, Menu_open_url_cb,0,0,0,0,0,0},
404 {"Open image in new tab", 0, Menu_open_url_nt_cb,0,0,0,0,0,0},
405 {"Open image in new window", 0, Menu_open_url_nw_cb, 0, FL_MENU_DIVIDER,
406 0,0,0,0},
407 {"Load image", 0, Menu_load_images_cb,0,0,0,0,0,0},
408 {"Bookmark this image", 0, Menu_add_bookmark_cb,0,0,0,0,0,0},
409 {"Copy image location", 0,Menu_copy_urlstr_cb,0,FL_MENU_DIVIDER,0,0,0,0},
410 {"Save image as...", 0, Menu_save_link_cb, 0, FL_MENU_DIVIDER,0,0,0,0},
411 {"Link menu", 0, Menu_link_cb,0,0,0,0,0,0},
412 {0,0,0,0,0,0,0,0,0}
413 };
414
415 popup_x = Fl::event_x();
416 popup_y = Fl::event_y();
417 popup_bw = bw;
418 a_Url_free(popup_url);
419 popup_url = a_Url_dup(url);
420 a_Url_free(popup_page_url);
421 popup_page_url = a_Url_dup(page_url);
422 a_Url_free(popup_link_url);
423 popup_link_url = a_Url_dup(link_url);
424
425
426 if (loaded_img) {
427 pm[3].deactivate();
428 } else {
429 pm[3].activate();
430 pm[3].user_data(popup_page_url);
431 }
432
433 if (link_url) {
434 pm[7].activate();
435 pm[7].user_data(popup_link_url);
436 } else {
437 pm[7].deactivate();
438 }
439
440 a_Timeout_add(0.0, Menu_popup_cb, (void*)pm);
441 }
442
443 /*
444 * Form popup menu (construction & popup)
445 */
446 void a_Menu_form_popup(BrowserWindow *bw, const DilloUrl *page_url,
447 void *formptr, bool_t hidvis)
448 {
449 static bool hiddens_visible;
450 static Fl_Menu_Item pm[] = {
451 {"Submit form", 0, Menu_form_submit_cb,0,0,0,0,0,0},
452 {"Reset form", 0, Menu_form_reset_cb,0,0,0,0,0,0},
453 {0, 0, Menu_form_hiddens_cb, &hiddens_visible, 0,0,0,0,0},
454 {0,0,0,0,0,0,0,0,0}
455 };
456
457 popup_x = Fl::event_x();
458 popup_y = Fl::event_y();
459 popup_bw = bw;
460 a_Url_free(popup_url);
461 popup_url = a_Url_dup(page_url);
462 popup_form = formptr;
463
464 hiddens_visible = hidvis;
465 pm[2].label(hiddens_visible ? "Hide hiddens": "Show hiddens");
466
467 a_Timeout_add(0.0, Menu_popup_cb, (void*)pm);
468 }
469
470 /*
471 * File popup menu (construction & popup)
472 */
473 void a_Menu_file_popup(BrowserWindow *bw, void *v_wid)
474 {
475 Fl_Widget *wid = (Fl_Widget*)v_wid;
476
477 static Fl_Menu_Item pm[] = {
478 {"New tab", Keys::getShortcut(KEYS_NEW_TAB), filemenu_cb,
479 (void*)"nt",0,0,0,0,0},
480 {"New window", Keys::getShortcut(KEYS_NEW_WINDOW), filemenu_cb,
481 (void*)"nw", FL_MENU_DIVIDER,0,0,0,0},
482 {"Open file...", Keys::getShortcut(KEYS_OPEN), filemenu_cb,
483 (void*)"of",0,0,0,0,0},
484 {"Open URL...", Keys::getShortcut(KEYS_GOTO), filemenu_cb,
485 (void*)"ou",0,0,0,0,0},
486 {"Close", Keys::getShortcut(KEYS_CLOSE_TAB), filemenu_cb,
487 (void*)"cw", FL_MENU_DIVIDER,0,0,0,0},
488 {"Exit Dillo", Keys::getShortcut(KEYS_CLOSE_ALL), filemenu_cb,
489 (void*)"ed",0,0,0,0,0},
490 {0,0,0,0,0,0,0,0,0}
491 };
492
493 popup_bw = bw;
494 popup_x = wid->x();
495 popup_y = wid->y() + wid->h();
496 a_Url_free(popup_url);
497 popup_url = NULL;
498
499 //pm->label(wid->visible() ? NULL : "File");
500 a_Timeout_add(0.0, Menu_popup_cb, (void*)pm);
501 }
502
503 /*
504 * Bugmeter popup menu (construction & popup)
505 */
506 void a_Menu_bugmeter_popup(BrowserWindow *bw, const DilloUrl *url)
507 {
508 static Fl_Menu_Item pm[] = {
509 {"Validate URL with W3C", 0, Menu_bugmeter_validate_w3c_cb,0,0,0,0,0,0},
510 {"Validate URL with WDG", 0, Menu_bugmeter_validate_wdg_cb, 0,
511 FL_MENU_DIVIDER,0,0,0,0},
512 {"About bug meter", 0, Menu_bugmeter_about_cb,0,0,0,0,0,0},
513 {0,0,0,0,0,0,0,0,0}
514 };
515
516 popup_x = Fl::event_x();
517 popup_y = Fl::event_y();
518 popup_bw = bw;
519 a_Url_free(popup_url);
520 popup_url = a_Url_dup(url);
521
522 a_Timeout_add(0.0, Menu_popup_cb, (void*)pm);
523 }
524
525 /*
526 * Navigation History popup menu (construction & popup)
527 *
528 * direction: {backward = -1, forward = 1}
529 */
530 void a_Menu_history_popup(BrowserWindow *bw, int direction)
531 {
532 static Fl_Menu_Item *pm = 0;
533 int i, n;
534
535 popup_bw = bw;
536 popup_x = Fl::event_x();
537 popup_y = Fl::event_y();
538 history_direction = direction;
539
540 // TODO: hook popdown event with delete or similar.
541 if (pm)
542 delete [] pm;
543 if (history_list)
544 dFree(history_list);
545
546 // Get a list of URLs for this popup
547 history_list = a_UIcmd_get_history(bw, direction);
548
549 for (n = 0; history_list[n] != -1; n++)
550 ;
551
552 pm = new Fl_Menu_Item[n + 1];
553 memset(pm, '\0', sizeof(Fl_Menu_Item[n + 1]));
554
555 for (i = 0; i < n; i++) {
556 pm[i].label(FL_NORMAL_LABEL, a_History_get_title(history_list[i], 1));
557 pm[i].callback(Menu_history_cb, INT2VOIDP(i+1));
558 }
559 a_Timeout_add(0.0, Menu_popup_cb, (void*)pm);
560 }
561
562 /*
563 * Toggle use of remote stylesheets
564 */
565 static void Menu_remote_css_cb(Fl_Widget *wid, void*)
566 {
567 Fl_Menu_Item *item = (Fl_Menu_Item*) wid;
568
569 item->flags ^= FL_MENU_VALUE;
570 prefs.load_stylesheets = item->flags & FL_MENU_VALUE ? 1 : 0;
571 a_UIcmd_repush(popup_bw);
572 }
573
574 /*
575 * Toggle use of embedded CSS style
576 */
577 static void Menu_embedded_css_cb(Fl_Widget *wid, void*)
578 {
579 Fl_Menu_Item *item = (Fl_Menu_Item*) wid;
580
581 item->flags ^= FL_MENU_VALUE;
582 prefs.parse_embedded_css = item->flags & FL_MENU_VALUE ? 1 : 0;
583 a_UIcmd_repush(popup_bw);
584 }
585
586 static void Menu_panel_change_cb(Fl_Widget*, void *user_data)
587 {
588 UI *ui = (UI*)popup_bw->ui;
589
590 if (VOIDP2INT(user_data) == 10) /* small icons */
591 ui->change_panel(ui->get_panelsize(), !ui->get_smallicons());
592 else
593 ui->change_panel(VOIDP2INT(user_data), ui->get_smallicons());
594 }
595
596 /*
597 * Toggle loading of images -- and load them if enabling.
598 */
599 static void Menu_imgload_toggle_cb(Fl_Widget *wid, void*)
600 {
601 Fl_Menu_Item *item = (Fl_Menu_Item*) wid;
602
603 item->flags ^= FL_MENU_VALUE;
604
605 if ((prefs.load_images = item->flags & FL_MENU_VALUE ? 1 : 0)) {
606 void *doc = a_Bw_get_current_doc(popup_bw);
607
608 if (doc) {
609 DilloUrl *pattern = NULL;
610 a_Html_load_images(doc, pattern);
611 }
612 }
613 }
614
615 /*
616 * Tools popup menu (construction & popup)
617 */
618 void a_Menu_tools_popup(BrowserWindow *bw, void *v_wid)
619 {
620 const Fl_Menu_Item *item;
621 Fl_Widget *wid = (Fl_Widget*)v_wid;
622 UI *ui = (UI*)bw->ui;
623
624 static Fl_Menu_Item pm[] = {
625 {"Use remote CSS", 0, Menu_remote_css_cb, 0, FL_MENU_TOGGLE,0,0,0,0},
626 {"Use embedded CSS", 0, Menu_embedded_css_cb, 0,
627 FL_MENU_TOGGLE|FL_MENU_DIVIDER,0,0,0,0},
628 {"Load images", 0, Menu_imgload_toggle_cb, 0,
629 FL_MENU_TOGGLE|FL_MENU_DIVIDER,0,0,0,0},
630 {"Panel size", 0, Menu_nop_cb, (void*)"Submenu1", FL_SUBMENU,0,0,0,0},
631 {"tiny", 0,Menu_panel_change_cb,(void*)0,FL_MENU_RADIO,0,0,0,0},
632 {"small", 0,Menu_panel_change_cb,(void*)1,FL_MENU_RADIO,0,0,0,0},
633 {"medium",0,Menu_panel_change_cb,(void*)2,FL_MENU_RADIO,0,0,0,0},
634 {"large", 0,Menu_panel_change_cb,(void*)3,
635 FL_MENU_RADIO|FL_MENU_DIVIDER,0,0,0,0},
636 {"small icons", 0,Menu_panel_change_cb,(void*)10,
637 FL_MENU_TOGGLE,0,0,0,0},
638 {0,0,0,0,0,0,0,0,0},
639 {0,0,0,0,0,0,0,0,0}
640 };
641
642 popup_bw = bw;
643 int cur_panelsize = ui->get_panelsize();
644 int cur_smallicons = ui->get_smallicons();
645
646 if (prefs.load_stylesheets)
647 pm[0].set();
648 if (prefs.parse_embedded_css)
649 pm[1].set();
650 if (prefs.load_images)
651 pm[2].set();
652 pm[4+cur_panelsize].setonly();
653 cur_smallicons ? pm[8].set() : pm[8].clear();
654
655 item = pm->popup(wid->x(), wid->y() + wid->h());
656 if (item) {
657 ((Fl_Widget *)item)->do_callback();
658 }
659 }
660
+0
-25
src/menu.h less more
0 #ifndef __MENU_H__
1 #define __MENU_H__
2
3 #include <gtk/gtk.h>
4
5 GtkWidget *a_Menu_mainbar_new (BrowserWindow *bw, gint tiny);
6 GtkWidget *a_Menu_popup_op_new (BrowserWindow *bw);
7 GtkWidget *a_Menu_popup_ol_new (BrowserWindow *bw);
8 GtkWidget *a_Menu_popup_oi_new (BrowserWindow *bw);
9 GtkWidget *a_Menu_popup_ob_new (BrowserWindow *bw);
10 GtkWidget *a_Menu_popup_history_new(BrowserWindow *bw, gint direction);
11 void a_Menu_popup_ol_show_oi(BrowserWindow *bw, gboolean show);
12 void a_Menu_popup_set_url(BrowserWindow *bw, const DilloUrl *url);
13 void a_Menu_popup_set_url2(BrowserWindow *bw, const DilloUrl *url);
14 void a_Menu_popup_clear_url2(GtkWidget *menu_popup);
15
16 DilloUrl *a_Menu_popup_get_url(BrowserWindow *bw);
17
18 void a_Menu_pagemarks_new (BrowserWindow *bw);
19 void a_Menu_pagemarks_destroy (BrowserWindow *bw);
20 void a_Menu_pagemarks_add(BrowserWindow *bw, void *page, void *style,
21 gint level);
22 void a_Menu_pagemarks_set_text(BrowserWindow *bw, const char *str);
23
24 #endif /* MENU_H */
0 #ifndef __MENU_HH__
1 #define __MENU_HH__
2
3 #include "bw.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9 void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
10 bool_t has_bugs, void *v_cssUrls);
11 void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url);
12 void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
13 bool_t loaded_img, DilloUrl *page_url,
14 DilloUrl *link_url);
15 void a_Menu_form_popup(BrowserWindow *bw, const DilloUrl *page_url,
16 void *vform, bool_t showing_hiddens);
17 void a_Menu_file_popup(BrowserWindow *bw, void *v_wid);
18 void a_Menu_bugmeter_popup(BrowserWindow *bw, const DilloUrl *url);
19 void a_Menu_history_popup(BrowserWindow *bw, int direction);
20 void a_Menu_tools_popup(BrowserWindow *bw, void *v_wid);
21
22
23 #ifdef __cplusplus
24 }
25 #endif /* __cplusplus */
26
27 #endif /* MENU_HH */
28
00 /*
11 * File: misc.c
22 *
3 * Copyright (C) 2000 Jorge Arellano Cid <jcid@dillo.org>,
4 * Jörgen Viksell <vsksga@hotmail.com>
3 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>,
54 *
65 * This program is free software; you can redistribute it and/or modify
76 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
98 * (at your option) any later version.
109 */
11
12 #include <glib.h>
1310
1411 #include <stdio.h>
1512 #include <stdlib.h>
16 #include <unistd.h>
1713 #include <string.h>
1814 #include <ctype.h>
19
15 #include <assert.h>
16
17 #include "utf8.hh"
2018 #include "msg.h"
2119 #include "misc.h"
2220
2321 /*
24 * Prepend the users home-dir to 'file' string i.e,
25 * pass in .dillo/bookmarks.html and it will return
26 * /home/imain/.dillo/bookmarks.html
27 *
28 * Remember to g_free() returned value!
29 */
30 gchar *a_Misc_prepend_user_home(const char *file)
31 {
32 return ( g_strconcat(g_get_home_dir(), "/", file, NULL) );
33 }
34
35 /*
3622 * Escape characters as %XX sequences.
37 * Return value: New string, or NULL if there's no need to escape.
38 */
39 gchar *a_Misc_escape_chars(const gchar *str, gchar *esc_set)
40 {
41 static const char *hex = "0123456789ABCDEF";
42 gchar *p = NULL;
43 GString *gstr;
44 gint i;
45
46 for (i = 0; str[i]; ++i)
47 if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i]))
23 * Return value: New string.
24 */
25 char *a_Misc_escape_chars(const char *str, const char *esc_set)
26 {
27 static const char *const hex = "0123456789ABCDEF";
28 char *p = NULL;
29 Dstr *dstr;
30 int i;
31
32 dstr = dStr_sized_new(64);
33 for (i = 0; str[i]; ++i) {
34 if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) {
35 dStr_append_c(dstr, '%');
36 dStr_append_c(dstr, hex[(str[i] >> 4) & 15]);
37 dStr_append_c(dstr, hex[str[i] & 15]);
38 } else {
39 dStr_append_c(dstr, str[i]);
40 }
41 }
42 p = dstr->str;
43 dStr_free(dstr, FALSE);
44
45 return p;
46 }
47
48 #define TAB_SIZE 8
49 /*
50 * Takes a string and converts any tabs to spaces.
51 */
52 int
53 a_Misc_expand_tabs(char **start, char *end, char *buf, int buflen)
54 {
55 int j, pos = 0, written = 0, old_pos, char_len;
56 uint_t code;
57 static const int combining_char_space = 32;
58
59 while (*start < end && written < buflen - TAB_SIZE - combining_char_space) {
60 code = a_Utf8_decode(*start, end, &char_len);
61
62 if (code == '\t') {
63 /* Fill with whitespaces until the next tab. */
64 old_pos = pos;
65 pos += TAB_SIZE - (pos % TAB_SIZE);
66 for (j = old_pos; j < pos; j++)
67 buf[written++] = ' ';
68 } else {
69 assert(char_len <= 4);
70 for (j = 0; j < char_len; j++)
71 buf[written++] = (*start)[j];
72 pos++;
73 }
74
75 *start += char_len;
76 }
77
78 /* If following chars are combining chars (e.g. accents) add them to the
79 * buffer. We have reserved combining_char_space bytes for this.
80 * If there should be more combining chars, we split nevertheless.
81 */
82 while (*start < end && written < buflen - 4) {
83 code = a_Utf8_decode(*start, end, &char_len);
84
85 if (! a_Utf8_combining_char(code))
4886 break;
4987
50 if (str[i]) {
51 /* needs escaping */
52 gstr = g_string_sized_new(64);
53 for (i = 0; str[i]; ++i) {
54 if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) {
55 g_string_append_c(gstr, '%');
56 g_string_append_c(gstr, hex[(str[i] >> 4) & 15]);
57 g_string_append_c(gstr, hex[str[i] & 15]);
58 } else {
59 g_string_append_c(gstr, str[i]);
60 }
61 }
62 p = gstr->str;
63 g_string_free(gstr, FALSE);
64 }
65 return p;
66 }
67
68 /*
69 * Case insensitive strstr
70 */
71 gchar *a_Misc_stristr(char *src, char *str)
72 {
73 int i, j;
74
75 for (i = 0, j = 0; src[i] && str[j]; ++i)
76 if (tolower(src[i]) == tolower(str[j])) {
77 ++j;
78 } else if (j) {
79 i -= j;
80 j = 0;
81 }
82
83 if (!str[j]) /* Got all */
84 return (src + i - j);
85 return NULL;
86 }
87
88 /*
89 * strsep implementation
90 */
91 gchar *a_Misc_strsep(char **orig, const char *delim)
92 {
93 gchar *str, *p;
94
95 if (!(str = *orig))
96 return NULL;
97
98 p = strpbrk(str, delim);
99 if (p) {
100 *p++ = 0;
101 *orig = p;
102 } else {
103 *orig = NULL;
104 }
105 return str;
106 }
107
108 #define TAB_SIZE 8
109 /*
110 * Takes a string and converts any tabs to spaces.
111 */
112 gchar *a_Misc_expand_tabs(const char *str)
113 {
114 GString *New = g_string_new("");
115 int len, i, j, pos, old_pos;
116 char *val;
117
118 if ( (len = strlen(str)) ) {
119 for (pos = 0, i = 0; i < len; i++) {
120 if (str[i] == '\t') {
121 /* Fill with whitespaces until the next tab. */
122 old_pos = pos;
123 pos += TAB_SIZE - (pos % TAB_SIZE);
124 for (j = old_pos; j < pos; j++)
125 g_string_append_c(New, ' ');
126 } else {
127 g_string_append_c(New, str[i]);
128 pos++;
129 }
130 }
131 }
132 val = New->str;
133 g_string_free(New, FALSE);
134 return val;
135 }
136
137 /*
138 * Split a string into tokens, at any character contained by delim,
139 * and return the starting and ending positions within the string. For
140 * n tokens, the returned array has at least 2 * n + 1 elements, and
141 * contains the start of token i at 2 * i, the end at 2 * i + 1. The
142 * array is terminated by -1.
143 */
144 gint *a_Misc_strsplitpos(const gchar *str, const gchar *delim)
145 {
146 gint array_max = 4;
147 gint *array = g_new(gint, array_max);
148 gint n = 0;
149 gint p1 = 0, p2;
150
151 while (TRUE) {
152 while (str[p1] != 0 && strchr(delim, str[p1]) != NULL)
153 p1++;
154 if (str[p1] == 0)
155 break;
156
157 p2 = p1;
158 while (str[p2] != 0 && strchr(delim, str[p2]) == NULL)
159 p2++;
160
161 if (array_max < 2 * n + 3) {
162 array_max <<= 2;
163 array = g_realloc(array, array_max * sizeof(gint));
164 }
165
166 array[2 * n] = p1;
167 array[2 * n + 1] = p2;
168 n++;
169
170 if (str[p2] == 0)
171 break;
172 else {
173 p1 = p2;
174 }
175 }
176
177 array[2 * n] = -1;
178 return array;
179 }
180
181 /*
182 * Return a copy of an array which was created by a_Misc_strsplitpos.
183 */
184 gint *a_Misc_strsplitposdup(gint *pos)
185 {
186 gint n = 0;
187 gint *pos2;
188 while (pos[2 * n] != -1)
189 n++;
190 pos2 = g_new(gint, 2 * n + 1);
191 memcpy(pos2, pos, (2 * n + 1) * sizeof(gint));
192 return pos2;
88 assert(char_len <= 4);
89 for (j = 0; j < char_len; j++)
90 buf[written++] = (*start)[j];
91
92 *start += char_len;
93 }
94
95 return written;
19396 }
19497
19598 /* TODO: could use dStr ADT! */
200103
201104 static const ContentType_t MimeTypes[] = {
202105 { "application/octet-stream", 24 },
106 { "application/xhtml+xml", 21 },
203107 { "text/html", 9 },
204108 { "text/plain", 10 },
205109 { "image/gif", 9 },
208112 { NULL, 0 }
209113 };
210114
115 typedef enum {
116 DT_OCTET_STREAM = 0,
117 DT_TEXT_HTML,
118 DT_TEXT_PLAIN,
119 DT_IMAGE_GIF,
120 DT_IMAGE_PNG,
121 DT_IMAGE_JPG,
122 } DetectedContentType;
123
211124 /*
212125 * Detects 'Content-Type' from a data stream sample.
213126 *
220133 */
221134 int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
222135 {
136 size_t i, non_ascci, non_ascci_text, bin_chars;
137 char *p = Data;
223138 int st = 1; /* default to "doubt' */
224 int Type = 0; /* default to "application/octet-stream" */
225 char *p = Data;
226 size_t i, non_ascci;
139 DetectedContentType Type = DT_OCTET_STREAM; /* default to binary */
227140
228141 /* HTML try */
229 for (i = 0; i < Size && isspace(p[i]); ++i);
230 if ((Size - i >= 5 && !g_strncasecmp(p+i, "<html", 5)) ||
231 (Size - i >= 5 && !g_strncasecmp(p+i, "<head", 5)) ||
232 (Size - i >= 6 && !g_strncasecmp(p+i, "<title", 6)) ||
233 (Size - i >= 14 && !g_strncasecmp(p+i, "<!doctype html", 14)) ||
142 for (i = 0; i < Size && dIsspace(p[i]); ++i);
143 if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) ||
144 (Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) ||
145 (Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) ||
146 (Size - i >= 14 && !dStrncasecmp(p+i, "<!doctype html", 14)) ||
234147 /* this line is workaround for FTP through the Squid proxy */
235 (Size - i >= 17 && !g_strncasecmp(p+i, "<!-- HTML listing", 17))) {
236
237 Type = 1;
148 (Size - i >= 17 && !dStrncasecmp(p+i, "<!-- HTML listing", 17))) {
149
150 Type = DT_TEXT_HTML;
238151 st = 0;
239152 /* Images */
240 } else if (Size >= 4 && !g_strncasecmp(p, "GIF8", 4)) {
241 Type = 3;
242 st = 0;
243 } else if (Size >= 4 && !g_strncasecmp(p, "\x89PNG", 4)) {
244 Type = 4;
245 st = 0;
246 } else if (Size >= 2 && !g_strncasecmp(p, "\xff\xd8", 2)) {
153 } else if (Size >= 4 && !dStrncasecmp(p, "GIF8", 4)) {
154 Type = DT_IMAGE_GIF;
155 st = 0;
156 } else if (Size >= 4 && !dStrncasecmp(p, "\x89PNG", 4)) {
157 Type = DT_IMAGE_PNG;
158 st = 0;
159 } else if (Size >= 2 && !dStrncasecmp(p, "\xff\xd8", 2)) {
247160 /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
248161 * at the character representation should be machine independent. */
249 Type = 5;
162 Type = DT_IMAGE_JPG;
250163 st = 0;
251164
252165 /* Text */
253166 } else {
254 /* We'll assume "text/plain" if the set of chars above 127 is <= 10
255 * in a 256-bytes sample. Better heuristics are welcomed! :-) */
256 non_ascci = 0;
167 /* Heuristic for "text/plain"
168 * {ASCII, LATIN1, UTF8, KOI8-R, CP-1251}
169 * All in the above set regard [00-31] as control characters.
170 * LATIN1: [7F-9F] unused
171 * CP-1251 {7F,98} unused (two characters).
172 *
173 * We'll use [0-31] as indicators of non-text content.
174 * Better heuristics are welcomed! :-) */
175
176 non_ascci = non_ascci_text = bin_chars = 0;
257177 Size = MIN (Size, 256);
258 for (i = 0; i < Size; i++)
259 if ((unsigned char) p[i] > 127)
178 for (i = 0; i < Size; i++) {
179 int ch = (uchar_t) p[i];
180 if (ch < 32 && !dIsspace(ch))
181 ++bin_chars;
182 if (ch > 126)
260183 ++non_ascci;
261 if (Size == 256) {
262 Type = (non_ascci > 10) ? 0 : 2;
184 if (ch > 190)
185 ++non_ascci_text;
186 }
187 if (bin_chars == 0 && (non_ascci - non_ascci_text) <= Size/10) {
188 /* Let's say text: if "rare" chars are <= 10% */
189 Type = DT_TEXT_PLAIN;
190 } else if (Size > 0) {
191 /* a special check for UTF-8 */
192 Size = a_Utf8_end_of_char(p, Size - 1) + 1;
193 if (a_Utf8_test(p, Size) > 0)
194 Type = DT_TEXT_PLAIN;
195 }
196 if (Size >= 256)
263197 st = 0;
264 } else {
265 Type = (non_ascci > 0) ? 0 : 2;
266 }
267198 }
268199
269200 *PT = MimeTypes[Type].str;
270201 return st;
202 }
203
204 /*
205 * Parse Content-Type string, e.g., "text/html; charset=utf-8".
206 * Content-Type is defined in RFC 2045 section 5.1.
207 */
208 void a_Misc_parse_content_type(const char *type, char **major, char **minor,
209 char **charset)
210 {
211 static const char tspecials_space[] = "()<>@,;:\\\"/[]?= ";
212 const char *str, *s;
213
214 if (major)
215 *major = NULL;
216 if (minor)
217 *minor = NULL;
218 if (charset)
219 *charset = NULL;
220 if (!(str = type))
221 return;
222
223 for (s = str; *s && !iscntrl((uchar_t)*s) && !strchr(tspecials_space, *s);
224 s++) ;
225 if (major)
226 *major = dStrndup(str, s - str);
227
228 if (*s == '/') {
229 for (str = ++s;
230 *s && !iscntrl((uchar_t)*s) && !strchr(tspecials_space, *s); s++) ;
231 if (minor)
232 *minor = dStrndup(str, s - str);
233 }
234 if (charset && *s &&
235 (dStrncasecmp(type, "text/", 5) == 0 ||
236 dStrncasecmp(type, "application/xhtml+xml", 21) == 0)) {
237 /* "charset" parameter defined for text media type in RFC 2046,
238 * application/xhtml+xml in RFC 3236.
239 *
240 * Note that RFC 3023 lists some main xml media types and provides
241 * the convention of using the "+xml" minor type suffix for other
242 * xml types, so it would be reasonable to check for that suffix if
243 * we have need to care about various xml types someday.
244 */
245 const char terminators[] = " ;\t";
246 const char key[] = "charset";
247
248 if ((s = dStristr(str, key)) &&
249 (s == str || strchr(terminators, s[-1]))) {
250 s += sizeof(key) - 1;
251 for ( ; *s == ' ' || *s == '\t'; ++s);
252 if (*s == '=') {
253 size_t len;
254 for (++s; *s == ' ' || *s == '\t'; ++s);
255 if ((len = strcspn(s, terminators))) {
256 if (*s == '"' && s[len-1] == '"' && len > 1) {
257 /* quoted string */
258 s++;
259 len -= 2;
260 }
261 *charset = dStrndup(s, len);
262 }
263 }
264 }
265 }
266 }
267
268 /*
269 * Compare two Content-Type strings.
270 * Return 0 if they are equivalent, and 1 otherwise.
271 */
272 int a_Misc_content_type_cmp(const char *ct1, const char *ct2)
273 {
274 char *major1, *major2, *minor1, *minor2, *charset1, *charset2;
275 int ret;
276
277 if ((!ct1 || !*ct1) && (!ct2 || !*ct2))
278 return 0;
279 if ((!ct1 || !*ct1) || (!ct2 || !*ct2))
280 return 1;
281
282 a_Misc_parse_content_type(ct1, &major1, &minor1, &charset1);
283 a_Misc_parse_content_type(ct2, &major2, &minor2, &charset2);
284
285 if (major1 && major2 && !dStrcasecmp(major1, major2) &&
286 minor1 && minor2 && !dStrcasecmp(minor1, minor2) &&
287 ((!charset1 && !charset2) ||
288 (charset1 && charset2 && !dStrcasecmp(charset1, charset2)) ||
289 (!charset1 && charset2 && !dStrcasecmp(charset2, "UTF-8")) ||
290 (charset1 && !charset2 && !dStrcasecmp(charset1, "UTF-8")))) {
291 ret = 0;
292 } else {
293 ret = 1;
294 }
295 dFree(major1); dFree(major2);
296 dFree(minor1); dFree(minor2);
297 dFree(charset1); dFree(charset2);
298
299 return ret;
271300 }
272301
273302 /*
278307 * 0, if they match
279308 * -1, if a mismatch is detected
280309 *
281 * There're many MIME types Dillo doesn't know, they're handled
310 * There are many MIME types Dillo doesn't know, they're handled
282311 * as "application/octet-stream" (as the SPEC says).
283312 *
284313 * A mismatch happens when receiving a binary stream as
292321 int i;
293322 int st = -1;
294323
295 MSG("Type check: [Srv: %s Det: %s]\n", EntryType, DetectedType);
324 _MSG("Type check: [Srv: %s Det: %s]\n", EntryType, DetectedType);
296325
297326 if (!EntryType)
298327 return 0; /* there's no mismatch without server type */
299328
300329 for (i = 1; MimeTypes[i].str; ++i)
301 if (g_strncasecmp(EntryType, MimeTypes[i].str, MimeTypes[i].len) == 0)
330 if (dStrncasecmp(EntryType, MimeTypes[i].str, MimeTypes[i].len) == 0)
302331 break;
303332
304333 if (!MimeTypes[i].str) {
305334 /* type not found, no mismatch */
306335 st = 0;
307 } else if (g_strncasecmp(EntryType, "image/", 6) == 0 &&
308 !g_strncasecmp(DetectedType,MimeTypes[i].str,MimeTypes[i].len)){
336 } else if (dStrncasecmp(EntryType, "image/", 6) == 0 &&
337 !dStrncasecmp(DetectedType,MimeTypes[i].str,MimeTypes[i].len)){
309338 /* An image, and there's an exact match */
310339 st = 0;
311 } else if (g_strncasecmp(EntryType, "text/", 5) ||
312 g_strncasecmp(DetectedType, "application/", 12)) {
340 } else if (dStrncasecmp(EntryType, "text/", 5) ||
341 dStrncasecmp(DetectedType, "application/", 12)) {
313342 /* Not an application sent as text */
314343 st = 0;
315 }
344 } else if (dStrncasecmp(EntryType, "application/xhtml+xml", 21) &&
345 dStrncasecmp(DetectedType, "text/html", 9)) {
346 /* XML version of HTML */
347 st = 0;
348 }
349 _MSG("Type check: %s\n", st == 0 ? "MATCH" : "MISMATCH");
316350
317351 return st;
318 }
352 }
319353
320354 /*
321355 * Parse a geometry string.
322356 */
323 gint a_Misc_parse_geometry(gchar *str, gint *x, gint *y, gint *w, gint *h)
324 {
325 gchar *p, *t1, *t2;
326 gint n1, n2;
327 gint ret = 0;
357 int a_Misc_parse_geometry(char *str, int *x, int *y, int *w, int *h)
358 {
359 char *p, *t1, *t2;
360 int n1, n2;
361 int ret = 0;
328362
329363 if ((p = strchr(str, 'x')) || (p = strchr(str, 'X'))) {
330364 n1 = strtol(str, &t1, 10);
351385 * Encodes string using base64 encoding.
352386 * Return value: new string or NULL if input string is empty.
353387 */
354 gchar *a_Misc_encode_base64(const gchar *in)
355 {
356 static const char *base64_hex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
357 "abcdefghijklmnopqrstuvwxyz"
358 "0123456789+/";
359 gchar *out = NULL;
388 char *a_Misc_encode_base64(const char *in)
389 {
390 static const char *const base64_hex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
391 "abcdefghijklmnopqrstuvwxyz"
392 "0123456789+/";
393 char *out = NULL;
360394 int len, i = 0;
361395
362396 if (in == NULL) return NULL;
363397 len = strlen(in);
364398
365 out = (gchar *)g_malloc((len + 2) / 3 * 4 + 1);
399 out = (char *)dMalloc((len + 2) / 3 * 4 + 1);
366400
367401 for (; len >= 3; len -= 3) {
368402 out[i++] = base64_hex[in[0] >> 2];
384418 out[i] = '\0';
385419 return out;
386420 }
421
422 /*
423 * Load a local file into a dStr.
424 * Return value: dStr on success, NULL on error.
425 * TODO: a filesize threshold may be implemented.
426 */
427 Dstr *a_Misc_file2dstr(const char *filename)
428 {
429 FILE *F_in;
430 int n;
431 char buf[4096];
432 Dstr *dstr = NULL;
433
434 if ((F_in = fopen(filename, "r"))) {
435 dstr = dStr_sized_new(4096);
436 while ((n = fread (buf, 1, 4096, F_in)) > 0) {
437 dStr_append_l(dstr, buf, n);
438 }
439 fclose(F_in);
440 }
441 return dstr;
442 }
0 #ifndef __MISC_H__
1 #define __MISC_H__
2 #include <stdlib.h>
0 #ifndef __DILLO_MISC_H__
1 #define __DILLO_MISC_H__
32
4 gchar *a_Misc_prepend_user_home(const char *file);
5 gchar *a_Misc_escape_chars(const gchar *str, gchar *esc_set);
6 gchar *a_Misc_stristr(char *src, char *str);
7 gchar *a_Misc_expand_tabs(const char *str);
8 gchar *a_Misc_strsep(char **orig, const char *delim);
9 #define d_strsep a_Misc_strsep
10 gint *a_Misc_strsplitpos(const gchar *str, const gchar *delim);
11 gint *a_Misc_strsplitposdup(gint *pos);
12 gint a_Misc_get_content_type_from_data(void *Data, size_t Size,
13 const char **PT);
3 #include <stddef.h> /* for size_t */
4
5
6 #ifdef __cplusplus
7 extern "C" {
8 #endif /* __cplusplus */
9
10
11 char *a_Misc_escape_chars(const char *str, const char *esc_set);
12 int a_Misc_expand_tabs(char **start, char *end, char *buf, int buflen);
13 int a_Misc_get_content_type_from_data(void *Data, size_t Size,const char **PT);
1414 int a_Misc_content_type_check(const char *EntryType, const char *DetectedType);
15 gint a_Misc_parse_geometry(gchar *geom, gint *x, gint *y, gint *w, gint *h);
16 gchar *a_Misc_encode_base64(const gchar *in);
15 void a_Misc_parse_content_type(const char *str, char **major, char **minor,
16 char **charset);
17 int a_Misc_content_type_cmp(const char* ct1, const char *ct2);
18 int a_Misc_parse_geometry(char *geom, int *x, int *y, int *w, int *h);
19 char *a_Misc_encode_base64(const char *in);
20 Dstr *a_Misc_file2dstr(const char *filename);
1721
18 /* Return a NULL-terminated string containing the characters from p1 to p2. */
19 #define a_Misc_strpdup(s, p1, p2) g_strndup((s) + (p1), (p2) - (p1))
22 #ifdef __cplusplus
23 }
24 #endif /* __cplusplus */
2025
21 #endif /* __MISC_H__ */
26 #endif /* __DILLO_MISC_H__ */
2227
00 #ifndef __MSG_H__
11 #define __MSG_H__
22
3 #include <stdio.h>
34 #include "prefs.h"
45
56 /*
67 * You can disable any MSG* macro by adding the '_' prefix.
78 */
8 #define _MSG(fmt...)
9 #define _MSG_HTML(fmt...)
10 #define _MSG_HTTP(fmt...)
9 #define _MSG(...)
10 #define _MSG_WARN(...)
11 #define _MSG_HTTP(...)
1112
13 #define MSG_INNARDS(prefix, ...) \
14 D_STMT_START { \
15 if (prefs.show_msg){ \
16 printf(prefix __VA_ARGS__); \
17 fflush (stdout); \
18 } \
19 } D_STMT_END
1220
13 #define MSG(fmt...) \
14 G_STMT_START { \
15 if (prefs.show_msg) \
16 g_print(fmt); \
17 } G_STMT_END
18
19 #define MSG_HTML(fmt...) \
20 G_STMT_START { \
21 Html_msg(html, fmt); \
22 } G_STMT_END
23
24 #define MSG_HTTP(fmt...) g_print("HTTP warning: " fmt)
21 #define MSG(...) MSG_INNARDS("", __VA_ARGS__)
22 #define MSG_WARN(...) MSG_INNARDS("** WARNING **: ", __VA_ARGS__)
23 #define MSG_ERR(...) MSG_INNARDS("** ERROR **: ", __VA_ARGS__)
24 #define MSG_HTTP(...) MSG_INNARDS("HTTP warning: ", __VA_ARGS__)
2525
2626 #endif /* __MSG_H__ */
00 /*
11 * File: nav.c
22 *
3 * Copyright (C) 1999 James McCollough <jamesm@gtwn.net>
4 * Copyright (C) 2000, 2001, 2002 Jorge Arellano Cid <jcid@dillo.org>
3 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
54 *
65 * This program is free software; you can redistribute it and/or modify
76 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
98 * (at your option) any later version.
109 */
1110
1211 /* Support for a navigation stack */
1312
1413 #include <stdio.h>
15 #include <gtk/gtk.h>
14 #include <sys/stat.h>
1615 #include "msg.h"
17 #include "list.h"
1816 #include "nav.h"
1917 #include "history.h"
20 #include "web.h"
21 #include "menu.h"
22 #include "interface.h"
23 #include "dw_gtk_scrolled_window.h"
18 #include "web.hh"
19 #include "uicmd.hh"
20 #include "dialog.hh"
2421 #include "prefs.h"
2522 #include "capi.h"
26
27 /* #define DEBUG_LEVEL 3 */
28 #include "debug.h"
29
30 /*
31 * Forward declarations
32 */
33 static void Nav_reload(BrowserWindow *bw);
34
35
36 /*
37 * Initialize the navigation structure with safe values
38 */
39 void a_Nav_init(BrowserWindow *bw)
40 {
41 bw->nav_stack_size = 0;
42 bw->nav_stack_size_max = 16;
43 bw->nav_stack = NULL;
44 bw->nav_stack_ptr = -1;
45 bw->nav_expecting = FALSE;
46 bw->nav_expect_url = NULL;
47 }
23 #include "timeout.hh"
24
25 /*
26 * For back and forward navigation, each bw keeps an url index,
27 * and its scroll position.
28 */
29 typedef struct _nav_stack_item nav_stack_item;
30 struct _nav_stack_item
31 {
32 int url_idx;
33 int posx, posy;
34 };
35
36
4837
4938 /*
5039 * Free memory used by this module
40 * TODO: this may be removed or called by a_Bw_free().
41 * Currently is not called from anywhere.
5142 */
5243 void a_Nav_free(BrowserWindow *bw)
5344 {
5445 a_Nav_cancel_expect(bw);
55 g_free(bw->nav_stack);
46 dFree(bw->nav_stack);
5647 }
5748
5849
6152 /*
6253 * Return current nav_stack pointer [0 based; -1 = empty]
6354 */
64 gint a_Nav_stack_ptr(BrowserWindow *bw)
55 int a_Nav_stack_ptr(BrowserWindow *bw)
6556 {
6657 return bw->nav_stack_ptr;
6758 }
6859
6960 /*
61 * Return the url index of i-th element in the stack. [-1 = Error]
62 */
63 int a_Nav_get_uidx(BrowserWindow *bw, int i)
64 {
65 nav_stack_item *nsi = dList_nth_data (bw->nav_stack, i);
66 return (nsi) ? nsi->url_idx : -1;
67 }
68
69 /*
70 * Return the url index of the top element in the stack.
71 */
72 int a_Nav_get_top_uidx(BrowserWindow *bw)
73 {
74 nav_stack_item *nsi;
75
76 nsi = dList_nth_data (bw->nav_stack, a_Nav_stack_ptr(bw));
77 return (nsi) ? nsi->url_idx : -1;
78 }
79
80 /*
7081 * Move the nav_stack pointer
7182 */
72 static void Nav_stack_move_ptr(BrowserWindow *bw, gint offset)
73 {
74 gint nptr;
75
76 g_return_if_fail (bw != NULL);
83 static void Nav_stack_move_ptr(BrowserWindow *bw, int offset)
84 {
85 int nptr;
86
87 dReturn_if_fail (bw != NULL);
7788 if (offset != 0) {
7889 nptr = bw->nav_stack_ptr + offset;
79 g_return_if_fail (nptr >= 0 && nptr < bw->nav_stack_size);
90 dReturn_if_fail (nptr >= 0 && nptr < a_Nav_stack_size(bw));
8091 bw->nav_stack_ptr = nptr;
8192 }
8293 }
8495 /*
8596 * Return size of nav_stack [1 based]
8697 */
87 gint a_Nav_stack_size(BrowserWindow *bw)
88 {
89 return bw->nav_stack_size;
90 }
91
92 /*
93 * Add an URL-index in the navigation stack.
94 */
95 static void Nav_stack_add(BrowserWindow *bw, gint idx)
96 {
97 g_return_if_fail (bw != NULL);
98
99 ++bw->nav_stack_ptr;
100 if ( bw->nav_stack_ptr == bw->nav_stack_size) {
101 a_List_add(bw->nav_stack, bw->nav_stack_size, bw->nav_stack_size_max);
102 ++bw->nav_stack_size;
98 int a_Nav_stack_size(BrowserWindow *bw)
99 {
100 return dList_length(bw->nav_stack);
101 }
102
103 /*
104 * Truncate the navigation stack including 'pos' and upwards.
105 */
106 static void Nav_stack_truncate(BrowserWindow *bw, int pos)
107 {
108 void *data;
109
110 dReturn_if_fail (bw != NULL && pos >= 0);
111
112 while (pos < dList_length(bw->nav_stack)) {
113 data = dList_nth_data(bw->nav_stack, pos);
114 dList_remove_fast (bw->nav_stack, data);
115 dFree(data);
116 }
117 }
118
119 /*
120 * Insert a nav_stack_item into the stack at a given position.
121 */
122 static void Nav_stack_append(BrowserWindow *bw, int url_idx)
123 {
124 nav_stack_item *nsi;
125
126 dReturn_if_fail (bw != NULL);
127
128 /* Insert the new element */
129 nsi = dNew(nav_stack_item, 1);
130 nsi->url_idx = url_idx;
131 nsi->posx = 0;
132 nsi->posy = 0;
133 dList_append (bw->nav_stack, nsi);
134 }
135
136 /*
137 * Get the scrolling position of the current page.
138 */
139 static void Nav_get_scroll_pos(BrowserWindow *bw, int *posx, int *posy)
140 {
141 nav_stack_item *nsi;
142
143 if ((nsi = dList_nth_data (bw->nav_stack, a_Nav_stack_ptr(bw)))) {
144 *posx = nsi->posx;
145 *posy = nsi->posy;
103146 } else {
104 bw->nav_stack_size = bw->nav_stack_ptr + 1;
105 }
106 bw->nav_stack[bw->nav_stack_ptr] = idx;
107 }
108
109 /*
110 * Remove an URL-index from the navigation stack.
111 */
112 static void Nav_stack_remove(BrowserWindow *bw, gint idx)
113 {
114 gint sz = a_Nav_stack_size(bw);
115
116 g_return_if_fail (bw != NULL && idx >=0 && idx < sz);
117
118 for ( ; idx < sz - 1; ++idx)
119 bw->nav_stack[idx] = bw->nav_stack[idx + 1];
120 if ( bw->nav_stack_ptr == --bw->nav_stack_size )
121 --bw->nav_stack_ptr;
122 }
123
124 /*
125 * Remove equal adyacent URLs at the top of the stack.
147 *posx = *posy = 0;
148 }
149 }
150
151 /*
152 * Save the scrolling position of the current page.
153 */
154 static void Nav_save_scroll_pos(BrowserWindow *bw, int idx, int posx, int posy)
155 {
156 nav_stack_item *nsi;
157
158 if ((nsi = dList_nth_data (bw->nav_stack, idx))) {
159 nsi->posx = posx;
160 nsi->posy = posy;
161 }
162 }
163
164 /*
165 * Remove equal adjacent URLs at the top of the stack.
126166 * (It may happen with redirections)
127167 */
128168 static void Nav_stack_clean(BrowserWindow *bw)
129169 {
130 gint i;
131
132 g_return_if_fail (bw != NULL);
170 int i;
171
172 dReturn_if_fail (bw != NULL);
133173
134174 if ((i = a_Nav_stack_size(bw)) >= 2 &&
135 bw->nav_stack[i-2] == bw->nav_stack[i-1])
136 Nav_stack_remove(bw, i - 1);
175 NAV_UIDX(bw,i - 2) == NAV_UIDX(bw,i - 1)) {
176 void *data = dList_nth_data (bw->nav_stack, i - 1);
177 dList_remove_fast (bw->nav_stack, data);
178 dFree(data);
179 if (bw->nav_stack_ptr >= a_Nav_stack_size(bw))
180 bw->nav_stack_ptr = a_Nav_stack_size(bw) - 1;
181 }
137182 }
138183
139184
145190 * This function requests the page's root-URL; images and related stuff
146191 * are fetched directly by the HTML module.
147192 */
148 static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url, gint offset)
149 {
150 DilloUrl *old_url = NULL;
151 gboolean MustLoad;
152 gint ClientKey;
193 static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url,
194 const DilloUrl *requester, int offset)
195 {
196 const DilloUrl *old_url;
197 bool_t MustLoad, ForceReload, IgnoreScroll;
198 int x, y, idx, ClientKey;
153199 DilloWeb *Web;
154 gboolean ForceReload = (URL_FLAGS(url) & URL_E2EReload);
155
156 MSG("Nav_open_url: Url=>%s<\n", URL_STR_(url));
200
201 MSG("Nav_open_url: new url='%s'\n", URL_STR_(url));
202
203 ForceReload = (URL_FLAGS(url) & (URL_E2EQuery + URL_ReloadFromCache)) != 0;
204 IgnoreScroll = (URL_FLAGS(url) & URL_IgnoreScroll) != 0;
157205
158206 /* Get the url of the current page */
159 if ( a_Nav_stack_ptr(bw) != -1 )
160 old_url = a_History_get_url(NAV_TOP(bw));
161
162 /* Record current scrolling position
163 * (the strcmp check is necessary because of redirections) */
164 if (old_url &&
165 !strcmp(URL_STR(old_url), a_Interface_get_location_text(bw))) {
166 old_url->scrolling_position_x =
167 a_Dw_gtk_scrolled_window_get_scrolling_position_x(
168 GTK_DW_SCROLLED_WINDOW(bw->docwin));
169 old_url->scrolling_position_y =
170 a_Dw_gtk_scrolled_window_get_scrolling_position_y(
171 GTK_DW_SCROLLED_WINDOW(bw->docwin));
207 idx = a_Nav_stack_ptr(bw);
208 old_url = a_History_get_url(NAV_UIDX(bw, idx));
209 _MSG("Nav_open_url: old_url='%s' idx=%d\n", URL_STR(old_url), idx);
210 /* Record current scrolling position */
211 if (old_url && !IgnoreScroll) {
212 a_UIcmd_get_scroll_xy(bw, &x, &y);
213 Nav_save_scroll_pos(bw, idx, x, y);
214 _MSG("Nav_open_url: saved scroll of '%s' at x=%d y=%d\n",
215 URL_STR(old_url), x, y);
172216 }
173217
174218 /* Update navigation-stack-pointer (offset may be zero) */
175219 Nav_stack_move_ptr(bw, offset);
176220
177 /* Page must be reloaded, if old and new url (without anchor) differ */
221 /* Page must be reloaded, if old and new url (considering anchor) differ */
178222 MustLoad = ForceReload || !old_url;
179223 if (old_url){
180 MustLoad |= a_Url_cmp(old_url, url);
181 MustLoad |= strcmp(URL_STR(old_url), a_Interface_get_location_text(bw));
182 }
183
184 if ( MustLoad ) {
185 a_Interface_stop(bw);
186 a_Interface_clean(bw);
187
188 a_Menu_pagemarks_new(bw);
189
190 Web = a_Web_new(url);
224 MustLoad |= (a_Url_cmp(old_url, url) ||
225 strcmp(URL_FRAGMENT(old_url), URL_FRAGMENT(url)));
226 }
227
228 if (MustLoad) {
229 a_Bw_stop_clients(bw, BW_Root + BW_Img);
230 a_Bw_cleanup(bw);
231
232 // a_Menu_pagemarks_new(bw);
233
234 Web = a_Web_new(url, requester);
191235 Web->bw = bw;
192236 Web->flags |= WEB_RootUrl;
193237 if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
194 a_Interface_add_client(bw, ClientKey, 1);
195 a_Interface_add_url(bw, url, WEB_RootUrl);
238 a_Bw_add_client(bw, ClientKey, 1);
239 a_Bw_add_url(bw, url);
196240 }
197 a_Interface_set_cursor(bw, GDK_LEFT_PTR);
198 }
199
200 /* Jump to #anchor position */
201 if (URL_FRAGMENT_(url)) {
202 /* todo: push on stack */
203 gchar *pf = a_Url_decode_hex_str(URL_FRAGMENT_(url));
204 a_Dw_gtk_scrolled_window_set_anchor(
205 GTK_DW_SCROLLED_WINDOW(bw->docwin), pf);
206 g_free(pf);
207241 }
208242 }
209243
214248 void a_Nav_cancel_expect(BrowserWindow *bw)
215249 {
216250 if (bw->nav_expecting) {
217 if (bw->nav_expect_url) {
218 a_Url_free(bw->nav_expect_url);
219 bw->nav_expect_url = NULL;
220 }
251 a_Url_free(bw->nav_expect_url);
252 bw->nav_expect_url = NULL;
221253 bw->nav_expecting = FALSE;
222 }
254 a_UIcmd_set_buttons_sens(bw);
255 }
256 if (bw->meta_refresh_status > 0)
257 --bw->meta_refresh_status;
258 }
259
260 /*
261 * Cancel the expect if 'url' matches.
262 */
263 void a_Nav_cancel_expect_if_eq(BrowserWindow *bw, const DilloUrl *url)
264 {
265 if (bw->nav_expecting && a_Url_cmp(url, bw->nav_expect_url) == 0)
266 a_Nav_cancel_expect(bw);
223267 }
224268
225269 /*
226270 * We have an answer! Set things accordingly.
271 * This function is called for root URLs only.
272 * Beware: this function is much more complex than it looks
273 * because URLs with repush pass twice through it.
227274 */
228275 void a_Nav_expect_done(BrowserWindow *bw)
229276 {
230 gint idx;
277 int m, url_idx, posx, posy, reload, repush, e2equery, goto_old_scroll=TRUE;
231278 DilloUrl *url;
232
233 g_return_if_fail(bw != NULL);
279 char *fragment = NULL;
280
281 dReturn_if_fail(bw != NULL);
234282
235283 if (bw->nav_expecting) {
236284 url = bw->nav_expect_url;
237 /* unset E2EReload before adding this url to history */
238 a_Url_set_flags(url, URL_FLAGS(url) & ~URL_E2EReload);
239 idx = a_History_add_url(url);
240 Nav_stack_add(bw, idx);
241
242 a_Url_free(url);
243 bw->nav_expect_url = NULL;
244 bw->nav_expecting = FALSE;
245 }
285 reload = (URL_FLAGS(url) & URL_ReloadPage);
286 repush = (URL_FLAGS(url) & URL_ReloadFromCache);
287 e2equery = (URL_FLAGS(url) & URL_E2EQuery);
288 fragment = a_Url_decode_hex_str(URL_FRAGMENT_(url));
289
290 /* Unset E2EQuery, ReloadPage, ReloadFromCache and IgnoreScroll
291 * before adding this url to history */
292 m = URL_E2EQuery|URL_ReloadPage|URL_ReloadFromCache|URL_IgnoreScroll;
293 a_Url_set_flags(url, URL_FLAGS(url) & ~m);
294 url_idx = a_History_add_url(url);
295
296 if (repush) {
297 MSG("a_Nav_expect_done: repush!\n");
298 } else if (reload) {
299 MSG("a_Nav_expect_done: reload!\n");
300 } else {
301 Nav_stack_truncate(bw, a_Nav_stack_ptr(bw) + 1);
302 Nav_stack_append(bw, url_idx);
303 Nav_stack_move_ptr(bw, 1);
304 }
305
306 if (fragment) {
307 goto_old_scroll = FALSE;
308 if (repush) {
309 Nav_get_scroll_pos(bw, &posx, &posy);
310 if (posx || posy)
311 goto_old_scroll = TRUE;
312 } else if (e2equery) {
313 /* Reset scroll, so repush goes to fragment in the next pass */
314 Nav_save_scroll_pos(bw, a_Nav_stack_ptr(bw), 0, 0);
315 }
316 }
317 a_Nav_cancel_expect(bw);
318 }
319
320 if (goto_old_scroll) {
321 /* Scroll to where we were in this page */
322 Nav_get_scroll_pos(bw, &posx, &posy);
323 a_UIcmd_set_scroll_xy(bw, posx, posy);
324 _MSG("Nav: expect_done scrolling to x=%d y=%d\n", posx, posy);
325 } else if (fragment) {
326 /* Scroll to fragment */
327 a_UIcmd_set_scroll_by_fragment(bw, fragment);
328 } else {
329 /* Scroll to origin */
330 a_UIcmd_set_scroll_xy(bw, 0, 0);
331 }
332
333 dFree(fragment);
246334 Nav_stack_clean(bw);
247 a_Interface_set_button_sens(bw);
335 a_UIcmd_set_buttons_sens(bw);
336 _MSG("Nav: a_Nav_expect_done\n");
248337 }
249338
250339 /*
252341 * - Set bw to expect the URL data
253342 * - Ask the cache to feed back the requested URL (via Nav_open_url)
254343 */
255 void a_Nav_push(BrowserWindow *bw, const DilloUrl *url)
256 {
257 g_return_if_fail (bw != NULL);
258
259 if (bw->nav_expecting && a_Url_cmp(bw->nav_expect_url, url) == 0 &&
260 URL_STRCAMP_EQ(URL_FRAGMENT_(bw->nav_expect_url), URL_FRAGMENT_(url))) {
344 void a_Nav_push(BrowserWindow *bw, const DilloUrl *url,
345 const DilloUrl *requester)
346 {
347 dReturn_if_fail (bw != NULL);
348
349 if (bw->nav_expecting && !a_Url_cmp(bw->nav_expect_url, url) &&
350 !strcmp(URL_FRAGMENT(bw->nav_expect_url),URL_FRAGMENT(url))) {
261351 /* we're already expecting that url (most probably a double-click) */
262352 return;
263353 }
264354 a_Nav_cancel_expect(bw);
265355 bw->nav_expect_url = a_Url_dup(url);
266356 bw->nav_expecting = TRUE;
267 Nav_open_url(bw, url, 0);
268 }
269
270 /*
271 * Same as a_Nav_push() but in a new window.
272 */
273 void a_Nav_push_nw(BrowserWindow *bw, const DilloUrl *url)
274 {
275 gint width, height;
276 BrowserWindow *newbw;
277
278 gdk_window_get_size (bw->main_window->window, &width, &height);
279 newbw = a_Interface_browser_window_new(width, height, 0);
280 a_Nav_push(newbw, url);
281 }
282
283 /*
284 * Wraps a_Nav_push to match 'DwPage->link' function type
285 */
286 void a_Nav_vpush(void *vbw, const DilloUrl *url)
287 {
288 a_Nav_push(vbw, url);
357 Nav_open_url(bw, url, requester, 0);
358 }
359
360 /*
361 * This one does a_Nav_repush's job.
362 */
363 static void Nav_repush(BrowserWindow *bw)
364 {
365 DilloUrl *url;
366
367 a_Nav_cancel_expect(bw);
368 if (a_Nav_stack_size(bw)) {
369 url = a_Url_dup(a_History_get_url(NAV_TOP_UIDX(bw)));
370 /* Let's make reload be from Cache */
371 a_Url_set_flags(url, URL_FLAGS(url) | URL_ReloadFromCache);
372 bw->nav_expect_url = a_Url_dup(url);
373 bw->nav_expecting = TRUE;
374 Nav_open_url(bw, url, NULL, 0);
375 a_Url_free(url);
376 }
377 }
378
379 static void Nav_repush_callback(void *data)
380 {
381 _MSG(">>>> Nav_repush_callback <<<<\n");
382 Nav_repush(data);
383 a_Timeout_remove();
384 }
385
386 /*
387 * Repush current URL: not an end-to-end reload but from cache.
388 * - Currently used to switch to a charset decoder given by the META element.
389 * - Delayed to let dillo finish the call flow into a known state.
390 *
391 * There's no need to stop the parser before calling this function:
392 * When the timeout activates, a_Bw_stop_clients will stop the data feed.
393 */
394 void a_Nav_repush(BrowserWindow *bw)
395 {
396 dReturn_if_fail (bw != NULL);
397 MSG(">>>> a_Nav_repush <<<<\n");
398 a_Timeout_add(0.0, Nav_repush_callback, (void*)bw);
399 }
400
401 /*
402 * This one does a_Nav_redirection0's job.
403 */
404 static void Nav_redirection0_callback(void *data)
405 {
406 BrowserWindow *bw = (BrowserWindow *)data;
407 const DilloUrl *referer_url = a_History_get_url(NAV_TOP_UIDX(bw));
408 _MSG(">>>> Nav_redirection0_callback <<<<\n");
409
410 if (bw->meta_refresh_status == 2) {
411 Nav_stack_move_ptr(bw, -1);
412 a_Nav_push(bw, bw->meta_refresh_url, referer_url);
413 }
414 a_Url_free(bw->meta_refresh_url);
415 bw->meta_refresh_url = NULL;
416 bw->meta_refresh_status = 0;
417 a_Timeout_remove();
418 }
419
420 /*
421 * Handle a zero-delay URL redirection given by META
422 */
423 void a_Nav_redirection0(BrowserWindow *bw, const DilloUrl *new_url)
424 {
425 dReturn_if_fail (bw != NULL);
426 _MSG(">>>> a_Nav_redirection0 <<<<\n");
427
428 a_Url_free(bw->meta_refresh_url);
429 bw->meta_refresh_url = a_Url_dup(new_url);
430 a_Url_set_flags(bw->meta_refresh_url,
431 URL_FLAGS(new_url)|URL_E2EQuery|URL_IgnoreScroll);
432 bw->meta_refresh_status = 2;
433 a_Timeout_add(0.0, Nav_redirection0_callback, (void*)bw);
289434 }
290435
291436 /*
293438 */
294439 void a_Nav_back(BrowserWindow *bw)
295440 {
296 gint idx = a_Nav_stack_ptr(bw);
441 int idx = a_Nav_stack_ptr(bw);
297442
298443 a_Nav_cancel_expect(bw);
299 if ( --idx >= 0 ){
300 a_Interface_msg(bw, "");
301 Nav_open_url(bw, a_History_get_url(NAV_IDX(bw,idx)), -1);
444 if (--idx >= 0){
445 a_UIcmd_set_msg(bw, "");
446 Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), NULL, -1);
302447 }
303448 }
304449
307452 */
308453 void a_Nav_forw(BrowserWindow *bw)
309454 {
310 gint idx = a_Nav_stack_ptr(bw);
455 int idx = a_Nav_stack_ptr(bw);
311456
312457 a_Nav_cancel_expect(bw);
313458 if (++idx < a_Nav_stack_size(bw)) {
314 a_Interface_msg(bw, "");
315 Nav_open_url(bw, a_History_get_url(NAV_IDX(bw,idx)), +1);
459 a_UIcmd_set_msg(bw, "");
460 Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), NULL, +1);
316461 }
317462 }
318463
321466 */
322467 void a_Nav_home(BrowserWindow *bw)
323468 {
324 a_Nav_push(bw, prefs.home);
325 }
326
327 /*
328 * Jump to an URL within the stack history
329 * NewBw: {0 = same window, 1 = new window}
330 */
331 void a_Nav_jump_callback(GtkWidget *widget, gpointer client_data, gint NewBw)
332 {
333 int idx;
334 BrowserWindow *bw = client_data;
335
336 idx = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT (widget), "nav_idx"));
337 if (idx >= 0 && idx < a_Nav_stack_size(bw)) {
338 if (NewBw == 1) {
339 a_Nav_push_nw(bw, a_History_get_url(NAV_IDX(bw,idx)));
340 } else {
341 Nav_open_url(bw, a_History_get_url(NAV_IDX(bw,idx)),
342 idx - a_Nav_stack_ptr(bw));
469 a_Nav_push(bw, prefs.home, NULL);
470 }
471
472 /*
473 * This one does a_Nav_reload's job!
474 */
475 static void Nav_reload_callback(void *data)
476 {
477 BrowserWindow *bw = data;
478 const DilloUrl *h_url;
479 DilloUrl *r_url;
480 int choice, confirmed = 1;
481
482 a_Nav_cancel_expect(bw);
483 if (a_Nav_stack_size(bw)) {
484 h_url = a_History_get_url(NAV_TOP_UIDX(bw));
485 if (strncmp(URL_STR(h_url), "dpi:/vsource/", 13) == 0) {
486 /* disable reload for view source dpi */
487 confirmed = 0;
488 } else if (URL_FLAGS(h_url) & URL_Post) {
489 /* Attempt to repost data, let's confirm... */
490 choice = a_Dialog_choice5("Repost form data?",
491 "No", "Yes", "Cancel", NULL, NULL);
492 confirmed = (choice == 2); /* "Yes" */
343493 }
344 }
345 }
346
347 /*
348 * Callback for reload confirmation
349 */
350 static void Nav_reload_confirmation_cb(BrowserWindow *bw)
351 {
352 DialogAnswer *answer = bw->question_dialog_answer;
353
354 _MSG("Nav_reload_confirmation_cb %p\n", bw->question_dialog_window);
355
356 if (answer->alt_num == 1) { /* "OK" */
357 DEBUG_MSG(3, "Nav_reload_confirmed\n");
358 if ( a_Nav_stack_size(bw) &&
359 bw->question_dialog_data == a_History_get_url(NAV_TOP(bw)) ) {
360 /* a genuine confirmation! */
361 DEBUG_MSG(3, "Nav_reload_confirmed test: OK\n");
362 Nav_reload(bw);
494
495 if (confirmed) {
496 r_url = a_Url_dup(h_url);
497 /* Mark URL as reload to differentiate from push */
498 a_Url_set_flags(r_url, URL_FLAGS(r_url) | URL_ReloadPage);
499 /* Let's make reload be end-to-end */
500 a_Url_set_flags(r_url, URL_FLAGS(r_url) | URL_E2EQuery);
501 /* This is an explicit reload, so clear the SpamSafe flag */
502 a_Url_set_flags(r_url, URL_FLAGS(r_url) & ~URL_SpamSafe);
503 bw->nav_expect_url = r_url;
504 bw->nav_expecting = TRUE;
505 Nav_open_url(bw, r_url, NULL, 0);
363506 }
364
365 } else { /* window closed or cancel button */
366 DEBUG_MSG(3, "Nav_reload_refused\n");
367 }
368
369 /* cleanup */
370 bw->question_dialog_data = NULL;
371 g_free(answer->this);
372 bw->question_dialog_answer = NULL;
373 }
374
375 /*
376 * This one does a_Nav_reload's job!
377 */
378 static void Nav_reload(BrowserWindow *bw)
379 {
380 DilloUrl *url, *ReqURL;
381
382 a_Nav_cancel_expect(bw);
383 if ( a_Nav_stack_size(bw) ) {
384 url = a_History_get_url(NAV_TOP(bw));
385 ReqURL = a_Url_dup(a_History_get_url(NAV_TOP(bw)));
386 /* Let's make reload be end-to-end */
387 a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) | URL_E2EReload);
388 /* This is an explicit reload, so clear the SpamSafe flag */
389 a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) & ~URL_SpamSafe);
390 Nav_open_url(bw, ReqURL, 0);
391 a_Url_free(ReqURL);
392507 }
393508 }
394509
395510 /*
396511 * Implement the RELOAD button functionality.
397 * (We haven't defined it yet ;)
512 * (Currently it only reloads the page, not its images)
513 * Note: the timeout lets CCC operations end before making the request.
398514 */
399515 void a_Nav_reload(BrowserWindow *bw)
400516 {
401 DilloUrl *url;
402
403 a_Nav_cancel_expect(bw);
404 if ( a_Nav_stack_size(bw) ) {
405 url = a_History_get_url(NAV_TOP(bw));
406 if (URL_FLAGS(url) & URL_Post) {
407 /* Attempt to repost data, let's confirm... */
408 bw->question_dialog_data = (gpointer)url;
409 a_Interface_question_dialog(
410 bw, "Repost form data?", TRUE,
411 "OK", "Cancel", NULL, NULL, NULL,
412 (GtkSignalFunc) Nav_reload_confirmation_cb);
413
414 } else {
415 Nav_reload(bw);
517 dReturn_if_fail (bw != NULL);
518 a_Timeout_add(0.0, Nav_reload_callback, (void*)bw);
519 }
520
521 /*
522 * Jump to a URL in the Navigation stack.
523 */
524 void a_Nav_jump(BrowserWindow *bw, int offset, int new_bw)
525 {
526 int idx = a_Nav_stack_ptr(bw) + offset;
527
528 if (new_bw) {
529 a_UIcmd_open_url_nw(bw, a_History_get_url(NAV_UIDX(bw,idx)));
530 } else {
531 a_Nav_cancel_expect(bw);
532 Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), NULL, offset);
533 a_UIcmd_set_buttons_sens(bw);
534 }
535 }
536
537
538 /* Specific methods -------------------------------------------------------- */
539
540 /*
541 * Receive data from the cache and save it to a local file
542 */
543 static void Nav_save_cb(int Op, CacheClient_t *Client)
544 {
545 DilloWeb *Web = Client->Web;
546 int Bytes;
547
548 if (Op){
549 struct stat st;
550
551 fflush(Web->stream);
552 fstat(fileno(Web->stream), &st);
553 fclose(Web->stream);
554 a_UIcmd_set_msg(Web->bw, "File saved (%d Bytes)", st.st_size);
555 } else {
556 if ((Bytes = Client->BufSize - Web->SavedBytes) > 0) {
557 Bytes = fwrite(Client->Buf + Web->SavedBytes, 1, Bytes, Web->stream);
558 Web->SavedBytes += Bytes;
416559 }
417560 }
418561 }
419562
563 /*
564 * Save a URL (from cache or from the net).
565 */
566 void a_Nav_save_url(BrowserWindow *bw,
567 const DilloUrl *url, const char *filename)
568 {
569 DilloWeb *Web = a_Web_new(url, NULL);
570 Web->bw = bw;
571 Web->filename = dStrdup(filename);
572 Web->flags |= WEB_Download;
573 /* TODO: keep track of this client */
574 a_Capi_open_url(Web, Nav_save_cb, Web);
575 }
576
577 /*
578 * Wrapper for a_Capi_get_buf.
579 */
580 int a_Nav_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
581 {
582 return a_Capi_get_buf(Url, PBuf, BufSize);
583 }
584
585 /*
586 * Wrapper for a_Capi_unref_buf().
587 */
588 void a_Nav_unref_buf(const DilloUrl *Url)
589 {
590 a_Capi_unref_buf(Url);
591 }
592
593 /*
594 * Wrapper for a_Capi_set_vsource_url().
595 */
596 void a_Nav_set_vsource_url(const DilloUrl *Url)
597 {
598 a_Capi_set_vsource_url(Url);
599 }
00 #ifndef __NAV_H__
11 #define __NAV_H__
22
3 #include "browser.h"
4 #include "dw_widget.h" /* for DwWidget */
3 #include "bw.h"
54
6
7 /* useful macros for the navigation stack */
8 #define NAV_IDX(bw, i) (bw)->nav_stack[i]
9 #define NAV_TOP(bw) (bw)->nav_stack[(bw)->nav_stack_ptr]
10
5 /*
6 * useful macros for the navigation stack
7 */
8 #define NAV_UIDX(bw, i) a_Nav_get_uidx(bw, i)
9 #define NAV_TOP_UIDX(bw) a_Nav_get_top_uidx(bw)
1110
1211 #ifdef __cplusplus
1312 extern "C" {
1413 #endif /* __cplusplus */
1514
16 void a_Nav_push(BrowserWindow *bw, const DilloUrl *url);
17 void a_Nav_push_nw(BrowserWindow *bw, const DilloUrl *url);
18 void a_Nav_vpush(void *vbw, const DilloUrl *url);
15 void a_Nav_redirection0(BrowserWindow *bw, const DilloUrl *new_url);
16 void a_Nav_push(BrowserWindow *bw, const DilloUrl *url,
17 const DilloUrl *requester);
18 void a_Nav_repush(BrowserWindow *bw);
1919 void a_Nav_back(BrowserWindow *bw);
2020 void a_Nav_forw(BrowserWindow *bw);
2121 void a_Nav_home(BrowserWindow *bw);
2222 void a_Nav_reload(BrowserWindow *bw);
23 void a_Nav_init(BrowserWindow *bw);
23 void a_Nav_jump(BrowserWindow *bw, int offset, int new_bw);
2424 void a_Nav_free(BrowserWindow *bw);
2525 void a_Nav_cancel_expect (BrowserWindow *bw);
26 void a_Nav_cancel_expect_if_eq(BrowserWindow *bw, const DilloUrl *url);
2627 void a_Nav_expect_done(BrowserWindow *bw);
27 void a_Nav_jump_callback(GtkWidget *widget, gpointer client_data, gint NewBw);
28 gint a_Nav_stack_ptr(BrowserWindow *bw);
29 gint a_Nav_stack_size(BrowserWindow *bw);
28 int a_Nav_stack_ptr(BrowserWindow *bw);
29 int a_Nav_stack_size(BrowserWindow *bw);
30 int a_Nav_get_uidx(BrowserWindow *bw, int i);
31 int a_Nav_get_top_uidx(BrowserWindow *bw);
3032
33 void a_Nav_save_url(BrowserWindow *bw,
34 const DilloUrl *url, const char *filename);
35 int a_Nav_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
36 void a_Nav_unref_buf(const DilloUrl *Url);
37 void a_Nav_set_vsource_url(const DilloUrl *Url);
3138
3239 #ifdef __cplusplus
3340 }
0 /*
1 * File: paths.cc
2 *
3 * Copyright 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <unistd.h>
12 #include <errno.h>
13 #include <sys/stat.h>
14
15 #include "msg.h"
16 #include "../dlib/dlib.h"
17 #include "paths.hh"
18
19 /*
20 * Local data
21 */
22
23 // Dillo works from an unmounted directory (/tmp)
24 static char* oldWorkingDir = NULL;
25
26 /*
27 * Changes current working directory to /tmp and creates ~/.dillo
28 * if not exists.
29 */
30 void Paths::init(void)
31 {
32 char *path;
33 struct stat st;
34 int rc = 0;
35
36 dFree(oldWorkingDir);
37 oldWorkingDir = dGetcwd();
38 rc = chdir("/tmp");
39 if (rc == -1) {
40 MSG("paths: error changing directory to /tmp: %s\n",
41 dStrerror(errno));
42 }
43
44 path = dStrconcat(dGethomedir(), "/.dillo", NULL);
45 if (stat(path, &st) == -1) {
46 if (errno == ENOENT) {
47 MSG("paths: creating directory %s.\n", path);
48 if (mkdir(path, 0700) < 0) {
49 MSG("paths: error creating directory %s: %s\n",
50 path, dStrerror(errno));
51 }
52 } else {
53 MSG("Dillo: error reading %s: %s\n", path, dStrerror(errno));
54 }
55 }
56
57 dFree(path);
58 }
59
60 /*
61 * Return the initial current working directory in a string.
62 */
63 char *Paths::getOldWorkingDir(void)
64 {
65 return oldWorkingDir;
66 }
67
68 /*
69 * Free memory
70 */
71 void Paths::free(void)
72 {
73 dFree(oldWorkingDir);
74 }
75
76 /*
77 * Examines the path for "rcFile" and assign its file pointer to "fp".
78 */
79 FILE *Paths::getPrefsFP(const char *rcFile)
80 {
81 FILE *fp;
82 char *path = dStrconcat(dGethomedir(), "/.dillo/", rcFile, NULL);
83
84 if (!(fp = fopen(path, "r"))) {
85 MSG("paths: Cannot open file '%s'\n", path);
86
87 char *path2 = dStrconcat(DILLO_SYSCONF, rcFile, NULL);
88 if (!(fp = fopen(path2, "r"))) {
89 MSG("paths: Cannot open file '%s'\n",path2);
90 MSG("paths: Using internal defaults...\n");
91 } else {
92 MSG("paths: Using %s\n", path2);
93 }
94 dFree(path2);
95 }
96
97 dFree(path);
98 return fp;
99 }
100
0 /*
1 * File: paths.hh
2 *
3 * Copyright 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #ifndef __PATHS_HH__
12 #define __PATHS_HH__
13
14 #define PATHS_RC_PREFS "dillorc"
15 #define PATHS_RC_KEYS "keysrc"
16
17 class Paths {
18 public:
19 static void init(void);
20 static void free(void);
21 static char *getOldWorkingDir(void);
22 static FILE *getPrefsFP(const char *rcFile);
23 };
24
25 #endif /* __PATHS_HH__ */
55 *
66 * This program is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
8 * the Free Software Foundation; either version 3 of the License, or
99 * (at your option) any later version.
1010 */
1111
1313 #define __PIXMAPS_H__
1414
1515 /* XPM
16 static char * history_xpm[] = {
16 static const char *const history_xpm[] = {
1717 "11 20 6 1",
1818 " c None",
1919 ". c #000000000000",
4343 " "};
4444 */
4545 /* XPM */
46 static char * left_xpm[] = {
46 static const char *const left_xpm[] = {
4747 "22 22 46 1",
4848 " c None",
4949 ". c #000000",
114114 " . ",
115115 " "};
116116 /* XPM */
117 static char * right_xpm[] = {
117 static const char *const right_xpm[] = {
118118 "22 22 46 1",
119119 " c None",
120120 ". c #000000",
185185 " . ",
186186 " "};
187187 /* XPM */
188 static char * reload_xpm[] = {
188 static const char *const reload_xpm[] = {
189189 "22 22 147 2",
190190 " c None",
191191 ". c #000000",
357357 " . k.l.m.j j n.[.o.h.p.3 3 3 3 3 3 { . ",
358358 " . . . . . . . . . . . . . . . . . "};
359359 /* XPM */
360 static char * home_xpm[] = {
360 static const char *const home_xpm[] = {
361361 "22 22 106 2",
362362 " c None",
363363 ". c #190E0B",
488488 " # # # # # # # # # # # # # # ",
489489 " "};
490490 /* XPM */
491 static char * save_xpm[] = {
491 static const char *const save_xpm[] = {
492492 "22 22 59 1",
493493 " c None",
494494 ". c #000000",
573573 " "};
574574
575575 /* XPM */
576 static char * stop_xpm[] = {
576 static const char *const stop_xpm[] = {
577577 "22 22 77 1",
578578 " c None",
579579 ". c #000000",
674674 " .KLLLLLLLK. ",
675675 " ......... ",
676676 " "};
677 /* XPM */
678 static char * bm_xpm[] = {
677
678 /* XPM */
679 static const char *const bm_xpm[] = {
679680 "22 22 86 1",
680681 " c None",
681682 ". c #000000",
786787 " .S. TU. ",
787788 " . . "};
788789
790 /* XPM */
791 static const char *const tools_xpm[] = {
792 "22 22 25 1",
793 " c #None",
794 ". c #696977777676",
795 "X c #96969B9B9B9B",
796 "o c #848490908F8F",
797 "O c #D7D7D7D7D7D7",
798 "+ c #B5B5B7B7B7B7",
799 "@ c #EAEAEAEAEAEA",
800 "# c #797987878787",
801 "$ c #F1F1EFEFEFEF",
802 "% c #CCCCCCCCCCCC",
803 "& c #8B8B97979696",
804 "* c #CACACACACACA",
805 "= c #787878787878",
806 "- c #AAAAB5B5B5B5",
807 "; c #F9F9F8F8F9F9",
808 ": c #A6A6A8A8A8A8",
809 "> c #7F7F90907F7F",
810 ", c #A4A4BDBDA4A4",
811 "< c #A5A58A8A5353",
812 "1 c #00008B8B0000",
813 "2 c #6C6C6C6C6C6C",
814 "3 c #BCBCC3C3C2C2",
815 "4 c #CACAD8D8D8D8",
816 "5 c #9999A8A8A8A8",
817 "6 c #848489898989",
818 " ",
819 " ",
820 " .XXo. ",
821 " oO $$+. ",
822 " oo@$$$@# ",
823 " #o@$$$O# ",
824 " #X$$$$%X ",
825 " &#% #X$$$@o ",
826 " #*o =-@$$$# ",
827 " oO*o #$@$$$$# ",
828 " oOOOX oO@;$$$$X ",
829 " #@O*OXo:>,<$$$$o ",
830 " XO@O*O*12>,<$$$# ",
831 " X$@O*+<12>,<$$& ",
832 " +$@O*+<12>,<;@ ",
833 " :$@O*+<12>,<$@ ",
834 " &34O*-<11>,<$$ ",
835 " -X&X&<12>,<$O ",
836 " 45<11>,<3 ",
837 " 45<1=>O# ",
838 " -&X6&=# ",
839 " "};
840
789841 /* Small icons here */
790842
791843 /* XPM */
792 static char * s_left_xpm[] = {
844 static const char *const left_s_xpm[] = {
793845 "16 16 33 1",
794846 " c None",
795847 ". c #000000",
841893 " .. .;. ",
842894 " . "};
843895 /* XPM */
844 static char * s_right_xpm[] = {
896 static const char *const right_s_xpm[] = {
845897 "16 16 56 1",
846898 " c None",
847899 ". c #000000",
916968 " .. .>. ",
917969 " . "};
918970 /* XPM */
919 static char * s_home_xpm[] = {
971 static const char *const home_s_xpm[] = {
920972 "16 16 54 1",
921973 " c None",
922974 ". c #170E0B",
9891041 " +lm+n+mmmml+ ",
9901042 " ++++o+++++++ "};
9911043 /* XPM */
992 static char * s_reload_xpm[] = {
1044 static const char *const reload_s_xpm[] = {
9931045 "16 16 74 1",
9941046 " c None",
9951047 ". c #000000",
10821134 " .GHI%%IHG. ",
10831135 " ...... "};
10841136 /* XPM */
1085 static char * s_save_xpm[] = {
1137 static const char *const save_s_xpm[] = {
10861138 "16 16 51 1",
10871139 " c None",
10881140 ". c #000000",
11521204 ".h.iiijkkkkkl.. ",
11531205 " ............. "};
11541206 /* XPM */
1155 static char * s_stop_xpm[] = {
1207 static const char *const stop_s_xpm[] = {
11561208 "16 16 65 1",
11571209 " c None",
11581210 ". c #000000",
12371289 " ........ "};
12381290
12391291 /* XPM */
1240 static char * s_bm_xpm[] = {
1292 static const char *const bm_s_xpm[] = {
12411293 "16 16 63 1",
12421294 " c None",
12431295 ". c #000000",
13201372 " .v. wx. "};
13211373
13221374 /* XPM */
1323 static char * s_new_xpm[] = {
1375 static const char *const tools_s_xpm[] = {
1376 "16 16 45 1",
1377 " c #7B127D1C7D08",
1378 ". c #82778EED8E15",
1379 "X c #8F3F975396FF",
1380 "o c #9A98A40CA38F",
1381 "O c #833D8EC28E02",
1382 "+ c #99C89EDF9EC2",
1383 "@ c #B347BD91BD42",
1384 "# c #7E4181A081AF",
1385 "$ c #8744915990DA",
1386 "% c #86728A968A7C",
1387 "& c #8D56924B9226",
1388 "* c #96DF9AC79AA6",
1389 "= c #9E01A473A438",
1390 "- c #AB41B55EB546",
1391 "; c #ABD1B155B117",
1392 ": c #B5BFBC3DBC2F",
1393 "> c #B917BCECBCAB",
1394 ", c #B558B98BB958",
1395 "< c #CEA2CFDBCFD4",
1396 "1 c #A699AC55ABF1",
1397 "2 c #C2A5C5A8C57A",
1398 "3 c #C8B2CA27CA1C",
1399 "4 c #D80FD952D955",
1400 "5 c #DF19E120E134",
1401 "6 c #EAC7E9ECE9FE",
1402 "7 c #F164EF95EF9D",
1403 "8 c #F6E0F5E6F61C",
1404 "9 c #F013EEAEEEBF",
1405 "0 c #487651675130",
1406 "q c #6E4D7BA97B06",
1407 "w c #88F190679051",
1408 "e c #92339C0E9BC1",
1409 "r c #A1BBAB0FAA98",
1410 "t c #A24AA7C4A7A7",
1411 "y c #86C7935E92DC",
1412 "u c #C8C4D721D725",
1413 "i c #D0E6D516D518",
1414 "p c #76A38450834A",
1415 "a c #6B4B7B507A6D",
1416 "s c #7C6D882B878D",
1417 "d c #88F395099487",
1418 "f c #DB34DCE9DCF5",
1419 "g c #5B05673F66CD",
1420 "h c #9402A0549FCC",
1421 " c None",
1422 /* pixels */
1423 " ",
1424 " gO+Oq ",
1425 " p*45<Xg ",
1426 " h.>885oa ",
1427 " p.3883Xg ",
1428 " i$O s$488;s ",
1429 " O>1p e;588;a ",
1430 " O331sr;7778:s ",
1431 " O343,**3678;a ",
1432 " f,643;#%368:s ",
1433 " t<64>= &374f ",
1434 " t244>= &379ih",
1435 " d@:;=& &475u",
1436 " ssr-* *44r",
1437 " u-%#*%0",
1438 " feww0g"
1439 };
1440
1441 /* XPM */
1442 static const char *const new_s_xpm[] = {
13241443 "11 11 35 1",
13251444 " c None",
13261445 ". c #000000",
13701489 " . . "};
13711490
13721491 /* XPM */
1373 static char * search_xpm[] = {
1492 static const char *const search_xpm[] = {
13741493 "14 16 11 1",
13751494 " c None",
13761495 ". c #000000",
14011520 " 11 "};
14021521
14031522 /* XPM */
1404 static char * full_screen_on_xpm[] = {
1523 static const char *const help_xpm[] = {
1524 "14 16 16 1",
1525 " c None",
1526 "1 c #DBDBDB",
1527 "2 c #B6B6B6",
1528 "3 c #929292",
1529 "4 c #6D6D6D",
1530 "5 c #F1EFEF",
1531 "6 c #018B00",
1532 "7 c #A48A53",
1533 "8 c #A4BDA4",
1534 "9 c #000000",
1535 "A c #000000",
1536 "B c #000000",
1537 "C c #000000",
1538 "D c #000000",
1539 "E c #000000",
1540 "F c #000000",
1541 " 5555555 ",
1542 " 544433355 ",
1543 " 54555438455 ",
1544 " 44555542835 ",
1545 " 44445548245 ",
1546 " 4444554274 ",
1547 " 544554274 ",
1548 " 55474 ",
1549 " 5474 ",
1550 " 544 ",
1551 " 44 ",
1552 " ",
1553 " 5445 ",
1554 " 4764 ",
1555 " 4674 ",
1556 " 5445 "};
1557
1558 /* XPM */
1559 static const char *const full_screen_on_xpm[] = {
14051560 "13 15 2 1",
14061561 " c None",
14071562 ". c #000000",
14221577 " "};
14231578
14241579 /* XPM */
1425 static char * full_screen_off_xpm[] = {
1580 static const char *const full_screen_off_xpm[] = {
14261581 "13 15 2 1",
14271582 " c None",
14281583 ". c #000000",
14431598 " "};
14441599
14451600 /* XPM */
1446 static char * mini_bug_xpm[] = {
1447 "15 16 6 1",
1448 " c None",
1449 ". c black",
1450 "X c #c0c0c0",
1451 "o c #808080",
1452 "b c #303030",
1453 "# c white"
1454 " ",
1455 " . . ",
1456 " ... ",
1457 " X.....X ",
1458 " o.#...o ",
1459 " o.#...o.o ",
1460 " ..#oXo... ",
1461 " .....X..... ",
1462 " ....X.... ",
1463 " .o...X...o. ",
1464 " ...X... ",
1465 " .X..X..X. ",
1466 " .o. ",
1467 " .bbb.",
1468 " .o. ",
1469 " . "};
1470
1471 /* XPM */
1472 static char * mini_ok_xpm[] = {
1601 static const char *const mini_bug_xpm[] = {
1602 "16 16 7 1",
1603 " c None",
1604 ". c #000000000000",
1605 "r c #FFFF00000000",
1606 "X c #BEFBC30BBEFB",
1607 "o c #861782078617",
1608 "O c #FFFFFFFFFFFF",
1609 "+ c #30C230C230C2",
1610 " ",
1611 " . . ",
1612 " ... ",
1613 " X.....X ",
1614 " o.O...o ",
1615 " o.O...o.o ",
1616 " .rrrXrrr. ",
1617 " .rrrrXrrrr. ",
1618 " .rrrXrrr. ",
1619 " .o.rrXrr.o. ",
1620 " .rrXrr. ",
1621 " .XrrXrrX. ",
1622 " .o. ",
1623 " .+++.",
1624 " .o. ",
1625 " . "};
1626
1627 /* XPM */
1628 static const char *const mini_ok_xpm[] = {
14731629 "15 15 5 1",
14741630 "@ c #000000",
14751631 "a c #808080",
14931649 " @ "
14941650 };
14951651
1652 /* XPM */
1653 static const char *const left_i_xpm[] = {
1654 "22 22 3 1",
1655 " c None",
1656 ". c #000000",
1657 "@ c gray70",
1658 " ",
1659 " ",
1660 " ",
1661 " ",
1662 " @@ ",
1663 " @@@ ",
1664 " @@@@ ",
1665 " @@@@@@@@@@ @@@@ ",
1666 " @@@@@@@@@@@ @@@@ ",
1667 " @@@@@@@@@@@@ @@@@ ",
1668 " @@@@@@@@@@@@@ @@@@ ",
1669 " @@@@@@@@@@@@@ @@@@ ",
1670 " @@@@@@@@@@@@ @@@@ ",
1671 " @@@@@@@@@@@ @@@@ ",
1672 " @@@@@@@@@@ @@@@ ",
1673 " @@@@ ",
1674 " @@@ ",
1675 " @@ ",
1676 " @@@@@",
1677 " @ @ ",
1678 " @ ",
1679 " "};
1680
1681 /* XPM */
1682 static const char *const right_i_xpm[] = {
1683 "22 22 3 1",
1684 " c None",
1685 ". c #000000",
1686 "@ c gray70",
1687 " ",
1688 " ",
1689 " ",
1690 " ",
1691 " @@ ",
1692 " @@@ ",
1693 " @@@@ ",
1694 " @@@@ @@@@@@@@@@ ",
1695 " @@@@ @@@@@@@@@@@ ",
1696 " @@@@ @@@@@@@@@@@@ ",
1697 " @@@@ @@@@@@@@@@@@@ ",
1698 " @@@@ @@@@@@@@@@@@@ ",
1699 " @@@@ @@@@@@@@@@@@ ",
1700 " @@@@ @@@@@@@@@@@ ",
1701 " @@@@ @@@@@@@@@@ ",
1702 " @@@@ ",
1703 " @@@ ",
1704 " @@ ",
1705 " @@@@@",
1706 " @ @ ",
1707 " @ ",
1708 " "};
1709
1710 /* XPM */
1711 static const char *const stop_i_xpm[] = {
1712 /* columns rows colors chars-per-pixel */
1713 "22 22 2 1",
1714 " c None",
1715 "@ c gray70",
1716 /* pixels */
1717 " ",
1718 " ",
1719 " @@@@@@@@@ ",
1720 " @@@@@@@@@@@ ",
1721 " @@@@@@@@@@@@@ ",
1722 " @@@@@@@@@@@@@@@ ",
1723 " @@@@@@@@@@@@@@@@@ ",
1724 " @@@@@@@@@@@@@@@@@@@ ",
1725 " @@@@@@ @@@ @@@@@@ ",
1726 " @@@@@@ @ @@@@@@ ",
1727 " @@@@@@@ @@@@@@@ ",
1728 " @@@@@@@@ @@@@@@@@ ",
1729 " @@@@@@@ @@@@@@@ ",
1730 " @@@@@@ @ @@@@@@ ",
1731 " @@@@@@ @@@ @@@@@@ ",
1732 " @@@@@@@@@@@@@@@@@@@ ",
1733 " @@@@@@@@@@@@@@@@@ ",
1734 " @@@@@@@@@@@@@@@ ",
1735 " @@@@@@@@@@@@@ ",
1736 " @@@@@@@@@@@ ",
1737 " @@@@@@@@@ ",
1738 " "
1739 };
1740
1741 /* XPM */
1742 static const char *const stop_si_xpm[] = {
1743 "16 16 2 1",
1744 " c None",
1745 "@ c gray70",
1746 " @@@@@@@@ ",
1747 " @@@@@@@@@@ ",
1748 " @@@@@@@@@@@@ ",
1749 " @@@@@@@@@@@@@@ ",
1750 "@@@@@@@@@@@@@@@@",
1751 "@@@@@ @@@@ @@@@@",
1752 "@@@@@@ @@ @@@@@@",
1753 "@@@@@@@ @@@@@@@",
1754 "@@@@@@@ @@@@@@@",
1755 "@@@@@@ @@ @@@@@@",
1756 "@@@@@ @@@@ @@@@@",
1757 "@@@@@@@@@@@@@@@@",
1758 " @@@@@@@@@@@@@@ ",
1759 " @@@@@@@@@@@@ ",
1760 " @@@@@@@@@@ ",
1761 " @@@@@@@@ "};
1762
1763 /* XPM */
1764 static const char *const left_si_xpm[] = {
1765 "16 16 2 1",
1766 " c None",
1767 "@ c gray70",
1768 " ",
1769 " @@ ",
1770 " @@@ ",
1771 " @@@@ ",
1772 " @@@@@@@@ @@@@",
1773 " @@@@@@@@@ @@@@",
1774 " @@@@@@@@@@ @@@@",
1775 "@@@@@@@@@@@ @@@@",
1776 "@@@@@@@@@@@ @@@@",
1777 " @@@@@@@@@@ @@@@",
1778 " @@@@@@@@@ @@@@",
1779 " @@@@@@@@ @@@@",
1780 " @@@@ ",
1781 " @@@ @@@@@",
1782 " @@ @ @ ",
1783 " @ "};
1784
1785 /* XPM */
1786 static const char *const right_si_xpm[] = {
1787 "16 16 2 1",
1788 " c None",
1789 "@ c gray70",
1790 " ",
1791 " @@ ",
1792 " @@@ ",
1793 " @@@@ ",
1794 "@@@@ @@@@@@@@ ",
1795 "@@@@ @@@@@@@@@ ",
1796 "@@@@ @@@@@@@@@@ ",
1797 "@@@@ @@@@@@@@@@@",
1798 "@@@@ @@@@@@@@@@@",
1799 "@@@@ @@@@@@@@@@ ",
1800 "@@@@ @@@@@@@@@ ",
1801 "@@@@ @@@@@@@@ ",
1802 " @@@@ ",
1803 " @@@@@@@@",
1804 " @@ @ @ ",
1805 " @ "};
1806
14961807 #endif /* __PIXMAPS_H__ */
+0
-208
src/plain.c less more
0 /*
1 * File: plain.c
2 *
3 * Copyright (C) 1997 Raph Levien <raph@acm.org>
4 * Copyright (C) 1999 James McCollough <jamesm@gtwn.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * Module for decoding a text/plain object into a gtk_page widget.
14 */
15
16 #include <string.h> /* for memcpy and memmove */
17 #include <math.h> /* for rint() */
18 #include <gtk/gtk.h>
19
20 #include "prefs.h"
21 #include "dw_page.h"
22 #include "cache.h"
23 #include "browser.h"
24 #include "web.h"
25 #include "interface.h"
26 #include "progressbar.h"
27 #include "misc.h"
28 #include "history.h"
29 #include "nav.h"
30 #include "menu.h"
31
32
33 typedef struct _DilloPlain {
34 DwWidget *dw;
35 size_t Start_Ofs; /* Offset of where to start reading next */
36 DwStyle *style;
37 BrowserWindow *bw;
38 gint state;
39 } DilloPlain;
40
41 /* FSM states */
42 enum {
43 ST_SeekingEol,
44 ST_Eol,
45 ST_Eof
46 };
47
48 /*
49 * Forward declarations
50 */
51 static void Plain_write(DilloPlain *plain, void *Buf, guint BufSize, gint Eof);
52 static void Plain_callback(int Op, CacheClient_t *Client);
53
54 /* exported function */
55 DwWidget* a_Plain_text(const char *type, void *P, CA_Callback_t *Call,
56 void **Data);
57
58
59 /*
60 * Popup the page menu ("button_press_event" callback of the viewport)
61 */
62 static int Plain_page_menu(GtkWidget *viewport, GdkEventButton *event,
63 BrowserWindow *bw)
64 {
65 if (event->button == 3) {
66 a_Menu_popup_set_url(bw, a_History_get_url(NAV_TOP(bw)));
67 gtk_menu_popup(GTK_MENU(bw->menu_popup.over_page), NULL, NULL,
68 NULL, NULL, event->button, event->time);
69 return TRUE;
70 } else
71 return FALSE;
72 }
73
74 /*
75 * Create and initialize a new DilloPlain structure.
76 */
77 static DilloPlain *Plain_new(BrowserWindow *bw)
78 {
79 DilloPlain *plain;
80 DwPage *page;
81 DwStyle style_attrs;
82 DwStyleFont font_attrs;
83
84 plain = g_new(DilloPlain, 1);
85 plain->state = ST_SeekingEol;
86 plain->Start_Ofs = 0;
87 plain->bw = bw;
88 plain->dw = a_Dw_page_new();
89 page = (DwPage *) plain->dw;
90
91 /* Create the font and attribute for the page. */
92 font_attrs.name = prefs.fw_fontname;
93 font_attrs.size = rint(12.0 * prefs.font_factor);
94 font_attrs.weight = 400;
95 font_attrs.style = DW_STYLE_FONT_STYLE_NORMAL;
96
97 a_Dw_style_init_values (&style_attrs, plain->bw->main_window->window);
98 style_attrs.font = a_Dw_style_font_new (&font_attrs);
99 style_attrs.color = a_Dw_style_color_new (prefs.text_color,
100 plain->bw->main_window->window);
101 plain->style = a_Dw_style_new (&style_attrs,
102 plain->bw->main_window->window);
103 /* a_Dw_widget_set_style (plain->dw, plain->style); */
104
105 /* The context menu */
106 gtk_signal_connect_while_alive
107 (GTK_OBJECT(GTK_BIN(plain->bw->docwin)->child),"button_press_event",
108 GTK_SIGNAL_FUNC(Plain_page_menu), (gpointer)plain->bw,
109 GTK_OBJECT (page));
110
111 return plain;
112 }
113
114 /*
115 * Set callback function and callback data for "text/" MIME major-type.
116 */
117 DwWidget* a_Plain_text(const char *type, void *P, CA_Callback_t *Call,
118 void **Data)
119 {
120 DilloWeb *web = P;
121 DilloPlain *plain = Plain_new(web->bw);
122
123 *Call = (CA_Callback_t)Plain_callback;
124 *Data = (void*)plain;
125
126 return plain->dw;
127 }
128
129 /*
130 * This function is a cache client
131 */
132 static void Plain_callback(int Op, CacheClient_t *Client)
133 {
134 DilloPlain *plain= Client->CbData;
135 DwPage *page = (DwPage *)plain->dw;
136
137 if ( Op ) {
138 /* Do the last line: */
139 if (plain->Start_Ofs < Client->BufSize)
140 Plain_write(plain, Client->Buf, Client->BufSize, 1);
141 /* remove this client from our active list */
142 a_Interface_close_client(plain->bw, Client->Key);
143 /* set progress bar insensitive */
144 a_Progressbar_update(plain->bw->progress, NULL, 0);
145
146 a_Dw_style_unref (plain->style);
147 g_free(plain);
148 } else {
149 Plain_write(plain, Client->Buf, Client->BufSize, 0);
150 }
151
152 a_Dw_page_flush(page);
153 }
154
155 /*
156 * Here we parse plain text and put it into the page structure.
157 * (This function is called by Plain_callback whenever there's new data)
158 */
159 static void Plain_write(DilloPlain *plain, void *Buf, guint BufSize, gint Eof)
160 {
161 DwPage *page = (DwPage *)plain->dw;
162 char *Start;
163 char *data;
164 guint i, len, MaxBytes;
165
166 Start = (char*)Buf + plain->Start_Ofs;
167 MaxBytes = BufSize - plain->Start_Ofs;
168 i = len = 0;
169 while ( i < MaxBytes ) {
170 switch ( plain->state ) {
171 case ST_SeekingEol:
172 if ( Start[i] == '\n' || Start[i] == '\r' )
173 plain->state = ST_Eol;
174 else {
175 ++i; ++len;
176 }
177 break;
178 case ST_Eol:
179 data = g_strndup(Start + i - len, len);
180 a_Dw_page_add_text(page, a_Misc_expand_tabs(data), plain->style);
181 g_free(data);
182 a_Dw_page_add_parbreak(page, 0, plain->style);
183 if ( Start[i] == '\r' && Start[i + 1] == '\n' ) ++i;
184 if ( i < MaxBytes ) ++i;
185 plain->state = ST_SeekingEol;
186 len = 0;
187 break;
188 }
189 }
190 plain->Start_Ofs += i - len;
191 if ( Eof && len ) {
192 data = g_strndup(Start + i - len, len);
193 a_Dw_page_add_text(page, a_Misc_expand_tabs(data), plain->style);
194 g_free(data);
195 a_Dw_page_add_parbreak(page, 0, plain->style);
196 plain->Start_Ofs += len;
197 }
198
199 if ( plain->bw ) {
200 gchar completestr[32];
201 g_snprintf(
202 completestr, 32, "%s%.1f Kb",
203 PBAR_PSTR(prefs.panel_size == 1),
204 (float)plain->Start_Ofs/1024);
205 a_Progressbar_update(plain->bw->progress, completestr, 1);
206 }
207 }
0 /*
1 * File: plain.cc
2 *
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * Module for decoding a text/plain object into a dw widget.
13 */
14
15 #include "msg.h"
16 #include "prefs.h"
17 #include "cache.h"
18 #include "bw.h"
19 #include "web.hh"
20 #include "misc.h"
21 #include "styleengine.hh"
22
23 #include "uicmd.hh"
24
25 #include "dw/core.hh"
26 #include "dw/textblock.hh"
27
28 // Dw to Textblock
29 #define DW2TB(dw) ((Textblock*)dw)
30
31 using namespace dw;
32 using namespace dw::core;
33
34
35 class DilloPlain {
36 private:
37 class PlainLinkReceiver: public dw::core::Layout::LinkReceiver {
38 public:
39 DilloPlain *plain;
40 bool press(dw::core::Widget *widget, int link, int img, int x, int y,
41 dw::core::EventButton *event);
42 };
43 PlainLinkReceiver plainReceiver;
44
45 void addLine(char *Buf, uint_t BufSize);
46
47 public:
48 BrowserWindow *bw;
49
50 Widget *dw;
51 style::Style *widgetStyle;
52 size_t Start_Ofs; /* Offset of where to start reading next */
53 int state;
54
55 DilloPlain(BrowserWindow *bw);
56 ~DilloPlain();
57
58 void write(void *Buf, uint_t BufSize, int Eof);
59 };
60
61 /* FSM states */
62 enum {
63 ST_SeekingEol,
64 ST_Eol,
65 ST_Eof
66 };
67
68 /*
69 * Exported function with C linkage.
70 */
71 extern "C" {
72 void *a_Plain_text(const char *type, void *P, CA_Callback_t *Call,void **Data);
73 }
74
75 /*
76 * Forward declarations
77 */
78 static void Plain_callback(int Op, CacheClient_t *Client);
79 void a_Plain_free(void *data);
80
81
82 /*
83 * Diplain constructor.
84 */
85 DilloPlain::DilloPlain(BrowserWindow *p_bw)
86 {
87 /* Init event receiver */
88 plainReceiver.plain = this;
89
90 /* Init internal variables */
91 bw = p_bw;
92 dw = new Textblock (prefs.limit_text_width);
93 Start_Ofs = 0;
94 state = ST_SeekingEol;
95
96 Layout *layout = (Layout*) bw->render_layout;
97 StyleEngine styleEngine (layout);
98
99 styleEngine.startElement ("body");
100 styleEngine.startElement ("pre");
101 widgetStyle = styleEngine.wordStyle ();
102 widgetStyle->ref ();
103
104 /* The context menu */
105 layout->connectLink (&plainReceiver);
106
107 /* Hook destructor to the dw delete call */
108 dw->setDeleteCallback(a_Plain_free, this);
109 }
110
111 /*
112 * Free memory used by the DilloPlain class.
113 */
114 DilloPlain::~DilloPlain()
115 {
116 _MSG("::~DilloPlain()\n");
117 widgetStyle->unref();
118 }
119
120 /*
121 * Receive the mouse button press event
122 */
123 bool DilloPlain::PlainLinkReceiver::press (Widget *widget, int, int, int, int,
124 EventButton *event)
125 {
126 _MSG("DilloPlain::PlainLinkReceiver::buttonPress\n");
127
128 if (event->button == 3) {
129 a_UIcmd_page_popup(plain->bw, FALSE, NULL);
130 return true;
131 }
132 return false;
133 }
134
135 void DilloPlain::addLine(char *Buf, uint_t BufSize)
136 {
137 int len;
138 char buf[128];
139 char *end = Buf + BufSize;
140
141 if (BufSize > 0) {
142 // Limit word length to avoid X11 coordinate
143 // overflow with extremely long lines.
144 while ((len = a_Misc_expand_tabs(&Buf, end, buf, sizeof(buf))))
145 DW2TB(dw)->addText(buf, len, widgetStyle);
146 } else {
147 // Add dummy word for empty lines - otherwise the parbreak is ignored.
148 DW2TB(dw)->addText("", 0, widgetStyle);
149 }
150
151 DW2TB(dw)->addParbreak(0, widgetStyle);
152 }
153
154 /*
155 * Here we parse plain text and put it into the page structure.
156 * (This function is called by Plain_callback whenever there's new data)
157 */
158 void DilloPlain::write(void *Buf, uint_t BufSize, int Eof)
159 {
160 char *Start;
161 uint_t i, len, MaxBytes;
162
163 _MSG("DilloPlain::write Eof=%d\n", Eof);
164
165 Start = (char*)Buf + Start_Ofs;
166 MaxBytes = BufSize - Start_Ofs;
167 i = len = 0;
168 while ( i < MaxBytes ) {
169 switch ( state ) {
170 case ST_SeekingEol:
171 if (Start[i] == '\n' || Start[i] == '\r')
172 state = ST_Eol;
173 else {
174 ++i; ++len;
175 }
176 break;
177 case ST_Eol:
178 addLine(Start + i - len, len);
179 if (Start[i] == '\r' && Start[i + 1] == '\n') ++i;
180 if (i < MaxBytes) ++i;
181 state = ST_SeekingEol;
182 len = 0;
183 break;
184 }
185 }
186 Start_Ofs += i - len;
187 if (Eof && len) {
188 addLine(Start + i - len, len);
189 Start_Ofs += len;
190 }
191
192 DW2TB(dw)->flush();
193 }
194
195 /*
196 * Set callback function and callback data for "text/" MIME major-type.
197 */
198 void *a_Plain_text(const char *type, void *P, CA_Callback_t *Call, void **Data)
199 {
200 DilloWeb *web = (DilloWeb*)P;
201 DilloPlain *plain = new DilloPlain(web->bw);
202
203 *Call = (CA_Callback_t)Plain_callback;
204 *Data = (void*)plain;
205
206 return (void*)plain->dw;
207 }
208
209 void a_Plain_free(void *data)
210 {
211 _MSG("a_Plain_free! %p\n", data);
212 delete ((DilloPlain *)data);
213 }
214
215 /*
216 * This function is a cache client
217 */
218 static void Plain_callback(int Op, CacheClient_t *Client)
219 {
220 DilloPlain *plain = (DilloPlain*)Client->CbData;
221
222 if (Op) {
223 /* Do the last line: */
224 plain->write(Client->Buf, Client->BufSize, 1);
225 /* remove this client from our active list */
226 a_Bw_close_client(plain->bw, Client->Key);
227 } else {
228 plain->write(Client->Buf, Client->BufSize, 0);
229 }
230 }
231
33 *
44 * Geoff Lane nov 1999 zzassgl@twirl.mcc.ac.uk
55 * Luca Rota, Jorge Arellano Cid, Eric Gaudet 2000
6 * Jorge Arellano Cid 2009
67 *
78 * "PNG: The Definitive Guide" by Greg Roelofs, O'Reilly
89 * ISBN 1-56592-542-4
1112 #include <config.h>
1213 #ifdef ENABLE_PNG
1314
14 #include <stdio.h>
15 #include <string.h>
1615 #include <stdlib.h> /* For abort() */
17
18 #include <zlib.h>
1916
2017 #ifdef HAVE_LIBPNG_PNG_H
2118 #include <libpng/png.h>
2320 #include <png.h>
2421 #endif
2522
26 #include <gtk/gtk.h>
2723 #include "msg.h"
28 #include "image.h"
29 #include "web.h"
24 #include "image.hh"
3025 #include "cache.h"
3126 #include "dicache.h"
32 #include "prefs.h"
33
34 #define DEBUG_LEVEL 6
35 #include "debug.h"
3627
3728 enum prog_state {
3829 IS_finished, IS_init, IS_nextdata
3930 };
4031
32 #if 0
4133 static char *prog_state_name[] =
4234 {
4335 "IS_finished", "IS_init", "IS_nextdata"
4436 };
37 #endif
4538
4639 /*
4740 * This holds the data that must be saved between calls to this module.
5952 * structure below so that processing can be suspended or resumed at any
6053 * point within an input image.
6154 *
62 * In the case of the libpng library, it maintains it's own state in
55 * In the case of the libpng library, it maintains its own state in
6356 * png_ptr and into_ptr so the FSM is very simple - much simpler than the
6457 * ones for XBM and PNM are.
6558 */
6861 struct _DilloPng {
6962 DilloImage *Image; /* Image meta data */
7063 DilloUrl *url; /* Primary Key for the dicache */
71 gint version; /* Secondary Key for the dicache */
64 int version; /* Secondary Key for the dicache */
7265
7366 double display_exponent; /* gamma correction */
74 gulong width; /* png image width */
75 gulong height; /* png image height */
67 png_uint_32 width; /* png image width */
68 png_uint_32 height; /* png image height */
7669 png_structp png_ptr; /* libpng private data */
7770 png_infop info_ptr; /* libpng private info */
78 guchar *image_data; /* decoded image data */
79 guchar **row_pointers; /* pntr to row starts */
71 uchar_t *image_data; /* decoded image data */
72 uchar_t **row_pointers; /* pntr to row starts */
8073 jmp_buf jmpbuf; /* png error processing */
81 gint error; /* error flag */
82 gint rowbytes; /* No. bytes in image row */
74 int error; /* error flag */
75 png_uint_32 previous_row;
76 int rowbytes; /* No. bytes in image row */
8377 short passes;
8478 short channels; /* No. image channels */
8579
9286 * ipbuf ipbufstart ipbufsize
9387 */
9488
95 guchar *ipbuf; /* image data in buffer */
96 gint ipbufstart; /* first valid image byte */
97 gint ipbufsize; /* size of valid data in */
89 uchar_t *ipbuf; /* image data in buffer */
90 int ipbufstart; /* first valid image byte */
91 int ipbufsize; /* size of valid data in */
9892
9993 enum prog_state state; /* FSM current state */
10094
101 guchar *linebuf; /* o/p raster data */
95 uchar_t *linebuf; /* o/p raster data */
10296
10397 } DilloPng;
10498
106100 #define BLACK 0
107101 #define WHITE 255
108102
109 /*
110 * Forward declarations
111 */
112 /* exported function */
113 DwWidget *a_Png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
114 void **Data);
115
116103
117104 static
118105 void Png_error_handling(png_structp png_ptr, png_const_charp msg)
119106 {
120107 DilloPng *png;
121108
122 DEBUG_MSG(6, "Png_error_handling: %s\n", msg);
109 MSG("Png_error_handling: %s\n", msg);
123110 png = png_get_error_ptr(png_ptr);
124111
125112 png->error = 1;
132119 Png_datainfo_callback(png_structp png_ptr, png_infop info_ptr)
133120 {
134121 DilloPng *png;
135 gint color_type;
136 gint bit_depth;
137 gint interlace_type;
138 guint i;
122 int color_type;
123 int bit_depth;
124 int interlace_type;
125 uint_t i;
139126 double gamma;
140127
141 DEBUG_MSG(5, "Png_datainfo_callback:\n");
128 _MSG("Png_datainfo_callback:\n");
142129
143130 png = png_get_progressive_ptr(png_ptr);
144 g_return_if_fail (png != NULL);
131 dReturn_if_fail (png != NULL);
145132
146133 png_get_IHDR(png_ptr, info_ptr, &png->width, &png->height,
147134 &bit_depth, &color_type, &interlace_type, NULL, NULL);
148135
149 DEBUG_MSG(5, "Png_datainfo_callback: png->width = %ld\n"
150 "Png_datainfo_callback: png->height = %ld\n",
151 png->width, png->height);
136 /* check max image size */
137 if (png->width == 0 || png->height == 0 ||
138 png->width > IMAGE_MAX_AREA / png->height) {
139 MSG("Png_datainfo_callback: suspicious image size request %lu x %lu\n",
140 (ulong_t) png->width, (ulong_t) png->height);
141 Png_error_handling(png_ptr, "Aborting...");
142 return; /* not reached */
143 }
144
145 _MSG("Png_datainfo_callback: png->width = %lu\n"
146 "Png_datainfo_callback: png->height = %lu\n",
147 (ulong_t) png->width, (ulong_t) png->height);
152148
153149 /* we need RGB/RGBA in the end */
154150 if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8) {
169165 }
170166
171167 /* Get and set gamma information. Beware: gamma correction 2.2 will
172 only work on PC's. todo: select screen gamma correction for other
168 only work on PC's. TODO: select screen gamma correction for other
173169 platforms. */
174170 if (png_get_gAMA(png_ptr, info_ptr, &gamma))
175171 png_set_gamma(png_ptr, 2.2, gamma);
185181 png->passes = png_set_interlace_handling(png_ptr);
186182 }
187183
188 /* get libpng to update it's state */
184 /* get libpng to update its state */
189185 png_read_update_info(png_ptr, info_ptr);
190186
191187 png_get_IHDR(png_ptr, info_ptr, &png->width, &png->height,
195191 png->channels = png_get_channels(png_ptr, info_ptr);
196192
197193 /* init Dillo specifics */
198 DEBUG_MSG(5, "Png_datainfo_callback: rowbytes = %d\n"
199 "Png_datainfo_callback: width = %ld\n"
200 "Png_datainfo_callback: height = %ld\n",
201 png->rowbytes, png->width, png->height);
202
203 png->image_data = (guchar *) g_malloc(png->rowbytes * png->height);
204 png->row_pointers = (guchar **) g_malloc(png->height * sizeof(guchar *));
194 _MSG("Png_datainfo_callback: rowbytes = %d\n"
195 "Png_datainfo_callback: width = %lu\n"
196 "Png_datainfo_callback: height = %lu\n",
197 png->rowbytes, (ulong_t) png->width, (ulong_t) png->height);
198
199 png->image_data = (uchar_t *) dMalloc(png->rowbytes * png->height);
200 png->row_pointers = (uchar_t **) dMalloc(png->height * sizeof(uchar_t *));
205201
206202 for (i = 0; i < png->height; i++)
207203 png->row_pointers[i] = png->image_data + (i * png->rowbytes);
208204
209 png->linebuf = g_malloc(3 * png->width);
205 png->linebuf = dMalloc(3 * png->width);
210206
211207 /* Initialize the dicache-entry here */
212208 a_Dicache_set_parms(png->url, png->version, png->Image,
213 (guint)png->width, (guint)png->height,
209 (uint_t)png->width, (uint_t)png->height,
214210 DILLO_IMG_TYPE_RGB);
215211 }
216212
217213 static void
218214 Png_datarow_callback(png_structp png_ptr, png_bytep new_row,
219 png_uint_32 row_num, gint pass)
215 png_uint_32 row_num, int pass)
220216 {
221217 DilloPng *png;
222 guint i;
218 uint_t i;
223219
224220 if (!new_row) /* work to do? */
225221 return;
226222
227 DEBUG_MSG(5, "Png_datarow_callback: row_num = %ld\n", row_num);
223 _MSG("Png_datarow_callback: row_num = %ld\n", row_num);
228224
229225 png = png_get_progressive_ptr(png_ptr);
230226
231227 png_progressive_combine_row(png_ptr, png->row_pointers[row_num], new_row);
228
229 _MSG("png: row_num=%u previous_row=%u\n", row_num, png->previous_row);
230 if (row_num < png->previous_row) {
231 a_Dicache_new_scan(png->url, png->version);
232 }
233 png->previous_row = row_num;
232234
233235 switch (png->channels) {
234236 case 3:
235 a_Dicache_write(png->Image, png->url, png->version,
237 a_Dicache_write(png->url, png->version,
236238 png->image_data + (row_num * png->rowbytes),
237 0, (guint)row_num);
239 (uint_t)row_num);
238240 break;
239241 case 4:
240242 {
241 /* todo: get the backgound color from the parent
243 /* TODO: get the backgound color from the parent
242244 * of the image widget -- Livio. */
243 gint a, bg_red, bg_green, bg_blue;
244 guchar *pl = png->linebuf;
245 guchar *data = png->image_data + (row_num * png->rowbytes);
246
247 /* todo: maybe change prefs.bg_color to `a_Dw_widget_get_bg_color`,
245 int a, bg_red, bg_green, bg_blue;
246 uchar_t *pl = png->linebuf;
247 uchar_t *data = png->image_data + (row_num * png->rowbytes);
248
249 /* TODO: maybe change prefs.bg_color to `a_Dw_widget_get_bg_color`,
248250 * when background colors are correctly implementated */
249251 bg_blue = (png->Image->bg_color) & 0xFF;
250252 bg_green = (png->Image->bg_color>>8) & 0xFF;
253255 for (i = 0; i < png->width; i++) {
254256 a = *(data+3);
255257
256 if ( a == 255 ) {
258 if (a == 255) {
257259 *(pl++) = *(data++);
258260 *(pl++) = *(data++);
259261 *(pl++) = *(data++);
260262 data++;
261 } else if ( a == 0 ) {
263 } else if (a == 0) {
262264 *(pl++) = bg_red;
263265 *(pl++) = bg_green;
264266 *(pl++) = bg_blue;
270272 data++;
271273 }
272274 }
273 a_Dicache_write(png->Image, png->url, png->version,
274 png->linebuf, 0, (guint)row_num);
275 a_Dicache_write(png->url, png->version, png->linebuf, (uint_t)row_num);
275276 break;
276277 }
277278 default:
278 MSG("Png_datarow_callback: unexpected number of channels = %d\n",
279 png->channels);
279 MSG("Png_datarow_callback: unexpected number of channels=%d pass=%d\n",
280 png->channels, pass);
280281 abort();
281282 }
282283 }
283284
284 static void
285 Png_dataend_callback(png_structp png_ptr, png_infop info_ptr)
285 static void Png_dataend_callback(png_structp png_ptr, png_infop info_ptr)
286286 {
287287 DilloPng *png;
288288
289 DEBUG_MSG(5, "Png_dataend_callback:\n");
289 _MSG("Png_dataend_callback:\n");
290 if (!info_ptr)
291 MSG("Png_dataend_callback: info_ptr = NULL\n");
290292
291293 png = png_get_progressive_ptr(png_ptr);
292294 png->state = IS_finished;
293295 }
294296
295
296 /*
297 * Op: Operation to perform.
298 * If (Op == 0)
299 * start or continue processing an image if image data exists.
300 * else
301 * terminate processing, cleanup any allocated memory,
302 * close down the decoding process.
303 *
304 * Client->CbData : pointer to previously allocated DilloPng work area.
305 * This holds the current state of the image processing and is saved
306 * across calls to this routine.
307 * Client->Buf : Pointer to data start.
308 * Client->BufSize : the size of the data buffer.
309 *
310 * You have to keep track of where you are in the image data and
311 * how much has been processed.
312 *
313 * It's entirely possible that you will not see the end of the data. The
314 * user may terminate transfer via a Stop button or there may be a network
315 * failure. This means that you can't just wait for all the data to be
316 * presented before starting conversion and display.
317 */
318 static void Png_callback(int Op, CacheClient_t *Client)
319 {
320 DilloPng *png = Client->CbData;
321
322 if ( Op ) {
323 /* finished - free up the resources for this image */
324 a_Dicache_close(png->url, png->version, Client);
325 g_free(png->image_data);
326 g_free(png->row_pointers);
327 g_free(png->linebuf);
328
329 if (setjmp(png->jmpbuf))
330 g_warning("PNG: can't destroy read structure\n");
331 else if (png->png_ptr)
332 png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
333 g_free(png);
334 return;
335 }
336
337 /* Let's make some sound if we have been called with no data */
338 g_return_if_fail ( Client->Buf != NULL && Client->BufSize > 0 );
339
340 DEBUG_MSG(5, "Png_callback BufSize = %d\n", Client->BufSize);
297 /*
298 * Free up the resources for this image.
299 */
300 static void Png_free(DilloPng *png)
301 {
302 _MSG("Png_free: png=%p\n", png);
303
304 dFree(png->image_data);
305 dFree(png->row_pointers);
306 dFree(png->linebuf);
307 if (setjmp(png->jmpbuf))
308 MSG_WARN("PNG: can't destroy read structure\n");
309 else if (png->png_ptr)
310 png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
311 dFree(png);
312 }
313
314 /*
315 * Finish the decoding process (and free the memory)
316 */
317 static void Png_close(DilloPng *png, CacheClient_t *Client)
318 {
319 _MSG("Png_close\n");
320 /* Let dicache know decoding is over */
321 a_Dicache_close(png->url, png->version, Client);
322 Png_free(png);
323 }
324
325 /*
326 * Receive and process new chunks of PNG image data
327 */
328 static void Png_write(DilloPng *png, void *Buf, uint_t BufSize)
329 {
330 dReturn_if_fail ( Buf != NULL && BufSize > 0 );
341331
342332 /* Keep local copies so we don't have to pass multiple args to
343333 * a number of functions. */
344 png->ipbuf = Client->Buf;
345 png->ipbufsize = Client->BufSize;
334 png->ipbuf = Buf;
335 png->ipbufsize = BufSize;
346336
347337 /* start/resume the FSM here */
348338 while (png->state != IS_finished && DATASIZE) {
349 DEBUG_MSG(5, "State = %s\n", prog_state_name[png->state]);
339 _MSG("State = %s\n", prog_state_name[png->state]);
350340
351341 switch (png->state) {
352342 case IS_init:
354344 return; /* need MORE data */
355345 }
356346 /* check the image signature - DON'T update ipbufstart! */
357 if (!png_check_sig(png->ipbuf, DATASIZE)) {
358 /* you lied to me about it being a PNG image */
347 if (png_sig_cmp(png->ipbuf, 0, DATASIZE)) {
348 MSG_WARN("\"%s\" is not a PNG file.\n", URL_STR(png->url));
359349 png->state = IS_finished;
360350 break;
361351 }
365355 png,
366356 (png_error_ptr)Png_error_handling,
367357 (png_error_ptr)Png_error_handling);
368 g_return_if_fail (png->png_ptr != NULL);
358 dReturn_if_fail (png->png_ptr != NULL);
369359 png->info_ptr = png_create_info_struct(png->png_ptr);
370 g_return_if_fail (png->info_ptr != NULL);
360 dReturn_if_fail (png->info_ptr != NULL);
371361
372362 setjmp(png->jmpbuf);
373363 if (!png->error) {
382372 break;
383373
384374 case IS_nextdata:
385 if ( setjmp(png->jmpbuf) ) {
375 if (setjmp(png->jmpbuf)) {
386376 png->state = IS_finished;
387377 } else if (!png->error) {
388378 png_process_data( png->png_ptr,
395385 break;
396386
397387 default:
398 g_warning("PNG decoder: bad state = %d\n", png->state);
388 MSG_WARN("PNG decoder: bad state = %d\n", png->state);
399389 abort();
400390 }
401391 }
402392 }
403393
404394 /*
395 * Op: Operation to perform.
396 * If (Op == 0)
397 * start or continue processing an image if image data exists.
398 * else
399 * terminate processing, cleanup any allocated memory,
400 * close down the decoding process.
401 *
402 * Client->CbData : pointer to previously allocated DilloPng work area.
403 * This holds the current state of the image processing and is kept
404 * across calls to this routine.
405 * Client->Buf : Pointer to data start.
406 * Client->BufSize : the size of the data buffer.
407 *
408 * You have to keep track of where you are in the image data and
409 * how much has been processed.
410 *
411 * It's entirely possible that you will not see the end of the data. The
412 * user may terminate transfer via a Stop button or there may be a network
413 * failure. This means that you can't just wait for all the data to be
414 * presented before starting conversion and display.
415 */
416 void a_Png_callback(int Op, void *data)
417 {
418 if (Op == CA_Send) {
419 CacheClient_t *Client = data;
420 Png_write(Client->CbData, Client->Buf, Client->BufSize);
421 } else if (Op == CA_Close) {
422 CacheClient_t *Client = data;
423 Png_close(Client->CbData, Client);
424 } else if (Op == CA_Abort) {
425 Png_free(data);
426 }
427 }
428
429 /*
405430 * Create the image state data that must be kept between calls
406431 */
407 static DilloPng *Png_new(DilloImage *Image, DilloUrl *url, gint version)
408 {
409 DilloPng *png = g_new0(DilloPng, 1);
432 void *a_Png_new(DilloImage *Image, DilloUrl *url, int version)
433 {
434 DilloPng *png = dNew0(DilloPng, 1);
435 _MSG("a_Png_new: png=%p\n", png);
410436
411437 png->Image = Image;
412438 png->url = url;
419445 png->linebuf = NULL;
420446 png->image_data = NULL;
421447 png->row_pointers = NULL;
448 png->previous_row = 0;
422449
423450 return png;
424451 }
425452
426 /*
427 * MIME handler for "image/png" type
428 * (Sets Png_callback or a_Dicache_callback as the cache-client)
429 */
430 DwWidget *a_Png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
431 void **Data)
432 {
433 /*
434 * Type: MIME type
435 * Ptr: points to a Web structure
436 * Call: Dillo calls this with more data/eod
437 * Data: raw image data
438 */
439
440 DilloWeb *web = Ptr;
441 DICacheEntry *DicEntry;
442
443 DEBUG_MSG(5, "a_Png_image: Type = %s\n"
444 "a_Png_image: libpng - Compiled %s; using %s.\n"
445 "a_Png_image: zlib - Compiled %s; using %s.\n",
446 Type, PNG_LIBPNG_VER_STRING, png_libpng_ver,
447 ZLIB_VERSION, zlib_version);
448
449 if ( !web->Image )
450 web->Image = a_Image_new(0, 0, NULL, prefs.bg_color);
451
452 /* Add an extra reference to the Image (for dicache usage) */
453 a_Image_ref(web->Image);
454
455 DicEntry = a_Dicache_get_entry(web->url);
456 if ( !DicEntry ) {
457 /* Let's create an entry for this image... */
458 DicEntry = a_Dicache_add_entry(web->url);
459
460 /* ... and let the decoder feed it! */
461 *Data = Png_new(web->Image, DicEntry->url, DicEntry->version);
462 *Call = (CA_Callback_t) Png_callback;
463 } else {
464 /* Let's feed our client from the dicache */
465 a_Dicache_ref(DicEntry->url, DicEntry->version);
466 *Data = web->Image;
467 *Call = (CA_Callback_t) a_Dicache_callback;
468 }
469 return DW_WIDGET (web->Image->dw);
470 }
453 #else /* ENABLE_PNG */
454
455 void *a_Png_new() { return 0; }
456 void a_Png_callback() { return; }
471457
472458 #endif /* ENABLE_PNG */
00 /*
1 * Preferences for dillo
1 * Preferences
22 *
3 * Copyright (C) 2000 Luca Rota <lrota@cclinf.polito.it>
3 * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
44 *
55 * This program is free software; you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
88 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
189 */
1910
20 #include <glib.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <stdlib.h>
24 #include <string.h> /* for strchr */
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <locale.h> /* for setlocale */
2811 #include "prefs.h"
29 #include "colors.h"
30 #include "misc.h"
3112
32 /* symbol array */
33 static const struct {
34 gchar *name;
35 guint token;
36 } symbols[] = {
37 { "geometry", DRC_TOKEN_GEOMETRY },
38 { "http_proxy", DRC_TOKEN_PROXY },
39 { "http_proxyuser", DRC_TOKEN_PROXYUSER },
40 { "no_proxy", DRC_TOKEN_NOPROXY },
41 { "link_color", DRC_TOKEN_LINK_COLOR },
42 { "visited_color", DRC_TOKEN_VISITED_COLOR, },
43 { "bg_color", DRC_TOKEN_BG_COLOR },
44 { "allow_white_bg", DRC_TOKEN_ALLOW_WHITE_BG },
45 { "force_my_colors", DRC_TOKEN_FORCE_MY_COLORS },
46 { "contrast_visited_color", DRC_TOKEN_CONTRAST_VISITED_COLOR },
47 { "text_color", DRC_TOKEN_TEXT_COLOR },
48 { "use_oblique", DRC_TOKEN_USE_OBLIQUE },
49 { "start_page", DRC_TOKEN_START_PAGE },
50 { "home", DRC_TOKEN_HOME },
51 { "show_tooltip", DRC_TOKEN_SHOW_TOOLTIP },
52 { "panel_size", DRC_TOKEN_PANEL_SIZE },
53 { "small_icons", DRC_TOKEN_SMALL_ICONS },
54 { "limit_text_width", DRC_TOKEN_LIMIT_TEXT_WIDTH },
55 { "w3c_plus_heuristics", DRC_TOKEN_W3C_PLUS_HEURISTICS },
56 { "font_factor", DRC_TOKEN_FONT_FACTOR },
57 { "use_dicache", DRC_TOKEN_USE_DICACHE },
58 { "show_back", DRC_TOKEN_SHOW_BACK },
59 { "show_forw", DRC_TOKEN_SHOW_FORW },
60 { "show_home", DRC_TOKEN_SHOW_HOME },
61 { "show_reload", DRC_TOKEN_SHOW_RELOAD },
62 { "show_save", DRC_TOKEN_SHOW_SAVE },
63 { "show_stop", DRC_TOKEN_SHOW_STOP },
64 { "show_bookmarks", DRC_TOKEN_SHOW_BOOKMARKS },
65 { "show_menubar", DRC_TOKEN_SHOW_MENUBAR },
66 { "show_clear_url", DRC_TOKEN_SHOW_CLEAR_URL },
67 { "show_url", DRC_TOKEN_SHOW_URL },
68 { "show_search", DRC_TOKEN_SHOW_SEARCH },
69 { "show_progress_box", DRC_TOKEN_SHOW_PROGRESS_BOX },
70 { "fullwindow_start", DRC_TOKEN_FULLWINDOW_START },
71 { "transient_dialogs", DRC_TOKEN_TRANSIENT_DIALOGS },
72 { "vw_fontname", DRC_TOKEN_VW_FONT },
73 { "fw_fontname", DRC_TOKEN_FW_FONT },
74 { "generate_submit", DRC_TOKEN_GENERATE_SUBMIT },
75 { "enterpress_forces_submit", DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT },
76 { "search_url", DRC_TOKEN_SEARCH_URL },
77 { "show_msg", DRC_TOKEN_SHOW_MSG },
78 { "show_extra_warnings", DRC_TOKEN_SHOW_EXTRA_WARNINGS }
79 };
13 #define PREFS_START_PAGE "about:splash"
14 #define PREFS_HOME "http://www.dillo.org/"
15 #define PREFS_FONT_SERIF "DejaVu Serif"
16 #define PREFS_FONT_SANS_SERIF "DejaVu Sans"
17 #define PREFS_FONT_CURSIVE "URW Chancery L"
18 #define PREFS_FONT_FANTASY "DejaVu Sans" /* TODO: find good default */
19 #define PREFS_FONT_MONOSPACE "DejaVu Sans Mono"
20 #define PREFS_SEARCH_URL "http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s"
21 #define PREFS_NO_PROXY "localhost 127.0.0.1"
22 #define PREFS_SAVE_DIR "/tmp/"
23 #define PREFS_HTTP_REFERER "host"
24 #define PREFS_HTTP_USER_AGENT "Dillo/" VERSION
8025
81 static const guint n_symbols = sizeof (symbols) / sizeof (symbols[0]);
26 /*-----------------------------------------------------------------------------
27 * Global Data
28 *---------------------------------------------------------------------------*/
29 DilloPrefs prefs;
8230
8331 /*
84 * Read tokens from dillorc and set values in the prefs structure.
85 * (Although this function can be called several times, and not leak,
86 * preferences aren't yet enabled for on-the-fly changes).
32 * Sets the default settings.
8733 */
88 static guint Prefs_parser(GScanner *scanner)
89 {
90 gint st;
91 guint symbol;
92
93 /* expect a valid symbol */
94 g_scanner_get_next_token(scanner);
95 symbol = scanner->token;
96 if (scanner->token == G_TOKEN_EQUAL_SIGN) {
97 g_scanner_get_next_token (scanner);
98 return G_TOKEN_NONE;
99 } else if (symbol < DRC_TOKEN_FIRST || symbol > DRC_TOKEN_LAST )
100 return G_TOKEN_SYMBOL;
101
102 /* expect '=' */
103 g_scanner_get_next_token(scanner);
104 if (scanner->token != G_TOKEN_EQUAL_SIGN)
105 return G_TOKEN_EQUAL_SIGN;
106
107 /* expect a string */
108 g_scanner_get_next_token(scanner);
109 if (scanner->token != G_TOKEN_STRING)
110 return G_TOKEN_STRING;
111
112 /* assign value and exit successfully */
113 switch (symbol) {
114 case DRC_TOKEN_GEOMETRY:
115 a_Misc_parse_geometry(scanner->value.v_string, &prefs.xpos, &prefs.ypos,
116 &prefs.width, &prefs.height);
117 break;
118 case DRC_TOKEN_PROXY:
119 a_Url_free(prefs.http_proxy);
120 prefs.http_proxy = a_Url_new(scanner->value.v_string, NULL, 0, 0, 0);
121 break;
122 case DRC_TOKEN_PROXYUSER:
123 g_free(prefs.http_proxyuser);
124 prefs.http_proxyuser = g_strdup(scanner->value.v_string);
125 break;
126 case DRC_TOKEN_NOPROXY:
127 g_free(prefs.no_proxy);
128 prefs.no_proxy = g_strdup(scanner->value.v_string);
129 prefs.no_proxy_vec = g_strsplit(prefs.no_proxy, " ", 0);
130 break;
131 case DRC_TOKEN_LINK_COLOR:
132 prefs.link_color = a_Color_parse(scanner->value.v_string,
133 prefs.link_color, &st);
134 break;
135 case DRC_TOKEN_VISITED_COLOR:
136 prefs.visited_color = a_Color_parse(scanner->value.v_string,
137 prefs.visited_color, &st);
138 break;
139 case DRC_TOKEN_TEXT_COLOR:
140 prefs.text_color = a_Color_parse(scanner->value.v_string,
141 prefs.text_color, &st);
142 break;
143 case DRC_TOKEN_BG_COLOR:
144 prefs.bg_color = a_Color_parse(scanner->value.v_string,
145 prefs.bg_color, &st);
146 break;
147 case DRC_TOKEN_ALLOW_WHITE_BG:
148 prefs.allow_white_bg = (strcmp(scanner->value.v_string, "YES") == 0);
149 break;
150 case DRC_TOKEN_FORCE_MY_COLORS:
151 prefs.force_my_colors = (strcmp(scanner->value.v_string, "YES") == 0);
152 break;
153 case DRC_TOKEN_CONTRAST_VISITED_COLOR:
154 prefs.contrast_visited_color =
155 (strcmp(scanner->value.v_string, "YES") == 0);
156 break;
157 case DRC_TOKEN_USE_OBLIQUE:
158 prefs.use_oblique = (strcmp(scanner->value.v_string, "YES") == 0);
159 break;
160 case DRC_TOKEN_PANEL_SIZE:
161 if (!g_strcasecmp(scanner->value.v_string, "tiny"))
162 prefs.panel_size = 1;
163 else if (!g_strcasecmp(scanner->value.v_string, "small"))
164 prefs.panel_size = 2;
165 else if (!g_strcasecmp(scanner->value.v_string, "medium"))
166 prefs.panel_size = 3;
167 else /* default to "large" */
168 prefs.panel_size = 4;
169 break;
170 case DRC_TOKEN_SMALL_ICONS:
171 prefs.small_icons = (strcmp(scanner->value.v_string, "YES") == 0);
172 break;
173 case DRC_TOKEN_START_PAGE:
174 a_Url_free(prefs.start_page);
175 prefs.start_page = a_Url_new(scanner->value.v_string, NULL, 0, 0, 0);
176 break;
177 case DRC_TOKEN_HOME:
178 a_Url_free(prefs.home);
179 prefs.home = a_Url_new(scanner->value.v_string, NULL, 0, 0, 0);
180 break;
181 case DRC_TOKEN_SHOW_TOOLTIP:
182 prefs.show_tooltip = (strcmp(scanner->value.v_string, "YES") == 0);
183 break;
184 case DRC_TOKEN_FONT_FACTOR:
185 prefs.font_factor = strtod(scanner->value.v_string, NULL);
186 break;
187 case DRC_TOKEN_LIMIT_TEXT_WIDTH:
188 prefs.limit_text_width = (strcmp(scanner->value.v_string, "YES") == 0);
189 break;
190 case DRC_TOKEN_W3C_PLUS_HEURISTICS:
191 prefs.w3c_plus_heuristics = (strcmp(scanner->value.v_string,"YES") == 0);
192 break;
193 case DRC_TOKEN_USE_DICACHE:
194 prefs.use_dicache = (strcmp(scanner->value.v_string, "YES") == 0);
195 break;
196 case DRC_TOKEN_SHOW_BACK:
197 prefs.show_back = (strcmp(scanner->value.v_string, "YES") == 0);
198 break;
199 case DRC_TOKEN_SHOW_FORW:
200 prefs.show_forw = (strcmp(scanner->value.v_string, "YES") == 0);
201 break;
202 case DRC_TOKEN_SHOW_HOME:
203 prefs.show_home = (strcmp(scanner->value.v_string, "YES") == 0);
204 break;
205 case DRC_TOKEN_SHOW_RELOAD:
206 prefs.show_reload = (strcmp(scanner->value.v_string, "YES") == 0);
207 break;
208 case DRC_TOKEN_SHOW_SAVE:
209 prefs.show_save = (strcmp(scanner->value.v_string, "YES") == 0);
210 break;
211 case DRC_TOKEN_SHOW_STOP:
212 prefs.show_stop = (strcmp(scanner->value.v_string, "YES") == 0);
213 break;
214 case DRC_TOKEN_SHOW_BOOKMARKS:
215 prefs.show_bookmarks = (strcmp(scanner->value.v_string, "YES") == 0);
216 break;
217 case DRC_TOKEN_SHOW_MENUBAR:
218 prefs.show_menubar = (strcmp(scanner->value.v_string, "YES") == 0);
219 break;
220 case DRC_TOKEN_SHOW_CLEAR_URL:
221 prefs.show_clear_url = (strcmp(scanner->value.v_string, "YES") == 0);
222 break;
223 case DRC_TOKEN_SHOW_URL:
224 prefs.show_url = (strcmp(scanner->value.v_string, "YES") == 0);
225 break;
226 case DRC_TOKEN_SHOW_SEARCH:
227 prefs.show_search = (strcmp(scanner->value.v_string, "YES") == 0);
228 break;
229 case DRC_TOKEN_SHOW_PROGRESS_BOX:
230 prefs.show_progress_box = (strcmp(scanner->value.v_string, "YES") == 0);
231 break;
232 case DRC_TOKEN_FULLWINDOW_START:
233 prefs.fullwindow_start = (strcmp(scanner->value.v_string, "YES") == 0);
234 break;
235 case DRC_TOKEN_TRANSIENT_DIALOGS:
236 prefs.transient_dialogs = (strcmp(scanner->value.v_string, "YES") == 0);
237 break;
238 case DRC_TOKEN_FW_FONT:
239 g_free(prefs.fw_fontname);
240 prefs.fw_fontname = g_strdup(scanner->value.v_string);
241 break;
242 case DRC_TOKEN_VW_FONT:
243 g_free(prefs.vw_fontname);
244 prefs.vw_fontname = g_strdup(scanner->value.v_string);
245 break;
246 case DRC_TOKEN_GENERATE_SUBMIT:
247 prefs.generate_submit = (strcmp(scanner->value.v_string, "YES") == 0);
248 break;
249 case DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT:
250 prefs.enterpress_forces_submit =
251 (strcmp(scanner->value.v_string, "YES") == 0);
252 break;
253 case DRC_TOKEN_SEARCH_URL:
254 g_free(prefs.search_url);
255 prefs.search_url = g_strdup(scanner->value.v_string);
256 break;
257 case DRC_TOKEN_SHOW_MSG:
258 prefs.show_msg =
259 (strcmp(scanner->value.v_string, "YES") == 0);
260 break;
261 case DRC_TOKEN_SHOW_EXTRA_WARNINGS:
262 prefs.show_extra_warnings =
263 (strcmp(scanner->value.v_string, "YES") == 0);
264 break;
265 default:
266 break; /* Not reached */
267 }
268 return G_TOKEN_NONE;
269 }
270
271 static gint Prefs_load(void)
272 {
273 GScanner *scanner;
274 gint fd;
275 guint i, expected_token;
276 gchar *file;
277
278 /* Here we load and set options from dillorc */
279 file = a_Misc_prepend_user_home(".dillo/dillorc");
280 fd = open(file, O_RDONLY);
281 g_free(file);
282 if (fd < 0 && (fd = open(DILLORC_SYS, O_RDONLY)) < 0)
283 return FILE_NOT_FOUND;
284
285 fcntl(fd, F_SETFD, FD_CLOEXEC | fcntl(fd, F_GETFD));
286
287 scanner = g_scanner_new(NULL);
288
289 /* Adjust lexing behaviour to suit our needs */
290 /* Specifies the chars which can be used in identifiers */
291 scanner->config->cset_identifier_nth = (
292 G_CSET_a_2_z
293 "~+-_:&%#/.0123456789"
294 G_CSET_A_2_Z
295 G_CSET_LATINS /*??? I don't know if we need these two */
296 G_CSET_LATINC /*??? */
297 );
298 /* Specifies the chars which can start identifiers */
299 scanner->config->cset_identifier_first = (
300 G_CSET_a_2_z
301 G_CSET_A_2_Z
302 "_0123456789"
303 );
304 /* Don't return G_TOKEN_SYMBOL, but the symbol's value */
305 scanner->config->symbol_2_token = TRUE;
306 /* Don't return G_TOKEN_IDENTIFIER, but convert it to string */
307 scanner->config->identifier_2_string = TRUE;
308
309 /* load symbols into the scanner */
310 g_scanner_freeze_symbol_table(scanner);
311 for (i = 0; i < n_symbols; i++)
312 g_scanner_add_symbol(scanner, symbols[i].name,
313 GINT_TO_POINTER (symbols[i].token));
314 g_scanner_thaw_symbol_table(scanner);
315
316 /* feed in the text */
317 g_scanner_input_file(scanner, fd);
318
319 /* give the error handler an idea on how the input is named */
320 scanner->input_name = "dillorc";
321
322 /*
323 * Scanning loop, we parse the input until it's end is reached,
324 * the scanner encountered a lexing error, or our sub routine came
325 * across invalid syntax
326 */
327 do {
328 expected_token = Prefs_parser(scanner);
329
330 /* Give an error message upon syntax errors */
331 if (expected_token == G_TOKEN_SYMBOL)
332 g_scanner_unexp_token (scanner, expected_token, NULL, "symbol", NULL,
333 NULL, FALSE);
334 else if (expected_token == G_TOKEN_STRING)
335 g_scanner_unexp_token (scanner, expected_token, NULL, "string", NULL,
336 NULL, FALSE);
337 else if (expected_token == G_TOKEN_EQUAL_SIGN)
338 g_scanner_unexp_token (scanner, expected_token, NULL, "=", NULL,
339 NULL, FALSE);
340 g_scanner_peek_next_token (scanner);
341 } while ( /* expected_token == G_TOKEN_NONE && */
342 scanner->next_token != G_TOKEN_EOF &&
343 scanner->next_token != G_TOKEN_ERROR);
344
345 /* give an error message upon syntax errors */
346 if (expected_token != G_TOKEN_NONE)
347 g_scanner_unexp_token(scanner, expected_token, NULL, "symbol", NULL,
348 NULL, TRUE);
349
350 /* finish parsing */
351 g_scanner_destroy(scanner);
352 close(fd);
353 return PARSE_OK;
354 }
35534
35635 void a_Prefs_init(void)
35736 {
358 gchar *old_locale;
37 prefs.allow_white_bg = TRUE;
38 prefs.bg_color = 0xdcd1ba;
39 prefs.buffered_drawing = 1;
40 prefs.contrast_visited_color = TRUE;
41 prefs.enterpress_forces_submit = FALSE;
42 prefs.filter_auto_requests = PREFS_FILTER_SAME_DOMAIN;
43 prefs.focus_new_tab = TRUE;
44 prefs.font_cursive = dStrdup(PREFS_FONT_CURSIVE);
45 prefs.font_factor = 1.0;
46 prefs.font_max_size = 100;
47 prefs.font_min_size = 6;
48 prefs.font_fantasy = dStrdup(PREFS_FONT_FANTASY);
49 prefs.font_monospace = dStrdup(PREFS_FONT_MONOSPACE);
50 prefs.font_sans_serif = dStrdup(PREFS_FONT_SANS_SERIF);
51 prefs.font_serif = dStrdup(PREFS_FONT_SERIF);
52 prefs.fullwindow_start = FALSE;
35953
360 prefs.width = D_GEOMETRY_DEFAULT_WIDTH;
361 prefs.height = D_GEOMETRY_DEFAULT_HEIGHT;
362 prefs.xpos = D_GEOMETRY_DEFAULT_XPOS;
363 prefs.ypos = D_GEOMETRY_DEFAULT_YPOS;
54 /* these four constitute the geometry */
55 prefs.width = PREFS_GEOMETRY_DEFAULT_WIDTH;
56 prefs.height = PREFS_GEOMETRY_DEFAULT_HEIGHT;
57 prefs.xpos = PREFS_GEOMETRY_DEFAULT_XPOS;
58 prefs.ypos = PREFS_GEOMETRY_DEFAULT_YPOS;
59
60 prefs.home = a_Url_new(PREFS_HOME, NULL);
61 prefs.http_language = NULL;
36462 prefs.http_proxy = NULL;
63 prefs.http_max_conns = 6;
36564 prefs.http_proxyuser = NULL;
366 prefs.no_proxy = NULL;
367 prefs.no_proxy_vec = NULL;
368 prefs.link_color = DW_COLOR_DEFAULT_BLUE;
369 prefs.visited_color = DW_COLOR_DEFAULT_PURPLE;
370 prefs.bg_color = DW_COLOR_DEFAULT_BGND;
371 prefs.text_color = DW_COLOR_DEFAULT_BLACK;
372 prefs.use_oblique = FALSE;
373 prefs.start_page = a_Url_new(DILLO_START_PAGE, NULL, 0, 0, 0);
374 prefs.home = a_Url_new(DILLO_HOME, NULL, 0, 0, 0);
375 prefs.allow_white_bg = TRUE;
376 prefs.force_my_colors = FALSE;
377 prefs.contrast_visited_color = FALSE;
378 prefs.show_tooltip = FALSE;
379 prefs.panel_size = 1;
65 prefs.http_referer = dStrdup(PREFS_HTTP_REFERER);
66 prefs.http_user_agent = dStrdup(PREFS_HTTP_USER_AGENT);
67 prefs.limit_text_width = FALSE;
68 prefs.load_images=TRUE;
69 prefs.load_stylesheets=TRUE;
70 prefs.middle_click_drags_page = TRUE;
71 prefs.middle_click_opens_new_tab = TRUE;
72 prefs.right_click_closes_tab = FALSE;
73 prefs.no_proxy = dStrdup(PREFS_NO_PROXY);
74 prefs.panel_size = P_medium;
75 prefs.parse_embedded_css=TRUE;
76 prefs.save_dir = dStrdup(PREFS_SAVE_DIR);
77 prefs.search_url = dStrdup(PREFS_SEARCH_URL);
78 prefs.show_back = TRUE;
79 prefs.show_bookmarks = TRUE;
80 prefs.show_clear_url = TRUE;
81 prefs.show_extra_warnings = FALSE;
82 prefs.show_filemenu=TRUE;
83 prefs.show_forw = TRUE;
84 prefs.show_help = TRUE;
85 prefs.show_home = TRUE;
86 prefs.show_msg = TRUE;
87 prefs.show_progress_box = TRUE;
88 prefs.show_reload = TRUE;
89 prefs.show_save = TRUE;
90 prefs.show_search = TRUE;
91 prefs.show_stop = TRUE;
92 prefs.show_tools = TRUE;
93 prefs.show_tooltip = TRUE;
94 prefs.show_url = TRUE;
38095 prefs.small_icons = FALSE;
381 prefs.limit_text_width = FALSE;
96 prefs.start_page = a_Url_new(PREFS_START_PAGE, NULL);
38297 prefs.w3c_plus_heuristics = TRUE;
383 prefs.font_factor = 1.0;
384 prefs.use_dicache = FALSE;
385 prefs.show_back=TRUE;
386 prefs.show_forw=TRUE;
387 prefs.show_home=TRUE;
388 prefs.show_reload=TRUE;
389 prefs.show_save=TRUE;
390 prefs.show_stop=TRUE;
391 prefs.show_bookmarks=TRUE;
392 prefs.show_menubar=TRUE;
393 prefs.show_clear_url=TRUE;
394 prefs.show_url=TRUE;
395 prefs.show_search=TRUE;
396 prefs.show_progress_box=TRUE;
397 prefs.fullwindow_start=FALSE;
398 prefs.transient_dialogs=FALSE;
399 prefs.vw_fontname = g_strdup("helvetica");
400 prefs.fw_fontname = g_strdup("courier");
401 prefs.generate_submit = FALSE;
402 prefs.enterpress_forces_submit = FALSE;
403 prefs.search_url = g_strdup("http://www.google.com/search?q=%s");
404 prefs.show_msg = TRUE;
405 prefs.show_extra_warnings = FALSE;
406
407 /* this locale stuff is to avoid parsing problems with float numbers */
408 old_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
409 setlocale (LC_NUMERIC, "C");
410
411 Prefs_load();
412
413 setlocale (LC_NUMERIC, old_locale);
414 g_free (old_locale);
41598 }
41699
417100 /*
418 * Preferences memory-deallocation
101 * memory-deallocation
419102 * (Call this one at exit time)
420103 */
421104 void a_Prefs_freeall(void)
422105 {
423 g_free(prefs.http_proxyuser);
424 g_free(prefs.no_proxy);
425 if (prefs.no_proxy_vec)
426 g_strfreev(prefs.no_proxy_vec);
106 dFree(prefs.font_cursive);
107 dFree(prefs.font_fantasy);
108 dFree(prefs.font_monospace);
109 dFree(prefs.font_sans_serif);
110 dFree(prefs.font_serif);
111 a_Url_free(prefs.home);
112 dFree(prefs.http_language);
427113 a_Url_free(prefs.http_proxy);
428 g_free(prefs.fw_fontname);
429 g_free(prefs.vw_fontname);
114 dFree(prefs.http_proxyuser);
115 dFree(prefs.http_referer);
116 dFree(prefs.http_user_agent);
117 dFree(prefs.no_proxy);
118 dFree(prefs.save_dir);
119 dFree(prefs.search_url);
430120 a_Url_free(prefs.start_page);
431 a_Url_free(prefs.home);
432 g_free(prefs.search_url);
433121 }
0 /*
1 * Preferences
2 *
3 * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
011 #ifndef __PREFS_H__
112 #define __PREFS_H__
213
617 extern "C" {
718 #endif /* __cplusplus */
819
9 #define DILLO_START_PAGE "about:splash"
10 #define DILLO_HOME "http://www.dillo.org/"
11 #define D_GEOMETRY_DEFAULT_WIDTH 640
12 #define D_GEOMETRY_DEFAULT_HEIGHT 550
13 #define D_GEOMETRY_DEFAULT_XPOS -9999
14 #define D_GEOMETRY_DEFAULT_YPOS -9999
20 #define PREFS_GEOMETRY_DEFAULT_WIDTH 780
21 #define PREFS_GEOMETRY_DEFAULT_HEIGHT 580
22 #define PREFS_GEOMETRY_DEFAULT_XPOS -9999
23 #define PREFS_GEOMETRY_DEFAULT_YPOS -9999
1524
16 #define DW_COLOR_DEFAULT_GREY 0xd6d6d6
17 #define DW_COLOR_DEFAULT_BLACK 0x000000
18 #define DW_COLOR_DEFAULT_BLUE 0x0000ff
19 #define DW_COLOR_DEFAULT_PURPLE 0x800080
20 #define DW_COLOR_DEFAULT_BGND 0xd6d6c0
25 /* Panel sizes */
26 enum { P_tiny = 0, P_small, P_medium, P_large };
2127
22 /* define enumeration values to be returned */
23 enum {
24 PARSE_OK = 0,
25 FILE_NOT_FOUND
26 };
27
28 /* define enumeration values to be returned for specific symbols */
29 typedef enum {
30 DRC_TOKEN_FIRST = G_TOKEN_LAST,
31 DRC_TOKEN_GEOMETRY,
32 DRC_TOKEN_PROXY,
33 DRC_TOKEN_PROXYUSER,
34 DRC_TOKEN_NOPROXY,
35 DRC_TOKEN_LINK_COLOR,
36 DRC_TOKEN_VISITED_COLOR,
37 DRC_TOKEN_BG_COLOR,
38 DRC_TOKEN_ALLOW_WHITE_BG,
39 DRC_TOKEN_FORCE_MY_COLORS,
40 DRC_TOKEN_CONTRAST_VISITED_COLOR,
41 DRC_TOKEN_TEXT_COLOR,
42 DRC_TOKEN_USE_OBLIQUE,
43 DRC_TOKEN_START_PAGE,
44 DRC_TOKEN_HOME,
45 DRC_TOKEN_PANEL_SIZE,
46 DRC_TOKEN_SMALL_ICONS,
47 DRC_TOKEN_FONT_FACTOR,
48 DRC_TOKEN_SHOW_TOOLTIP,
49 DRC_TOKEN_LIMIT_TEXT_WIDTH,
50 DRC_TOKEN_W3C_PLUS_HEURISTICS,
51 DRC_TOKEN_USE_DICACHE,
52 DRC_TOKEN_SHOW_BACK,
53 DRC_TOKEN_SHOW_FORW,
54 DRC_TOKEN_SHOW_HOME,
55 DRC_TOKEN_SHOW_RELOAD,
56 DRC_TOKEN_SHOW_SAVE,
57 DRC_TOKEN_SHOW_STOP,
58 DRC_TOKEN_SHOW_BOOKMARKS,
59 DRC_TOKEN_SHOW_MENUBAR,
60 DRC_TOKEN_SHOW_CLEAR_URL,
61 DRC_TOKEN_SHOW_URL,
62 DRC_TOKEN_SHOW_SEARCH,
63 DRC_TOKEN_SHOW_PROGRESS_BOX,
64 DRC_TOKEN_FULLWINDOW_START,
65 DRC_TOKEN_TRANSIENT_DIALOGS,
66 DRC_TOKEN_FW_FONT,
67 DRC_TOKEN_VW_FONT,
68 DRC_TOKEN_GENERATE_SUBMIT,
69 DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT,
70 DRC_TOKEN_SEARCH_URL,
71 DRC_TOKEN_SHOW_MSG,
72 DRC_TOKEN_SHOW_EXTRA_WARNINGS,
73
74 DRC_TOKEN_LAST
75 } Dillo_Rc_TokenType;
28 enum {PREFS_FILTER_ALLOW_ALL,
29 PREFS_FILTER_SAME_DOMAIN};
7630
7731 typedef struct _DilloPrefs DilloPrefs;
7832
7933 struct _DilloPrefs {
80 gint width;
81 gint height;
82 gint xpos;
83 gint ypos;
34 int width;
35 int height;
36 int xpos;
37 int ypos;
38 char *http_language;
39 int32_t http_max_conns;
8440 DilloUrl *http_proxy;
85 gchar *http_proxyuser;
86 gchar *no_proxy;
87 gchar **no_proxy_vec;
41 char *http_proxyuser;
42 char *http_referer;
43 char *http_user_agent;
44 char *no_proxy;
8845 DilloUrl *start_page;
8946 DilloUrl *home;
90 gint32 link_color;
91 gint32 visited_color;
92 gint32 bg_color;
93 gint32 text_color;
94 gboolean allow_white_bg;
95 gboolean use_oblique;
96 gboolean force_my_colors;
97 gboolean contrast_visited_color;
98 gboolean show_tooltip;
99 gint panel_size;
100 gboolean small_icons;
101 gboolean limit_text_width;
102 gboolean w3c_plus_heuristics;
103 gdouble font_factor;
104 gboolean use_dicache;
105 gboolean show_back;
106 gboolean show_forw;
107 gboolean show_home;
108 gboolean show_reload;
109 gboolean show_save;
110 gboolean show_stop;
111 gboolean show_bookmarks;
112 gboolean show_menubar;
113 gboolean show_clear_url;
114 gboolean show_url;
115 gboolean show_search;
116 gboolean show_progress_box;
117 gboolean fullwindow_start;
118 gboolean transient_dialogs;
119 gchar *vw_fontname;
120 gchar *fw_fontname;
121 gboolean generate_submit;
122 gboolean enterpress_forces_submit;
123 gchar *search_url;
124 gboolean show_msg;
125 gboolean show_extra_warnings;
47 bool_t allow_white_bg;
48 int32_t bg_color;
49 bool_t contrast_visited_color;
50 bool_t show_tooltip;
51 int panel_size;
52 bool_t small_icons;
53 bool_t limit_text_width;
54 bool_t w3c_plus_heuristics;
55 bool_t focus_new_tab;
56 double font_factor;
57 int32_t font_max_size;
58 int32_t font_min_size;
59 bool_t show_back;
60 bool_t show_forw;
61 bool_t show_home;
62 bool_t show_reload;
63 bool_t show_save;
64 bool_t show_stop;
65 bool_t show_bookmarks;
66 bool_t show_tools;
67 bool_t show_filemenu;
68 bool_t show_clear_url;
69 bool_t show_url;
70 bool_t show_search;
71 bool_t show_help;
72 bool_t show_progress_box;
73 bool_t fullwindow_start;
74 bool_t load_images;
75 bool_t load_stylesheets;
76 bool_t parse_embedded_css;
77 int filter_auto_requests;
78 int32_t buffered_drawing;
79 char *font_serif;
80 char *font_sans_serif;
81 char *font_cursive;
82 char *font_fantasy;
83 char *font_monospace;
84 bool_t enterpress_forces_submit;
85 bool_t middle_click_opens_new_tab;
86 bool_t right_click_closes_tab;
87 char *search_url;
88 char *save_dir;
89 bool_t show_msg;
90 bool_t show_extra_warnings;
91 bool_t middle_click_drags_page;
12692 };
12793
12894 /* Global Data */
129 DilloPrefs prefs;
95 extern DilloPrefs prefs;
13096
13197 void a_Prefs_init(void);
13298 void a_Prefs_freeall(void);
0 /*
1 * Preferences parser
2 *
3 * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <sys/types.h>
12 #include <stdlib.h>
13 #include <locale.h> /* for setlocale */
14
15 #include "prefs.h"
16 #include "misc.h"
17 #include "msg.h"
18 #include "colors.h"
19
20 #include "prefsparser.hh"
21
22 typedef enum {
23 PREFS_BOOL,
24 PREFS_COLOR,
25 PREFS_STRING,
26 PREFS_URL,
27 PREFS_INT32,
28 PREFS_DOUBLE,
29 PREFS_GEOMETRY,
30 PREFS_FILTER,
31 PREFS_PANEL_SIZE
32 } PrefType_t;
33
34 typedef struct SymNode_ {
35 const char *name;
36 void *pref;
37 PrefType_t type;
38 } SymNode_t;
39
40 /*
41 * Parse a name/value pair and set preferences accordingly.
42 */
43 int PrefsParser::parseOption(char *name, char *value)
44 {
45 const SymNode_t *node;
46 uint_t i;
47 int st;
48
49 /* Symbol array, sorted alphabetically */
50 const SymNode_t symbols[] = {
51 { "allow_white_bg", &prefs.allow_white_bg, PREFS_BOOL },
52 { "bg_color", &prefs.bg_color, PREFS_COLOR },
53 { "buffered_drawing", &prefs.buffered_drawing, PREFS_INT32 },
54 { "contrast_visited_color", &prefs.contrast_visited_color, PREFS_BOOL },
55 { "enterpress_forces_submit", &prefs.enterpress_forces_submit,
56 PREFS_BOOL },
57 { "filter_auto_requests", &prefs.filter_auto_requests, PREFS_FILTER },
58 { "focus_new_tab", &prefs.focus_new_tab, PREFS_BOOL },
59 { "font_cursive", &prefs.font_cursive, PREFS_STRING },
60 { "font_factor", &prefs.font_factor, PREFS_DOUBLE },
61 { "font_fantasy", &prefs.font_fantasy, PREFS_STRING },
62 { "font_max_size", &prefs.font_max_size, PREFS_INT32 },
63 { "font_min_size", &prefs.font_min_size, PREFS_INT32 },
64 { "font_monospace", &prefs.font_monospace, PREFS_STRING },
65 { "font_sans_serif", &prefs.font_sans_serif, PREFS_STRING },
66 { "font_serif", &prefs.font_serif, PREFS_STRING },
67 { "fullwindow_start", &prefs.fullwindow_start, PREFS_BOOL },
68 { "geometry", NULL, PREFS_GEOMETRY },
69 { "home", &prefs.home, PREFS_URL },
70 { "http_language", &prefs.http_language, PREFS_STRING },
71 { "http_max_conns", &prefs.http_max_conns, PREFS_INT32 },
72 { "http_proxy", &prefs.http_proxy, PREFS_URL },
73 { "http_proxyuser", &prefs.http_proxyuser, PREFS_STRING },
74 { "http_referer", &prefs.http_referer, PREFS_STRING },
75 { "http_user_agent", &prefs.http_user_agent, PREFS_STRING },
76 { "limit_text_width", &prefs.limit_text_width, PREFS_BOOL },
77 { "load_images", &prefs.load_images, PREFS_BOOL },
78 { "load_stylesheets", &prefs.load_stylesheets, PREFS_BOOL },
79 { "middle_click_drags_page", &prefs.middle_click_drags_page,
80 PREFS_BOOL },
81 { "middle_click_opens_new_tab", &prefs.middle_click_opens_new_tab,
82 PREFS_BOOL },
83 { "right_click_closes_tab", &prefs.right_click_closes_tab, PREFS_BOOL },
84 { "no_proxy", &prefs.no_proxy, PREFS_STRING },
85 { "panel_size", &prefs.panel_size, PREFS_PANEL_SIZE },
86 { "parse_embedded_css", &prefs.parse_embedded_css, PREFS_BOOL },
87 { "save_dir", &prefs.save_dir, PREFS_STRING },
88 { "search_url", &prefs.search_url, PREFS_STRING },
89 { "show_back", &prefs.show_back, PREFS_BOOL },
90 { "show_bookmarks", &prefs.show_bookmarks, PREFS_BOOL },
91 { "show_clear_url", &prefs.show_clear_url, PREFS_BOOL },
92 { "show_extra_warnings", &prefs.show_extra_warnings, PREFS_BOOL },
93 { "show_filemenu", &prefs.show_filemenu, PREFS_BOOL },
94 { "show_forw", &prefs.show_forw, PREFS_BOOL },
95 { "show_help", &prefs.show_help, PREFS_BOOL },
96 { "show_home", &prefs.show_home, PREFS_BOOL },
97 { "show_msg", &prefs.show_msg, PREFS_BOOL },
98 { "show_progress_box", &prefs.show_progress_box, PREFS_BOOL },
99 { "show_reload", &prefs.show_reload, PREFS_BOOL },
100 { "show_save", &prefs.show_save, PREFS_BOOL },
101 { "show_search", &prefs.show_search, PREFS_BOOL },
102 { "show_stop", &prefs.show_stop, PREFS_BOOL },
103 { "show_tools", &prefs.show_tools, PREFS_BOOL },
104 { "show_tooltip", &prefs.show_tooltip, PREFS_BOOL },
105 { "show_url", &prefs.show_url, PREFS_BOOL },
106 { "small_icons", &prefs.small_icons, PREFS_BOOL },
107 { "start_page", &prefs.start_page, PREFS_URL },
108 { "w3c_plus_heuristics", &prefs.w3c_plus_heuristics, PREFS_BOOL }
109 };
110
111 node = NULL;
112 for (i = 0; i < sizeof(symbols) / sizeof(SymNode_t); i++) {
113 if (!strcmp(symbols[i].name, name)) {
114 node = & (symbols[i]);
115 break;
116 }
117 }
118
119 if (!node) {
120 MSG("prefs: {%s} is not a recognized token.\n", name);
121 return -1;
122 }
123
124 switch (node->type) {
125 case PREFS_BOOL:
126 *(bool_t *)node->pref = (!dStrcasecmp(value, "yes") ||
127 !dStrcasecmp(value, "true"));
128 break;
129 case PREFS_COLOR:
130 *(int32_t *)node->pref = a_Color_parse(value, *(int32_t*)node->pref,&st);
131 break;
132 case PREFS_STRING:
133 dFree(*(char **)node->pref);
134 *(char **)node->pref = dStrdup(value);
135 break;
136 case PREFS_URL:
137 a_Url_free(*(DilloUrl **)node->pref);
138 *(DilloUrl **)node->pref = a_Url_new(value, NULL);
139 break;
140 case PREFS_INT32:
141 *(int32_t *)node->pref = strtol(value, NULL, 10);
142 break;
143 case PREFS_DOUBLE:
144 *(double *)node->pref = strtod(value, NULL);
145 break;
146 case PREFS_GEOMETRY:
147 a_Misc_parse_geometry(value, &prefs.xpos, &prefs.ypos,
148 &prefs.width, &prefs.height);
149 break;
150 case PREFS_FILTER:
151 if (!dStrcasecmp(value, "same_domain"))
152 prefs.filter_auto_requests = PREFS_FILTER_SAME_DOMAIN;
153 else {
154 if (dStrcasecmp(value, "allow_all"))
155 MSG_WARN("prefs: unrecognized value for filter_auto_requests\n");
156 prefs.filter_auto_requests = PREFS_FILTER_ALLOW_ALL;
157 }
158 break;
159 case PREFS_PANEL_SIZE:
160 if (!dStrcasecmp(value, "tiny"))
161 prefs.panel_size = P_tiny;
162 else if (!dStrcasecmp(value, "small"))
163 prefs.panel_size = P_small;
164 else if (!dStrcasecmp(value, "large"))
165 prefs.panel_size = P_large;
166 else /* default to "medium" */
167 prefs.panel_size = P_medium;
168 break;
169 default:
170 MSG_WARN("prefs: {%s} IS recognized but not handled!\n", name);
171 break; /* Not reached */
172 }
173 return 0;
174 }
175
176 /*
177 * Parses the dillorc and sets the values in the prefs structure.
178 */
179 void PrefsParser::parse(FILE *fp)
180 {
181 char *line, *name, *value, *oldLocale;
182 int st;
183
184 // changing the LC_NUMERIC locale (temporarily) to C
185 // avoids parsing problems with float numbers
186 oldLocale = dStrdup(setlocale(LC_NUMERIC, NULL));
187 setlocale(LC_NUMERIC, "C");
188
189 // scan the file line by line
190 while ((line = dGetline(fp)) != NULL) {
191 st = dParser_parse_rc_line(&line, &name, &value);
192
193 if (st == 0) {
194 _MSG("prefsparser: name=%s, value=%s\n", name, value);
195 parseOption(name, value);
196 } else if (st < 0) {
197 MSG_ERR("prefsparser: Syntax error in dillorc:"
198 " name=\"%s\" value=\"%s\"\n", name, value);
199 }
200
201 dFree(line);
202 }
203 fclose(fp);
204
205 // restore the old numeric locale
206 setlocale(LC_NUMERIC, oldLocale);
207 dFree(oldLocale);
208
209
210 if (prefs.limit_text_width) {
211 /* BUG: causes 100% CPU usage with <button> or <input type="image"> */
212 MSG_WARN("Disabling limit_text_width preference (currently broken).\n");
213 prefs.limit_text_width = FALSE;
214 }
215 }
0 /*
1 * Preferences parser
2 *
3 * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #ifndef __PREFS_HH__
12 #define __PREFS_HH__
13
14 #ifdef __cplusplus
15 class PrefsParser {
16 public:
17 static int parseOption(char *name, char *value);
18 static int parseLine(char *line, char *name, char *value);
19 static void parse(FILE *fp);
20 };
21 #endif /* __cplusplus */
22
23 #endif /* __PREFS_HH__ */
+0
-43
src/progressbar.c less more
0 /*
1 * File: progressbar.c
2 *
3 * Copyright (C) 2004 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "progressbar.h"
12
13
14 /*
15 * The progressbar is basically a GtkFrame with a text label.
16 */
17 GtkWidget* a_Progressbar_new(void)
18 {
19 GtkWidget *p_bar, *label;
20
21 p_bar = gtk_frame_new(NULL);
22 label = gtk_label_new("");
23 gtk_frame_set_shadow_type(GTK_FRAME(p_bar), GTK_SHADOW_IN);
24 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.4);
25 gtk_container_add(GTK_CONTAINER (p_bar), label);
26 gtk_widget_show(label);
27 return p_bar;
28 }
29
30 /*
31 * Update the specified progress bar.
32 * updatestr : String to display within the bar (NULL is ignored)
33 * sens : sensitivity (0 = set insensitive, 1 = set sensitive)
34 */
35 void a_Progressbar_update(GtkWidget *p_bar, const char *updatestr, gint sens)
36 {
37 gtk_widget_set_sensitive(p_bar, (sens == 0) ? FALSE : TRUE);
38
39 if ( updatestr != NULL )
40 gtk_label_set_text(GTK_LABEL(GTK_BIN(p_bar)->child), updatestr);
41 }
42
+0
-43
src/progressbar.h less more
0 /* GTK - The GIMP Toolkit
1 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Library General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Library General Public License for more details.
12 *
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library; if not, write to the Free
15 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17 #ifndef __PROGRESSBAR_H__
18 #define __PROGRESSBAR_H__
19
20
21 #include <gdk/gdk.h>
22 #include <gtk/gtk.h>
23
24
25 #ifdef __cplusplus
26 extern "C" {
27 #endif /* __cplusplus */
28
29 #define PBAR_ISTR(s) (s) ? "" : "Images: "
30 #define PBAR_PSTR(s) (s) ? "" : "Page: "
31
32 GtkWidget* a_Progressbar_new(void);
33 void a_Progressbar_update(GtkWidget *pbar, const char *updatestr,
34 gint op);
35
36 #ifdef __cplusplus
37 }
38 #endif /* __cplusplus */
39
40
41 #endif /* __PROGRESSBAR_H__ */
42
+0
-668
src/selection.c less more
0 /*
1 * File: selection.c
2 *
3 * Copyright 2003 Sebastian Geerken <s.geerken@ping.de>,
4 * Eric Gaudet <eric@rti-zone.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * See doc/Selection.txt for informations about this module.
14 */
15
16 #include "selection.h"
17 #include "strbuf.h"
18 #include "dw_gtk_viewport.h"
19 #include <stdio.h>
20 #include <string.h>
21 #include <gtk/gtk.h>
22
23 /*#define DEBUG_LEVEL 2*/
24 #include "debug.h"
25
26 static void Selection_reset_selection (Selection *selection);
27 static void Selection_reset_link (Selection *selection);
28 static void Selection_switch_link_to_selection (Selection *selection,
29 DwIterator *it,
30 gint32 char_pos);
31 static void Selection_adjust_selection (Selection *selection,
32 DwIterator *it,
33 gint32 char_pos);
34 static gint Selection_correct_char_pos (DwExtIterator *it,
35 gint32 char_pos);
36 static void Selection_highlight (Selection *selection,
37 gboolean fl);
38 static void Selection_highlight0 (gboolean fl,
39 DwExtIterator *from,
40 gint from_char,
41 DwExtIterator *to,
42 gint to_char);
43 static void Selection_copy (Selection *selection);
44
45 /* Not defined as static, to avoid compiler warnings. */
46 char *selection_state_descr[] = { "none", "selecting", "selected" };
47
48 Selection *a_Selection_new (void)
49 {
50 Selection *selection;
51
52 selection = g_new (Selection, 1);
53
54 selection->selection_state = SELECTION_NONE;
55 selection->from = NULL;
56 selection->to = NULL;
57
58 selection->link_state = SELECTION_LINK_NONE;
59 selection->link = NULL;
60
61 selection->dclick_callback = NULL;
62 selection->callback_data = NULL;
63
64 selection->owner = NULL;
65 return selection;
66 }
67
68
69 /*
70 * Sets a callback function for double click, this is normally
71 * the function toggling the full screen mode.
72 * (More than one is never needed, so this instead of a signal.)
73 */
74 void a_Selection_set_dclick_callback (Selection *selection,
75 void (*fn) (gpointer data),
76 gpointer data)
77 {
78 g_return_if_fail (selection->dclick_callback == NULL);
79 selection->dclick_callback = fn;
80 selection->callback_data = data;
81 }
82
83 /*
84 * Set the widget that owns the selection.
85 */
86 void a_Selection_set_owner(Selection *selection, gpointer owner)
87 {
88 selection->owner = owner;
89 a_Selection_init_selection(owner);
90 }
91
92 void a_Selection_reset (Selection *selection)
93 {
94 Selection_reset_selection (selection);
95 Selection_reset_link (selection);
96 }
97
98
99 void a_Selection_free (Selection *selection)
100 {
101 a_Selection_reset (selection);
102 g_free (selection);
103 }
104
105 static void Selection_reset_selection (Selection *selection)
106 {
107 if (selection->from)
108 a_Dw_ext_iterator_free(selection->from);
109 selection->from = NULL;
110 if (selection->to)
111 a_Dw_ext_iterator_free(selection->to);
112 selection->to = NULL;
113 selection->selection_state = SELECTION_NONE;
114 }
115
116
117 static void Selection_reset_link (Selection *selection)
118 {
119 if (selection->link)
120 a_Dw_ext_iterator_free(selection->link);
121 selection->link = NULL;
122 selection->link_state = SELECTION_LINK_NONE;
123 }
124
125 gint a_Selection_button_press (Selection *selection,
126 DwIterator *it,
127 gint char_pos,
128 gint link,
129 GdkEventButton *event,
130 gboolean within_content)
131 {
132 DwWidget *it_widget = it->widget;
133 gboolean ret = FALSE, dummy;
134
135 DEBUG_MSG (3, "PRESS%s(%s, %d, %d): %s -> ...\n",
136 (event && event->type == GDK_2BUTTON_PRESS) ? "2" : "",
137 a_Dw_iterator_text (it), char_pos, link,
138 selection_state_descr[selection->selection_state]);
139
140 #if defined (DEBUG_LEVEL) && 1 >= DEBUG_LEVEL
141 a_Dw_widget_print_tree (GTK_DW_VIEWPORT(it->widget->viewport)->child);
142 #endif
143
144 if (event && event->button == 1 &&
145 !within_content && event->type == GDK_2BUTTON_PRESS) {
146 /* When the user double-clicks on empty parts, call the callback
147 * function instead of normal processing. Used for full screen
148 * mode. */
149 if (selection->dclick_callback)
150 selection->dclick_callback (selection->callback_data);
151 /* Reset everything, so that a_Selection_release will ignore the
152 * "release" event following soon. */
153 Selection_highlight (selection, FALSE);
154 a_Selection_reset (selection);
155 ret = TRUE;
156 } else {
157 if (link != -1) {
158 /* link handling */
159 if (event) {
160 gtk_signal_emit_by_name (GTK_OBJECT (it_widget),
161 "link_pressed", link, -1, -1, event,
162 &dummy);
163 Selection_reset_link (selection);
164 selection->link_state = SELECTION_LINK_PRESSED;
165 selection->link_button = event->button;
166 selection->link = a_Dw_ext_iterator_new (it);
167
168 /* It may be that the user has pressed on something activatable
169 * (link != -1), but there is no contents, e.g. with images
170 * without ALTernative text. */
171 if (selection->link) {
172 selection->link_char =
173 Selection_correct_char_pos (selection->link, char_pos);
174 selection->link_number = link;
175 }
176 /* We do not return the value of the signal function (dummy),
177 * but we do actually process this event. */
178 ret = TRUE;
179 }
180 } else {
181 /* normal selection handling */
182 if (event && event->button == 1) {
183 Selection_highlight (selection, FALSE);
184 Selection_reset_selection (selection);
185
186 selection->from = a_Dw_ext_iterator_new (it);
187 /* a_Dw_ext_iterator_new may return NULL, if the page has no
188 * contents. */
189 if (selection->from) {
190 selection->selection_state = SELECTION_SELECTING;
191 selection->from_char =
192 Selection_correct_char_pos (selection->from, char_pos);
193 selection->to = a_Dw_ext_iterator_clone (selection->from);
194 selection->to_char =
195 Selection_correct_char_pos (selection->to, char_pos);
196 ret = TRUE;
197 } else
198 /* if there is no content */
199 ret = FALSE;
200 }
201 }
202 }
203
204 DEBUG_MSG (3, " ... -> %s, processed = %s\n",
205 selection_state_descr[selection->selection_state],
206 ret ? "TRUE" : "FALSE");
207 return ret;
208 }
209
210
211 gint a_Selection_button_release (Selection *selection,
212 DwIterator *it,
213 gint char_pos,
214 gint link,
215 GdkEventButton *event,
216 gboolean within_content)
217 {
218 DwWidget *it_widget = it->widget;
219 gboolean ret = FALSE, dummy;
220
221 DEBUG_MSG (3, "RELEASE(%s, %d, %d): %s -> ...\n",
222 a_Dw_iterator_text (it), char_pos, link,
223 selection_state_descr[selection->selection_state]);
224
225 if (selection->link_state == SELECTION_LINK_PRESSED &&
226 event && event->button == selection->link_button) {
227 /* link handling */
228 ret = TRUE;
229 if (link != -1)
230 gtk_signal_emit_by_name (GTK_OBJECT (it_widget),
231 "link_released", link, -1, -1, event,
232 &dummy);
233
234 /* The link where the user clicked the mouse button? */
235 if (link == selection->link_number) {
236 Selection_reset_link (selection);
237 gtk_signal_emit_by_name (GTK_OBJECT (it_widget),
238 "link_clicked", link, -1, -1, event,
239 &dummy);
240 } else {
241 if (event->button == 1)
242 /* Reset links and switch to selection mode. The selection
243 * state will be set to SELECTING, which is handled some lines
244 * below. */
245 Selection_switch_link_to_selection (selection, it, char_pos);
246 }
247 }
248
249 if (selection->selection_state == SELECTION_SELECTING &&
250 event && event->button == 1) {
251 /* normal selection */
252 ret = TRUE;
253 Selection_adjust_selection (selection, it, char_pos);
254
255 if (a_Dw_ext_iterator_compare (selection->from, selection->to) == 0 &&
256 selection->from_char == selection->to_char)
257 /* nothing selected */
258 Selection_reset_selection (selection);
259 else {
260 Selection_copy (selection);
261 selection->selection_state = SELECTION_SELECTED;
262 }
263 }
264
265 DEBUG_MSG (3, " ... -> %s, processed = %s\n",
266 selection_state_descr[selection->selection_state],
267 ret ? "TRUE" : "FALSE");
268 return ret;
269 }
270
271
272 gint a_Selection_button_motion (Selection *selection,
273 DwIterator *it,
274 gint char_pos,
275 gint link,
276 GdkEventButton *event,
277 gboolean within_content)
278 {
279 DEBUG_MSG (3, "MOTION (%s, %d, %d): %s -> ...\n",
280 a_Dw_iterator_text (it), char_pos, link,
281 selection_state_descr[selection->selection_state]);
282
283 if (selection->link_state == SELECTION_LINK_PRESSED) {
284 /* link handling */
285 if (link != selection->link_number)
286 /* No longer the link where the user clicked the mouse button.
287 * Reset links and switch to selection mode. */
288 Selection_switch_link_to_selection (selection, it, char_pos);
289 /* Still in link: do nothing. */
290 } else if (selection->selection_state == SELECTION_SELECTING) {
291 /* selection */
292 Selection_adjust_selection (selection, it, char_pos);
293 }
294
295 DEBUG_MSG (3, " ... -> %s, processed = TRUE\n",
296 selection_state_descr[selection->selection_state]);
297 return TRUE;
298 }
299
300 /*
301 * This function is called when the user decides not to activate a link,
302 * but instead select text.
303 */
304 static void Selection_switch_link_to_selection (Selection *selection,
305 DwIterator *it,
306 gint32 char_pos)
307 {
308 /* It may be that selection->link is NULL, see a_Selection_button_press. */
309 if (selection->link) {
310 /* Reset old selection. */
311 Selection_highlight (selection, FALSE);
312 Selection_reset_selection (selection);
313
314 /* Transfer link state into selection state. */
315 selection->from = a_Dw_ext_iterator_clone (selection->link);
316 selection->from_char = selection->link_char;
317 selection->to = a_Dw_ext_iterator_new_variant (selection->from, it);
318 selection->to_char =
319 Selection_correct_char_pos (selection->to, char_pos);
320 selection->selection_state = SELECTION_SELECTING;
321
322 /* Reset link status. */
323 Selection_reset_link (selection);
324
325 Selection_highlight (selection, TRUE);
326
327 DEBUG_MSG (2, " after switching: from (%s, %d) to (%s, %d)\n",
328 a_Dw_ext_iterator_text (selection->from),
329 selection->from_char,
330 a_Dw_ext_iterator_text (selection->to), selection->to_char);
331 } else {
332 /* A link was pressed on, but there is nothing to select. Reset
333 * everything. */
334 Selection_reset_selection (selection);
335 Selection_reset_link (selection);
336 }
337 }
338
339 /*
340 * This function is used by a_Selection_button_motion and
341 * a_Selection_button_release, and changes the second limit of the already
342 * existing selection region.
343 */
344 static void Selection_adjust_selection (Selection *selection,
345 DwIterator *it,
346 gint32 char_pos)
347 {
348 DwExtIterator *new_to;
349 gint new_to_char, cmp_old, cmp_new, cmp_diff, len;
350 gboolean brute_highlighting = FALSE;
351
352 new_to = a_Dw_ext_iterator_new_variant (selection->to, it);
353 new_to_char = Selection_correct_char_pos (new_to, char_pos);
354
355 cmp_old = a_Dw_ext_iterator_compare (selection->to, selection->from);
356 cmp_new = a_Dw_ext_iterator_compare (new_to, selection->from);
357
358 if (cmp_old == 0 || cmp_new == 0) {
359 /* Either before, or now, the limits differ only by the character
360 * position. */
361 brute_highlighting = TRUE;
362 } else if (cmp_old * cmp_new < 0) {
363 /* The selection order has changed, i.e. the user moved the selection
364 * end again beyond the position he started. */
365 brute_highlighting = TRUE;
366 } else {
367 /* Here, cmp_old and cmp_new are equivalent and != 0. */
368 cmp_diff = a_Dw_ext_iterator_compare (new_to, selection->to);
369
370 DEBUG_MSG (2, "Selection_adjust_selection: cmp_old = cmp_new = %d, "
371 "cmp_diff = %d\n", cmp_old, cmp_diff);
372
373 if (cmp_old * cmp_diff > 0) {
374 /* The user has enlarged the selection. Highlight the difference. */
375 if (cmp_diff < 0) {
376 len = Selection_correct_char_pos (selection->to, SELECTION_EOW);
377 Selection_highlight0 (TRUE, new_to, new_to_char, selection->to,
378 len + 1);
379 } else {
380 Selection_highlight0 (TRUE, selection->to, 0, new_to, new_to_char);
381 }
382 } else {
383 if (cmp_old * cmp_diff < 0) {
384 /* The user has reduced the selection. Unighlight the difference.
385 */
386 Selection_highlight0 (FALSE, selection->to, 0, new_to, 0);
387 }
388
389 /* Otherwise, the user has changed the position only slightly.
390 * In both cases, re-highlight the new position. */
391 if (cmp_old < 0) {
392 len = Selection_correct_char_pos (new_to, SELECTION_EOW);
393 a_Dw_ext_iterator_highlight (new_to, new_to_char, len + 1,
394 DW_HIGHLIGHT_SELECTION);
395 } else
396 a_Dw_ext_iterator_highlight (new_to, 0, new_to_char,
397 DW_HIGHLIGHT_SELECTION);
398 }
399 }
400
401 if (brute_highlighting)
402 Selection_highlight (selection, FALSE);
403
404 a_Dw_ext_iterator_free(selection->to);
405 selection->to = new_to;
406 selection->to_char = new_to_char;
407
408 if (brute_highlighting)
409 Selection_highlight (selection, TRUE);
410
411 DEBUG_MSG (2, " selection now from (%s, %d) to (%s, %d)\n",
412 a_Dw_ext_iterator_text (selection->from), selection->from_char,
413 a_Dw_ext_iterator_text (selection->to), selection->to_char);
414 }
415
416 /*
417 * This function deals especially with the case that a widget passes
418 * SELECTION_EOW. See Selection.txt in the doc dir for more informations.
419 */
420 static gint Selection_correct_char_pos (DwExtIterator *it,
421 gint32 char_pos)
422 {
423 DwIterator *top = it->stack[it->stack_top];
424 gint len;
425
426 if (top->content.type == DW_CONTENT_TEXT)
427 len = strlen(top->content.data.text);
428 else
429 len = 1;
430
431 return MIN(char_pos, len);
432 }
433
434
435 static void Selection_highlight (Selection *selection,
436 gboolean fl)
437 {
438 Selection_highlight0 (fl, selection->from, selection->from_char,
439 selection->to, selection->to_char);
440 }
441
442
443 static void Selection_highlight0 (gboolean fl,
444 DwExtIterator *from,
445 gint from_char,
446 DwExtIterator *to,
447 gint to_char)
448 {
449 DwExtIterator *a, *b, *i;
450 gint cmp, a_char, b_char;
451 gboolean start;
452
453 if (from && to) {
454 DEBUG_MSG (2, " %shighlighting from %s / %d to %s / %d\n",
455 fl ? "" : "un", a_Dw_ext_iterator_text (from),
456 from_char, a_Dw_ext_iterator_text (to), to_char);
457 cmp = a_Dw_ext_iterator_compare (from, to);
458 if (cmp == 0) {
459 if (fl) {
460 if (from_char < to_char)
461 a_Dw_ext_iterator_highlight (from, from_char, to_char,
462 DW_HIGHLIGHT_SELECTION);
463 else
464 a_Dw_ext_iterator_highlight (from, to_char, from_char,
465 DW_HIGHLIGHT_SELECTION);
466 } else
467 a_Dw_ext_iterator_unhighlight (from, DW_HIGHLIGHT_SELECTION);
468 return;
469 }
470
471 if (cmp < 0) {
472 a = from;
473 a_char = from_char;
474 b = to;
475 b_char = to_char;
476 } else {
477 a = to;
478 a_char = to_char;
479 b = from;
480 b_char = from_char;
481 }
482
483 for (i = a_Dw_ext_iterator_clone (a), start = TRUE;
484 (cmp = a_Dw_ext_iterator_compare (i, b)) <= 0;
485 a_Dw_ext_iterator_next (i), start = FALSE) {
486 if (i->content.type == DW_CONTENT_TEXT) {
487 if (fl) {
488 if (start) {
489 DEBUG_MSG (2, " highlighting %s from %d to %d\n",
490 a_Dw_ext_iterator_text (i), a_char,
491 strlen (i->content.data.text) + 1);
492 a_Dw_ext_iterator_highlight (i, a_char,
493 strlen (i->content.data.text)
494 + 1,
495 DW_HIGHLIGHT_SELECTION);
496 } else if (cmp == 0) {
497 /* the end */
498 DEBUG_MSG (2, " highlighting %s from %d to %d\n",
499 a_Dw_ext_iterator_text (i), 0, b_char);
500 a_Dw_ext_iterator_highlight (i, 0, b_char,
501 DW_HIGHLIGHT_SELECTION);
502 } else {
503 DEBUG_MSG (2, " highlighting %s from %d to %d\n",
504 a_Dw_ext_iterator_text (i), 0,
505 strlen (i->content.data.text) + 1);
506 a_Dw_ext_iterator_highlight (i, 0,
507 strlen (i->content.data.text)
508 + 1,
509 DW_HIGHLIGHT_SELECTION);
510 }
511 } else {
512 DEBUG_MSG (2, " unhighlighting %s\n",
513 a_Dw_ext_iterator_text (i));
514 a_Dw_ext_iterator_unhighlight (i, DW_HIGHLIGHT_SELECTION);
515 }
516 }
517 }
518 a_Dw_ext_iterator_free (i);
519 }
520 }
521
522
523 static void Selection_copy(Selection *selection)
524 {
525 DwIterator *si;
526 DwExtIterator *a, *b, *i;
527 gint cmp, a_char, b_char;
528 gboolean start;
529 gchar *tmp;
530 Strbuf_t *strbuf;
531
532 if (selection->from && selection->to) {
533 strbuf = a_Strbuf_new ();
534
535 cmp = a_Dw_ext_iterator_compare (selection->from, selection->to);
536 if (cmp == 0) {
537 if (selection->from->content.type == DW_CONTENT_TEXT) {
538 si = selection->from->stack[selection->from->stack_top];
539 if (selection->from_char < selection->to_char)
540 tmp = g_strndup(si->content.data.text + selection->from_char,
541 selection->to_char - selection->from_char);
542 else
543 tmp = g_strndup(si->content.data.text + selection->to_char,
544 selection->from_char - selection->to_char);
545 a_Strbuf_append (strbuf, tmp);
546 g_free (tmp);
547 }
548 } else {
549 if (cmp < 0) {
550 a = selection->from;
551 a_char = selection->from_char;
552 b = selection->to;
553 b_char = selection->to_char;
554 } else {
555 a = selection->to;
556 a_char = selection->to_char;
557 b = selection->from;
558 b_char = selection->from_char;
559 }
560
561 for (i = a_Dw_ext_iterator_clone (a), start = TRUE;
562 (cmp = a_Dw_ext_iterator_compare (i, b)) <= 0;
563 a_Dw_ext_iterator_next (i), start = FALSE) {
564 si = i->stack[i->stack_top];
565 switch (si->content.type) {
566 case DW_CONTENT_TEXT:
567 if (start) {
568 tmp = g_strndup(si->content.data.text + a_char,
569 strlen (i->content.data.text) - a_char);
570 a_Strbuf_append (strbuf, tmp);
571 g_free (tmp);
572 } else if (cmp == 0) {
573 /* the end */
574 tmp = g_strndup(si->content.data.text, b_char);
575 a_Strbuf_append (strbuf, tmp);
576 g_free (tmp);
577 } else
578 a_Strbuf_append (strbuf, si->content.data.text);
579
580 if (si->content.space && cmp != 0)
581 a_Strbuf_append (strbuf, " ");
582
583 break;
584
585 case DW_CONTENT_BREAK:
586 if (si->content.data.break_space > 0)
587 a_Strbuf_append (strbuf, "\n\n");
588 else
589 a_Strbuf_append (strbuf, "\n");
590 break;
591 default:
592 /* Make pedantic compilers happy. Especially
593 * DW_CONTENT_WIDGET is never returned by a DwExtIterator. */
594 break;
595 }
596 }
597 a_Dw_ext_iterator_free (i);
598 }
599 a_Selection_set_selection(selection->owner, a_Strbuf_chars(strbuf));
600 a_Strbuf_free (strbuf);
601 }
602 }
603
604
605 /* GENERAL SELECTION STUFF */
606
607 /*
608 * Local data
609 */
610 static char *selection = NULL;
611
612 /*
613 * Sets the widget to copy the selection from.
614 */
615 void a_Selection_init_selection(GtkWidget *widget)
616 {
617 gtk_signal_connect(GTK_OBJECT(widget), "selection_clear_event",
618 GTK_SIGNAL_FUNC(a_Selection_clear_selection_callback), NULL);
619 gtk_signal_connect(GTK_OBJECT(widget), "selection_get",
620 GTK_SIGNAL_FUNC(a_Selection_give_selection_callback), NULL);
621 gtk_selection_add_target(widget, GDK_SELECTION_PRIMARY,
622 GDK_SELECTION_TYPE_STRING, 1);
623 }
624
625 /*
626 * Sets the selection, and associates it with the current widget for
627 * copying.
628 */
629 void a_Selection_set_selection(GtkWidget *widget, gchar *str)
630 {
631 if (gtk_selection_owner_set(widget,
632 GDK_SELECTION_PRIMARY,
633 GDK_CURRENT_TIME)) {
634 /* --EG: Why would it fail? (this question points to that the
635 * GTK+ example tests the return value.) */
636
637 /* As the clear-selection callback is not being called automatically
638 * before setting the new value, it'll be cleared here. --Jcid
639 * todo: this selection code needs a revision */
640 g_free(selection);
641 selection = g_strdup(str);
642 }
643 }
644
645 /*
646 * Callback for Paste (when another application wants the selection)
647 */
648 void a_Selection_give_selection_callback(GtkWidget *widget,
649 GtkSelectionData *data, guint info, guint time)
650 {
651 gtk_selection_data_set(data, GDK_SELECTION_TYPE_STRING,
652 8, selection, strlen(selection));
653 }
654
655 /*
656 * Clear selection
657 */
658 gint a_Selection_clear_selection_callback(GtkWidget *widget,
659 GdkEventSelection *event)
660 {
661 g_free(selection);
662 selection = NULL;
663
664 /* here we should un-highlight the selected text */
665 return TRUE;
666 }
667
+0
-75
src/selection.h less more
0 #ifndef __SELECTION_H__
1 #define __SELECTION_H__
2
3 #include "dw_ext_iterator.h"
4
5 #define SELECTION_EOW (1 << 30)
6
7 typedef struct
8 {
9 /* selection */
10 enum {
11 SELECTION_NONE,
12 SELECTION_SELECTING,
13 SELECTION_SELECTED
14 } selection_state;
15 DwExtIterator *from, *to;
16 gint from_char, to_char;
17
18 /* link handling */
19 enum {
20 SELECTION_LINK_NONE,
21 SELECTION_LINK_PRESSED
22 } link_state;
23 guint link_button;
24 DwExtIterator *link;
25 gint link_char, link_number;
26
27 /* "full screen" feature */
28 void (*dclick_callback) (gpointer data);
29 gpointer callback_data;
30
31 /* widget that owns this selection */
32 gpointer owner;
33
34 } Selection;
35
36 Selection* a_Selection_new (void);
37 void a_Selection_free (Selection *selection);
38 void a_Selection_reset (Selection *selection);
39 void a_Selection_set_dclick_callback (Selection *selection,
40 void (*fn) (gpointer data),
41 gpointer callback_data);
42 void a_Selection_set_owner (Selection *selection,
43 gpointer owner);
44 gint a_Selection_button_press (Selection *selection,
45 DwIterator *it,
46 gint char_pos,
47 gint link,
48 GdkEventButton *event,
49 gboolean within_content);
50 gint a_Selection_button_release (Selection *selection,
51 DwIterator *it,
52 gint char_pos,
53 gint link,
54 GdkEventButton *event,
55 gboolean within_content);
56 gint a_Selection_button_motion (Selection *selection,
57 DwIterator *it,
58 gint char_pos,
59 gint link,
60 GdkEventButton *event,
61 gboolean within_content);
62
63 void a_Selection_init_selection (GtkWidget *widget);
64 void a_Selection_set_selection (GtkWidget *widget,
65 gchar* str);
66 void a_Selection_give_selection_callback (GtkWidget *widget,
67 GtkSelectionData *data,
68 guint info,
69 guint time);
70 gint a_Selection_clear_selection_callback (GtkWidget *,
71 GdkEventSelection *);
72
73
74 #endif /* __SELECTION_H__ */
33 # Enjoy!
44 # Jorge.-
55
6 if [ ! $# = 1 ]; then
6 if [ $# = 1 ]; then
7 FLAGS="-H"
8 elif [ $# = 2 ]; then
9 FLAGS="-H $1"
10 shift 1
11 else
712 echo "Usage:"
8 echo " srch <token>"
9 else
13 echo " srch [-options] <token>"
14 exit 0
15 fi
16
1017 # find "./" -name "*.[ch]" -print -exec grep $1 {} \;
11 egrep "$1" *.[ch]
12 egrep "$1" IO/*.[ch]
13 egrep -H "$1" ../dpi/*.[ch]
14 egrep -H "$1" ../dpid/*.[ch]
15 egrep -H "$1" ../dpip/*.[ch]
16 fi
18 egrep $FLAGS "$1" *.[ch][ch]
19 egrep $FLAGS "$1" *.[ch]
20 egrep $FLAGS "$1" IO/*.[ch][ch]*
21 egrep $FLAGS "$1" IO/*.[ch]
22 egrep $FLAGS "$1" ../dpi/*.[ch]
23 egrep $FLAGS "$1" ../dpi/*.[ch][ch]
24 egrep $FLAGS "$1" ../dpid/*.[ch]
25 egrep $FLAGS "$1" ../dpip/*.[ch]
26 egrep $FLAGS "$1" ../dlib/*.[ch]
27
28 #egrep $FLAGS "$1" dw/*[ch]
29 #egrep $FLAGS "$1" lout/*[ch]
30 egrep $FLAGS "$1" ../dw/*[ch]
31 egrep $FLAGS "$1" ../lout/*[ch]
32
+0
-95
src/strbuf.c less more
0 /*
1 * File: strbuf.c
2 *
3 * Copyright 2003 Sebastian Geerken <s.geerken@ping.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 /*
12 * A simple structure for fast concatenation of a large number of strings.
13 */
14
15 #include <string.h>
16 #include "strbuf.h"
17
18
19 /*
20 * Create a new string buffer.
21 */
22 Strbuf_t *a_Strbuf_new (void)
23 {
24 Strbuf_t *strbuf;
25
26 strbuf = g_new (Strbuf_t, 1);
27 strbuf->list = NULL;
28 strbuf->num_chars = 0;
29 strbuf->str = NULL;
30 strbuf->str_valid = FALSE;
31 return strbuf;
32 }
33
34
35 /*
36 * Free a string buffer.
37 */
38 void a_Strbuf_free (Strbuf_t *strbuf)
39 {
40 GSList *it;
41
42 for (it = strbuf->list; it; it = it->next)
43 g_free (it->data);
44 g_slist_free (strbuf->list);
45 g_free (strbuf->str);
46 g_free (strbuf);
47 }
48
49 /*
50 * Append a NUL-terminated string to the buffer.
51 * A copy is kept in the buffer, so the caller does not have to care
52 * about memory management.
53 */
54 void a_Strbuf_append (Strbuf_t *strbuf,
55 gchar *str)
56 {
57 /* Remember that g_slist_prepend is faster than g_slist_prepend! */
58 strbuf->list = g_slist_prepend (strbuf->list, g_strdup (str));
59 strbuf->num_chars += strlen (str);
60 strbuf->str_valid = FALSE;
61 }
62
63 /*
64 * Return a NUL-terminated strings containing all appended strings.
65 * The caller does not have to free the string, this is done in
66 * a_Strbuf_free().
67 */
68 gchar* a_Strbuf_chars (Strbuf_t *strbuf)
69 {
70 GSList *it;
71 gchar *p, *s;
72 int l;
73
74 if (strbuf->str_valid)
75 return strbuf->str;
76
77 g_free (strbuf->str);
78 strbuf->str = g_new (gchar, strbuf->num_chars + 1);
79
80 /* The strings in the list are prepended so we have to start from the tail
81 * and work our way to the start of the new string */
82 p = strbuf->str + strbuf->num_chars;
83 for (it = strbuf->list; it; it = it->next) {
84 s = (gchar*)(it->data);
85 l = strlen (s);
86 p -= l;
87 memcpy (p, s, l * sizeof (gchar));
88 }
89
90 p[strbuf->num_chars] = 0;
91 strbuf->str_valid = TRUE;
92
93 return strbuf->str;
94 }
+0
-23
src/strbuf.h less more
0 #ifndef __STRBUF_H__
1 #define __STRBUF_H__
2
3 #include <glib.h>
4
5 typedef struct _Strbuf Strbuf_t;
6
7 struct _Strbuf {
8 GSList *list;
9 int num_chars;
10 gchar *str;
11 gboolean str_valid;
12 };
13
14 Strbuf_t* a_Strbuf_new (void);
15 void a_Strbuf_free (Strbuf_t *strbuf);
16 void a_Strbuf_append (Strbuf_t *strbuf,
17 gchar *str);
18 gchar* a_Strbuf_chars (Strbuf_t *strbuf);
19
20
21
22 #endif /* __STRBUF_H__ */
0 /*
1 * File: styleengine.cc
2 *
3 * Copyright 2008-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "../dlib/dlib.h"
12 #include "msg.h"
13 #include "prefs.h"
14 #include "html_common.hh"
15 #include "styleengine.hh"
16
17 using namespace lout::misc;
18 using namespace dw::core::style;
19
20 StyleEngine::StyleEngine (dw::core::Layout *layout) {
21 StyleAttrs style_attrs;
22 FontAttrs font_attrs;
23
24 doctree = new Doctree ();
25 stack = new lout::misc::SimpleVector <Node> (1);
26 cssContext = new CssContext ();
27 this->layout = layout;
28 importDepth = 0;
29
30 stack->increase ();
31 Node *n = stack->getRef (stack->size () - 1);
32
33 /* Create a dummy font, attribute, and tag for the bottom of the stack. */
34 font_attrs.name = prefs.font_sans_serif;
35 font_attrs.size = roundInt(14 * prefs.font_factor);
36 if (font_attrs.size < prefs.font_min_size)
37 font_attrs.size = prefs.font_min_size;
38 if (font_attrs.size > prefs.font_max_size)
39 font_attrs.size = prefs.font_max_size;
40 font_attrs.weight = 400;
41 font_attrs.style = FONT_STYLE_NORMAL;
42 font_attrs.letterSpacing = 0;
43 font_attrs.fontVariant = FONT_VARIANT_NORMAL;
44
45 style_attrs.initValues ();
46 style_attrs.font = Font::create (layout, &font_attrs);
47 style_attrs.color = Color::create (layout, 0);
48 style_attrs.backgroundColor = Color::create (layout, 0xffffff);
49
50 n->style = Style::create (layout, &style_attrs);
51 n->wordStyle = NULL;
52 n->backgroundStyle = NULL;
53 n->styleAttrProperties = NULL;
54 n->styleAttrPropertiesImportant = NULL;
55 n->nonCssProperties = NULL;
56 n->inheritBackgroundColor = false;
57 }
58
59 StyleEngine::~StyleEngine () {
60 while (doctree->top ())
61 endElement (doctree->top ()->element);
62 assert (stack->size () == 1); // dummy node on the bottom of the stack
63 Node *n = stack->getRef (stack->size () - 1);
64 if (n->style)
65 n->style->unref ();
66 if (n->wordStyle)
67 n->wordStyle->unref ();
68 if (n->backgroundStyle)
69 n->backgroundStyle->unref ();
70 delete stack;
71 delete doctree;
72 delete cssContext;
73 }
74
75 /**
76 * \brief tell the styleEngine that a new html element has started.
77 */
78 void StyleEngine::startElement (int element) {
79 if (stack->getRef (stack->size () - 1)->style == NULL)
80 style0 (stack->size () - 1);
81
82 stack->increase ();
83 Node *n = stack->getRef (stack->size () - 1);
84 n->styleAttrProperties = NULL;
85 n->styleAttrPropertiesImportant = NULL;
86 n->nonCssProperties = NULL;
87 n->style = NULL;
88 n->wordStyle = NULL;
89 n->backgroundStyle = NULL;
90 n->inheritBackgroundColor = false;
91
92 DoctreeNode *dn = doctree->push ();
93 dn->element = element;
94 n->doctreeNode = dn;
95 }
96
97 void StyleEngine::startElement (const char *tagname) {
98 startElement (a_Html_tag_index (tagname));
99 }
100
101 void StyleEngine::setId (const char *id) {
102 DoctreeNode *dn = doctree->top ();
103 assert (dn->id == NULL);
104 dn->id = dStrdup (id);
105 };
106
107 /**
108 * \brief split a string at sep chars and return a SimpleVector of strings
109 */
110 static lout::misc::SimpleVector<char *> *splitStr (const char *str, char sep) {
111 const char *p1 = NULL;
112 lout::misc::SimpleVector<char *> *list =
113 new lout::misc::SimpleVector<char *> (1);
114
115 for (;; str++) {
116 if (*str != '\0' && *str != sep) {
117 if (!p1)
118 p1 = str;
119 } else if (p1) {
120 list->increase ();
121 list->set (list->size () - 1, dStrndup (p1, str - p1));
122 p1 = NULL;
123 }
124
125 if (*str == '\0')
126 break;
127 }
128
129 return list;
130 }
131
132 void StyleEngine::setClass (const char *klass) {
133 DoctreeNode *dn = doctree->top ();
134 assert (dn->klass == NULL);
135 dn->klass = splitStr (klass, ' ');
136 };
137
138 void StyleEngine::setStyle (const char *styleAttr) {
139 Node *n = stack->getRef (stack->size () - 1);
140 assert (n->styleAttrProperties == NULL);
141 // parse style information from style="" attribute, if it exists
142 if (styleAttr && prefs.parse_embedded_css) {
143 n->styleAttrProperties = new CssPropertyList (true);
144 n->styleAttrPropertiesImportant = new CssPropertyList (true);
145
146 CssParser::parseDeclarationBlock (styleAttr, strlen (styleAttr),
147 n->styleAttrProperties,
148 n->styleAttrPropertiesImportant);
149 }
150 };
151
152 /**
153 * \brief Instruct StyleEngine to use the nonCssHints from parent element
154 * This is only used for tables where nonCssHints on the TABLE-element
155 * (e.g. border=1) also affect child elements like TD.
156 */
157 void StyleEngine::inheritNonCssHints () {
158 Node *pn = stack->getRef (stack->size () - 2);
159 Node *n = stack->getRef (stack->size () - 1);
160
161 if (pn->nonCssProperties)
162 n->nonCssProperties = new CssPropertyList (*pn->nonCssProperties, true);
163 }
164
165 void StyleEngine::clearNonCssHints () {
166 Node *n = stack->getRef (stack->size () - 1);
167
168 if (n->nonCssProperties) {
169 delete n->nonCssProperties;
170 n->nonCssProperties = NULL;
171 }
172 }
173
174 /**
175 * \brief Use of the background color of the parent style as default.
176 * This is only used in table code to allow for colors specified for
177 * table rows as table rows are currently no widgets and therefore
178 * don't draw any background.
179 */
180 void StyleEngine::inheritBackgroundColor () {
181 stack->getRef (stack->size () - 1)->inheritBackgroundColor = true;
182 }
183
184 dw::core::style::Color *StyleEngine::backgroundColor () {
185 for (int i = 1; i < stack->size (); i++) {
186 Node *n = stack->getRef (i);
187
188 if (n->style && n->style->backgroundColor)
189 return n->style->backgroundColor;
190 }
191
192 return NULL;
193 }
194
195 /**
196 * \brief set the CSS pseudo class :link.
197 */
198 void StyleEngine::setPseudoLink () {
199 DoctreeNode *dn = doctree->top ();
200 dn->pseudo = "link";
201 }
202
203 /**
204 * \brief set the CSS pseudo class :visited.
205 */
206 void StyleEngine::setPseudoVisited () {
207 DoctreeNode *dn = doctree->top ();
208 dn->pseudo = "visited";
209 }
210
211 /**
212 * \brief tell the styleEngine that a html element has ended.
213 */
214 void StyleEngine::endElement (int element) {
215 assert (element == doctree->top ()->element);
216
217 Node *n = stack->getRef (stack->size () - 1);
218
219 if (n->styleAttrProperties)
220 delete n->styleAttrProperties;
221 if (n->styleAttrPropertiesImportant)
222 delete n->styleAttrPropertiesImportant;
223 if (n->nonCssProperties)
224 delete n->nonCssProperties;
225 if (n->style)
226 n->style->unref ();
227 if (n->wordStyle)
228 n->wordStyle->unref ();
229 if (n->backgroundStyle)
230 n->backgroundStyle->unref ();
231
232 doctree->pop ();
233 stack->setSize (stack->size () - 1);
234 }
235
236 void StyleEngine::preprocessAttrs (dw::core::style::StyleAttrs *attrs) {
237 /* workaround for styling of inline elements */
238 if (stack->getRef (stack->size () - 2)->inheritBackgroundColor) {
239 attrs->backgroundColor =
240 stack->getRef (stack->size () - 2)->style->backgroundColor;
241
242 attrs->valign = stack->getRef (stack->size () - 2)->style->valign;
243 }
244 attrs->borderColor.top = (Color *) -1;
245 attrs->borderColor.bottom = (Color *) -1;
246 attrs->borderColor.left = (Color *) -1;
247 attrs->borderColor.right = (Color *) -1;
248 /* initial value of border-width is 'medium' */
249 attrs->borderWidth.top = 2;
250 attrs->borderWidth.bottom = 2;
251 attrs->borderWidth.left = 2;
252 attrs->borderWidth.right = 2;
253 }
254
255 void StyleEngine::postprocessAttrs (dw::core::style::StyleAttrs *attrs) {
256 /* if border-color is not specified, use color as computed value */
257 if (attrs->borderColor.top == (Color *) -1)
258 attrs->borderColor.top = attrs->color;
259 if (attrs->borderColor.bottom == (Color *) -1)
260 attrs->borderColor.bottom = attrs->color;
261 if (attrs->borderColor.left == (Color *) -1)
262 attrs->borderColor.left = attrs->color;
263 if (attrs->borderColor.right == (Color *) -1)
264 attrs->borderColor.right = attrs->color;
265 /* computed value of border-width is 0 if border-style
266 is 'none' or 'hidden' */
267 if (attrs->borderStyle.top == BORDER_NONE ||
268 attrs->borderStyle.top == BORDER_HIDDEN)
269 attrs->borderWidth.top = 0;
270 if (attrs->borderStyle.bottom == BORDER_NONE ||
271 attrs->borderStyle.bottom == BORDER_HIDDEN)
272 attrs->borderWidth.bottom = 0;
273 if (attrs->borderStyle.left == BORDER_NONE ||
274 attrs->borderStyle.left == BORDER_HIDDEN)
275 attrs->borderWidth.left = 0;
276 if (attrs->borderStyle.right == BORDER_NONE ||
277 attrs->borderStyle.right == BORDER_HIDDEN)
278 attrs->borderWidth.right = 0;
279 }
280
281 /**
282 * \brief Make changes to StyleAttrs attrs according to CssPropertyList props.
283 */
284 void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) {
285 FontAttrs fontAttrs = *attrs->font;
286 Font *parentFont = stack->get (i - 1).style->font;
287 char *c, *fontName;
288 int lineHeight;
289
290 /* Determine font first so it can be used to resolve relative lengths. */
291 for (int i = 0; i < props->size (); i++) {
292 CssProperty *p = props->getRef (i);
293
294 switch (p->name) {
295 case CSS_PROPERTY_FONT_FAMILY:
296 // Check font names in comma separated list.
297 // Note, that p->value.strVal is modified, so that in future calls
298 // the matching font name can be used directly.
299 fontName = NULL;
300 while (p->value.strVal) {
301 if ((c = strchr(p->value.strVal, ',')))
302 *c = '\0';
303 dStrstrip(p->value.strVal);
304
305 if (strcmp (p->value.strVal, "serif") == 0)
306 fontName = prefs.font_serif;
307 else if (strcmp (p->value.strVal, "sans-serif") == 0)
308 fontName = prefs.font_sans_serif;
309 else if (strcmp (p->value.strVal, "cursive") == 0)
310 fontName = prefs.font_cursive;
311 else if (strcmp (p->value.strVal, "fantasy") == 0)
312 fontName = prefs.font_fantasy;
313 else if (strcmp (p->value.strVal, "monospace") == 0)
314 fontName = prefs.font_monospace;
315 else if (Font::exists(layout, p->value.strVal))
316 fontName = p->value.strVal;
317
318 if (fontName) { // font found
319 fontAttrs.name = fontName;
320 break;
321 } else if (c) { // try next from list
322 memmove(p->value.strVal, c + 1, strlen(c + 1) + 1);
323 } else { // no font found
324 break;
325 }
326 }
327
328 break;
329 case CSS_PROPERTY_FONT_SIZE:
330 if (p->type == CSS_TYPE_ENUM) {
331 switch (p->value.intVal) {
332 case CSS_FONT_SIZE_XX_SMALL:
333 fontAttrs.size = roundInt(11.0 * prefs.font_factor);
334 break;
335 case CSS_FONT_SIZE_X_SMALL:
336 fontAttrs.size = roundInt(12.0 * prefs.font_factor);
337 break;
338 case CSS_FONT_SIZE_SMALL:
339 fontAttrs.size = roundInt(13.0 * prefs.font_factor);
340 break;
341 case CSS_FONT_SIZE_MEDIUM:
342 fontAttrs.size = roundInt(14.0 * prefs.font_factor);
343 break;
344 case CSS_FONT_SIZE_LARGE:
345 break;
346 case CSS_FONT_SIZE_X_LARGE:
347 fontAttrs.size = roundInt(16.0 * prefs.font_factor);
348 break;
349 case CSS_FONT_SIZE_XX_LARGE:
350 fontAttrs.size = roundInt(17.0 * prefs.font_factor);
351 break;
352 case CSS_FONT_SIZE_SMALLER:
353 fontAttrs.size -= roundInt(1.0 * prefs.font_factor);
354 break;
355 case CSS_FONT_SIZE_LARGER:
356 fontAttrs.size += roundInt(1.0 * prefs.font_factor);
357 break;
358 default:
359 assert(false); // invalid font-size enum
360 }
361 } else {
362 computeValue (&fontAttrs.size, p->value.intVal, parentFont,
363 parentFont->size);
364 }
365
366 if (fontAttrs.size < prefs.font_min_size)
367 fontAttrs.size = prefs.font_min_size;
368 if (fontAttrs.size > prefs.font_max_size)
369 fontAttrs.size = prefs.font_max_size;
370
371 break;
372 case CSS_PROPERTY_FONT_STYLE:
373 fontAttrs.style = (FontStyle) p->value.intVal;
374 break;
375 case CSS_PROPERTY_FONT_WEIGHT:
376
377 if (p->type == CSS_TYPE_ENUM) {
378 switch (p->value.intVal) {
379 case CSS_FONT_WEIGHT_BOLD:
380 fontAttrs.weight = 700;
381 break;
382 case CSS_FONT_WEIGHT_BOLDER:
383 fontAttrs.weight += 300;
384 break;
385 case CSS_FONT_WEIGHT_LIGHT:
386 fontAttrs.weight = 100;
387 break;
388 case CSS_FONT_WEIGHT_LIGHTER:
389 fontAttrs.weight -= 300;
390 break;
391 case CSS_FONT_WEIGHT_NORMAL:
392 fontAttrs.weight = 400;
393 break;
394 default:
395 assert(false); // invalid font weight value
396 break;
397 }
398 } else {
399 fontAttrs.weight = p->value.intVal;
400 }
401
402 if (fontAttrs.weight < 100)
403 fontAttrs.weight = 100;
404 if (fontAttrs.weight > 900)
405 fontAttrs.weight = 900;
406
407 break;
408 case CSS_PROPERTY_LETTER_SPACING:
409 if (p->type == CSS_TYPE_ENUM) {
410 if (p->value.intVal == CSS_LETTER_SPACING_NORMAL) {
411 fontAttrs.letterSpacing = 0;
412 }
413 } else {
414 computeValue (&fontAttrs.letterSpacing, p->value.intVal,
415 parentFont, parentFont->size);
416 }
417
418 /* Limit letterSpacing to reasonable values to avoid overflows e.g,
419 * when measuring word width.
420 */
421 if (fontAttrs.letterSpacing > 1000)
422 fontAttrs.letterSpacing = 1000;
423 else if (fontAttrs.letterSpacing < -1000)
424 fontAttrs.letterSpacing = -1000;
425 break;
426 case CSS_PROPERTY_FONT_VARIANT:
427 fontAttrs.fontVariant = (FontVariant) p->value.intVal;
428 break;
429 default:
430 break;
431 }
432 }
433
434 attrs->font = Font::create (layout, &fontAttrs);
435
436 for (int i = 0; i < props->size (); i++) {
437 CssProperty *p = props->getRef (i);
438
439 switch (p->name) {
440 /* \todo missing cases */
441 case CSS_PROPERTY_BACKGROUND_COLOR:
442 if (prefs.allow_white_bg || p->value.intVal != 0xffffff)
443 attrs->backgroundColor = Color::create(layout, p->value.intVal);
444 else
445 //attrs->backgroundColor = Color::create(layout, 0xdcd1ba);
446 attrs->backgroundColor = Color::create(layout, 0xe0e0a3);
447 break;
448 case CSS_PROPERTY_BORDER_COLLAPSE:
449 attrs->borderCollapse = (BorderCollapse) p->value.intVal;
450 break;
451 case CSS_PROPERTY_BORDER_TOP_COLOR:
452 attrs->borderColor.top = (p->type == CSS_TYPE_ENUM) ? NULL :
453 Color::create (layout, p->value.intVal);
454 break;
455 case CSS_PROPERTY_BORDER_BOTTOM_COLOR:
456 attrs->borderColor.bottom = (p->type == CSS_TYPE_ENUM) ? NULL :
457 Color::create (layout, p->value.intVal);
458 break;
459 case CSS_PROPERTY_BORDER_LEFT_COLOR:
460 attrs->borderColor.left = (p->type == CSS_TYPE_ENUM) ? NULL :
461 Color::create (layout, p->value.intVal);
462 break;
463 case CSS_PROPERTY_BORDER_RIGHT_COLOR:
464 attrs->borderColor.right = (p->type == CSS_TYPE_ENUM) ? NULL :
465 Color::create (layout, p->value.intVal);
466 break;
467 case CSS_PROPERTY_BORDER_BOTTOM_STYLE:
468 attrs->borderStyle.bottom = (BorderStyle) p->value.intVal;
469 break;
470 case CSS_PROPERTY_BORDER_LEFT_STYLE:
471 attrs->borderStyle.left = (BorderStyle) p->value.intVal;
472 break;
473 case CSS_PROPERTY_BORDER_RIGHT_STYLE:
474 attrs->borderStyle.right = (BorderStyle) p->value.intVal;
475 break;
476 case CSS_PROPERTY_BORDER_TOP_STYLE:
477 attrs->borderStyle.top = (BorderStyle) p->value.intVal;
478 break;
479 case CSS_PROPERTY_BORDER_BOTTOM_WIDTH:
480 computeBorderWidth (&attrs->borderWidth.bottom, p, attrs->font);
481 break;
482 case CSS_PROPERTY_BORDER_LEFT_WIDTH:
483 computeBorderWidth (&attrs->borderWidth.left, p, attrs->font);
484 break;
485 case CSS_PROPERTY_BORDER_RIGHT_WIDTH:
486 computeBorderWidth (&attrs->borderWidth.right, p, attrs->font);
487 break;
488 case CSS_PROPERTY_BORDER_TOP_WIDTH:
489 computeBorderWidth (&attrs->borderWidth.top, p, attrs->font);
490 break;
491 case CSS_PROPERTY_BORDER_SPACING:
492 computeValue (&attrs->hBorderSpacing, p->value.intVal,attrs->font);
493 computeValue (&attrs->vBorderSpacing, p->value.intVal,attrs->font);
494 break;
495 case CSS_PROPERTY_COLOR:
496 attrs->color = Color::create (layout, p->value.intVal);
497 break;
498 case CSS_PROPERTY_CURSOR:
499 attrs->cursor = (Cursor) p->value.intVal;
500 break;
501 case CSS_PROPERTY_DISPLAY:
502 attrs->display = (DisplayType) p->value.intVal;
503 break;
504 case CSS_PROPERTY_LINE_HEIGHT:
505 if (p->type == CSS_TYPE_ENUM) { //only valid enum value is "normal"
506 attrs->lineHeight = dw::core::style::LENGTH_AUTO;
507 } else if (p->type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER) {
508 if (CSS_LENGTH_TYPE (p->value.intVal) == CSS_LENGTH_TYPE_NONE) {
509 attrs->lineHeight =
510 createPerLength(CSS_LENGTH_VALUE(p->value.intVal));
511 } else if (computeValue (&lineHeight, p->value.intVal,
512 attrs->font, attrs->font->size)) {
513 attrs->lineHeight = createAbsLength(lineHeight);
514 }
515 }
516 break;
517 case CSS_PROPERTY_LIST_STYLE_POSITION:
518 attrs->listStylePosition = (ListStylePosition) p->value.intVal;
519 break;
520 case CSS_PROPERTY_LIST_STYLE_TYPE:
521 attrs->listStyleType = (ListStyleType) p->value.intVal;
522 break;
523 case CSS_PROPERTY_MARGIN_BOTTOM:
524 computeValue (&attrs->margin.bottom, p->value.intVal, attrs->font);
525 if (attrs->margin.bottom < 0) // \todo fix negative margins in dw/*
526 attrs->margin.bottom = 0;
527 break;
528 case CSS_PROPERTY_MARGIN_LEFT:
529 computeValue (&attrs->margin.left, p->value.intVal, attrs->font);
530 if (attrs->margin.left < 0) // \todo fix negative margins in dw/*
531 attrs->margin.left = 0;
532 break;
533 case CSS_PROPERTY_MARGIN_RIGHT:
534 computeValue (&attrs->margin.right, p->value.intVal, attrs->font);
535 if (attrs->margin.right < 0) // \todo fix negative margins in dw/*
536 attrs->margin.right = 0;
537 break;
538 case CSS_PROPERTY_MARGIN_TOP:
539 computeValue (&attrs->margin.top, p->value.intVal, attrs->font);
540 if (attrs->margin.top < 0) // \todo fix negative margins in dw/*
541 attrs->margin.top = 0;
542 break;
543 case CSS_PROPERTY_PADDING_TOP:
544 computeValue (&attrs->padding.top, p->value.intVal, attrs->font);
545 break;
546 case CSS_PROPERTY_PADDING_BOTTOM:
547 computeValue (&attrs->padding.bottom, p->value.intVal,attrs->font);
548 break;
549 case CSS_PROPERTY_PADDING_LEFT:
550 computeValue (&attrs->padding.left, p->value.intVal, attrs->font);
551 break;
552 case CSS_PROPERTY_PADDING_RIGHT:
553 computeValue (&attrs->padding.right, p->value.intVal, attrs->font);
554 break;
555 case CSS_PROPERTY_TEXT_ALIGN:
556 attrs->textAlign = (TextAlignType) p->value.intVal;
557 break;
558 case CSS_PROPERTY_TEXT_DECORATION:
559 attrs->textDecoration |= p->value.intVal;
560 break;
561 case CSS_PROPERTY_TEXT_INDENT:
562 computeLength (&attrs->textIndent, p->value.intVal, attrs->font);
563 break;
564 case CSS_PROPERTY_VERTICAL_ALIGN:
565 attrs->valign = (VAlignType) p->value.intVal;
566 break;
567 case CSS_PROPERTY_WHITE_SPACE:
568 attrs->whiteSpace = (WhiteSpace) p->value.intVal;
569 break;
570 case CSS_PROPERTY_WIDTH:
571 computeLength (&attrs->width, p->value.intVal, attrs->font);
572 break;
573 case CSS_PROPERTY_HEIGHT:
574 computeLength (&attrs->height, p->value.intVal, attrs->font);
575 break;
576 case CSS_PROPERTY_WORD_SPACING:
577 if (p->type == CSS_TYPE_ENUM) {
578 if (p->value.intVal == CSS_WORD_SPACING_NORMAL) {
579 attrs->wordSpacing = 0;
580 }
581 } else {
582 computeValue(&attrs->wordSpacing, p->value.intVal, attrs->font);
583 }
584
585 /* Limit to reasonable values to avoid overflows */
586 if (attrs->wordSpacing > 1000)
587 attrs->wordSpacing = 1000;
588 else if (attrs->wordSpacing < -1000)
589 attrs->wordSpacing = -1000;
590 break;
591 case PROPERTY_X_LINK:
592 attrs->x_link = p->value.intVal;
593 break;
594 case PROPERTY_X_IMG:
595 attrs->x_img = p->value.intVal;
596 break;
597 case PROPERTY_X_TOOLTIP:
598 attrs->x_tooltip = Tooltip::create(layout, p->value.strVal);
599 break;
600 default:
601 break;
602 }
603 }
604
605 }
606
607 /**
608 * \brief Resolve relative lengths to absolute values.
609 */
610 bool StyleEngine::computeValue (int *dest, CssLength value, Font *font) {
611 static float dpmm;
612
613 if (dpmm == 0.0)
614 dpmm = layout->dpiX () / 25.4; /* assume dpiX == dpiY */
615
616 switch (CSS_LENGTH_TYPE (value)) {
617 case CSS_LENGTH_TYPE_PX:
618 *dest = (int) CSS_LENGTH_VALUE (value);
619 return true;
620 case CSS_LENGTH_TYPE_MM:
621 *dest = roundInt (CSS_LENGTH_VALUE (value) * dpmm);
622 return true;
623 case CSS_LENGTH_TYPE_EM:
624 *dest = roundInt (CSS_LENGTH_VALUE (value) * font->size);
625 return true;
626 case CSS_LENGTH_TYPE_EX:
627 *dest = roundInt (CSS_LENGTH_VALUE(value) * font->xHeight);
628 return true;
629 case CSS_LENGTH_TYPE_NONE:
630 // length values other than 0 without unit are only allowed
631 // in special cases (line-height) and have to be handled
632 // separately.
633 assert ((int) CSS_LENGTH_VALUE (value) == 0);
634 *dest = 0;
635 return true;
636 default:
637 break;
638 }
639
640 return false;
641 }
642
643 bool StyleEngine::computeValue (int *dest, CssLength value, Font *font,
644 int percentageBase) {
645 if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_PERCENTAGE) {
646 *dest = roundInt (CSS_LENGTH_VALUE (value) * percentageBase);
647 return true;
648 } else
649 return computeValue (dest, value, font);
650 }
651
652 bool StyleEngine::computeLength (dw::core::style::Length *dest,
653 CssLength value, Font *font) {
654 int v;
655
656 if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_PERCENTAGE) {
657 *dest = createPerLength (CSS_LENGTH_VALUE (value));
658 return true;
659 } else if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_AUTO) {
660 *dest = dw::core::style::LENGTH_AUTO;
661 return true;
662 } else if (computeValue (&v, value, font)) {
663 *dest = createAbsLength (v);
664 return true;
665 }
666
667 return false;
668 }
669
670 void StyleEngine::computeBorderWidth (int *dest, CssProperty *p,
671 dw::core::style::Font *font) {
672 if (p->type == CSS_TYPE_ENUM) {
673 switch (p->value.intVal) {
674 case CSS_BORDER_WIDTH_THIN:
675 *dest = 1;
676 break;
677 case CSS_BORDER_WIDTH_MEDIUM:
678 *dest = 2;
679 break;
680 case CSS_BORDER_WIDTH_THICK:
681 *dest = 3;
682 break;
683 default:
684 assert(false);
685 }
686 } else {
687 computeValue (dest, p->value.intVal, font);
688 }
689 }
690
691 /**
692 * \brief Similar to StyleEngine::style(), but with backgroundColor set.
693 * A normal style might have backgroundColor == NULL to indicate a transparent
694 * background. This method ensures that backgroundColor is set.
695 */
696 Style * StyleEngine::backgroundStyle () {
697 if (!stack->getRef (stack->size () - 1)->backgroundStyle) {
698 StyleAttrs attrs = *style ();
699
700 for (int i = stack->size () - 1; i >= 0 && ! attrs.backgroundColor; i--)
701 attrs.backgroundColor = stack->getRef (i)->style->backgroundColor;
702
703 assert (attrs.backgroundColor);
704 stack->getRef (stack->size () - 1)->backgroundStyle =
705 Style::create (layout, &attrs);
706 }
707 return stack->getRef (stack->size () - 1)->backgroundStyle;
708 }
709
710 /**
711 * \brief Create a new style object based on the previously opened / closed
712 * HTML elements and the nonCssProperties that have been set.
713 * This method is private. Call style() to get a current style object.
714 */
715 Style * StyleEngine::style0 (int i) {
716 CssPropertyList props, *styleAttrProperties, *styleAttrPropertiesImportant;
717 CssPropertyList *nonCssProperties;
718 // get previous style from the stack
719 StyleAttrs attrs = *stack->getRef (i - 1)->style;
720
721 // Ensure that StyleEngine::style0() has not been called before for
722 // this element.
723 // Style computation is expensive so limit it as much as possible.
724 // If this assertion is hit, you need to rearrange the code that is
725 // doing styleEngine calls to call setNonCssHint() before calling
726 // style() or wordStyle() for each new element.
727 assert (stack->getRef (i)->style == NULL);
728
729 // reset values that are not inherited according to CSS
730 attrs.resetValues ();
731 preprocessAttrs (&attrs);
732
733 styleAttrProperties = stack->getRef (i)->styleAttrProperties;
734 styleAttrPropertiesImportant = stack->getRef(i)->styleAttrPropertiesImportant;
735 nonCssProperties = stack->getRef (i)->nonCssProperties;
736
737 // merge style information
738 cssContext->apply (&props, doctree, stack->getRef(i)->doctreeNode,
739 styleAttrProperties, styleAttrPropertiesImportant,
740 nonCssProperties);
741
742 // apply style
743 apply (i, &attrs, &props);
744
745 postprocessAttrs (&attrs);
746
747 stack->getRef (i)->style = Style::create (layout, &attrs);
748
749 return stack->getRef (i)->style;
750 }
751
752 Style * StyleEngine::wordStyle0 () {
753 StyleAttrs attrs = *style ();
754 attrs.resetValues ();
755
756 if (stack->getRef (stack->size() - 1)->inheritBackgroundColor)
757 attrs.backgroundColor = style ()->backgroundColor;
758
759 attrs.valign = style ()->valign;
760
761 stack->getRef(stack->size() - 1)->wordStyle = Style::create(layout, &attrs);
762 return stack->getRef (stack->size () - 1)->wordStyle;
763 }
764
765 /**
766 * \brief Recompute all style information from scratch
767 * This is used to take into account CSS styles for the HTML-element.
768 * The CSS data is only completely available after parsing the HEAD-section
769 * and thereby after the HTML-element has been opened.
770 * Note that restyle() does not change any styles in the widget tree.
771 */
772 void StyleEngine::restyle () {
773 for (int i = 1; i < stack->size (); i++) {
774 Node *n = stack->getRef (i);
775 if (n->style) {
776 n->style->unref ();
777 n->style = NULL;
778 }
779 if (n->wordStyle) {
780 n->wordStyle->unref ();
781 n->wordStyle = NULL;
782 }
783 if (n->backgroundStyle) {
784 n->backgroundStyle->unref ();
785 n->backgroundStyle = NULL;
786 }
787
788 style0 (i);
789 }
790 }
791
792 void StyleEngine::parse (DilloHtml *html, DilloUrl *url, const char *buf,
793 int buflen, CssOrigin origin) {
794 if (importDepth > 10) { // avoid looping with recursive @import directives
795 MSG_WARN("Maximum depth of CSS @import reached--ignoring stylesheet.\n");
796 return;
797 }
798
799 importDepth++;
800 CssParser::parse (html, url, cssContext, buf, buflen, origin);
801 importDepth--;
802 }
0 #ifndef __STYLEENGINE_HH__
1 #define __STYLEENGINE_HH__
2
3 class StyleEngine;
4
5 #include "dw/core.hh"
6 #include "doctree.hh"
7 #include "css.hh"
8 #include "cssparser.hh"
9
10 /**
11 * \brief This class provides the glue between HTML parser and CSS subsystem.
12 *
13 * It maintains a document tree and creates and caches style objects for use
14 * by the HTML parser.
15 * The HTML parser in turn informs StyleEngine about opened or closed
16 * HTML elements and their attributes via the startElement() / endElement()
17 * methods.
18 */
19 class StyleEngine {
20 private:
21 struct Node {
22 CssPropertyList *styleAttrProperties;
23 CssPropertyList *styleAttrPropertiesImportant;
24 CssPropertyList *nonCssProperties;
25 dw::core::style::Style *style;
26 dw::core::style::Style *wordStyle;
27 dw::core::style::Style *backgroundStyle;
28 bool inheritBackgroundColor;
29 DoctreeNode *doctreeNode;
30 };
31
32 dw::core::Layout *layout;
33 lout::misc::SimpleVector <Node> *stack;
34 CssContext *cssContext;
35 Doctree *doctree;
36 int importDepth;
37
38 dw::core::style::Style *style0 (int i);
39 dw::core::style::Style *wordStyle0 ();
40 inline void setNonCssHint(CssPropertyName name, CssValueType type,
41 CssPropertyValue value) {
42 Node *n = stack->getRef (stack->size () - 1);
43
44 if (!n->nonCssProperties)
45 n->nonCssProperties = new CssPropertyList (true);
46 n->nonCssProperties->set(name, type, value);
47 }
48 void preprocessAttrs (dw::core::style::StyleAttrs *attrs);
49 void postprocessAttrs (dw::core::style::StyleAttrs *attrs);
50 void apply (int i, dw::core::style::StyleAttrs *attrs, CssPropertyList *props);
51 bool computeValue (int *dest, CssLength value,
52 dw::core::style::Font *font);
53 bool computeValue (int *dest, CssLength value,
54 dw::core::style::Font *font, int percentageBase);
55 bool computeLength (dw::core::style::Length *dest, CssLength value,
56 dw::core::style::Font *font);
57 void computeBorderWidth (int *dest, CssProperty *p,
58 dw::core::style::Font *font);
59
60 public:
61 StyleEngine (dw::core::Layout *layout);
62 ~StyleEngine ();
63
64 void parse (DilloHtml *html, DilloUrl *url, const char *buf, int buflen,
65 CssOrigin origin);
66 void startElement (int tag);
67 void startElement (const char *tagname);
68 void setId (const char *id);
69 const char * getId () { return doctree->top ()->id; };
70 void setClass (const char *klass);
71 void setStyle (const char *style);
72 void endElement (int tag);
73 void setPseudoLink ();
74 void setPseudoVisited ();
75 inline void setNonCssHint(CssPropertyName name, CssValueType type,
76 int value) {
77 CssPropertyValue v;
78 v.intVal = value;
79 setNonCssHint (name, type, v);
80 }
81 inline void setNonCssHint(CssPropertyName name, CssValueType type,
82 const char *value) {
83 CssPropertyValue v;
84 v.strVal = dStrdup(value);
85 setNonCssHint (name, type, v);
86 }
87 void inheritNonCssHints ();
88 void clearNonCssHints ();
89 void restyle ();
90 void inheritBackgroundColor (); /* \todo get rid of this somehow */
91 dw::core::style::Style *backgroundStyle ();
92 dw::core::style::Color *backgroundColor ();
93
94 inline dw::core::style::Style *style () {
95 dw::core::style::Style *s = stack->getRef (stack->size () - 1)->style;
96 if (s)
97 return s;
98 else
99 return style0 (stack->size () - 1);
100 };
101
102 inline dw::core::style::Style *wordStyle () {
103 dw::core::style::Style *s = stack->getRef(stack->size()-1)->wordStyle;
104 if (s)
105 return s;
106 else
107 return wordStyle0 ();
108 };
109 };
110
111 #endif
0 /*
1 * File: table.cc
2 *
3 * Copyright 2008 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "table.hh"
12 #include "html_common.hh"
13
14 #include "dw/style.hh"
15 #include "dw/textblock.hh"
16 #include "dw/table.hh"
17
18 #include "prefs.h"
19 #include "msg.h"
20 #include "css.hh"
21
22 using namespace dw;
23 using namespace dw::core;
24 using namespace dw::core::style;
25
26 /*
27 * Forward declarations
28 */
29
30 static void Html_tag_open_table_cell(DilloHtml *html,
31 const char *tag, int tagsize,
32 dw::core::style::TextAlignType text_align);
33
34 /*
35 * <TABLE>
36 */
37 void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
38 {
39 dw::core::Widget *table;
40 const char *attrbuf;
41 int32_t border = -1, cellspacing = -1, cellpadding = -1, bgcolor = -1;
42 CssLength cssLength;
43
44 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border")))
45 border = isdigit(attrbuf[0]) ? strtol (attrbuf, NULL, 10) : 1;
46 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellspacing")))
47 cellspacing = strtol (attrbuf, NULL, 10);
48 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellpadding")))
49 cellpadding = strtol (attrbuf, NULL, 10);
50
51 if (border != -1) {
52 cssLength = CSS_CREATE_LENGTH (border, CSS_LENGTH_TYPE_PX);
53 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_WIDTH,
54 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
55 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
56 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
57 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_WIDTH,
58 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
59 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_WIDTH,
60 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
61 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_STYLE,
62 CSS_TYPE_ENUM, BORDER_OUTSET);
63 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_STYLE,
64 CSS_TYPE_ENUM, BORDER_OUTSET);
65 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_STYLE,
66 CSS_TYPE_ENUM, BORDER_OUTSET);
67 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_STYLE,
68 CSS_TYPE_ENUM, BORDER_OUTSET);
69 }
70
71 if (cellspacing != -1) {
72 cssLength = CSS_CREATE_LENGTH (cellspacing, CSS_LENGTH_TYPE_PX);
73 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_SPACING,
74 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
75 }
76
77 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "width")))
78 html->styleEngine->setNonCssHint (CSS_PROPERTY_WIDTH,
79 CSS_TYPE_LENGTH_PERCENTAGE,
80 a_Html_parse_length (html, attrbuf));
81
82 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) {
83 if (dStrcasecmp (attrbuf, "left") == 0)
84 html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN,
85 CSS_TYPE_ENUM, TEXT_ALIGN_LEFT);
86 else if (dStrcasecmp (attrbuf, "right") == 0)
87 html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN,
88 CSS_TYPE_ENUM, TEXT_ALIGN_RIGHT);
89 else if (dStrcasecmp (attrbuf, "center") == 0)
90 html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN,
91 CSS_TYPE_ENUM, TEXT_ALIGN_CENTER);
92 }
93
94 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
95 bgcolor = a_Html_color_parse(html, attrbuf, -1);
96 if (bgcolor != -1)
97 html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
98 CSS_TYPE_COLOR, bgcolor);
99 }
100
101 HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
102
103 /* The style for the cells */
104 html->styleEngine->clearNonCssHints ();
105 if (border > 0) {
106 cssLength = CSS_CREATE_LENGTH (1, CSS_LENGTH_TYPE_PX);
107 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_WIDTH,
108 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
109 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
110 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
111 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_WIDTH,
112 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
113 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_WIDTH,
114 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
115 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_STYLE,
116 CSS_TYPE_ENUM, BORDER_INSET);
117 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_STYLE,
118 CSS_TYPE_ENUM, BORDER_INSET);
119 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_LEFT_STYLE,
120 CSS_TYPE_ENUM, BORDER_INSET);
121 html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_RIGHT_STYLE,
122 CSS_TYPE_ENUM, BORDER_INSET);
123 }
124
125 if (cellpadding != -1) {
126 cssLength = CSS_CREATE_LENGTH (cellpadding, CSS_LENGTH_TYPE_PX);
127 html->styleEngine->setNonCssHint (CSS_PROPERTY_PADDING_TOP,
128 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
129 html->styleEngine->setNonCssHint (CSS_PROPERTY_PADDING_BOTTOM,
130 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
131 html->styleEngine->setNonCssHint (CSS_PROPERTY_PADDING_LEFT,
132 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
133 html->styleEngine->setNonCssHint (CSS_PROPERTY_PADDING_RIGHT,
134 CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
135 }
136
137 table = new dw::Table(prefs.limit_text_width);
138 HT2TB(html)->addWidget (table, html->styleEngine->style ());
139
140 S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TOP;
141 S_TOP(html)->table_border_mode = DILLO_HTML_TABLE_BORDER_SEPARATE;
142 S_TOP(html)->cell_text_align_set = FALSE;
143 S_TOP(html)->table = table;
144 }
145
146 /*
147 * <TR>
148 */
149 void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize)
150 {
151 const char *attrbuf;
152 int32_t bgcolor = -1;
153
154 html->styleEngine->inheritNonCssHints ();
155
156 switch (S_TOP(html)->table_mode) {
157 case DILLO_HTML_TABLE_MODE_NONE:
158 _MSG("Invalid HTML syntax: <tr> outside <table>\n");
159 return;
160
161 case DILLO_HTML_TABLE_MODE_TOP:
162 case DILLO_HTML_TABLE_MODE_TR:
163 case DILLO_HTML_TABLE_MODE_TD:
164
165 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
166 bgcolor = a_Html_color_parse(html, attrbuf, -1);
167 if (bgcolor != -1)
168 html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
169 CSS_TYPE_COLOR, bgcolor);
170 }
171
172 if (a_Html_get_attr (html, tag, tagsize, "align")) {
173 S_TOP(html)->cell_text_align_set = TRUE;
174 a_Html_tag_set_align_attr (html, tag, tagsize);
175 }
176
177 html->styleEngine->inheritBackgroundColor ();
178
179 ((dw::Table*)S_TOP(html)->table)->addRow (html->styleEngine->style ());
180
181 if (bgcolor != -1) {
182 html->styleEngine->setNonCssHint(CSS_PROPERTY_BACKGROUND_COLOR,
183 CSS_TYPE_COLOR, bgcolor);
184 }
185 a_Html_tag_set_valign_attr (html, tag, tagsize);
186 break;
187 default:
188 break;
189 }
190
191 S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TR;
192 }
193
194 /*
195 * <TD>
196 */
197 void Html_tag_open_td(DilloHtml *html, const char *tag, int tagsize)
198 {
199 Html_tag_open_table_cell (html, tag, tagsize,
200 dw::core::style::TEXT_ALIGN_LEFT);
201 }
202
203 /*
204 * <TH>
205 */
206 void Html_tag_open_th(DilloHtml *html, const char *tag, int tagsize)
207 {
208 Html_tag_open_table_cell (html, tag, tagsize,
209 dw::core::style::TEXT_ALIGN_CENTER);
210 }
211
212 /*
213 * Utilities
214 */
215
216 /*
217 * The table border model is stored in the table's stack item
218 */
219 static int Html_table_get_border_model(DilloHtml *html)
220 {
221 static int i_TABLE = -1;
222 if (i_TABLE == -1)
223 i_TABLE = a_Html_tag_index("table");
224
225 int s_idx = html->stack->size();
226 while (--s_idx > 0 && html->stack->getRef(s_idx)->tag_idx != i_TABLE)
227 ;
228 return html->stack->getRef(s_idx)->table_border_mode;
229 }
230
231 /*
232 * Set current table's border model
233 */
234 static void Html_table_set_border_model(DilloHtml *html,
235 DilloHtmlTableBorderMode mode)
236 {
237 int s_idx = html->stack->size(), i_TABLE = a_Html_tag_index("table");
238
239 while (--s_idx > 0 && html->stack->getRef(s_idx)->tag_idx != i_TABLE) ;
240 if (s_idx > 0)
241 html->stack->getRef(s_idx)->table_border_mode = mode;
242 }
243
244 /* WORKAROUND: collapsing border model requires moving rendering code from
245 * the cell to the table, and making table-code aware of each
246 * cell style.
247 * This workaround mimics collapsing model within separate model. This is not
248 * a complete emulation but should be enough for most cases.
249 */
250 static void Html_set_collapsing_border_model(DilloHtml *html, Widget *col_tb)
251 {
252 dw::core::style::Style *collapseStyle, *tableStyle;
253 dw::core::style::StyleAttrs collapseCellAttrs, collapseTableAttrs;
254 int borderWidth, marginWidth;
255
256 tableStyle = ((dw::Table*)S_TOP(html)->table)->getStyle ();
257 borderWidth = html->styleEngine->style ()->borderWidth.top;
258 marginWidth = tableStyle->margin.top;
259
260 collapseCellAttrs = *(html->styleEngine->style ());
261 collapseCellAttrs.margin.setVal (0);
262 collapseCellAttrs.borderWidth.left = 0;
263 collapseCellAttrs.borderWidth.top = 0;
264 collapseCellAttrs.borderWidth.right = borderWidth;
265 collapseCellAttrs.borderWidth.bottom = borderWidth;
266 collapseCellAttrs.hBorderSpacing = 0;
267 collapseCellAttrs.vBorderSpacing = 0;
268 collapseStyle = Style::create(HT2LT(html), &collapseCellAttrs);
269 col_tb->setStyle (collapseStyle);
270
271 if (Html_table_get_border_model(html) != DILLO_HTML_TABLE_BORDER_COLLAPSE) {
272 Html_table_set_border_model(html, DILLO_HTML_TABLE_BORDER_COLLAPSE);
273 collapseTableAttrs = *tableStyle;
274 collapseTableAttrs.margin.setVal (marginWidth);
275 collapseTableAttrs.borderWidth.left = borderWidth;
276 collapseTableAttrs.borderWidth.top = borderWidth;
277 collapseTableAttrs.borderWidth.right = 0;
278 collapseTableAttrs.borderWidth.bottom = 0;
279 collapseTableAttrs.hBorderSpacing = 0;
280 collapseTableAttrs.vBorderSpacing = 0;
281 collapseTableAttrs.borderColor = collapseCellAttrs.borderColor;
282 collapseTableAttrs.borderStyle = collapseCellAttrs.borderStyle;
283 /* CSS2 17.6.2: table does not have padding (in collapsing mode) */
284 collapseTableAttrs.padding.setVal (0);
285 collapseStyle = Style::create(HT2LT(html), &collapseTableAttrs);
286 ((dw::Table*)S_TOP(html)->table)->setStyle (collapseStyle);
287 }
288 }
289
290 /*
291 * Adjust style for separate border model.
292 * (Dw uses this model internally).
293 */
294 static void Html_set_separate_border_model(DilloHtml *html, Widget *col_tb)
295 {
296 dw::core::style::Style *separateStyle;
297 dw::core::style::StyleAttrs separateCellAttrs;
298
299 separateCellAttrs = *(html->styleEngine->style ());
300 /* CSS2 17.5: Internal table elements do not have margins */
301 separateCellAttrs.margin.setVal (0);
302 separateStyle = Style::create(HT2LT(html), &separateCellAttrs);
303 col_tb->setStyle (separateStyle);
304 }
305
306 /*
307 * used by <TD> and <TH>
308 */
309 static void Html_tag_open_table_cell(DilloHtml *html,
310 const char *tag, int tagsize,
311 dw::core::style::TextAlignType text_align)
312 {
313 Widget *col_tb;
314 int colspan = 1, rowspan = 1;
315 const char *attrbuf;
316 int32_t bgcolor;
317
318 html->styleEngine->inheritNonCssHints ();
319
320 switch (S_TOP(html)->table_mode) {
321 case DILLO_HTML_TABLE_MODE_NONE:
322 BUG_MSG("<td> or <th> outside <table>\n");
323 return;
324
325 case DILLO_HTML_TABLE_MODE_TOP:
326 BUG_MSG("<td> or <th> outside <tr>\n");
327 /* a_Dw_table_add_cell takes care that dillo does not crash. */
328 /* continues */
329 case DILLO_HTML_TABLE_MODE_TR:
330 case DILLO_HTML_TABLE_MODE_TD:
331 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "colspan"))) {
332 char *invalid;
333 colspan = strtol(attrbuf, &invalid, 10);
334 if ((colspan < 0) || (attrbuf == invalid))
335 colspan = 1;
336 }
337 /* TODO: check errors? */
338 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rowspan")))
339 rowspan = MAX(1, strtol (attrbuf, NULL, 10));
340
341 /* text style */
342 if (!S_TOP(html)->cell_text_align_set) {
343 html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN,
344 CSS_TYPE_ENUM, text_align);
345 }
346 if (a_Html_get_attr(html, tag, tagsize, "nowrap"))
347 html->styleEngine->setNonCssHint(CSS_PROPERTY_WHITE_SPACE,
348 CSS_TYPE_ENUM, WHITE_SPACE_NOWRAP);
349 else
350 html->styleEngine->setNonCssHint(CSS_PROPERTY_WHITE_SPACE,
351 CSS_TYPE_ENUM, WHITE_SPACE_NORMAL);
352
353 a_Html_tag_set_align_attr (html, tag, tagsize);
354
355 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "width"))) {
356 html->styleEngine->setNonCssHint (CSS_PROPERTY_WIDTH,
357 CSS_TYPE_LENGTH_PERCENTAGE,
358 a_Html_parse_length (html, attrbuf));
359 }
360
361 a_Html_tag_set_valign_attr (html, tag, tagsize);
362
363 if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
364 bgcolor = a_Html_color_parse(html, attrbuf, -1);
365 if (bgcolor != -1)
366 html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
367 CSS_TYPE_COLOR, bgcolor);
368 }
369
370 if (html->styleEngine->style ()->textAlign
371 == TEXT_ALIGN_STRING)
372 col_tb = new dw::TableCell (
373 ((dw::Table*)S_TOP(html)->table)->getCellRef (),
374 prefs.limit_text_width);
375 else
376 col_tb = new Textblock (prefs.limit_text_width);
377
378 if (html->styleEngine->style()->borderCollapse == BORDER_MODEL_COLLAPSE){
379 Html_set_collapsing_border_model(html, col_tb);
380 } else {
381 Html_set_separate_border_model(html, col_tb);
382 }
383
384 ((dw::Table*)S_TOP(html)->table)->addCell (col_tb, colspan, rowspan);
385 S_TOP(html)->textblock = html->dw = col_tb;
386 break;
387
388 default:
389 /* compiler happiness */
390 break;
391 }
392
393 S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TD;
394 }
0 #ifndef __TABLE_HH__
1 #define __TABLE_HH__
2
3 /*
4 * Classes
5 */
6
7 class DilloHtml;
8
9 /*
10 * Table parsing functions
11 */
12
13 void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize);
14 void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize);
15 void Html_tag_open_td(DilloHtml *html, const char *tag, int tagsize);
16 void Html_tag_open_th(DilloHtml *html, const char *tag, int tagsize);
17
18 #endif /* __TABLE_HH__ */
0 /*
1 * File: timeout.cc
2 *
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 // Simple ADT for timeout functions
12
13 #include <FL/Fl.H>
14 #include "timeout.hh"
15
16 // C++ functions with C linkage ----------------------------------------------
17
18 /*
19 * Hook a one-time timeout function 'cb' after 't' seconds
20 * with 'cbdata" as its data.
21 */
22 void a_Timeout_add(float t, TimeoutCb_t cb, void *cbdata)
23 {
24 Fl::add_timeout(t, cb, cbdata);
25 }
26
27 /*
28 * To be called from inside the 'cb' function when it wants to keep running
29 */
30 void a_Timeout_repeat(float t, TimeoutCb_t cb, void *cbdata)
31 {
32 Fl::add_timeout(t, cb, cbdata);
33 }
34
35 /*
36 * Stop running a timeout function
37 */
38 void a_Timeout_remove()
39 {
40 /* in FLTK, timeouts run one time by default */
41 }
42
0 #ifndef __TIMEOUT_HH__
1 #define __TIMEOUT_HH__
2
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7 typedef void (*TimeoutCb_t)(void *data);
8
9 void a_Timeout_add(float t, TimeoutCb_t cb, void *cbdata);
10 void a_Timeout_repeat(float t, TimeoutCb_t cb, void *cbdata);
11 void a_Timeout_remove();
12
13
14 #ifdef __cplusplus
15 }
16 #endif /* __cplusplus */
17
18 #endif /* __TIMEOUT_HH__ */
19
0 /*
1 * File: ui.cc
2 *
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 // UI for Dillo
12
13 #include <unistd.h>
14 #include <stdio.h>
15
16 #include "keys.hh"
17 #include "ui.hh"
18 #include "msg.h"
19 #include "timeout.hh"
20 #include "utf8.hh"
21
22 #include <FL/Fl.H>
23 #include <FL/Fl_Pixmap.H>
24 #include <FL/Fl_Box.H>
25 #include <FL/names.h>
26
27 // Include image data
28 #include "pixmaps.h"
29 #include "uicmd.hh"
30
31 struct iconset {
32 Fl_Image *ImgMeterOK, *ImgMeterBug,
33 *ImgHome, *ImgReload, *ImgSave, *ImgBook, *ImgTools,
34 *ImgClear,*ImgSearch, *ImgHelp, *ImgLeft, *ImgLeftIn,
35 *ImgRight, *ImgRightIn, *ImgStop, *ImgStopIn;
36 };
37
38 static struct iconset standard_icons = {
39 new Fl_Pixmap(mini_ok_xpm),
40 new Fl_Pixmap(mini_bug_xpm),
41 new Fl_Pixmap(home_xpm),
42 new Fl_Pixmap(reload_xpm),
43 new Fl_Pixmap(save_xpm),
44 new Fl_Pixmap(bm_xpm),
45 new Fl_Pixmap(tools_xpm),
46 new Fl_Pixmap(new_s_xpm),
47 new Fl_Pixmap(search_xpm),
48 new Fl_Pixmap(help_xpm),
49 new Fl_Pixmap(left_xpm),
50 new Fl_Pixmap(left_i_xpm),
51 new Fl_Pixmap(right_xpm),
52 new Fl_Pixmap(right_i_xpm),
53 new Fl_Pixmap(stop_xpm),
54 new Fl_Pixmap(stop_i_xpm),
55 };
56
57 static struct iconset small_icons = {
58 standard_icons.ImgMeterOK,
59 standard_icons.ImgMeterBug,
60 new Fl_Pixmap(home_s_xpm),
61 new Fl_Pixmap(reload_s_xpm),
62 new Fl_Pixmap(save_s_xpm),
63 new Fl_Pixmap(bm_s_xpm),
64 new Fl_Pixmap(tools_s_xpm),
65 new Fl_Pixmap(new_s_xpm),
66 standard_icons.ImgSearch,
67 standard_icons.ImgHelp,
68 new Fl_Pixmap(left_s_xpm),
69 new Fl_Pixmap(left_si_xpm),
70 new Fl_Pixmap(right_s_xpm),
71 new Fl_Pixmap(right_si_xpm),
72 new Fl_Pixmap(stop_s_xpm),
73 new Fl_Pixmap(stop_si_xpm),
74 };
75
76
77 static struct iconset *icons = &standard_icons;
78
79 /*
80 * Local sub classes
81 */
82
83 //----------------------------------------------------------------------------
84
85 /*
86 * (Used to avoid certain shortcuts in the location bar)
87 */
88 class CustInput : public Fl_Input {
89 public:
90 CustInput (int x, int y, int w, int h, const char* l=0) :
91 Fl_Input(x,y,w,h,l) {};
92 int handle(int e);
93 };
94
95 /*
96 * Disable keys: Up, Down, Page_Up, Page_Down, Tab and
97 * CTRL+{o,r,Home,End} SHIFT+{Left,Right}.
98 */
99 int CustInput::handle(int e)
100 {
101 int k = Fl::event_key();
102
103 _MSG("CustInput::handle event=%d\n", e);
104
105 // We're only interested in some flags
106 unsigned modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL | FL_ALT);
107
108 // Don't focus with arrow keys
109 if (e == FL_FOCUS &&
110 (k == FL_Up || k == FL_Down || k == FL_Left || k == FL_Right)) {
111 return 0;
112 } else if (e == FL_KEYBOARD) {
113 if (k == FL_Escape && modifier == 0) {
114 // Let the parent group handle this Esc key
115 return 0;
116 } else if (modifier == FL_SHIFT) {
117 if (k == FL_Left || k == FL_Right) {
118 // Let these keys get to the UI
119 return 0;
120 }
121 } else if (modifier == FL_CTRL) {
122 if (k == 'a' || k == 'e') {
123 position(k == 'a' ? 0 : size());
124 return 1;
125 } else if (k == 'k') {
126 cut(position(), size());
127 return 1;
128 } else if (k == 'd') {
129 cut(position(), position()+1);
130 return 1;
131 } else if (k == 'l') {
132 // Make text selected when already focused.
133 position(size(), 0);
134 return 1;
135 } else if (k == 'h' || k == 'o' || k == 'r' ||
136 k == FL_Home || k == FL_End) {
137 // Let these keys get to the UI
138 return 0;
139 }
140 } else if (modifier == 0) {
141 if (k == FL_Down || k == FL_Up ||
142 k == FL_Page_Down || k == FL_Page_Up || k == FL_Tab) {
143 // Give up focus and honor the key
144 a_UIcmd_focus_main_area(a_UIcmd_get_bw_by_widget(this));
145 return 0;
146 }
147 }
148 }
149
150 return Fl_Input::handle(e);
151 }
152
153 //----------------------------------------------------------------------------
154
155 /*
156 * A button that highlights on mouse over
157 */
158 class CustLightButton : public Fl_Button {
159 Fl_Color norm_color;
160 public:
161 CustLightButton(int x, int y, int w, int h, const char *l=0) :
162 Fl_Button(x,y,w,h,l) { norm_color = color(); };
163 virtual int handle(int e);
164 };
165
166 int CustLightButton::handle(int e)
167 {
168 if (active()) {
169 if (e == FL_ENTER) {
170 color(51); // {17,26,51}
171 redraw();
172 } else if (e == FL_LEAVE || e == FL_RELEASE) {
173 color(norm_color);
174 redraw();
175 }
176 }
177 return Fl_Button::handle(e);
178 }
179
180 //----------------------------------------------------------------------------
181
182 /*
183 * Used to handle "paste" within the toolbar's Clear button.
184 */
185 class CustPasteButton : public CustLightButton {
186 public:
187 CustPasteButton(int x, int y, int w, int h, const char *l=0) :
188 CustLightButton(x,y,w,h,l) {};
189 int handle(int e);
190 };
191
192 int CustPasteButton::handle(int e)
193 {
194 if (e == FL_PASTE) {
195 const char* t = Fl::event_text();
196 if (t && *t) {
197 a_UIcmd_set_location_text(a_UIcmd_get_bw_by_widget(this), t);
198 a_UIcmd_open_urlstr(a_UIcmd_get_bw_by_widget(this), t);
199 return 1;
200 }
201 }
202 return CustLightButton::handle(e);
203 }
204
205 //----------------------------------------------------------------------------
206
207 /*
208 * Used to resize the progress boxes automatically.
209 */
210 class CustProgressBox : public Fl_Box {
211 int padding;
212 public:
213 CustProgressBox(int x, int y, int w, int h, const char *l=0) :
214 Fl_Box(x,y,w,h,l) { padding = 0; };
215 void update_label(const char *lbl) {
216 int w = 0, h = 0;
217 if (!padding) {
218 copy_label("W");
219 measure_label(w, h);
220 padding = w > 2 ? w/2 : 1;
221 }
222 copy_label(lbl);
223 //measure_label(w,h);
224 //size(w+padding,this->h());
225 }
226 };
227
228 //
229 // Toolbar buttons -----------------------------------------------------------
230 //
231 //static const char *button_names[] = {
232 // "Back", "Forward", "Home", "Reload", "Save", "Stop", "Bookmarks", "Tools",
233 // "Clear", "Search"
234 //};
235
236
237 //
238 // Callback functions --------------------------------------------------------
239 //
240
241 /*
242 * Callback for the search button.
243 */
244 static void search_cb(Fl_Widget *wid, void *data)
245 {
246 int b = Fl::event_button();
247
248 if (b == FL_LEFT_MOUSE) {
249 a_UIcmd_search_dialog(a_UIcmd_get_bw_by_widget(wid));
250 } else if (b == FL_MIDDLE_MOUSE) {
251 ((UI*)data)->color_change_cb_i();
252 } else if (b == FL_RIGHT_MOUSE) {
253 // nothing ATM
254 }
255 }
256
257 /*
258 * Callback for the help button.
259 */
260 static void help_cb(Fl_Widget *w, void *)
261 {
262 char *path = dStrconcat(DILLO_DOCDIR, "user_help.html", NULL);
263 BrowserWindow *bw = a_UIcmd_get_bw_by_widget(w);
264
265 if (access(path, R_OK) == 0) {
266 char *urlstr = dStrconcat("file:", path, NULL);
267 a_UIcmd_open_urlstr(bw, urlstr);
268 dFree(urlstr);
269 } else {
270 MSG("Can't read local help file at \"%s\"."
271 " Getting remote help...\n", path);
272 a_UIcmd_open_urlstr(bw, "http://www.dillo.org/dillo3-help.html");
273 }
274 dFree(path);
275 }
276
277 /*
278 * Callback for the File menu button.
279 */
280 static void filemenu_cb(Fl_Widget *wid, void *)
281 {
282 int b = Fl::event_button();
283 if (b == FL_LEFT_MOUSE || b == FL_RIGHT_MOUSE) {
284 a_UIcmd_file_popup(a_UIcmd_get_bw_by_widget(wid), wid);
285 }
286 }
287
288 /*
289 * Callback for the location's clear-button.
290 */
291 static void clear_cb(Fl_Widget *w, void *data)
292 {
293 UI *ui = (UI*)data;
294
295 int b = Fl::event_button();
296 if (b == FL_LEFT_MOUSE) {
297 ui->set_location("");
298 ui->focus_location();
299 } if (b == FL_MIDDLE_MOUSE) {
300 ui->paste_url();
301 }
302 }
303
304 /*
305 * Change the color of the location bar.
306 *
307 static void color_change_cb(Fl_Widget *wid, void *data)
308 {
309 ((UI*)data)->color_change_cb_i();
310 }
311 */
312
313
314 /*
315 * Send the browser to the new URL in the location.
316 */
317 static void location_cb(Fl_Widget *wid, void *data)
318 {
319 Fl_Input *i = (Fl_Input*)wid;
320 UI *ui = (UI*)data;
321
322 _MSG("location_cb()\n");
323 a_UIcmd_open_urlstr(a_UIcmd_get_bw_by_widget(i), i->value());
324
325 if (ui->get_panelmode() == UI_TEMPORARILY_SHOW_PANELS) {
326 ui->set_panelmode(UI_HIDDEN);
327 }
328 }
329
330
331 /*
332 * Callback handler for button press on the panel
333 */
334 static void b1_cb(Fl_Widget *wid, void *cb_data)
335 {
336 int bn = VOIDP2INT(cb_data);
337 int b = Fl::event_button();
338 if (b >= FL_LEFT_MOUSE && b <= FL_RIGHT_MOUSE) {
339 _MSG("[%s], mouse button %d was pressed\n", button_names[bn], b);
340 _MSG("mouse button %d was pressed\n", b);
341 }
342 switch (bn) {
343 case UI_BACK:
344 if (b == FL_LEFT_MOUSE) {
345 a_UIcmd_back(a_UIcmd_get_bw_by_widget(wid));
346 } else if (b == FL_RIGHT_MOUSE) {
347 a_UIcmd_back_popup(a_UIcmd_get_bw_by_widget(wid));
348 }
349 break;
350 case UI_FORW:
351 if (b == FL_LEFT_MOUSE) {
352 a_UIcmd_forw(a_UIcmd_get_bw_by_widget(wid));
353 } else if (b == FL_RIGHT_MOUSE) {
354 a_UIcmd_forw_popup(a_UIcmd_get_bw_by_widget(wid));
355 }
356 break;
357 case UI_HOME:
358 if (b == FL_LEFT_MOUSE) {
359 a_UIcmd_home(a_UIcmd_get_bw_by_widget(wid));
360 }
361 break;
362 case UI_RELOAD:
363 if (b == FL_LEFT_MOUSE) {
364 a_UIcmd_reload(a_UIcmd_get_bw_by_widget(wid));
365 }
366 break;
367 case UI_SAVE:
368 if (b == FL_LEFT_MOUSE) {
369 a_UIcmd_save(a_UIcmd_get_bw_by_widget(wid));
370 }
371 break;
372 case UI_STOP:
373 if (b == FL_LEFT_MOUSE) {
374 a_UIcmd_stop(a_UIcmd_get_bw_by_widget(wid));
375 }
376 break;
377 case UI_BOOK:
378 if (b == FL_LEFT_MOUSE) {
379 a_UIcmd_book(a_UIcmd_get_bw_by_widget(wid));
380 }
381 break;
382 case UI_TOOLS:
383 if (b == FL_LEFT_MOUSE || b == FL_RIGHT_MOUSE) {
384 a_UIcmd_tools(a_UIcmd_get_bw_by_widget(wid), wid);
385 }
386 break;
387 default:
388 break;
389 }
390 }
391
392 /*
393 * Callback for the bug meter button.
394 */
395 static void bugmeter_cb(Fl_Widget *wid, void *data)
396 {
397 int b = Fl::event_button();
398 if (b == FL_LEFT_MOUSE) {
399 a_UIcmd_view_page_bugs(a_UIcmd_get_bw_by_widget(wid));
400 } else if (b == FL_RIGHT_MOUSE) {
401 a_UIcmd_bugmeter_popup(a_UIcmd_get_bw_by_widget(wid));
402 }
403 }
404
405 //////////////////////////////////////////////////////////////////////////////
406 // UI class methods
407 //
408
409 //----------------------------
410 // Panel construction methods
411 //----------------------------
412
413 /*
414 * Make a generic navigation button
415 */
416 Fl_Button *UI::make_button(const char *label, Fl_Image *img, Fl_Image *deimg,
417 int b_n, int start)
418 {
419 if (start)
420 p_xpos = 0;
421
422 Fl_Button *b = new CustLightButton(p_xpos, 0, bw, bh, (lbl) ? label : NULL);
423 if (img)
424 b->image(img);
425 if (deimg)
426 b->deimage(deimg);
427 b->callback(b1_cb, INT2VOIDP(b_n));
428 b->clear_visible_focus();
429 b->labelsize(12);
430 b->box(FL_FLAT_BOX);
431 b->down_box(FL_THIN_DOWN_FRAME);
432 p_xpos += bw;
433 return b;
434 }
435
436 /*
437 * Create the archetipic browser buttons
438 */
439 void UI::make_toolbar(int tw, int th)
440 {
441 Back = make_button("Back", icons->ImgLeft, icons->ImgLeftIn, UI_BACK, 1);
442 Forw = make_button("Forw", icons->ImgRight, icons->ImgRightIn, UI_FORW);
443 Home = make_button("Home", icons->ImgHome, NULL, UI_HOME);
444 Reload = make_button("Reload", icons->ImgReload, NULL, UI_RELOAD);
445 Save = make_button("Save", icons->ImgSave, NULL, UI_SAVE);
446 Stop = make_button("Stop", icons->ImgStop, icons->ImgStopIn, UI_STOP);
447 Bookmarks = make_button("Book", icons->ImgBook, NULL, UI_BOOK);
448 Tools = make_button("Tools", icons->ImgTools, NULL, UI_TOOLS);
449
450 if (prefs.show_tooltip) {
451 Back->tooltip("Previous page");
452 Forw->tooltip("Next page");
453 Home->tooltip("Go to the Home page");
454 Reload->tooltip("Reload");
455 Save->tooltip("Save this page");
456 Stop->tooltip("Stop loading");
457 Bookmarks->tooltip("View bookmarks");
458 Tools->tooltip("Settings");
459 }
460 }
461
462 /*
463 * Create the location box (Clear/Input/Search)
464 */
465 void UI::make_location(int ww)
466 {
467 Fl_Button *b;
468
469 Clear = b = new CustPasteButton(p_xpos,0,16,lh,0);
470 b->image(icons->ImgClear);
471 b->callback(clear_cb, this);
472 b->clear_visible_focus();
473 b->box(FL_THIN_UP_BOX);
474 p_xpos += b->w();
475
476 Fl_Input *i = Location = new CustInput(p_xpos,0,ww-p_xpos-32,lh,0);
477 i->color(CuteColor);
478 i->when(FL_WHEN_ENTER_KEY);
479 i->callback(location_cb, this);
480 p_xpos += i->w();
481
482 Search = b = new CustLightButton(p_xpos,0,16,lh,0);
483 b->image(icons->ImgSearch);
484 b->callback(search_cb, this);
485 b->clear_visible_focus();
486 b->box(FL_THIN_UP_BOX);
487 p_xpos += b->w();
488
489 Help = b = new CustLightButton(p_xpos,0,16,lh,0);
490 b->image(icons->ImgHelp);
491 b->callback(help_cb, this);
492 b->clear_visible_focus();
493 b->box(FL_THIN_UP_BOX);
494 p_xpos += b->w();
495
496 if (prefs.show_tooltip) {
497 Clear->tooltip("Clear the URL box.\nMiddle-click to paste a URL.");
498 Location->tooltip("Location");
499 Search->tooltip("Search the Web");
500 Help->tooltip("Help");
501 }
502 }
503
504 /*
505 * Create the progress bars
506 */
507 void UI::make_progress_bars(int wide, int thin_up)
508 {
509 // Images
510 IProg = new CustProgressBox(p_xpos,p_ypos,pw,bh);
511 IProg->labelsize(12);
512 IProg->box(thin_up ? FL_THIN_UP_BOX : FL_EMBOSSED_BOX);
513 IProg->labelcolor(FL_GRAY_RAMP + 2);
514 IProg->update_label(wide ? "Images\n0 of 0" : "0 of 0");
515 p_xpos += pw;
516 // Page
517 PProg = new CustProgressBox(p_xpos,p_ypos,pw,bh);
518 PProg->labelsize(12);
519 PProg->box(thin_up ? FL_THIN_UP_BOX : FL_EMBOSSED_BOX);
520 PProg->labelcolor(FL_GRAY_RAMP + 2);
521 PProg->update_label(wide ? "Page\n0.0KB" : "0.0KB");
522 }
523
524 /*
525 * Create the "File" menu
526 * Static function for File menu callbacks.
527 */
528 Fl_Widget *UI::make_filemenu_button()
529 {
530 Fl_Button *btn;
531 int w = 0, h = 0, padding;
532
533 FileButton = btn = new Fl_Button(p_xpos,0,bw,bh,"W");
534 btn->labeltype(FL_FREE_LABELTYPE);
535 btn->measure_label(w, h);
536 padding = w;
537 btn->copy_label(PanelSize == P_tiny ? "&F" : "&File");
538 btn->measure_label(w,h);
539 h = (PanelSize == P_large) ? mh : (PanelSize == P_tiny) ? bh : lh;
540 btn->size(w+padding, h);
541 p_xpos += btn->w();
542 _MSG("UI::make_filemenu_button w=%d h=%d padding=%d\n", w, h, padding);
543 btn->box(PanelSize == P_large ? FL_THIN_UP_BOX : FL_THIN_UP_BOX);
544 btn->callback(filemenu_cb, this);
545 if (prefs.show_tooltip)
546 btn->tooltip("File menu");
547 btn->clear_visible_focus();
548 if (!prefs.show_filemenu)
549 btn->hide();
550 return btn;
551 }
552
553
554 /*
555 * Create the control panel
556 */
557 void UI::make_panel(int ww)
558 {
559 Fl_Widget *w;
560
561 if (Small_Icons)
562 icons = &small_icons;
563 else
564 icons = &standard_icons;
565
566 pw = 70;
567 p_xpos = p_ypos = 0;
568 if (PanelSize == P_tiny) {
569 if (Small_Icons)
570 bw = 22, bh = 22, mh = 0, lh = 22, lbl = 0;
571 else
572 bw = 28, bh = 28, mh = 0, lh = 28, lbl = 0;
573 } else if (PanelSize == P_small) {
574 if (Small_Icons)
575 bw = 20, bh = 20, mh = 0, lh = 20, lbl = 0;
576 else
577 bw = 28, bh = 28, mh = 0, lh = 28, lbl = 0;
578 } else if (PanelSize == P_medium) {
579 if (Small_Icons)
580 bw = 42, bh = 36, mh = 0, lh = 22, lbl = 1;
581 else
582 bw = 45, bh = 45, mh = 0, lh = 28, lbl = 1;
583 } else { // P_large
584 if (Small_Icons)
585 bw = 42, bh = 36, mh = 22, lh = 22, lbl = 1;
586 else
587 bw = 45, bh = 45, mh = 24, lh = 28, lbl = 1;
588 }
589 nh = bh, fh = 28; sh = 20;
590
591 current(0);
592 if (PanelSize == P_tiny) {
593 NavBar = new CustGroupHorizontal(0,0,ww,nh);
594 NavBar->box(FL_NO_BOX);
595 NavBar->begin();
596 make_toolbar(ww,bh);
597 make_filemenu_button();
598 make_location(ww);
599 NavBar->resizable(Location);
600 make_progress_bars(0,1);
601 NavBar->box(FL_THIN_UP_FRAME);
602 NavBar->end();
603 NavBar->rearrange();
604 TopGroup->insert(*NavBar,0);
605 } else {
606 if (PanelSize == P_large) {
607 MenuBar = new CustGroupHorizontal(0,0,ww,mh);
608 MenuBar->begin();
609 MenuBar->box(FL_THIN_UP_BOX);
610 Fl_Widget *bn = make_filemenu_button();
611 MenuBar->add_resizable(*new Fl_Box(bn->w(),0,ww - bn->w(),mh));
612 MenuBar->end();
613 MenuBar->rearrange();
614 TopGroup->insert(*MenuBar,0);
615
616 p_xpos = 0;
617 LocBar = new CustGroupHorizontal(0,0,ww,lh);
618 LocBar->begin();
619 make_location(ww);
620 LocBar->resizable(Location);
621 LocBar->end();
622 LocBar->rearrange();
623 TopGroup->insert(*LocBar,1);
624 } else {
625 LocBar = new CustGroupHorizontal(0,0,ww,lh);
626 LocBar->box(FL_NO_BOX);
627 LocBar->begin();
628 p_xpos = 0;
629 make_filemenu_button();
630 make_location(ww);
631 LocBar->resizable(Location);
632 LocBar->end();
633 LocBar->rearrange();
634 TopGroup->insert(*LocBar,0);
635 }
636
637 // Toolbar
638 p_ypos = 0;
639 NavBar = new CustGroupHorizontal(0,0,ww,bh);
640 NavBar->box(FL_NO_BOX);
641 NavBar->begin();
642 make_toolbar(ww,bh);
643 w = new Fl_Box(p_xpos,0,ww-p_xpos-2*pw,bh);
644 w->box(FL_FLAT_BOX);
645 NavBar->resizable(w);
646 p_xpos = ww - 2*pw;
647 if (PanelSize == P_small) {
648 make_progress_bars(0,0);
649 } else {
650 make_progress_bars(1,0);
651 }
652 NavBar->end();
653 NavBar->rearrange();
654 TopGroup->insert(*NavBar,(MenuBar ? 2 : 1));
655 }
656 }
657
658 /*
659 * Create the status panel
660 */
661 void UI::make_status_bar(int ww, int wh)
662 {
663 const int bm_w = 20;
664 StatusBar = new CustGroupHorizontal(0, wh-sh, ww, sh);
665 StatusBar->box(FL_NO_BOX);
666
667 // Status box
668 StatusOutput = new Fl_Output(0, wh-sh, ww-bm_w, sh);
669 StatusOutput->value("http://www.dillo.org");
670 StatusOutput->labelsize(8);
671 StatusOutput->box(FL_THIN_DOWN_BOX);
672 StatusOutput->clear_visible_focus();
673 StatusOutput->color(FL_GRAY_RAMP + 18);
674
675 // Bug Meter
676 BugMeter = new CustLightButton(ww-bm_w,wh-sh,bm_w,sh);
677 BugMeter->image(icons->ImgMeterOK);
678 BugMeter->box(FL_THIN_DOWN_BOX);
679 BugMeter->align(FL_ALIGN_INSIDE | FL_ALIGN_TEXT_NEXT_TO_IMAGE);
680 if (prefs.show_tooltip)
681 BugMeter->tooltip("Show HTML bugs\n(right-click for menu)");
682 BugMeter->callback(bugmeter_cb, this);
683 BugMeter->clear_visible_focus();
684
685 StatusBar->end();
686 StatusBar->resizable(StatusOutput);
687 StatusBar->rearrange();
688 }
689
690 /*
691 * User Interface constructor
692 */
693 UI::UI(int x, int y, int ui_w, int ui_h, const char* label, const UI *cur_ui) :
694 CustGroupVertical(x, y, ui_w, ui_h, label)
695 {
696 PointerOnLink = FALSE;
697
698 MenuBar = LocBar = NavBar = StatusBar = NULL;
699
700 Tabs = NULL;
701 TabTooltip = NULL;
702 TopGroup = this;
703 TopGroup->box(FL_NO_BOX);
704 clear_flag(SHORTCUT_LABEL);
705
706 if (cur_ui) {
707 PanelSize = cur_ui->PanelSize;
708 CuteColor = cur_ui->CuteColor;
709 Small_Icons = cur_ui->Small_Icons;
710 if (cur_ui->Panelmode == UI_HIDDEN ||
711 cur_ui->Panelmode == UI_TEMPORARILY_SHOW_PANELS)
712 Panelmode = UI_HIDDEN;
713 else
714 Panelmode = UI_NORMAL;
715 } else {
716 // Set some default values
717 //PanelSize = P_tiny, CuteColor = 26, Small_Icons = 0;
718 PanelSize = prefs.panel_size;
719 Small_Icons = prefs.small_icons;
720 CuteColor = 206;
721 Panelmode = (prefs.fullwindow_start) ? UI_HIDDEN : UI_NORMAL;
722 }
723
724 // Control panel
725 TopGroup->begin();
726 make_panel(ui_w);
727
728 // Render area
729 Main = new Fl_Group(0,0,0,0,"Welcome..."); // size is set by rearrange()
730 Main->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
731 Main->box(FL_FLAT_BOX);
732 Main->color(FL_GRAY_RAMP + 3);
733 Main->labelfont(FL_HELVETICA_BOLD_ITALIC);
734 Main->labelsize(36);
735 Main->labeltype(FL_SHADOW_LABEL);
736 Main->labelcolor(FL_WHITE);
737 TopGroup->add(Main);
738 TopGroup->resizable(Main);
739 MainIdx = TopGroup->find(Main);
740
741 // Find text bar
742 FindBarSpace = 1;
743 FindBar = new Findbar(ui_w, fh);
744 TopGroup->add(FindBar);
745
746 // Status Panel
747 make_status_bar(ui_w, ui_h);
748 TopGroup->add(StatusBar);
749 TopGroup->end();
750 TopGroup->rearrange();
751
752 // Make the full screen button (to be attached to the viewport later)
753 // TODO: attach to the viewport
754 //FullScreen = new Fl_Button(0,0,15,15);
755 //FullScreen->image(ImgFullScreenOn);
756 //FullScreen->tooltip("Hide Controls");
757 //FullScreen->callback(fullscreen_cb, this);
758
759 customize(0);
760
761 if (Panelmode == UI_HIDDEN) {
762 panels_toggle();
763 }
764 }
765
766 /*
767 * UI destructor
768 */
769 UI::~UI()
770 {
771 _MSG("UI::~UI()\n");
772 dFree(TabTooltip);
773
774 if (!FindBarSpace)
775 delete FindBar;
776 }
777
778 /*
779 * FLTK event handler for this window.
780 */
781 int UI::handle(int event)
782 {
783 _MSG("UI::handle event=%s\n", fl_eventnames[event]);
784
785 int ret = 0;
786 if (event == FL_KEYBOARD) {
787 /* WORKAROUND: remove the Panel's fltk-tooltip.
788 * Although the expose event is delivered, it has an offset. This
789 * extra call avoids the lingering tooltip. */
790 if (Fl::event_inside(NavBar) ||
791 (LocBar && Fl::event_inside(LocBar)) ||
792 (MenuBar && Fl::event_inside(MenuBar)) ||
793 (StatusBar && Fl::event_inside(StatusBar)))
794 window()->damage(FL_DAMAGE_EXPOSE,0,0,1,1);
795
796 return 0; // Receive as shortcut
797 } else if (event == FL_SHORTCUT) {
798 KeysCommand_t cmd = Keys::getKeyCmd();
799 if (cmd == KEYS_NOP) {
800 // Do nothing
801 } else if (cmd == KEYS_SCREEN_UP || cmd == KEYS_SCREEN_DOWN ||
802 cmd == KEYS_LINE_UP || cmd == KEYS_LINE_DOWN ||
803 cmd == KEYS_LEFT || cmd == KEYS_RIGHT ||
804 cmd == KEYS_TOP || cmd == KEYS_BOTTOM) {
805 a_UIcmd_scroll(a_UIcmd_get_bw_by_widget(this), cmd);
806 ret = 1;
807 } else if (cmd == KEYS_BACK) {
808 a_UIcmd_back(a_UIcmd_get_bw_by_widget(this));
809 ret = 1;
810 } else if (cmd == KEYS_FORWARD) {
811 a_UIcmd_forw(a_UIcmd_get_bw_by_widget(this));
812 ret = 1;
813 } else if (cmd == KEYS_BOOKMARKS) {
814 a_UIcmd_book(a_UIcmd_get_bw_by_widget(this));
815 ret = 1;
816 } else if (cmd == KEYS_FIND) {
817 findbar_toggle(1);
818 ret = 1;
819 } else if (cmd == KEYS_WEBSEARCH) {
820 a_UIcmd_search_dialog(a_UIcmd_get_bw_by_widget(this));
821 ret = 1;
822 } else if (cmd == KEYS_GOTO) {
823 focus_location();
824 ret = 1;
825 } else if (cmd == KEYS_HIDE_PANELS) {
826 /* Hide findbar if present, hide panels if not */
827 (FindBarSpace) ? findbar_toggle(0) : panels_toggle();
828 ret = 1;
829 //if (get_panelmode() == UI_TEMPORARILY_SHOW_PANELS)
830 // set_panelmode(UI_HIDDEN);
831 } else if (cmd == KEYS_OPEN) {
832 a_UIcmd_open_file(a_UIcmd_get_bw_by_widget(this));
833 ret = 1;
834 } else if (cmd == KEYS_HOME) {
835 a_UIcmd_home(a_UIcmd_get_bw_by_widget(this));
836 ret = 1;
837 } else if (cmd == KEYS_RELOAD) {
838 a_UIcmd_reload(a_UIcmd_get_bw_by_widget(this));
839 ret = 1;
840 } else if (cmd == KEYS_STOP) {
841 a_UIcmd_stop(a_UIcmd_get_bw_by_widget(this));
842 ret = 1;
843 } else if (cmd == KEYS_SAVE) {
844 a_UIcmd_save(a_UIcmd_get_bw_by_widget(this));
845 ret = 1;
846 } else if (cmd == KEYS_FILE_MENU) {
847 a_UIcmd_file_popup(a_UIcmd_get_bw_by_widget(this), FileButton);
848 ret = 1;
849 }
850 } else if (event == FL_RELEASE) {
851 if (Fl::event_button() == FL_MIDDLE_MOUSE &&
852 prefs.middle_click_drags_page == 0) {
853 /* nobody claimed the event; try paste */
854 paste_url();
855 ret = 1;
856 }
857 }
858
859 if (!ret) {
860 ret = Fl_Group::handle(event);
861 }
862 if (!ret && event == FL_PUSH && !prefs.middle_click_drags_page) {
863 /* nobody claimed FL_PUSH: ask for FL_RELEASE,
864 * which is necessary for middle-click paste URL) */
865 ret = 1;
866 }
867
868 return ret;
869 }
870
871
872 //----------------------------
873 // API for the User Interface
874 //----------------------------
875
876 /*
877 * Get the text from the location input-box.
878 */
879 const char *UI::get_location()
880 {
881 return Location->value();
882 }
883
884 /*
885 * Set a new URL in the location input-box.
886 */
887 void UI::set_location(const char *str)
888 {
889 if (!str) str = "";
890 Location->value(str);
891 Location->position(strlen(str));
892 }
893
894 /*
895 * Focus location entry.
896 * If it's not visible, show it until the callback is done.
897 */
898 void UI::focus_location()
899 {
900 if (get_panelmode() == UI_HIDDEN) {
901 // Temporary panel handling is disabled now.
902 //set_panelmode(UI_TEMPORARILY_SHOW_PANELS);
903 }
904 Location->take_focus();
905 // Make text selected when already focused.
906 Location->position(Location->size(), 0);
907 }
908
909 /*
910 * Focus Main area.
911 */
912 void UI::focus_main()
913 {
914 Main->take_focus();
915 }
916
917 /*
918 * Set a new message in the status bar.
919 */
920 void UI::set_status(const char *str)
921 {
922 StatusOutput->value(str);
923 }
924
925 /*
926 * Set the page progress text
927 * cmd: 0 Deactivate, 1 Update, 2 Clear
928 */
929 void UI::set_page_prog(size_t nbytes, int cmd)
930 {
931 char str[32];
932
933 if (cmd == 0) {
934 PProg->deactivate();
935 } else {
936 PProg->activate();
937 if (cmd == 1) {
938 snprintf(str, 32, "%s%.1f KB",
939 (PanelSize == 0) ? "" : "Page\n", nbytes/1024.0);
940 } else if (cmd == 2) {
941 str[0] = '\0';
942 }
943 PProg->update_label(str);
944 }
945 }
946
947 /*
948 * Set the image progress text
949 * cmd: 0 Deactivate, 1 Update, 2 Clear
950 */
951 void UI::set_img_prog(int n_img, int t_img, int cmd)
952 {
953 char str[32];
954
955 if (cmd == 0) {
956 IProg->deactivate();
957 } else {
958 IProg->activate();
959 if (cmd == 1) {
960 snprintf(str, 32, "%s%d of %d",
961 (PanelSize == 0) ? "" : "Images\n", n_img, t_img);
962 } else if (cmd == 2) {
963 str[0] = '\0';
964 }
965 IProg->update_label(str);
966 }
967 }
968
969 /*
970 * Set the bug meter progress text
971 */
972 void UI::set_bug_prog(int n_bug)
973 {
974 char str[32];
975 int new_w = 20;
976
977 if (n_bug == 0) {
978 BugMeter->image(icons->ImgMeterOK);
979 BugMeter->label("");
980 } else if (n_bug >= 1) {
981 if (n_bug == 1)
982 BugMeter->image(icons->ImgMeterBug);
983 snprintf(str, 32, "%d", n_bug);
984 BugMeter->copy_label(str);
985 new_w = strlen(str)*9 + 20;
986 }
987 BugMeter->size(new_w, BugMeter->h());
988 StatusBar->rearrange();
989 }
990
991 /*
992 * Customize the UI's panel (show/hide buttons)
993 */
994 void UI::customize(int flags)
995 {
996 // flags argument not currently used
997
998 if ( !prefs.show_back )
999 Back->hide();
1000 if ( !prefs.show_forw )
1001 Forw->hide();
1002 if ( !prefs.show_home )
1003 Home->hide();
1004 if ( !prefs.show_reload )
1005 Reload->hide();
1006 if ( !prefs.show_save )
1007 Save->hide();
1008 if ( !prefs.show_stop )
1009 Stop->hide();
1010 if ( !prefs.show_bookmarks )
1011 Bookmarks->hide();
1012 if ( !prefs.show_tools )
1013 Tools->hide();
1014 if ( !prefs.show_clear_url )
1015 Clear->hide();
1016 if ( !prefs.show_url )
1017 Location->hide();
1018 if ( !prefs.show_search )
1019 Search->hide();
1020 if ( !prefs.show_help )
1021 Help->hide();
1022 if ( !prefs.show_progress_box ) {
1023 IProg->hide();
1024 PProg->hide();
1025 }
1026
1027 if (NavBar)
1028 NavBar->rearrange();
1029 if (LocBar)
1030 LocBar->rearrange();
1031 }
1032
1033 /*
1034 * On-the-fly panel style change
1035 */
1036 void UI::change_panel(int new_size, int small_icons)
1037 {
1038 // Remove current panel's bars
1039 init_sizes();
1040 TopGroup->remove(MenuBar);
1041 Fl::delete_widget(MenuBar);
1042 TopGroup->remove(LocBar);
1043 Fl::delete_widget(LocBar);
1044 TopGroup->remove(NavBar);
1045 Fl::delete_widget(NavBar);
1046 MenuBar = LocBar = NavBar = NULL;
1047
1048 // Set internal vars for panel size
1049 PanelSize = new_size;
1050 Small_Icons = small_icons;
1051
1052 // make a new panel
1053 make_panel(TopGroup->w());
1054 customize(0);
1055 a_UIcmd_set_buttons_sens(a_UIcmd_get_bw_by_widget(this));
1056
1057 TopGroup->rearrange();
1058 Location->take_focus();
1059 }
1060
1061 /*
1062 * On-the-fly color style change
1063 */
1064 void UI::color_change_cb_i()
1065 {
1066 const int cols[] = {7,17,26,51,140,156,205,206,215,-1};
1067 static int ncolor = 0;
1068
1069 ncolor = (cols[ncolor+1] < 0) ? 0 : ncolor + 1;
1070 CuteColor = cols[ncolor];
1071 MSG("Location color %d\n", CuteColor);
1072 Location->color(CuteColor);
1073 Location->redraw();
1074 }
1075
1076 /*
1077 * Set or remove the Panelmode flag and update the UI accordingly
1078 */
1079 void UI::set_panelmode(UIPanelmode mode)
1080 {
1081 if (mode == UI_HIDDEN) {
1082 //Panel->hide();
1083 StatusBar->hide();
1084 } else {
1085 /* UI_NORMAL or UI_TEMPORARILY_SHOW_PANELS */
1086 //Panel->show();
1087 StatusBar->show();
1088 }
1089 Panelmode = mode;
1090 TopGroup->rearrange();
1091 }
1092
1093 /*
1094 * Get the value of the panelmode flag
1095 */
1096 UIPanelmode UI::get_panelmode()
1097 {
1098 return Panelmode;
1099 }
1100
1101 /*
1102 * Toggle the Control Panel out of the way
1103 */
1104 void UI::panelmode_cb_i()
1105 {
1106 set_panelmode((UIPanelmode) !Panelmode);
1107 }
1108
1109 /*
1110 * Set 'nw' as the main render area widget
1111 */
1112 void UI::set_render_layout(Fl_Group *nw)
1113 {
1114 // Resize layout widget to current height
1115 nw->resize(0,Main->y(),Main->w(),Main->h());
1116
1117 TopGroup->insert(*nw, Main);
1118 remove(Main);
1119 delete(Main);
1120 Main = nw;
1121 TopGroup->resizable(Main);
1122 }
1123
1124 /*
1125 * Set button sensitivity (Back/Forw/Stop)
1126 */
1127 void UI::button_set_sens(UIButton btn, int sens)
1128 {
1129 switch (btn) {
1130 case UI_BACK:
1131 (sens) ? Back->activate() : Back->deactivate();
1132 // Back->redraw(DAMAGE_HIGHLIGHT);
1133 break;
1134 case UI_FORW:
1135 (sens) ? Forw->activate() : Forw->deactivate();
1136 // Forw->redraw(DAMAGE_HIGHLIGHT);
1137 break;
1138 case UI_STOP:
1139 (sens) ? Stop->activate() : Stop->deactivate();
1140 // Stop->redraw(DAMAGE_HIGHLIGHT);
1141 break;
1142 default:
1143 break;
1144 }
1145 }
1146
1147 /*
1148 * Paste a middle-click-selection into "Clear" button as URL
1149 */
1150 void UI::paste_url()
1151 {
1152 Fl::paste(*Clear, false);
1153 }
1154
1155 /*
1156 * Adjust space for the findbar (if necessary) and show or remove it
1157 */
1158 void UI::findbar_toggle(bool add)
1159 {
1160 /* WORKAROUND:
1161 * This is tricky: As fltk-1.3 resizes hidden widgets (which it
1162 * doesn't resize when visible!). We need to go through hoops to
1163 * get the desired behaviour.
1164 * (STR#2639 in FLTK bug tracker).
1165 */
1166
1167 if (add) {
1168 if (!FindBarSpace) {
1169 // show
1170 Main->size(Main->w(), Main->h()-FindBar->h());
1171 insert(*FindBar, StatusBar);
1172 FindBar->show();
1173 FindBarSpace = 1;
1174 } else {
1175 // select text
1176 FindBar->show();
1177 }
1178 } else if (!add && FindBarSpace) {
1179 // hide
1180 Main->size(Main->w(), Main->h()+FindBar->h());
1181 remove(FindBar);
1182 FindBarSpace = 0;
1183 }
1184 TopGroup->rearrange();
1185 }
1186
1187 /*
1188 * Make panels disappear growing the render area.
1189 * WORKAROUND: here we avoid hidden widgets resize by setting their
1190 * size to (0,0) while hidden.
1191 * (Already reported to FLTK team)
1192 */
1193 void UI::panels_toggle()
1194 {
1195 int hide = StatusBar->visible();
1196
1197 // hide/show panels
1198 init_sizes();
1199 if (MenuBar) {
1200 hide ? MenuBar->size(0,0) : MenuBar->size(w(),mh);
1201 hide ? MenuBar->hide() : MenuBar->show();
1202 }
1203 if (LocBar) {
1204 hide ? LocBar->size(0,0) : LocBar->size(w(),lh);
1205 hide ? LocBar->hide() : LocBar->show();
1206 }
1207 if (NavBar) {
1208 hide ? NavBar->size(0,0) : NavBar->size(w(),nh);
1209 hide ? NavBar->hide() : NavBar->show();
1210 }
1211 if (StatusBar) {
1212 hide ? StatusBar->size(0,0) : StatusBar->size(w(),sh);;
1213 hide ? StatusBar->hide() : StatusBar->show();;
1214 StatusBar->rearrange();
1215 }
1216
1217 TopGroup->rearrange();
1218 }
0 #ifndef __UI_HH__
1 #define __UI_HH__
2
3 // UI for dillo --------------------------------------------------------------
4
5 #include <FL/Fl_Window.H>
6 #include <FL/Fl_Widget.H>
7 #include <FL/Fl_Button.H>
8 #include <FL/Fl_Input.H>
9 #include <FL/Fl_Output.H>
10 #include <FL/Fl_Image.H>
11 #include <FL/Fl_Tabs.H>
12
13 #include "findbar.hh"
14
15 typedef enum {
16 UI_BACK = 0,
17 UI_FORW,
18 UI_HOME,
19 UI_RELOAD,
20 UI_SAVE,
21 UI_STOP,
22 UI_BOOK,
23 UI_TOOLS,
24 UI_CLEAR,
25 UI_SEARCH
26 } UIButton;
27
28 typedef enum {
29 UI_NORMAL = 0, /* make sure it's compatible with bool */
30 UI_HIDDEN = 1,
31 UI_TEMPORARILY_SHOW_PANELS
32 } UIPanelmode;
33
34 // Private classes
35 class CustProgressBox;
36 class CustTabs;
37
38
39 // Class definition ----------------------------------------------------------
40 /*
41 * Used to reposition group's widgets when some of them are hidden.
42 * All children get the height of the group but retain their original width.
43 * The resizable child get's the remaining space.
44 */
45 class CustGroupHorizontal : public Fl_Group {
46 public:
47 CustGroupHorizontal(int x,int y,int w ,int h,const char *l = 0) :
48 Fl_Group(x,y,w,h,l) { };
49
50 void rearrange() {
51 Fl_Widget*const* a = array();
52 int sum = 0, _x = x();
53 int children_ = children();
54
55 for (int i=0; i < children_; i++)
56 if (a[i] != resizable() && a[i]->visible())
57 sum += a[i]->w();
58
59 for (int i=0; i < children_; i++) {
60 if (a[i] == resizable()) {
61 a[i]->resize(_x, y(), w() - sum, h());
62 } else {
63 a[i]->resize(_x, y(), a[i]->w(), h());
64 }
65 if (a[i]->visible())
66 _x += a[i]->w();
67 }
68 init_sizes();
69 redraw();
70 }
71 };
72
73 class CustGroupVertical : public Fl_Group {
74 public:
75 CustGroupVertical(int x,int y,int w ,int h,const char *l = 0) :
76 Fl_Group(x,y,w,h,l) { };
77
78 void rearrange() {
79 Fl_Widget*const* a = array();
80 int sum = 0, _y = y();
81 int children_ = children();
82
83 for (int i=0; i < children_; i++)
84 if (a[i] != resizable() && a[i]->visible())
85 sum += a[i]->h();
86
87 for (int i=0; i < children_; i++) {
88 if (a[i] == resizable()) {
89 a[i]->resize(x(), _y, w(), h() - sum);
90 } else {
91 a[i]->resize(x(), _y, w(), a[i]->h());
92 }
93 if (a[i]->visible())
94 _y += a[i]->h();
95 }
96 init_sizes();
97 redraw();
98 }
99 };
100
101
102 //
103 // UI class definition -------------------------------------------------------
104 //
105 class UI : public CustGroupVertical {
106 CustTabs *Tabs;
107 char *TabTooltip;
108
109 CustGroupVertical *TopGroup;
110 Fl_Button *Back, *Forw, *Home, *Reload, *Save, *Stop, *Bookmarks, *Tools,
111 *Clear, *Search, *Help, *FullScreen, *BugMeter, *FileButton;
112 CustGroupHorizontal *MenuBar, *LocBar, *NavBar, *StatusBar;
113 Fl_Input *Location;
114 CustProgressBox *PProg, *IProg;
115 Fl_Group *Panel, *Main;
116 Fl_Output *StatusOutput;
117 Findbar *FindBar;
118
119 int FindBarSpace, MainIdx;
120 // Panel customization variables
121 int PanelSize, CuteColor, Small_Icons;
122 int p_xpos, p_ypos, bw, bh, mh, lh, nh, fh, sh, pw, lbl;
123
124 UIPanelmode Panelmode;
125 int PointerOnLink;
126 Fl_Button *make_button(const char *label, Fl_Image *img,
127 Fl_Image*deimg, int b_n, int start = 0);
128 void make_toolbar(int tw, int th);
129 void make_location(int ww);
130 void make_progress_bars(int wide, int thin_up);
131 void make_menubar(int x, int y, int w, int h);
132 Fl_Widget *make_filemenu_button();
133 void make_panel(int ww);
134 void make_status_bar(int ww, int wh);
135
136 public:
137
138 UI(int x,int y,int w,int h, const char* label = 0, const UI *cur_ui=NULL);
139 ~UI();
140
141 // To manage what events to catch and which to let pass
142 int handle(int event);
143
144 const char *get_location();
145 void set_location(const char *str);
146 void focus_location();
147 void focus_main();
148 void set_status(const char *str);
149 void set_page_prog(size_t nbytes, int cmd);
150 void set_img_prog(int n_img, int t_img, int cmd);
151 void set_bug_prog(int n_bug);
152 void set_render_layout(Fl_Group *nw);
153 void customize(int flags);
154 void button_set_sens(UIButton btn, int sens);
155 void paste_url();
156 void set_panelmode(UIPanelmode mode);
157 UIPanelmode get_panelmode();
158 int get_panelsize() { return PanelSize; }
159 int get_smallicons() { return Small_Icons; }
160 void change_panel(int new_size, int small_icons);
161 void findbar_toggle(bool add);
162 void panels_toggle();
163
164 CustTabs *tabs() { return Tabs; }
165 void tabs(CustTabs *tabs) { Tabs = tabs; }
166 int pointerOnLink() { return PointerOnLink; }
167 void pointerOnLink(int flag) { PointerOnLink = flag; }
168
169 // Hooks to method callbacks
170 void color_change_cb_i();
171 void toggle_cb_i();
172 void panelmode_cb_i();
173 };
174
175 #endif // __UI_HH__
0 /*
1 * File: uicmd.cc
2 *
3 * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 // Functions/Methods for commands triggered from the UI
12
13
14 #include <stdio.h>
15 #include <stdarg.h>
16 #include <math.h> /* for rint */
17
18 #include <FL/Fl.H>
19 #include <FL/Fl_Widget.H>
20 #include <FL/Fl_Double_Window.H>
21 #include <FL/Fl_Wizard.H>
22 #include <FL/Fl_Box.H>
23 #include <FL/names.h>
24
25 #include "paths.hh"
26 #include "keys.hh"
27 #include "ui.hh"
28 #include "uicmd.hh"
29 #include "timeout.hh"
30 #include "utf8.hh"
31 #include "menu.hh"
32 #include "dialog.hh"
33 #include "xembed.hh"
34 #include "bookmark.h"
35 #include "history.h"
36 #include "msg.h"
37 #include "prefs.h"
38
39 #include "dw/fltkviewport.hh"
40
41 #include "nav.h"
42
43 #define DEFAULT_TAB_LABEL "Dillo"
44
45 // Handy macro
46 #define BW2UI(bw) ((UI*)((bw)->ui))
47
48 // Platform idependent part
49 using namespace dw::core;
50 // FLTK related
51 using namespace dw::fltk;
52
53
54 /*
55 * Local data
56 */
57 static char *save_dir = NULL;
58
59 /*
60 * Forward declarations
61 */
62 static BrowserWindow *UIcmd_tab_new(CustTabs *tabs, UI *old_ui, int focus);
63
64 //----------------------------------------------------------------------------
65
66 /*
67 * CustTabs ---------------------------------------------------------------
68 */
69
70 /*
71 * stores the respective UI pointer
72 */
73 class CustTabButton : public Fl_Button {
74 UI *ui_;
75 public:
76 CustTabButton (int x,int y,int w,int h, const char* label = 0) :
77 Fl_Button (x,y,w,h,label) { ui_ = NULL; };
78 void ui(UI *pui) { ui_ = pui; }
79 UI *ui(void) { return ui_; }
80 };
81
82 /*
83 * Allows fine control of the tabbed interface
84 */
85 class CustTabs : public CustGroupHorizontal {
86 int tab_w, tab_h, ctab_h, tab_n;
87 Fl_Wizard *Wizard;
88 int tabcolor_inactive, tabcolor_active, curtab_idx;
89 public:
90 CustTabs (int ww, int wh, int th, const char *lbl=0) :
91 CustGroupHorizontal(0,0,ww,th,lbl) {
92 tab_w = 80, tab_h = th, ctab_h = 1, tab_n = 0, curtab_idx = -1;
93 tabcolor_active = FL_DARK_CYAN; tabcolor_inactive = 206;
94 resize(0,0,ww,ctab_h);
95 resizable(NULL);
96 box(FL_FLAT_BOX);
97 end();
98
99 Wizard = new Fl_Wizard(0,ctab_h,ww,wh-ctab_h);
100 Wizard->box(FL_NO_BOX);
101 Wizard->end();
102 };
103 int handle(int e);
104 UI *add_new_tab(UI *old_ui, int focus);
105 void remove_tab(UI *ui);
106 Fl_Wizard *wizard(void) { return Wizard; }
107 int get_btn_idx(UI *ui);
108 int num_tabs() { return children(); }
109 void switch_tab(CustTabButton *cbtn);
110 void prev_tab(void);
111 void next_tab(void);
112
113 void set_tab_label(UI *ui, const char *title);
114 };
115
116 /*
117 * Callback for mouse click
118 */
119 static void tab_btn_cb (Fl_Widget *w, void *cb_data)
120 {
121 CustTabButton *btn = (CustTabButton*) w;
122 CustTabs *tabs = (CustTabs*) cb_data;
123 int b = Fl::event_button();
124
125 if (b == FL_LEFT_MOUSE) {
126 tabs->switch_tab(btn);
127 } else if ((b == FL_RIGHT_MOUSE && prefs.right_click_closes_tab) ||
128 (b == FL_MIDDLE_MOUSE && !prefs.right_click_closes_tab)) {
129 // TODO: just an example, not necessarily final
130 a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(btn->ui()));
131 }
132 }
133
134 int CustTabs::handle(int e)
135 {
136 int ret = 0;
137
138 _MSG("CustTabs::handle e=%s\n", fl_eventnames[e]);
139 if (e == FL_KEYBOARD) {
140 return 0; // Receive as shortcut
141 } else if (e == FL_SHORTCUT) {
142 UI *ui = (UI*)wizard()->value();
143 BrowserWindow *bw = a_UIcmd_get_bw_by_widget(ui);
144 KeysCommand_t cmd = Keys::getKeyCmd();
145 if (Fl::event_key() == FL_Escape) {
146 // Hide findbar if present
147 ui->findbar_toggle(0);
148 ret = 1;
149 } else if (cmd == KEYS_NOP) {
150 // Do nothing
151 _MSG("CustTabs::handle KEYS_NOP\n");
152 } else if (cmd == KEYS_NEW_TAB) {
153 a_UIcmd_open_url_nt(bw, NULL, 1);
154 ret = 1;
155 } else if (cmd == KEYS_CLOSE_TAB) {
156 a_UIcmd_close_bw(bw);
157 ret = 1;
158 } else if (cmd == KEYS_LEFT_TAB) {
159 prev_tab();
160 ret = 1;
161 } else if (cmd == KEYS_RIGHT_TAB) {
162 next_tab();
163 ret = 1;
164 } else if (cmd == KEYS_NEW_WINDOW) {
165 a_UIcmd_open_url_nw(bw, NULL);
166 ret = 1;
167 } else if (cmd == KEYS_CLOSE_ALL) {
168 a_Timeout_add(0.0, a_UIcmd_close_all_bw, NULL);
169 ret = 1;
170 }
171 }
172
173 return (ret) ? ret : CustGroupHorizontal::handle(e);
174 }
175
176 /*
177 * Create a new tab with its own UI
178 */
179 UI *CustTabs::add_new_tab(UI *old_ui, int focus)
180 {
181 char tab_label[64];
182
183 if (num_tabs() == 1) {
184 // Show tabbar
185 ctab_h = tab_h;
186 Wizard->resize(0,ctab_h,Wizard->w(),window()->h()-ctab_h);
187 resize(0,0,window()->w(),ctab_h); // tabbar
188 child(0)->show(); // first tab button
189 window()->init_sizes();
190 }
191
192 current(0);
193 UI *new_ui = new UI(0,ctab_h,Wizard->w(),Wizard->h(),0,old_ui);
194 new_ui->tabs(this);
195 Wizard->add(new_ui);
196 new_ui->show();
197
198 snprintf(tab_label, 64,"ctab%d", ++tab_n);
199 CustTabButton *btn = new CustTabButton(num_tabs()*tab_w,0,tab_w,ctab_h);
200 btn->align(FL_ALIGN_INSIDE|FL_ALIGN_CLIP);
201 btn->copy_label(tab_label);
202 btn->clear_visible_focus();
203 btn->box(FL_PLASTIC_ROUND_UP_BOX);
204 btn->color(focus ? tabcolor_active : tabcolor_inactive);
205 btn->ui(new_ui);
206 add(btn);
207 btn->callback(tab_btn_cb, this);
208
209 if (focus) {
210 switch_tab(btn);
211 } else if (num_tabs() == 2) {
212 // no focus and tabbar added: redraw current page
213 Wizard->redraw();
214 }
215 if (num_tabs() == 1)
216 btn->hide();
217 rearrange();
218
219 return new_ui;
220 }
221
222 /*
223 * Remove tab by UI
224 */
225 void CustTabs::remove_tab(UI *ui)
226 {
227 CustTabButton *btn;
228
229 // get active tab idx
230 int act_idx = get_btn_idx((UI*)Wizard->value());
231 // get to-be-removed tab idx
232 int rm_idx = get_btn_idx(ui);
233 btn = (CustTabButton*)child(rm_idx);
234
235 if (act_idx == rm_idx) {
236 // Active tab is being closed, switch to another one
237 rm_idx > 0 ? prev_tab() : next_tab();
238 }
239 remove(rm_idx);
240 delete btn;
241 rearrange();
242
243 Wizard->remove(ui);
244 delete(ui);
245
246 if (num_tabs() == 0) {
247 window()->hide();
248 // TODO: free memory
249 //delete window();
250
251 } else if (num_tabs() == 1) {
252 // hide tabbar
253 ctab_h = 1;
254 child(0)->hide(); // first tab button
255 resize(0,0,window()->w(),ctab_h); // tabbar
256 Wizard->resize(0,ctab_h,Wizard->w(),window()->h()-ctab_h);
257 window()->init_sizes();
258 window()->redraw();
259 }
260 }
261
262 int CustTabs::get_btn_idx(UI *ui)
263 {
264 for (int i = 0; i < num_tabs(); ++i) {
265 CustTabButton *btn = (CustTabButton*)child(i);
266 if (btn->ui() == ui)
267 return i;
268 }
269 return -1;
270 }
271
272 /*
273 * Make cbtn's tab the active one
274 */
275 void CustTabs::switch_tab(CustTabButton *cbtn)
276 {
277 int idx;
278 CustTabButton *btn;
279 BrowserWindow *bw;
280 UI *old_ui = (UI*)Wizard->value();
281
282 if (cbtn->ui() != old_ui) {
283 // Set old tab label to normal color
284 if ((idx = get_btn_idx(old_ui)) != -1) {
285 btn = (CustTabButton*)child(idx);
286 btn->color(tabcolor_inactive);
287 btn->redraw();
288 }
289 Wizard->value(cbtn->ui());
290 cbtn->color(tabcolor_active);
291 cbtn->redraw();
292
293 // Update window title
294 if ((bw = a_UIcmd_get_bw_by_widget(cbtn->ui()))) {
295 const char *title = (cbtn->ui())->label();
296 cbtn->window()->copy_label(title ? title : "");
297 }
298 }
299 }
300
301 void CustTabs::prev_tab()
302 {
303 int idx;
304
305 if ((idx = get_btn_idx((UI*)Wizard->value())) != -1)
306 switch_tab( (CustTabButton*)child(idx > 0 ? idx-1 : num_tabs()-1) );
307 }
308
309 void CustTabs::next_tab()
310 {
311 int idx;
312
313 if ((idx = get_btn_idx((UI*)Wizard->value())) != -1)
314 switch_tab( (CustTabButton*)child((idx+1 < num_tabs()) ? idx+1 : 0) );
315 }
316
317 /*
318 * Set this UI's tab button label
319 */
320 void CustTabs::set_tab_label(UI *ui, const char *label)
321 {
322 char title[128];
323 int idx = get_btn_idx(ui);
324
325 if (idx != -1) {
326 // Make a label for this tab
327 size_t tab_chars = 7, label_len = strlen(label);
328
329 if (label_len > tab_chars)
330 tab_chars = a_Utf8_end_of_char(label, tab_chars - 1) + 1;
331 snprintf(title, tab_chars + 1, "%s", label);
332 if (label_len > tab_chars)
333 snprintf(title + tab_chars, 4, "...");
334
335 // Avoid unnecessary redraws
336 if (strcmp(child(idx)->label(), title)) {
337 child(idx)->copy_label(title);
338 }
339 }
340 }
341
342
343 //----------------------------------------------------------------------------
344
345 static void win_cb (Fl_Widget *w, void *cb_data) {
346 int choice = 1;
347 CustTabs *tabs = (CustTabs*) cb_data;
348
349 if (tabs->num_tabs() > 1)
350 choice = a_Dialog_choice5("Window contains more than one tab.",
351 "Close", "Cancel", NULL, NULL, NULL);
352 if (choice == 1)
353 while (tabs->num_tabs())
354 a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(tabs->wizard()->value()));
355 }
356
357 /*
358 * Given a UI or UI child widget, return its bw.
359 */
360 BrowserWindow *a_UIcmd_get_bw_by_widget(void *v_wid)
361 {
362 BrowserWindow *bw;
363 for (int i = 0; i < a_Bw_num(); ++i) {
364 bw = a_Bw_get(i);
365 if (((UI*)bw->ui)->contains((Fl_Widget*)v_wid)) {
366 return bw;
367 }
368 }
369 return NULL;
370 }
371
372 /*
373 * Create a new UI and its associated BrowserWindow data structure.
374 * Use style from v_ui. If non-NULL it must be of type UI*.
375 */
376 BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh,
377 uint32_t xid, const void *vbw)
378 {
379 BrowserWindow *old_bw = (BrowserWindow*)vbw;
380 BrowserWindow *new_bw = NULL;
381 UI *old_ui = old_bw ? BW2UI(old_bw) : NULL;
382 Fl_Window *win;
383
384 if (ww <= 0 || wh <= 0) {
385 // Set default geometry from dillorc.
386 ww = prefs.width;
387 wh = prefs.height;
388 }
389
390 if (xid)
391 win = new Xembed(xid, ww, wh);
392 else if (prefs.buffered_drawing != 2)
393 win = new Fl_Window(ww, wh);
394 else
395 win = new Fl_Double_Window(ww, wh);
396
397 win->box(FL_NO_BOX);
398 CustTabs *DilloTabs = new CustTabs(ww, wh, 16);
399 win->end();
400
401 int focus = 1;
402 new_bw = UIcmd_tab_new(DilloTabs, old_ui, focus);
403 win->resizable(BW2UI(new_bw));
404 win->show();
405
406 if (old_bw == NULL && prefs.xpos >= 0 && prefs.ypos >= 0) {
407 // position the first window according to preferences
408 DilloTabs->window()->position(prefs.xpos, prefs.ypos);
409 }
410
411 win->callback(win_cb, DilloTabs);
412
413 return new_bw;
414 }
415
416 /*
417 * Create a new Tab button, UI and its associated BrowserWindow data
418 * structure.
419 */
420 static BrowserWindow *UIcmd_tab_new(CustTabs *tabs, UI *old_ui, int focus)
421 {
422 _MSG(" UIcmd_tab_new\n");
423
424 // Create and set the UI
425 UI *new_ui = tabs->add_new_tab(old_ui, focus);
426
427 // Now create the Dw render layout and viewport
428 FltkPlatform *platform = new FltkPlatform ();
429 Layout *layout = new Layout (platform);
430 style::Color *bgColor = style::Color::create (layout, prefs.bg_color);
431 layout->setBgColor (bgColor);
432
433 // set_render_layout() sets the proper viewport size
434 FltkViewport *viewport = new FltkViewport (0, 0, 0, 1);
435 viewport->box(FL_NO_BOX);
436 viewport->setBufferedDrawing (prefs.buffered_drawing ? true : false);
437 viewport->setDragScroll (prefs.middle_click_drags_page ? true : false);
438 layout->attachView (viewport);
439 new_ui->set_render_layout(viewport);
440 viewport->setScrollStep((int) rint(28.0 * prefs.font_factor));
441
442 // Now, create a new browser window structure
443 BrowserWindow *new_bw = a_Bw_new();
444
445 // Reference the UI from the bw
446 new_bw->ui = (void *)new_ui;
447 // Copy the layout pointer into the bw data
448 new_bw->render_layout = (void*)layout;
449
450 // WORKAROUND: see findbar_toggle()
451 new_ui->findbar_toggle(0);
452
453 return new_bw;
454 }
455
456 /*
457 * Close one browser window
458 */
459 void a_UIcmd_close_bw(void *vbw)
460 {
461 BrowserWindow *bw = (BrowserWindow *)vbw;
462 UI *ui = BW2UI(bw);
463 Layout *layout = (Layout*)bw->render_layout;
464
465 MSG("a_UIcmd_close_bw\n");
466 a_Bw_stop_clients(bw, BW_Root + BW_Img + BW_Force);
467 delete(layout);
468 if (ui->tabs()) {
469 ui->tabs()->remove_tab(ui);
470 }
471 a_Bw_free(bw);
472 }
473
474 /*
475 * Close all the browser windows
476 */
477 void a_UIcmd_close_all_bw(void *)
478 {
479 BrowserWindow *bw;
480 int choice = 1;
481
482 if (a_Bw_num() > 1)
483 choice = a_Dialog_choice5("More than one open tab or window.",
484 "Quit", "Cancel", NULL, NULL, NULL);
485 if (choice == 1)
486 while ((bw = a_Bw_get(0)))
487 a_UIcmd_close_bw((void*)bw);
488 }
489
490 /*
491 * Open a new URL in the given browser window.
492 *
493 * our custom "file:" URIs are normalized here too.
494 */
495 void a_UIcmd_open_urlstr(void *vbw, const char *urlstr)
496 {
497 char *new_urlstr;
498 DilloUrl *url;
499 int ch;
500 BrowserWindow *bw = (BrowserWindow*)vbw;
501
502 if (urlstr && *urlstr) {
503 /* Filter URL string */
504 new_urlstr = a_Url_string_strip_delimiters(urlstr);
505
506 if (!dStrncasecmp(new_urlstr, "file:", 5)) {
507 /* file URI */
508 ch = new_urlstr[5];
509 if (!ch || ch == '.') {
510 url = a_Url_new(Paths::getOldWorkingDir(), "file:");
511 } else if (ch == '~') {
512 url = a_Url_new(dGethomedir(), "file:");
513 } else {
514 url = a_Url_new(new_urlstr, "file:");
515 }
516
517 } else {
518 /* common case */
519 url = a_Url_new(new_urlstr, NULL);
520 }
521 dFree(new_urlstr);
522
523 if (url) {
524 a_UIcmd_open_url(bw, url);
525 a_Url_free(url);
526 }
527 }
528 }
529
530 /*
531 * Open a new URL in the given browser window
532 */
533 void a_UIcmd_open_url(BrowserWindow *bw, const DilloUrl *url)
534 {
535 if (url) {
536 a_Nav_push(bw, url, NULL);
537 } else {
538 // Used to start a bw with a blank screen
539 BW2UI(bw)->focus_location();
540 a_UIcmd_set_buttons_sens(bw);
541 }
542 #if 0
543 if (BW2UI(bw)->get_panelmode() == UI_TEMPORARILY_SHOW_PANELS)
544 BW2UI(bw)->set_panelmode(UI_HIDDEN);
545 a_UIcmd_focus_main_area(bw);
546 #endif
547 }
548
549 static void UIcmd_open_url_nbw(BrowserWindow *new_bw, const DilloUrl *url)
550 {
551 /* When opening a new BrowserWindow (tab or real window) we focus
552 * Location if we don't yet have an URL, main otherwise.
553 */
554 if (url) {
555 a_Nav_push(new_bw, url, NULL);
556 BW2UI(new_bw)->focus_main();
557 } else {
558 BW2UI(new_bw)->focus_location();
559 a_UIcmd_set_buttons_sens(new_bw);
560 }
561 }
562
563 /*
564 * Open a new URL in a new browser window
565 */
566 void a_UIcmd_open_url_nw(BrowserWindow *bw, const DilloUrl *url)
567 {
568 int w, h;
569 BrowserWindow *new_bw;
570
571 a_UIcmd_get_wh(bw, &w, &h);
572 new_bw = a_UIcmd_browser_window_new(w, h, 0, bw);
573
574 UIcmd_open_url_nbw(new_bw, url);
575 }
576
577 /*
578 * Open a new URL in a new tab in the same browser window
579 */
580 void a_UIcmd_open_url_nt(void *vbw, const DilloUrl *url, int focus)
581 {
582 BrowserWindow *bw = (BrowserWindow *)vbw;
583 BrowserWindow *new_bw = UIcmd_tab_new(BW2UI(bw)->tabs(),
584 bw ? BW2UI(bw) : NULL, focus);
585 UIcmd_open_url_nbw(new_bw, url);
586 }
587
588 /*
589 * Send the browser back to previous page
590 */
591 void a_UIcmd_back(void *vbw)
592 {
593 a_Nav_back((BrowserWindow*)vbw);
594 }
595
596 /*
597 * Popup the navigation menu of the Back button
598 */
599 void a_UIcmd_back_popup(void *vbw)
600 {
601 a_Menu_history_popup((BrowserWindow*)vbw, -1);
602 }
603
604 /*
605 * Send the browser to next page in the history list
606 */
607 void a_UIcmd_forw(void *vbw)
608 {
609 a_Nav_forw((BrowserWindow*)vbw);
610 }
611
612 /*
613 * Popup the navigation menu of the Forward button
614 */
615 void a_UIcmd_forw_popup(void *vbw)
616 {
617 a_Menu_history_popup((BrowserWindow*)vbw, 1);
618 }
619
620 /*
621 * Send the browser to home URL
622 */
623 void a_UIcmd_home(void *vbw)
624 {
625 a_UIcmd_open_url((BrowserWindow*)vbw, prefs.home);
626 }
627
628 /*
629 * Reload current URL
630 */
631 void a_UIcmd_reload(void *vbw)
632 {
633 a_Nav_reload((BrowserWindow*)vbw);
634 }
635
636 /*
637 * Repush current URL
638 */
639 void a_UIcmd_repush(void *vbw)
640 {
641 a_Nav_repush((BrowserWindow*)vbw);
642 }
643
644 /*
645 * Zero-delay URL redirection.
646 */
647 void a_UIcmd_redirection0(void *vbw, const DilloUrl *url)
648 {
649 a_Nav_redirection0((BrowserWindow*)vbw, url);
650 }
651
652 /*
653 * Return a suitable filename for a given URL path.
654 */
655 static char *UIcmd_make_save_filename(const char *pathstr)
656 {
657 size_t MaxLen = 64;
658 char *FileName, *newname, *o, *n;
659 const char *name, *dir = a_UIcmd_get_save_dir();
660
661 if ((name = strrchr(pathstr, '/'))) {
662 if (strlen(++name) > MaxLen) {
663 name = name + strlen(name) - MaxLen;
664 }
665 /* Replace %20 and ' ' with '_' in Filename */
666 o = n = newname = dStrdup(name);
667 for (int i = 0; o[i]; i++) {
668 *n++ = (o[i] == ' ') ? '_' :
669 (o[i] == '%' && o[i+1] == '2' && o[i+2] == '0') ?
670 i+=2, '_' : o[i];
671 }
672 *n = 0;
673 FileName = dStrconcat(dir ? dir : "", newname, NULL);
674 dFree(newname);
675 } else {
676 FileName = dStrconcat(dir ? dir : "", pathstr, NULL);
677 }
678 return FileName;
679 }
680
681 /*
682 * Get the default directory for saving files.
683 */
684 const char *a_UIcmd_get_save_dir()
685 {
686 return save_dir;
687 }
688
689 /*
690 * Set the default directory for saving files.
691 */
692 void a_UIcmd_set_save_dir(const char *dir)
693 {
694 const char *p;
695
696 if (dir && (p = strrchr(dir, '/'))) {
697 dFree(save_dir);
698 // assert a trailing '/'
699 save_dir = dStrconcat(dir, (p[1] != 0) ? "/" : "", NULL);
700 }
701 }
702
703 /*
704 * Save current URL
705 */
706 void a_UIcmd_save(void *vbw)
707 {
708 const char *name;
709 char *SuggestedName;
710 BrowserWindow *bw = (BrowserWindow *)vbw;
711 const DilloUrl *url = a_History_get_url(NAV_TOP_UIDX(bw));
712
713 if (url) {
714 a_UIcmd_set_save_dir(prefs.save_dir);
715 SuggestedName = UIcmd_make_save_filename(URL_PATH(url));
716 name = a_Dialog_save_file("Save Page as File", NULL, SuggestedName);
717 MSG("a_UIcmd_save: %s\n", name);
718 dFree(SuggestedName);
719
720 if (name) {
721 a_Nav_save_url(bw, url, name);
722 }
723 }
724 }
725
726 /*
727 * Select a file
728 */
729 const char *a_UIcmd_select_file()
730 {
731 return a_Dialog_select_file("Select a File", NULL, NULL);
732 }
733
734 /*
735 * Stop network activity on this bw.
736 * The stop button was pressed: stop page (and images) downloads.
737 */
738 void a_UIcmd_stop(void *vbw)
739 {
740 BrowserWindow *bw = (BrowserWindow *)vbw;
741
742 MSG("a_UIcmd_stop()\n");
743 a_Nav_cancel_expect(bw);
744 a_Bw_stop_clients(bw, BW_Root + BW_Img + BW_Force);
745 a_UIcmd_set_buttons_sens(bw);
746 }
747
748 /*
749 * Popup the tools menu
750 */
751 void a_UIcmd_tools(void *vbw, void *v_wid)
752 {
753 a_Menu_tools_popup((BrowserWindow*)vbw, v_wid);
754 }
755
756 /*
757 * Open URL with dialog chooser
758 */
759 void a_UIcmd_open_file(void *vbw)
760 {
761 char *name;
762 DilloUrl *url;
763
764 name = a_Dialog_open_file("Open File", NULL, "");
765
766 if (name) {
767 url = a_Url_new(name, "file:");
768 a_UIcmd_open_url((BrowserWindow*)vbw, url);
769 a_Url_free(url);
770 dFree(name);
771 }
772 }
773
774 /*
775 * Returns a newly allocated string holding a search url generated from
776 * a string of keywords (separarated by blanks) and prefs.search_url.
777 * The search string is urlencoded.
778 */
779 static char *UIcmd_make_search_str(const char *str)
780 {
781 char *keys = a_Url_encode_hex_str(str), *c = prefs.search_url;
782 Dstr *ds = dStr_sized_new(128);
783 char *search_url;
784
785 for (; *c; c++) {
786 if (*c == '%')
787 switch(*++c) {
788 case 's':
789 dStr_append(ds, keys); break;;
790 case '%':
791 dStr_append_c(ds, '%'); break;;
792 case 0:
793 MSG_WARN("search_url ends with '%%'\n"); c--; break;;
794 default:
795 MSG_WARN("illegal specifier '%%%c' in search_url\n", *c);
796 }
797 else
798 dStr_append_c(ds, *c);
799 }
800 dFree(keys);
801
802 search_url = ds->str;
803 dStr_free(ds, 0);
804 return search_url;
805 }
806
807 /*
808 * Get a query from a dialog and open it
809 */
810 void a_UIcmd_search_dialog(void *vbw)
811 {
812 const char *query;
813
814 if ((query = a_Dialog_input("Search the Web:"))) {
815 char *url_str = UIcmd_make_search_str(query);
816 a_UIcmd_open_urlstr(vbw, url_str);
817 dFree(url_str);
818 }
819 }
820
821 /*
822 * Get password for user
823 */
824 const char *a_UIcmd_get_passwd(const char *user)
825 {
826 const char *passwd;
827 char *prompt = dStrconcat("Password for user \"", user, "\"", NULL);
828 passwd = a_Dialog_passwd(prompt);
829 dFree(prompt);
830 return passwd;
831 }
832
833 /*
834 * Save link URL
835 */
836 void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url)
837 {
838 const char *name;
839 char *SuggestedName;
840
841 a_UIcmd_set_save_dir(prefs.save_dir);
842
843 SuggestedName = UIcmd_make_save_filename(URL_STR(url));
844 name = a_Dialog_save_file("Save Link as File", NULL, SuggestedName);
845 MSG("a_UIcmd_save_link: %s\n", name);
846 dFree(SuggestedName);
847
848 if (name) {
849 a_Nav_save_url(bw, url, name);
850 }
851 }
852
853 /*
854 * Request the bookmarks page
855 */
856 void a_UIcmd_book(void *vbw)
857 {
858 DilloUrl *url = a_Url_new("dpi:/bm/", NULL);
859 a_UIcmd_open_url((BrowserWindow*)vbw, url);
860 a_Url_free(url);
861 }
862
863 /*
864 * Add a bookmark for a certain URL
865 */
866 void a_UIcmd_add_bookmark(BrowserWindow *bw, const DilloUrl *url)
867 {
868 a_Bookmarks_add(bw, url);
869 }
870
871
872 /*
873 * Popup the page menu
874 */
875 void a_UIcmd_page_popup(void *vbw, bool_t has_bugs, void *v_cssUrls)
876 {
877 BrowserWindow *bw = (BrowserWindow*)vbw;
878 const DilloUrl *url = a_History_get_url(NAV_TOP_UIDX(bw));
879 a_Menu_page_popup(bw, url, has_bugs, v_cssUrls);
880 }
881
882 /*
883 * Popup the link menu
884 */
885 void a_UIcmd_link_popup(void *vbw, const DilloUrl *url)
886 {
887 a_Menu_link_popup((BrowserWindow*)vbw, url);
888 }
889
890 /*
891 * Pop up the image menu
892 */
893 void a_UIcmd_image_popup(void *vbw, const DilloUrl *url, bool_t loaded_img,
894 DilloUrl *page_url, DilloUrl *link_url)
895 {
896 a_Menu_image_popup((BrowserWindow*)vbw, url, loaded_img, page_url,link_url);
897 }
898
899 /*
900 * Pop up the form menu
901 */
902 void a_UIcmd_form_popup(void *vbw, const DilloUrl *url, void *vform,
903 bool_t showing_hiddens)
904 {
905 a_Menu_form_popup((BrowserWindow*)vbw, url, vform, showing_hiddens);
906 }
907
908 /*
909 * Pop up the file menu
910 */
911 void a_UIcmd_file_popup(void *vbw, void *v_wid)
912 {
913 a_Menu_file_popup((BrowserWindow*)vbw, v_wid);
914 }
915
916 /*
917 * Copy url string to paste buffer
918 */
919 void a_UIcmd_copy_urlstr(BrowserWindow *bw, const char *urlstr)
920 {
921 Layout *layout = (Layout*)bw->render_layout;
922 layout->copySelection(urlstr);
923 }
924
925 /*
926 * Ask the vsource dpi to show this URL's source
927 */
928 void a_UIcmd_view_page_source(BrowserWindow *bw, const DilloUrl *url)
929 {
930 char *buf;
931 int buf_size;
932 Dstr *dstr_url;
933 DilloUrl *vs_url;
934 static int post_id = 0;
935 char tag[8];
936
937 if (a_Nav_get_buf(url, &buf, &buf_size)) {
938 a_Nav_set_vsource_url(url);
939 dstr_url = dStr_new("dpi:/vsource/:");
940 dStr_append(dstr_url, URL_STR(url));
941 if (URL_FLAGS(url) & URL_Post) {
942 /* append a custom string to differentiate POST URLs */
943 post_id = (post_id < 9999) ? post_id + 1 : 0;
944 snprintf(tag, 8, "_%.4d", post_id);
945 dStr_append(dstr_url, tag);
946 }
947 vs_url = a_Url_new(dstr_url->str, NULL);
948 a_UIcmd_open_url_nt(bw, vs_url, 1);
949 a_Url_free(vs_url);
950 dStr_free(dstr_url, 1);
951 a_Nav_unref_buf(url);
952 }
953 }
954
955 /*
956 * Show the browser window's HTML errors in a text window
957 */
958 void a_UIcmd_view_page_bugs(void *vbw)
959 {
960 BrowserWindow *bw = (BrowserWindow*)vbw;
961
962 if (bw->num_page_bugs > 0) {
963 a_Dialog_text_window(bw->page_bugs->str, "Detected HTML errors");
964 } else {
965 a_Dialog_msg("Zero detected HTML errors!");
966 }
967 }
968
969 /*
970 * Popup the bug meter menu
971 */
972 void a_UIcmd_bugmeter_popup(void *vbw)
973 {
974 BrowserWindow *bw = (BrowserWindow*)vbw;
975
976 a_Menu_bugmeter_popup(bw, a_History_get_url(NAV_TOP_UIDX(bw)));
977 }
978
979 /*
980 * Make a list of URL indexes for the history popup
981 * based on direction (-1 = back, 1 = forward)
982 */
983 int *a_UIcmd_get_history(BrowserWindow *bw, int direction)
984 {
985 int i, j, n;
986 int *hlist;
987
988 // Count the number of URLs
989 i = a_Nav_stack_ptr(bw) + direction;
990 for (n = 0 ; i >= 0 && i < a_Nav_stack_size(bw); i+=direction)
991 ++n;
992 hlist = dNew(int, n + 1);
993
994 // Fill the list
995 i = a_Nav_stack_ptr(bw) + direction;
996 for (j = 0 ; i >= 0 && i < a_Nav_stack_size(bw); i+=direction, j += 1) {
997 hlist[j] = NAV_UIDX(bw,i);
998 }
999 hlist[j] = -1;
1000
1001 return hlist;
1002 }
1003
1004 /*
1005 * Jump to a certain URL in the navigation stack.
1006 */
1007 void a_UIcmd_nav_jump(BrowserWindow *bw, int offset, int new_bw)
1008 {
1009 a_Nav_jump(bw, offset, new_bw);
1010 }
1011
1012 // UI binding functions -------------------------------------------------------
1013
1014 /*
1015 * Return browser window width and height
1016 */
1017 void a_UIcmd_get_wh(BrowserWindow *bw, int *w, int *h)
1018 {
1019 *w = BW2UI(bw)->w();
1020 *h = BW2UI(bw)->h();
1021 _MSG("a_UIcmd_wh: w=%d, h=%d\n", *w, *h);
1022 }
1023
1024 /*
1025 * Get the scroll position (x, y offset pair)
1026 */
1027 void a_UIcmd_get_scroll_xy(BrowserWindow *bw, int *x, int *y)
1028 {
1029 Layout *layout = (Layout*)bw->render_layout;
1030
1031 if (layout) {
1032 *x = layout->getScrollPosX();
1033 *y = layout->getScrollPosY();
1034 }
1035 }
1036
1037 /*
1038 * Set the scroll position ({x, y} offset pair)
1039 */
1040 void a_UIcmd_set_scroll_xy(BrowserWindow *bw, int x, int y)
1041 {
1042 Layout *layout = (Layout*)bw->render_layout;
1043
1044 if (layout) {
1045 layout->scrollTo(HPOS_LEFT, VPOS_TOP, x, y, 0, 0);
1046 }
1047 }
1048
1049 /*
1050 * Set the scroll position by fragment (from URL)
1051 */
1052 void a_UIcmd_set_scroll_by_fragment(BrowserWindow *bw, const char *f)
1053 {
1054 Layout *layout = (Layout*)bw->render_layout;
1055
1056 if (layout && f) {
1057 layout->setAnchor(f);
1058 }
1059 }
1060
1061 /*
1062 * Pass scrolling command to dw.
1063 */
1064 void a_UIcmd_scroll(BrowserWindow *bw, int icmd)
1065 {
1066 Layout *layout = (Layout*)bw->render_layout;
1067
1068 if (layout) {
1069 typedef struct {
1070 KeysCommand_t keys_cmd;
1071 ScrollCommand dw_cmd;
1072 } mapping_t;
1073
1074 const mapping_t map[] = {
1075 {KEYS_SCREEN_UP, SCREEN_UP_CMD},
1076 {KEYS_SCREEN_DOWN, SCREEN_DOWN_CMD},
1077 {KEYS_LINE_UP, LINE_UP_CMD},
1078 {KEYS_LINE_DOWN, LINE_DOWN_CMD},
1079 {KEYS_LEFT, LEFT_CMD},
1080 {KEYS_RIGHT, RIGHT_CMD},
1081 {KEYS_TOP, TOP_CMD},
1082 {KEYS_BOTTOM, BOTTOM_CMD},
1083 };
1084 KeysCommand_t keycmd = (KeysCommand_t)icmd;
1085
1086 for (uint_t i = 0; i < (sizeof(map)/sizeof(mapping_t)); i++) {
1087 if (keycmd == map[i].keys_cmd) {
1088 layout->scroll(map[i].dw_cmd);
1089 break;
1090 }
1091 }
1092 }
1093 }
1094
1095 /*
1096 * Get location's text
1097 */
1098 char *a_UIcmd_get_location_text(BrowserWindow *bw)
1099 {
1100 return dStrdup(BW2UI(bw)->get_location());
1101 }
1102
1103 /*
1104 * Set location's text
1105 */
1106 void a_UIcmd_set_location_text(void *vbw, const char *text)
1107 {
1108 BrowserWindow *bw = (BrowserWindow*)vbw;
1109 BW2UI(bw)->set_location(text);
1110 }
1111
1112 /*
1113 * Set the page progress bar
1114 * cmd: 0 Deactivate, 1 Update, 2 Clear
1115 */
1116 void a_UIcmd_set_page_prog(BrowserWindow *bw, size_t nbytes, int cmd)
1117 {
1118 BW2UI(bw)->set_page_prog(nbytes, cmd);
1119 }
1120
1121 /*
1122 * Set the images progress bar
1123 * cmd: 0 Deactivate, 1 Update, 2 Clear
1124 */
1125 void a_UIcmd_set_img_prog(BrowserWindow *bw, int n_img, int t_img, int cmd)
1126 {
1127 BW2UI(bw)->set_img_prog(n_img, t_img, cmd);
1128 #if 0
1129 if (!cmd)
1130 a_UIcmd_close_bw(bw);
1131 #endif
1132 }
1133
1134 /*
1135 * Set the bug meter progress label
1136 */
1137 void a_UIcmd_set_bug_prog(BrowserWindow *bw, int n_bug)
1138 {
1139 BW2UI(bw)->set_bug_prog(n_bug);
1140 }
1141
1142 /*
1143 * Set the page title in the window titlebar and tab label.
1144 * (Update window titlebar for the current tab only)
1145 */
1146 void a_UIcmd_set_page_title(BrowserWindow *bw, const char *label)
1147 {
1148 const int size = 128;
1149 char title[size];
1150
1151 if (a_UIcmd_get_bw_by_widget(BW2UI(bw)->tabs()->wizard()->value()) == bw) {
1152 // This is the focused bw, set window title
1153 if (snprintf(title, size, "Dillo: %s", label) >= size) {
1154 uint_t i = MIN(size - 4, 1 + a_Utf8_end_of_char(title, size - 8));
1155 snprintf(title + i, 4, "...");
1156 }
1157 BW2UI(bw)->copy_label(title);
1158 BW2UI(bw)->window()->copy_label(title);
1159 }
1160 BW2UI(bw)->tabs()->set_tab_label(BW2UI(bw), label);
1161 }
1162
1163 /*
1164 * Set a printf-like status string on the bottom of the dillo window.
1165 * Beware: The safe way to set an arbitrary string is
1166 * a_UIcmd_set_msg(bw, "%s", str)
1167 */
1168 void a_UIcmd_set_msg(BrowserWindow *bw, const char *format, ...)
1169 {
1170 va_list argp;
1171 Dstr *ds = dStr_sized_new(128);
1172 va_start(argp, format);
1173 dStr_vsprintf(ds, format, argp);
1174 va_end(argp);
1175 BW2UI(bw)->set_status(ds->str);
1176 dStr_free(ds, 1);
1177 }
1178
1179 /*
1180 * Set the sensitivity of back/forw/stop buttons.
1181 */
1182 void a_UIcmd_set_buttons_sens(BrowserWindow *bw)
1183 {
1184 int sens;
1185
1186 // Stop
1187 sens = (dList_length(bw->ImageClients) || dList_length(bw->RootClients));
1188 BW2UI(bw)->button_set_sens(UI_STOP, sens);
1189 // Back
1190 sens = (a_Nav_stack_ptr(bw) > 0);
1191 BW2UI(bw)->button_set_sens(UI_BACK, sens);
1192 // Forward
1193 sens = (a_Nav_stack_ptr(bw) < a_Nav_stack_size(bw) - 1 &&
1194 !bw->nav_expecting);
1195 BW2UI(bw)->button_set_sens(UI_FORW, sens);
1196 }
1197
1198 /*
1199 * Keep track of mouse pointer over a link.
1200 */
1201 void a_UIcmd_set_pointer_on_link(BrowserWindow *bw, int flag)
1202 {
1203 BW2UI(bw)->pointerOnLink(flag);
1204 }
1205
1206 /*
1207 * Is the mouse pointer over a link?
1208 */
1209 int a_UIcmd_pointer_on_link(BrowserWindow *bw)
1210 {
1211 return BW2UI(bw)->pointerOnLink();
1212 }
1213
1214 /*
1215 * Toggle control panel
1216 */
1217 void a_UIcmd_panels_toggle(BrowserWindow *bw)
1218 {
1219 BW2UI(bw)->panels_toggle();
1220 }
1221
1222 /*
1223 * Search for next/previous occurrence of key.
1224 */
1225 void a_UIcmd_findtext_search(BrowserWindow *bw, const char *key,
1226 int case_sens, int backward)
1227 {
1228 Layout *l = (Layout *)bw->render_layout;
1229
1230 switch (l->search(key, case_sens, backward)) {
1231 case FindtextState::RESTART:
1232 a_UIcmd_set_msg(bw, backward?"Top reached; restarting from the bottom."
1233 :"Bottom reached; restarting from the top.");
1234 break;
1235 case FindtextState::NOT_FOUND:
1236 a_UIcmd_set_msg(bw, "\"%s\" not found.", key);
1237 break;
1238 case FindtextState::SUCCESS:
1239 default:
1240 a_UIcmd_set_msg(bw, "");
1241 }
1242 }
1243
1244 /*
1245 * Reset text search state.
1246 */
1247 void a_UIcmd_findtext_reset(BrowserWindow *bw)
1248 {
1249 Layout *l = (Layout *)bw->render_layout;
1250 l->resetSearch();
1251
1252 a_UIcmd_set_msg(bw, "");
1253 }
1254
1255 /*
1256 * Tell the UI to hide/show the findbar
1257 */
1258 void a_UIcmd_findbar_toggle(BrowserWindow *bw, int on)
1259 {
1260 BW2UI(bw)->findbar_toggle(on);
1261 }
1262
1263 /*
1264 * Focus the rendered area.
1265 */
1266 void a_UIcmd_focus_main_area(BrowserWindow *bw)
1267 {
1268 BW2UI(bw)->focus_main();
1269 }
1270
1271 /*
1272 * Focus the location bar.
1273 */
1274 void a_UIcmd_focus_location(void *vbw)
1275 {
1276 BrowserWindow *bw = (BrowserWindow*)vbw;
1277 BW2UI(bw)->focus_location();
1278 }
1279
0 #ifndef __UICMD_HH__
1 #define __UICMD_HH__
2
3 #include "bw.h"
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif /* __cplusplus */
8
9
10 BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh,
11 uint32_t xid, const void *v_bw);
12 BrowserWindow *a_UIcmd_get_bw_by_widget(void *v_wid);
13 void a_UIcmd_send_event_to_tabs_by_wid(int e, void *v_wid);
14 void a_UIcmd_open_urlstr(void *vbw, const char *urlstr);
15 void a_UIcmd_open_url(BrowserWindow *bw, const DilloUrl *url);
16 void a_UIcmd_open_url_nw(BrowserWindow *bw, const DilloUrl *url);
17 void a_UIcmd_open_url_nt(void *vbw, const DilloUrl *url, int focus);
18 void a_UIcmd_back(void *vbw);
19 void a_UIcmd_back_popup(void *vbw);
20 void a_UIcmd_forw(void *vbw);
21 void a_UIcmd_forw_popup(void *vbw);
22 void a_UIcmd_home(void *vbw);
23 void a_UIcmd_reload(void *vbw);
24 void a_UIcmd_repush(void *vbw);
25 void a_UIcmd_redirection0(void *vbw, const DilloUrl *url);
26 void a_UIcmd_save(void *vbw);
27 void a_UIcmd_stop(void *vbw);
28 void a_UIcmd_tools(void *vbw, void *v_wid);
29 void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url);
30 void a_UIcmd_open_file(void *vbw);
31 const char *a_UIcmd_select_file();
32 void a_UIcmd_search_dialog(void *vbw);
33 const char *a_UIcmd_get_passwd(const char *user);
34 void a_UIcmd_book(void *vbw);
35 void a_UIcmd_add_bookmark(BrowserWindow *bw, const DilloUrl *url);
36 void a_UIcmd_panels_toggle(BrowserWindow *bw);
37 void a_UIcmd_findtext_dialog(BrowserWindow *bw);
38 void a_UIcmd_findtext_search(BrowserWindow *bw,const char *key,int case_sens,
39 int backwards);
40 void a_UIcmd_findtext_reset(BrowserWindow *bw);
41 void a_UIcmd_findbar_toggle(BrowserWindow *bw, int on);
42 void a_UIcmd_focus_main_area(BrowserWindow *bw);
43 void a_UIcmd_focus_location(void *vbw);
44 void a_UIcmd_page_popup(void *vbw, bool_t has_bugs, void *v_cssUrls);
45 void a_UIcmd_link_popup(void *vbw, const DilloUrl *url);
46 void a_UIcmd_image_popup(void *vbw, const DilloUrl *url, bool_t loaded_img,
47 DilloUrl *page_url, DilloUrl *link_url);
48 void a_UIcmd_form_popup(void *vbw, const DilloUrl *url, void *vform,
49 bool_t showing_hiddens);
50 void a_UIcmd_file_popup(void *vbw, void *v_wid);
51 void a_UIcmd_copy_urlstr(BrowserWindow *bw, const char *urlstr);
52 void a_UIcmd_view_page_source(BrowserWindow *bw, const DilloUrl *url);
53 void a_UIcmd_view_page_bugs(void *vbw);
54 void a_UIcmd_bugmeter_popup(void *vbw);
55 int *a_UIcmd_get_history(BrowserWindow *bw, int direction);
56 void a_UIcmd_nav_jump(BrowserWindow *bw, int offset, int new_bw);
57
58 void a_UIcmd_close_bw(void *vbw);
59 void a_UIcmd_close_all_bw(void *p);
60
61 const char *a_UIcmd_get_save_dir();
62 void a_UIcmd_set_save_dir(const char *dir);
63
64
65 // UI binding functions -------------------------------------------------------
66
67 void a_UIcmd_get_wh(BrowserWindow *bw, int *w, int *h);
68 void a_UIcmd_get_scroll_xy(BrowserWindow *bw, int *x, int *y);
69 void a_UIcmd_set_scroll_xy(BrowserWindow *bw, int x, int y);
70 void a_UIcmd_set_scroll_by_fragment(BrowserWindow *bw, const char *f);
71 void a_UIcmd_scroll(BrowserWindow *bw, int icmd);
72 char *a_UIcmd_get_location_text(BrowserWindow *bw);
73 void a_UIcmd_set_location_text(void *vbw, const char *text);
74 void a_UIcmd_set_page_prog(BrowserWindow *bw, size_t nbytes, int cmd);
75 void a_UIcmd_set_img_prog(BrowserWindow *bw, int n_img, int t_img, int cmd);
76 void a_UIcmd_set_bug_prog(BrowserWindow *bw, int n_bug);
77 void a_UIcmd_set_page_title(BrowserWindow *bw, const char *label);
78 void a_UIcmd_set_msg(BrowserWindow *bw, const char *format, ...);
79 void a_UIcmd_set_buttons_sens(BrowserWindow *bw);
80 void a_UIcmd_set_pointer_on_link(BrowserWindow *bw, int flag);
81 int a_UIcmd_pointer_on_link(BrowserWindow *bw);
82
83 #ifdef __cplusplus
84 }
85 #endif /* __cplusplus */
86
87 #endif // __UICMD_HH__
00 /*
11 * File: url.c
22 *
3 * Copyright (C) 2001 Jorge Arellano Cid <jcid@dillo.org>
4 * 2001 Livio Baldini Soares <livio@linux.ime.usp.br>
3 * Copyright (C) 2001-2009 Jorge Arellano Cid <jcid@dillo.org>
54 *
65 * This program is free software; you can redistribute it and/or modify
76 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
7 * the Free Software Foundation; either version 3 of the License, or
98 * (at your option) any later version.
109 */
1110
1716 */
1817
1918 /*
20 * Regular Expression as given in RFC2396 for URL parsing.
19 * Regular Expression as given in RFC3986 for URL parsing.
2120 *
2221 * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
2322 * 12 3 4 5 6 7 8 9
2726 * path = $5
2827 * query = $7
2928 * fragment = $9
30 */
31
32
33 #include <stdio.h>
29 *
30 *
31 * RFC-2396 BNF:
32 *
33 * absoluteURI = scheme ":" (hier_part | opaque_part)
34 * hier_part = (net_path | abs_path) ["?" query]
35 * net_path = "//" authority[abs_path]
36 * abs_path = "/" path_segments
37 *
38 * Notes:
39 * - "undefined" means "preceeding separator does not appear".
40 * - path is never "undefined" though it may be "empty".
41 */
42
3443 #include <stdlib.h>
3544 #include <string.h>
3645 #include <ctype.h>
37 #include <glib.h>
3846
3947 #include "url.h"
40
41 /*#define DEBUG_LEVEL 2 */
42 #include "debug.h"
43
48 #include "msg.h"
49
50 static const char *HEX = "0123456789ABCDEF";
51
52 /* URL-field compare methods */
53 #define URL_STR_FIELD_CMP(s1,s2) \
54 (s1) && (s2) ? strcmp(s1,s2) : !(s1) && !(s2) ? 0 : (s1) ? 1 : -1
55 #define URL_STR_FIELD_I_CMP(s1,s2) \
56 (s1) && (s2) ? dStrcasecmp(s1,s2) : !(s1) && !(s2) ? 0 : (s1) ? 1 : -1
4457
4558 /*
4659 * Return the url as a string.
47 * (initializing 'url_string' camp if necessary)
48 */
49 gchar *a_Url_str(const DilloUrl *u)
60 * (initializing 'url_string' field if necessary)
61 */
62 char *a_Url_str(const DilloUrl *u)
5063 {
5164 /* Internal url handling IS transparent to the caller */
5265 DilloUrl *url = (DilloUrl *) u;
5366
54 g_return_val_if_fail (url != NULL, NULL);
67 dReturn_val_if_fail (url != NULL, NULL);
5568
5669 if (!url->url_string) {
57 url->url_string = g_string_sized_new(60);
58 g_string_sprintf(
70 url->url_string = dStr_sized_new(60);
71 dStr_sprintf(
5972 url->url_string, "%s%s%s%s%s%s%s%s%s%s",
6073 url->scheme ? url->scheme : "",
6174 url->scheme ? ":" : "",
6275 url->authority ? "//" : "",
6376 url->authority ? url->authority : "",
64 (url->path && url->path[0] != '/' && url->authority) ? "/" : "",
77 // (url->path && url->path[0] != '/' && url->authority) ? "/" : "",
78 (url->authority && (!url->path || *url->path != '/')) ? "/" : "",
6579 url->path ? url->path : "",
6680 url->query ? "?" : "",
6781 url->query ? url->query : "",
7488
7589 /*
7690 * Return the hostname as a string.
77 * (initializing 'hostname' and 'port' camps if necessary)
91 * (initializing 'hostname' and 'port' fields if necessary)
7892 * Note: a similar approach can be taken for user:password auth.
7993 */
80 const gchar *a_Url_hostname(const DilloUrl *u)
81 {
82 gchar *p;
94 const char *a_Url_hostname(const DilloUrl *u)
95 {
96 char *p;
8397 /* Internal url handling IS transparent to the caller */
8498 DilloUrl *url = (DilloUrl *) u;
8599
86100 if (!url->hostname && url->authority) {
87 if ((p = strchr(url->authority, ':'))) {
88 url->port = strtol(p + 1, NULL, 10);
89 url->hostname = g_strndup(url->authority,(guint)(p - url->authority));
90 } else
91 url->hostname = url->authority;
101 if (url->authority[0] == '[' && (p = strchr(url->authority, ']'))) {
102 /* numeric ipv6 address, strip the brackets */
103 url->hostname = dStrndup(url->authority + 1,
104 (uint_t)(p - url->authority - 1));
105 if ((p = strchr(p, ':'))) {
106 url->port = strtol(p + 1, NULL, 10);
107 }
108 } else {
109 /* numeric ipv4 or hostname */
110 if ((p = strchr(url->authority, ':'))) {
111 url->port = strtol(p + 1, NULL, 10);
112 url->hostname = dStrndup(url->authority,
113 (uint_t)(p - url->authority));
114 } else {
115 url->hostname = url->authority;
116 }
117 }
92118 }
93119
94120 return url->hostname;
98124 * Create a DilloUrl object and initialize it.
99125 * (buffer, scheme, authority, path, query and fragment).
100126 */
101 static DilloUrl *Url_object_new(const gchar *uri_str)
127 static DilloUrl *Url_object_new(const char *uri_str)
102128 {
103129 DilloUrl *url;
104 gchar *s, *p;
105
106 g_return_val_if_fail (uri_str != NULL, NULL);
107
108 url = g_new0(DilloUrl, 1);
130 char *s, *p;
131
132 dReturn_val_if_fail (uri_str != NULL, NULL);
133
134 url = dNew0(DilloUrl, 1);
109135
110136 /* remove leading & trailing space from buffer */
111 for (p = (gchar *)uri_str; isspace(*p); ++p);
112 url->buffer = g_strchomp(g_strdup(p));
113
114 s = (gchar *) url->buffer;
137 url->buffer = dStrstrip(dStrdup(uri_str));
138
139 s = (char *) url->buffer;
115140 p = strpbrk(s, ":/?#");
116141 if (p && p[0] == ':' && p > s) { /* scheme */
117142 *p = 0;
148173 s = p + 1;
149174 url->query = s;
150175 p = strpbrk(s, "#");
176 url->flags |= URL_Get;
151177 }
152178 if (p && p[0] == '#') { /* fragment */
153179 *p = 0;
160186
161187 /*
162188 * Free a DilloUrl
189 * Do nothing if the argument is NULL
163190 */
164191 void a_Url_free(DilloUrl *url)
165192 {
166193 if (url) {
167194 if (url->url_string)
168 g_string_free(url->url_string, TRUE);
195 dStr_free(url->url_string, TRUE);
169196 if (url->hostname != url->authority)
170 g_free((gchar *)url->hostname);
171 g_free((gchar *)url->buffer);
172 g_free((gchar *)url->data);
173 g_free((gchar *)url->alt);
174 g_free(url);
175 }
176 }
177
178 /*
179 * Resolve the URL as RFC2396 suggests.
180 */
181 static GString *Url_resolve_relative(const gchar *RelStr,
182 DilloUrl *BaseUrlPar,
183 const gchar *BaseStr)
184 {
185 gchar *p, *s, *e;
186 gint i;
187 GString *SolvedUrl, *Path;
197 dFree((char *)url->hostname);
198 dFree((char *)url->buffer);
199 dStr_free(url->data, 1);
200 dFree((char *)url->alt);
201 dFree(url);
202 }
203 }
204
205 /*
206 * Resolve the URL as RFC3986 suggests.
207 */
208 static Dstr *Url_resolve_relative(const char *RelStr,
209 DilloUrl *BaseUrlPar,
210 const char *BaseStr)
211 {
212 char *p, *s, *e;
213 int i;
214 Dstr *SolvedUrl, *Path;
188215 DilloUrl *RelUrl, *BaseUrl = NULL;
189216
190217 /* parse relative URL */
197224 BaseUrl = Url_object_new(BaseStr);
198225 }
199226
200 SolvedUrl = g_string_sized_new(64);
201 Path = g_string_sized_new(64);
202
203 /* path empty && scheme, authority and query undefined */
204 if (!RelUrl->path && !RelUrl->scheme &&
205 !RelUrl->authority && !RelUrl->query) {
206 g_string_append(SolvedUrl, BaseStr);
207
227 SolvedUrl = dStr_sized_new(64);
228 Path = dStr_sized_new(64);
229
230 /* path empty && scheme and authority undefined */
231 if (!RelUrl->path && !RelUrl->scheme && !RelUrl->authority) {
232 dStr_append(SolvedUrl, BaseStr);
233 if ((p = strchr(SolvedUrl->str, '#')))
234 dStr_truncate(SolvedUrl, p - SolvedUrl->str);
235
236 if (RelUrl->query) { /* query */
237 if (BaseUrl->query)
238 dStr_truncate(SolvedUrl, BaseUrl->query - BaseUrl->buffer - 1);
239 dStr_append_c(SolvedUrl, '?');
240 dStr_append(SolvedUrl, RelUrl->query);
241 }
208242 if (RelUrl->fragment) { /* fragment */
209 if (BaseUrl->fragment)
210 g_string_truncate(SolvedUrl, BaseUrl->fragment-BaseUrl->buffer-1);
211 g_string_append_c(SolvedUrl, '#');
212 g_string_append(SolvedUrl, RelUrl->fragment);
243 dStr_append_c(SolvedUrl, '#');
244 dStr_append(SolvedUrl, RelUrl->fragment);
213245 }
214246 goto done;
215247
216248 } else if (RelUrl->scheme) { /* scheme */
217 g_string_append(SolvedUrl, RelStr);
249 dStr_append(SolvedUrl, RelStr);
218250 goto done;
219251
220252 } else if (RelUrl->authority) { /* authority */
221 /* Set the Path buffer and goto "STEP 7"; */
253 // Set the Path buffer and goto "STEP 7";
222254 if (RelUrl->path)
223 g_string_append(Path, RelUrl->path);
224
225 } else if (RelUrl->path && RelUrl->path[0] == '/') { /* path */
226 g_string_append(Path, RelUrl->path);
255 dStr_append(Path, RelUrl->path);
227256
228257 } else {
229 /* solve relative path */
230 if (BaseUrl->path) {
231 g_string_append(Path, BaseUrl->path);
232 for (i = Path->len; --i >= 0 && Path->str[i] != '/'; );
258 if (RelUrl->path && RelUrl->path[0] == '/') { /* absolute path */
259 ; /* Ignore BaseUrl path */
260 } else if (BaseUrl->path) { /* relative path */
261 dStr_append(Path, BaseUrl->path);
262 for (i = Path->len; --i >= 0 && Path->str[i] != '/'; ) ;
233263 if (Path->str[i] == '/')
234 g_string_truncate(Path, ++i);
264 dStr_truncate(Path, ++i);
235265 }
236266 if (RelUrl->path)
237 g_string_append(Path, RelUrl->path);
238
239 /* erase "./" */
267 dStr_append(Path, RelUrl->path);
268
269 // erase "./"
240270 while ((p=strstr(Path->str, "./")) &&
241271 (p == Path->str || p[-1] == '/'))
242 g_string_erase(Path, p - Path->str, 2);
243 /* erase last "." */
272 dStr_erase(Path, p - Path->str, 2);
273 // erase last "."
244274 if (Path->len && Path->str[Path->len - 1] == '.' &&
245275 (Path->len == 1 || Path->str[Path->len - 2] == '/'))
246 g_string_truncate(Path, Path->len - 1);
247
248 /* erase "<segment>/../" and "<segment>/.." */
276 dStr_truncate(Path, Path->len - 1);
277
278 // erase "<segment>/../" and "<segment>/.."
249279 s = p = Path->str;
250280 while ( (p = strstr(p, "/..")) != NULL ) {
251 if ((p[3] == '/' || !p[3]) && (p - s)) { /* "/../" | "/.." */
252
253 for (e = p + 3 ; p[-1] != '/' && p > s; --p);
254 if (p[0] != '.' || p[1] != '.' || p[2] != '/') {
255 g_string_erase(Path, p - Path->str, e - p + (*e != 0));
256 p -= (p > Path->str);
257 } else
258 p = e;
281 if (p[3] == '/' || !p[3]) { // "/../" | "/.."
282 for (e = p + 3 ; p > s && p[-1] != '/'; --p) ;
283 dStr_erase(Path, p - Path->str, e - p + (p > s && *e != 0));
284 p -= (p > Path->str);
259285 } else
260286 p += 3;
261287 }
266292
267293 /* scheme */
268294 if (BaseUrl->scheme) {
269 g_string_append(SolvedUrl, BaseUrl->scheme);
270 g_string_append_c(SolvedUrl, ':');
295 dStr_append(SolvedUrl, BaseUrl->scheme);
296 dStr_append_c(SolvedUrl, ':');
271297 }
272298
273299 /* authority */
274300 if (RelUrl->authority) {
275 g_string_append(SolvedUrl, "//");
276 g_string_append(SolvedUrl, RelUrl->authority);
301 dStr_append(SolvedUrl, "//");
302 dStr_append(SolvedUrl, RelUrl->authority);
277303 } else if (BaseUrl->authority) {
278 g_string_append(SolvedUrl, "//");
279 g_string_append(SolvedUrl, BaseUrl->authority);
304 dStr_append(SolvedUrl, "//");
305 dStr_append(SolvedUrl, BaseUrl->authority);
280306 }
281307
282308 /* path */
283309 if ((RelUrl->authority || BaseUrl->authority) &&
284310 ((Path->len == 0 && (RelUrl->query || RelUrl->fragment)) ||
285311 (Path->len && Path->str[0] != '/')))
286 g_string_append_c(SolvedUrl, '/'); /* hack? */
287 g_string_append(SolvedUrl, Path->str);
312 dStr_append_c(SolvedUrl, '/'); /* hack? */
313 dStr_append(SolvedUrl, Path->str);
288314
289315 /* query */
290316 if (RelUrl->query) {
291 g_string_append_c(SolvedUrl, '?');
292 g_string_append(SolvedUrl, RelUrl->query);
317 dStr_append_c(SolvedUrl, '?');
318 dStr_append(SolvedUrl, RelUrl->query);
293319 }
294320
295321 /* fragment */
296322 if (RelUrl->fragment) {
297 g_string_append_c(SolvedUrl, '#');
298 g_string_append(SolvedUrl, RelUrl->fragment);
323 dStr_append_c(SolvedUrl, '#');
324 dStr_append(SolvedUrl, RelUrl->fragment);
299325 }
300326
301327 done:
302 g_string_free(Path, TRUE);
328 dStr_free(Path, TRUE);
303329 a_Url_free(RelUrl);
304330 if (BaseUrl != BaseUrlPar)
305331 a_Url_free(BaseUrl);
319345 * fragment = "part2"
320346 * hostname = "dillo.sf.net"
321347 * port = 8080
322 * flags = 0
323 * data = NULL
348 * flags = URL_Get
349 * data = Dstr * ("")
324350 * alt = NULL
325351 * ismap_url_len = 0
326 * scrolling_position = 0
327352 * }
328353 *
329354 * Return NULL if URL is badly formed.
330355 */
331 DilloUrl* a_Url_new(const gchar *url_str, const gchar *base_url,
332 gint flags, gint32 posx, gint32 posy)
356 DilloUrl* a_Url_new(const char *url_str, const char *base_url)
333357 {
334358 DilloUrl *url;
335 gchar *urlstring, *p, *new_str = NULL;
336 GString *SolvedUrl;
337 gint n_ic, n_ic_spc;
338
339 g_return_val_if_fail (url_str != NULL, NULL);
359 char *urlstr = (char *)url_str; /* auxiliar variable, don't free */
360 char *p, *str1 = NULL, *str2 = NULL;
361 Dstr *SolvedUrl;
362 int i, n_ic, n_ic_spc;
363
364 dReturn_val_if_fail (url_str != NULL, NULL);
340365
341366 /* Count illegal characters (0x00-0x1F, 0x7F and space) */
342 urlstring = (gchar *)url_str;
343367 n_ic = n_ic_spc = 0;
344 for (p = urlstring; *p; p++) {
368 for (p = (char*)url_str; *p; p++) {
345369 n_ic_spc += (*p == ' ') ? 1 : 0;
346370 n_ic += (*p != ' ' && *p > 0x1F && *p != 0x7F) ? 0 : 1;
347371 }
348372 if (n_ic) {
349 /* Strip illegal characters (they could also be encoded).
350 * There's no standard for illegal chars; we chose to strip. */
351 for (p = new_str = g_strdup(urlstring); *urlstring; urlstring++)
352 if (*urlstring > 0x1F && *urlstring != 0x7F && *urlstring != ' ')
353 *p++ = *urlstring;
373 /* Encode illegal characters (they could also be stripped).
374 * There's no standard for illegal chars; we chose to encode. */
375 p = str1 = dNew(char, strlen(url_str) + 2*n_ic + 1);
376 for (i = 0; url_str[i]; ++i)
377 if (url_str[i] > 0x1F && url_str[i] != 0x7F && url_str[i] != ' ')
378 *p++ = url_str[i];
379 else {
380 *p++ = '%';
381 *p++ = HEX[(url_str[i] >> 4) & 15];
382 *p++ = HEX[url_str[i] & 15];
383 }
354384 *p = 0;
355 urlstring = new_str;
385 urlstr = str1;
356386 }
357387
358388 /* let's use a heuristic to set http: as default */
359389 if (!base_url) {
360390 base_url = "http:";
361 if (urlstring[0] != '/') {
362 p = strpbrk(urlstring, "/#?:");
391 if (urlstr[0] != '/') {
392 p = strpbrk(urlstr, "/#?:");
363393 if (!p || *p != ':')
364 urlstring = g_strconcat("//", urlstring, NULL);
365 } else if (urlstring[1] != '/')
366 urlstring = g_strconcat("/", urlstring, NULL);
394 urlstr = str2 = dStrconcat("//", urlstr, NULL);
395 } else if (urlstr[1] != '/')
396 urlstr = str2 = dStrconcat("/", urlstr, NULL);
367397 }
368398
369399 /* Resolve the URL */
370 SolvedUrl = Url_resolve_relative(urlstring, NULL, base_url);
371 DEBUG_MSG(2, "SolvedUrl = %s\n", SolvedUrl->str);
372 g_return_val_if_fail (SolvedUrl != NULL, NULL);
400 SolvedUrl = Url_resolve_relative(urlstr, NULL, base_url);
401 _MSG("SolvedUrl = %s\n", SolvedUrl->str);
373402
374403 /* Fill url data */
375404 url = Url_object_new(SolvedUrl->str);
405 url->data = dStr_new("");
376406 url->url_string = SolvedUrl;
377 url->flags = flags;
378 url->scrolling_position_x = posx;
379 url->scrolling_position_y = posy;
380407 url->illegal_chars = n_ic;
381408 url->illegal_chars_spc = n_ic_spc;
382409
383 g_free(new_str);
410 dFree(str1);
411 dFree(str2);
384412 return url;
385413 }
386414
393421 DilloUrl *url;
394422
395423 url = Url_object_new(URL_STR_(ori));
396 g_return_val_if_fail (url != NULL, NULL);
397
398 url->url_string = g_string_new(URL_STR(ori));
424 dReturn_val_if_fail (url != NULL, NULL);
425
426 url->url_string = dStr_new(URL_STR(ori));
399427 url->port = ori->port;
400428 url->flags = ori->flags;
401 url->data = g_strdup(ori->data);
402 url->alt = g_strdup(ori->alt);
429 url->alt = dStrdup(ori->alt);
403430 url->ismap_url_len = ori->ismap_url_len;
404 url->scrolling_position_x = ori->scrolling_position_x;
405 url->scrolling_position_y = ori->scrolling_position_y;
406431 url->illegal_chars = ori->illegal_chars;
407432 url->illegal_chars_spc = ori->illegal_chars_spc;
408
433 url->data = dStr_sized_new(URL_DATA(ori)->len);
434 dStr_append_l(url->data, URL_DATA(ori)->str, URL_DATA(ori)->len);
409435 return url;
410436 }
411437
412438 /*
413 * Compare two Url's to check if they are the same.
439 * Compare two Url's to check if they're the same, or which one is bigger.
440 *
414441 * The fields which are compared here are:
415442 * <scheme>, <authority>, <path>, <query> and <data>
416443 * Other fields are left for the caller to check
417444 *
418 * Return value: 0 if equal, 1 otherwise
419 */
420 gint a_Url_cmp(const DilloUrl *A, const DilloUrl *B)
421 {
422 if (!A || !B)
423 return 1;
445 * Return value: 0 if equal, > 0 if A > B, < 0 if A < B.
446 *
447 * Note: this function defines a sorting order different from strcmp!
448 */
449 int a_Url_cmp(const DilloUrl *A, const DilloUrl *B)
450 {
451 int st;
452
453 dReturn_val_if_fail(A && B, 1);
424454
425455 if (A == B ||
426 (URL_STRCAMP_I_EQ(A->authority, B->authority) &&
427 URL_STRCAMP_EQ(A->path, B->path) &&
428 URL_STRCAMP_EQ(A->query, B->query) &&
429 URL_STRCAMP_EQ(A->data, B->data) &&
430 URL_STRCAMP_I_EQ(A->scheme, B->scheme)))
456 ((st = URL_STR_FIELD_I_CMP(A->authority, B->authority)) == 0 &&
457 (st = strcmp(A->path ? A->path + (*A->path == '/') : "",
458 B->path ? B->path + (*B->path == '/') : "")) == 0 &&
459 //(st = URL_STR_FIELD_CMP(A->path, B->path)) == 0 &&
460 (st = URL_STR_FIELD_CMP(A->query, B->query)) == 0 &&
461 (st = dStr_cmp(A->data, B->data)) == 0 &&
462 (st = URL_STR_FIELD_I_CMP(A->scheme, B->scheme)) == 0))
431463 return 0;
432 return 1;
464 return st;
433465 }
434466
435467 /*
436468 * Set DilloUrl flags
437469 */
438 void a_Url_set_flags(DilloUrl *u, gint flags)
470 void a_Url_set_flags(DilloUrl *u, int flags)
439471 {
440472 if (u)
441473 u->flags = flags;
444476 /*
445477 * Set DilloUrl data (like POST info, etc.)
446478 */
447 void a_Url_set_data(DilloUrl *u, gchar *data)
479 void a_Url_set_data(DilloUrl *u, Dstr **data)
448480 {
449481 if (u) {
450 g_free((gchar *)u->data);
451 u->data = g_strdup(data);
482 dStr_free(u->data, 1);
483 u->data = *data;
484 *data = NULL;
452485 }
453486 }
454487
455488 /*
456489 * Set DilloUrl alt (alternate text to the URL. Used by image maps)
457490 */
458 void a_Url_set_alt(DilloUrl *u, const gchar *alt)
491 void a_Url_set_alt(DilloUrl *u, const char *alt)
459492 {
460493 if (u) {
461 g_free((gchar *)u->alt);
462 u->alt = g_strdup(alt);
463 }
464 }
465
466 /*
467 * Set DilloUrl scrolling position
468 */
469 void a_Url_set_pos(DilloUrl *u, gint32 posx, gint32 posy)
470 {
471 if (u) {
472 u->scrolling_position_x = posx;
473 u->scrolling_position_y = posy;
494 dFree((char *)u->alt);
495 u->alt = dStrdup(alt);
474496 }
475497 }
476498
478500 * Set DilloUrl ismap coordinates
479501 * (this is optimized for not hogging the CPU)
480502 */
481 void a_Url_set_ismap_coords(DilloUrl *u, gchar *coord_str)
482 {
483 g_return_if_fail(u && coord_str);
484
485 if ( !u->ismap_url_len ) {
503 void a_Url_set_ismap_coords(DilloUrl *u, char *coord_str)
504 {
505 dReturn_if_fail (u && coord_str);
506
507 if (!u->ismap_url_len) {
486508 /* Save base-url length (without coords) */
487509 u->ismap_url_len = URL_STR_(u) ? u->url_string->len : 0;
488510 a_Url_set_flags(u, URL_FLAGS(u) | URL_Ismap);
489511 }
490512 if (u->url_string) {
491 g_string_truncate(u->url_string, u->ismap_url_len);
492 g_string_append(u->url_string, coord_str);
513 dStr_truncate(u->url_string, u->ismap_url_len);
514 dStr_append(u->url_string, coord_str);
493515 u->query = u->url_string->str + u->ismap_url_len + 1;
494516 }
495517 }
498520 * Given an hex octet (e.g., e3, 2F, 20), return the corresponding
499521 * character if the octet is valid, and -1 otherwise
500522 */
501 static int Url_decode_hex_octet(const gchar *s)
502 {
503 gint hex_value;
504 gchar *tail, hex[3];
523 static int Url_decode_hex_octet(const char *s)
524 {
525 int hex_value;
526 char *tail, hex[3];
505527
506528 if (s && (hex[0] = s[0]) && (hex[1] = s[1])) {
507529 hex[2] = 0;
516538 * Parse possible hexadecimal octets in the URI path.
517539 * Returns a new allocated string.
518540 */
519 gchar *a_Url_decode_hex_str(const gchar *str)
520 {
521 gchar *new_str, *dest;
541 char *a_Url_decode_hex_str(const char *str)
542 {
543 char *new_str, *dest;
522544 int i, val;
523545
524546 if (!str)
526548
527549 /* most cases won't have hex octets */
528550 if (!strchr(str, '%'))
529 return g_strdup(str);
530
531 dest = new_str = g_new(gchar, strlen(str) + 1);
551 return dStrdup(str);
552
553 dest = new_str = dNew(char, strlen(str) + 1);
532554
533555 for (i = 0; str[i]; i++) {
534556 *dest++ = (str[i] == '%' && (val = Url_decode_hex_octet(str+i+1)) >= 0) ?
536558 }
537559 *dest++ = 0;
538560
539 new_str = g_realloc(new_str, sizeof(gchar) * (dest - new_str));
561 new_str = dRealloc(new_str, sizeof(char) * (dest - new_str));
540562 return new_str;
541563 }
542564
550572 * Note: the content type "application/x-www-form-urlencoded" is used:
551573 * i.e., ' ' -> '+' and '\n' -> CR LF (see HTML 4.01, Sec. 17.13.4)
552574 */
553 gchar *a_Url_encode_hex_str(const gchar *str)
554 {
555 static const char *verbatim = "-_.*";
556 static const char *hex = "0123456789ABCDEF";
575 char *a_Url_encode_hex_str(const char *str)
576 {
577 static const char *const verbatim = "-_.*";
557578 char *newstr, *c;
558579
559580 if (!str)
560581 return NULL;
561582
562 newstr = g_new(char, 6*strlen(str)+1);
583 newstr = dNew(char, 6*strlen(str)+1);
563584
564585 for (c = newstr; *str; str++)
565 if ((isalnum(*str) && !(*str & 0x80)) || strchr(verbatim, *str))
586 if ((dIsalnum(*str) && !(*str & 0x80)) || strchr(verbatim, *str))
566587 /* we really need isalnum for the "C" locale */
567588 *c++ = *str;
568589 else if (*str == ' ')
576597 *c++ = 'A';
577598 } else {
578599 *c++ = '%';
579 *c++ = hex[(*str >> 4) & 15];
580 *c++ = hex[*str & 15];
600 *c++ = HEX[(*str >> 4) & 15];
601 *c++ = HEX[*str & 15];
581602 }
582603 *c = 0;
583604
586607
587608
588609 /*
589 * RFC-2396 suggests this stripping when "importing" URLs from other media.
610 * RFC-3986 suggests this stripping when "importing" URLs from other media.
590611 * Strip: "URL:", enclosing < >, and embedded whitespace.
591612 * (We also strip illegal chars: 00-1F and 7F)
592613 */
593 gchar *a_Url_string_strip_delimiters(const gchar *str)
594 {
595 gchar *p, *new_str, *text;
596
597 new_str = text = g_strdup(str);
614 char *a_Url_string_strip_delimiters(const char *str)
615 {
616 char *p, *new_str, *text;
617
618 new_str = text = dStrdup(str);
598619
599620 if (new_str) {
600621 if (strncmp(new_str, "URL:", 4) == 0)
611632 }
612633 return new_str;
613634 }
635
636 /*
637 * Is the provided hostname an IP address?
638 */
639 static bool_t Url_host_is_ip(const char *host)
640 {
641 uint_t len;
642
643 if (!host || !*host)
644 return FALSE;
645
646 len = strlen(host);
647
648 if (len == strspn(host, "0123456789.")) {
649 _MSG("an IPv4 address\n");
650 return TRUE;
651 }
652 if (*host == '[' &&
653 (len == strspn(host, "0123456789abcdefABCDEF:.[]"))) {
654 /* The precise format is shown in section 3.2.2 of rfc 3986 */
655 _MSG("an IPv6 address\n");
656 return TRUE;
657 }
658 return FALSE;
659 }
660
661 /*
662 * How many internal dots are in the public portion of this hostname?
663 * e.g., for "www.dillo.org", it is one because everything under "dillo.org",
664 * as a .org domain, is part of one organization.
665 *
666 * Of course this is only a simple and imperfect approximation of
667 * organizational boundaries.
668 */
669 static uint_t Url_host_public_internal_dots(const char *host)
670 {
671 uint_t ret = 1;
672
673 if (host) {
674 int start, after, tld_len;
675
676 /* We may be able to trust the format of the host string more than
677 * I am here. Trailing dots and no dots are real possibilities, though.
678 */
679 after = strlen(host);
680 if (after > 0 && host[after - 1] == '.')
681 after--;
682 start = after;
683 while (start > 0 && host[start - 1] != '.')
684 start--;
685 tld_len = after - start;
686
687 if (tld_len > 0) {
688 /* These TLDs were chosen by examining the current publicsuffix list
689 * in January 2010 and picking out those where it was simplest for
690 * them to describe the situation by beginning with a "*.[tld]" rule.
691 */
692 const char *const tlds[] = {"ar","au","bd","bn","bt","ck","cy","do",
693 "eg","er","et","fj","fk","gt","gu","id",
694 "il","jm","ke","kh","kw","ml","mm","mt",
695 "mz","ni","np","nz","om","pg","py","qa",
696 "sv","tr","uk","uy","ve","ye","yu","za",
697 "zm","zw"};
698 uint_t i, tld_num = sizeof(tlds) / sizeof(tlds[0]);
699
700 for (i = 0; i < tld_num; i++) {
701 if (strlen(tlds[i]) == (uint_t) tld_len &&
702 !dStrncasecmp(tlds[i], host + start, tld_len)) {
703 _MSG("TLD code matched %s\n", tlds[i]);
704 ret++;
705 break;
706 }
707 }
708 }
709 }
710 return ret;
711 }
712
713 /*
714 * Given a URL host string, return the portion that is public, i.e., the
715 * domain that is in a registry outside the organization.
716 * For 'www.dillo.org', that would be 'dillo.org'.
717 */
718 const char *a_Url_host_find_public_suffix(const char *host)
719 {
720 const char *s;
721 uint_t dots;
722
723 if (!host || !*host || Url_host_is_ip(host))
724 return host;
725
726 s = host;
727
728 while (s[1])
729 s++;
730
731 if (s > host && *s == '.') {
732 /* don't want to deal with trailing dot */
733 s--;
734 }
735
736 dots = Url_host_public_internal_dots(host);
737
738 /* With a proper host string, we should not be pointing to a dot now. */
739
740 while (s > host) {
741 if (s[-1] == '.') {
742 if (dots == 0)
743 break;
744 else
745 dots--;
746 }
747 s--;
748 }
749
750 _MSG("public suffix of %s is %s\n", host, s);
751 return s;
752 }
00 /*
11 * File : url.h - Dillo
22 *
3 * Copyright (C) 2001 Jorge Arellano Cid <jcid@users.sourceforge.net>
4 * 2001 Livio Baldini Soares <livio@linux.ime.usp.br>
3 * Copyright (C) 2001 Jorge Arellano Cid <jcid@dillo.org>
54 *
65 * Parse and normalize all URL's inside Dillo.
76 */
98 #ifndef __URL_H__
109 #define __URL_H__
1110
12 #include <string.h> /* for strcmp */
13 #include <glib.h>
11 #include "d_size.h"
12 #include "../dlib/dlib.h"
1413
1514
1615 #define DILLO_URL_HTTP_PORT 80
3231 #define URL_Ismap (1 << 3)
3332 #define URL_RealmAccess (1 << 4)
3433
35 #define URL_E2EReload (1 << 5)
34 #define URL_E2EQuery (1 << 5)
3635 #define URL_ReloadImages (1 << 6)
3736 #define URL_ReloadPage (1 << 7)
3837 #define URL_ReloadFromCache (1 << 8)
3938
40 #define URL_ReloadIncomplete (1 << 9)
39 #define URL_IgnoreScroll (1 << 9)
4140 #define URL_SpamSafe (1 << 10)
41
42 #define URL_MultipartEnc (1 << 11)
4243
4344 /*
4445 * Access methods to fields inside DilloURL.
5152 #define URL_QUERY_(u) u->query
5253 #define URL_FRAGMENT_(u) u->fragment
5354 #define URL_HOST_(u) a_Url_hostname(u)
54 #define URL_DATA_(u) u->data
5555 #define URL_ALT_(u) u->alt
5656 #define URL_STR_(u) a_Url_str(u)
57 /* this returns a Dstr* */
58 #define URL_DATA_(u) u->data
5759 /* these return an integer */
58 #define URL_PORT_(u) (URL_HOST(u) ? u->port : u->port)
60 #define URL_PORT_(u) (URL_HOST(u), u->port)
5961 #define URL_FLAGS_(u) u->flags
60 #define URL_POSX_(u) u->scrolling_position_x
61 #define URL_POSY_(u) u->scrolling_position_y
6262 #define URL_ILLEGAL_CHARS_(u) url->illegal_chars
6363 #define URL_ILLEGAL_CHARS_SPC_(u) url->illegal_chars_spc
6464
6565 /*
66 * Access methods that always return a string:
66 * Access methods that never return NULL.
6767 * When the "empty" and "undefined" concepts of RFC-2396 are irrelevant to
6868 * the caller, and a string is required, use these methods instead:
6969 */
7474 #define URL_QUERY(u) NPTR2STR(URL_QUERY_(u))
7575 #define URL_FRAGMENT(u) NPTR2STR(URL_FRAGMENT_(u))
7676 #define URL_HOST(u) NPTR2STR(URL_HOST_(u))
77 #define URL_DATA(u) NPTR2STR(URL_DATA_(u))
77 #define URL_DATA(u) URL_DATA_(u)
7878 #define URL_ALT(u) NPTR2STR(URL_ALT_(u))
7979 #define URL_STR(u) NPTR2STR(URL_STR_(u))
8080 #define URL_PORT(u) URL_PORT_(u)
8181 #define URL_FLAGS(u) URL_FLAGS_(u)
82 #define URL_POSX(u) URL_POSX_(u)
83 #define URL_POSY(u) URL_POSY_(u)
8482 #define URL_ILLEGAL_CHARS(u) URL_ILLEGAL_CHARS_(u)
8583 #define URL_ILLEGAL_CHARS_SPC(u) URL_ILLEGAL_CHARS_SPC_(u)
86
87
88
89 /* URL-camp compare methods */
90 #define URL_STRCAMP_EQ(s1,s2) \
91 ((!(s1) && !(s2)) || ((s1) && (s2) && !strcmp(s1,s2)))
92 #define URL_STRCAMP_I_EQ(s1,s2) \
93 ((!(s1) && !(s2)) || ((s1) && (s2) && !g_strcasecmp(s1,s2)))
94 #define URL_GSTRCAMP_EQ(s1,s2) \
95 ((!(s1) && !(s2)) || ((s1) && (s2) && !strcmp((s1)->str,(s2)->str)))
96 #define URL_GSTRCAMP_I_EQ(s1,s2) \
97 ((!(s1) && !(s2)) || ((s1) && (s2) && !g_strcasecmp((s1)->str,(s2)->str)))
9884
9985
10086 typedef struct _DilloUrl DilloUrl;
10490 #endif /* __cplusplus */
10591
10692 struct _DilloUrl {
107 GString *url_string;
108 const gchar *buffer;
109 const gchar *scheme; /* */
110 const gchar *authority; /* */
111 const gchar *path; /* These are references only */
112 const gchar *query; /* (no need to free them) */
113 const gchar *fragment; /* */
114 const gchar *hostname; /* */
115 gint port;
116 gint flags;
117 const gchar *data; /* POST */
118 const gchar *alt; /* "alt" text (used by image maps) */
119 gint ismap_url_len; /* Used by server side image maps */
120 gint32 scrolling_position_x, scrolling_position_y;
121 /* remember position of visited urls */
122 gint illegal_chars; /* number of illegal chars */
123 gint illegal_chars_spc; /* number of illegal space chars */
93 Dstr *url_string;
94 const char *buffer;
95 const char *scheme; /**/
96 const char *authority; /**/
97 const char *path; /* These are references only */
98 const char *query; /* (no need to free them) */
99 const char *fragment; /**/
100 const char *hostname; /**/
101 int port;
102 int flags;
103 Dstr *data; /* POST */
104 const char *alt; /* "alt" text (used by image maps) */
105 int ismap_url_len; /* Used by server side image maps */
106 int illegal_chars; /* number of illegal chars */
107 int illegal_chars_spc; /* number of illegal space chars */
124108 };
125109
126110
127 DilloUrl* a_Url_new(const gchar *url_str, const gchar *base_url,
128 gint flags, gint32 posx, gint32 posy);
111 DilloUrl* a_Url_new(const char *url_str, const char *base_url);
129112 void a_Url_free(DilloUrl *u);
130 gchar *a_Url_str(const DilloUrl *url);
131 const gchar *a_Url_hostname(const DilloUrl *u);
113 char *a_Url_str(const DilloUrl *url);
114 const char *a_Url_hostname(const DilloUrl *u);
132115 DilloUrl* a_Url_dup(const DilloUrl *u);
133 gint a_Url_cmp(const DilloUrl *A, const DilloUrl *B);
134 void a_Url_set_flags(DilloUrl *u, gint flags);
135 void a_Url_set_data(DilloUrl *u, gchar *data);
136 void a_Url_set_alt(DilloUrl *u, const gchar *alt);
137 void a_Url_set_pos(DilloUrl *u, gint32 posx, gint32 posy);
138 void a_Url_set_ismap_coords(DilloUrl *u, gchar *coord_str);
139 gchar *a_Url_decode_hex_str(const gchar *str);
140 gchar *a_Url_encode_hex_str(const gchar *str);
141 gchar *a_Url_string_strip_delimiters(const gchar *str);
142
116 int a_Url_cmp(const DilloUrl *A, const DilloUrl *B);
117 void a_Url_set_flags(DilloUrl *u, int flags);
118 void a_Url_set_data(DilloUrl *u, Dstr **data);
119 void a_Url_set_alt(DilloUrl *u, const char *alt);
120 void a_Url_set_ismap_coords(DilloUrl *u, char *coord_str);
121 char *a_Url_decode_hex_str(const char *str);
122 char *a_Url_encode_hex_str(const char *str);
123 char *a_Url_string_strip_delimiters(const char *str);
124 const char *a_Url_host_find_public_suffix(const char *host);
143125 #ifdef __cplusplus
144126 }
145127 #endif /* __cplusplus */
0 /*
1 * File: utf8.c
2 *
3 * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <FL/fl_utf8.h>
12
13 #include "../dlib/dlib.h" /* TRUE/FALSE */
14 #include "utf8.hh"
15
16 // C++ functions with C linkage ----------------------------------------------
17
18 /*
19 * Return index of the last byte of the UTF-8-encoded character that str + i
20 * points to or into.
21 */
22 uint_t a_Utf8_end_of_char(const char *str, uint_t i)
23 {
24 /* We can almost get what we want from utf8fwd(p+1,...)-1, but that
25 * does not work for the last character in a string, and the fn makes some
26 * assumptions that do not suit us.
27 * Here's something very simpleminded instead:
28 */
29 if (str && *str && (str[i] & 0x80)) {
30 int internal_bytes = (str[i] & 0x40) ? 0 : 1;
31
32 while (((str[i + 1] & 0xc0) == 0x80) && (++internal_bytes < 4))
33 i++;
34 }
35 return i;
36 }
37
38 /*
39 * Decode a single UTF-8-encoded character starting at p.
40 * The resulting Unicode value (in the range 0-0x10ffff) is returned,
41 * and len is set to the number of bytes in the UTF-8 encoding.
42 * Note that utf8decode(), if given non-UTF-8 data, will interpret
43 * it as ISO-8859-1 or CP1252 if possible.
44 */
45 uint_t a_Utf8_decode(const char* str, const char* end, int* len)
46 {
47 return fl_utf8decode(str, end, len);
48 }
49
50 /*
51 * Write UTF-8 encoding of ucs into buf and return number of bytes written.
52 */
53 int a_Utf8_encode(unsigned int ucs, char *buf)
54 {
55 return fl_utf8encode(ucs, buf);
56 }
57
58 /*
59 * Examine first srclen bytes of src.
60 * Return 0 if not legal UTF-8, 1 if all ASCII, 2 if all below 0x800,
61 * 3 if all below 0x10000, and 4 otherwise.
62 */
63 int a_Utf8_test(const char* src, unsigned int srclen)
64 {
65 return fl_utf8test(src, srclen);
66 }
67
68 /*
69 * Does s point to a UTF-8-encoded ideographic character?
70 *
71 * This is based on http://unicode.org/reports/tr14/#ID plus some guesses
72 * for what might make the most sense for Dillo. Surprisingly, they include
73 * Hangul Compatibility Jamo, but they're the experts, so I'll follow along.
74 */
75 bool_t a_Utf8_ideographic(const char *s, const char *end, int *len)
76 {
77 bool_t ret = FALSE;
78
79 if ((uchar_t)*s >= 0xe2) {
80 /* Unicode char >= U+2000. */
81 unsigned unicode = a_Utf8_decode(s, end, len);
82
83 if (unicode >= 0x2e80 &&
84 ((unicode <= 0xa4cf) ||
85 (unicode >= 0xf900 && unicode <= 0xfaff) ||
86 (unicode >= 0xff00 && unicode <= 0xff9f))) {
87 ret = TRUE;
88 }
89 } else {
90 *len = 1 + (int)a_Utf8_end_of_char(s, 0);
91 }
92 return ret;
93 }
94
95 bool_t a_Utf8_combining_char(int unicode)
96 {
97 return ((unicode >= 0x0300 && unicode <= 0x036f) ||
98 (unicode >= 0x1dc0 && unicode <= 0x1dff) ||
99 (unicode >= 0x20d0 && unicode <= 0x20ff) ||
100 (unicode >= 0xfe20 && unicode <= 0xfe2f));
101 }
102
103 int a_Utf8_char_count(const char *str, int len)
104 {
105 return fl_utf_nb_char((const uchar_t*)str, len);
106 }
0 #ifndef __UTF8_HH__
1 #define __UTF8_HH__
2
3 #ifdef __cplusplus
4 extern "C" {
5 #endif /* __cplusplus */
6
7
8 #include "d_size.h"
9
10 /*
11 * Unicode replacement character U+FFFD
12 * "used to replace an incoming character whose value is unknown or otherwise
13 * unrepresentable in Unicode"
14 */
15 static const char utf8_replacement_char[] = "\xEF\xBF\xBD";
16
17 /* Unicode zero width space U+200B */
18 static const char utf8_zero_width_space[] = "\xE2\x80\x8B";
19
20 uint_t a_Utf8_end_of_char(const char *str, uint_t i);
21 uint_t a_Utf8_decode(const char*, const char* end, int* len);
22 int a_Utf8_encode(unsigned int ucs, char *buf);
23 int a_Utf8_test(const char* src, unsigned int srclen);
24 bool_t a_Utf8_ideographic(const char *s, const char *end, int *len);
25 bool_t a_Utf8_combining_char(int unicode);
26 int a_Utf8_char_count(const char *str, int len);
27
28 #ifdef __cplusplus
29 }
30 #endif /* __cplusplus */
31
32 #endif /* __UTF8_HH__ */
33
+0
-155
src/web.c less more
0 /*
1 * File: web.c
2 *
3 * Copyright 2000 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <gtk/gtk.h>
14 #include <math.h> /* for rint */
15
16 #include "msg.h"
17 #include "browser.h"
18 #include "nav.h"
19 #include "interface.h"
20 #include "IO/IO.h"
21 #include "IO/mime.h"
22
23 #include "dw_widget.h"
24 #include "dw_gtk_scrolled_window.h"
25 #include "dw_embed_gtk.h"
26 #include "prefs.h"
27 #include "web.h"
28
29 #define DEBUG_LEVEL 5
30 #include "debug.h"
31
32 /*
33 * Local data
34 */
35 static GSList *ValidWebs = NULL; /* Active web structures list; it holds
36 * pointers to DilloWeb structures. */
37
38 /*
39 * Given the MIME content type, and a fd to read it from,
40 * this function connects the proper MIME viewer to it.
41 * Return value: 1 on success, -1 for unhandled MIME types
42 */
43 gint a_Web_dispatch_by_type (const char *Type, DilloWeb *Web,
44 CA_Callback_t *Call, void **Data)
45 {
46 DwWidget *dw = NULL;
47 DwStyle style_attrs, *style;
48 DwStyleFont font;
49
50 DEBUG_MSG(1, "a_Web_dispatch_by_type\n");
51
52 g_return_val_if_fail(Web->bw != NULL, -1);
53
54 if (Web->flags & WEB_RootUrl) {
55 /* We have RootUrl! */
56 dw = a_Mime_set_viewer(Type, Web, Call, Data);
57 if (dw == NULL)
58 return -1;
59
60 /* Set a style for the widget */
61 font.name = prefs.vw_fontname; /* must be defined */
62 font.size = rint(12.0 * prefs.font_factor);
63 font.weight = 400;
64 font.style = DW_STYLE_FONT_STYLE_NORMAL;
65
66 a_Dw_style_init_values (&style_attrs, Web->bw->main_window->window);
67 a_Dw_style_box_set_val (&style_attrs.margin, 5);
68 style_attrs.font = a_Dw_style_font_new (&font);
69 style_attrs.color =
70 a_Dw_style_color_new (prefs.text_color, Web->bw->main_window->window);
71 style_attrs.background_color =
72 a_Dw_style_color_new (prefs.bg_color, Web->bw->main_window->window);
73 style = a_Dw_style_new (&style_attrs, Web->bw->main_window->window);
74 a_Dw_widget_set_style (dw, style);
75 a_Dw_style_unref (style);
76
77 a_Dw_gtk_scrolled_window_set_dw(
78 GTK_DW_SCROLLED_WINDOW(Web->bw->docwin), dw);
79
80 if (URL_POSX(Web->url) || URL_POSY(Web->url)) {
81 a_Dw_gtk_scrolled_window_set_scrolling_position(
82 GTK_DW_SCROLLED_WINDOW(Web->bw->docwin),
83 URL_POSX(Web->url), URL_POSY(Web->url));
84 } else {
85 gchar *pf = a_Url_decode_hex_str(URL_FRAGMENT_(Web->url));
86 a_Dw_gtk_scrolled_window_set_anchor(
87 GTK_DW_SCROLLED_WINDOW(Web->bw->docwin), pf);
88 g_free(pf);
89 }
90
91 /* Clear the title bar for pages without a <TITLE> tag */
92 a_Interface_set_page_title(Web->bw, "");
93 a_Interface_set_location_text(Web->bw, URL_STR(Web->url));
94 a_Interface_reset_progress_bars(Web->bw);
95 /* Reset the bug meter */
96 a_Interface_bug_meter_update(Web->bw, 0);
97
98 /* Let the Nav module know... */
99 a_Nav_expect_done(Web->bw);
100
101 } else {
102 /* A non-RootUrl. At this moment we only handle image-children */
103 if (!g_strncasecmp(Type, "image/", 6))
104 dw = a_Mime_set_viewer(Type, Web, Call, Data);
105 }
106
107 if (!dw) {
108 MSG_HTTP("unhandled MIME type: \"%s\"\n", Type);
109 }
110 return (dw ? 1 : -1);
111 }
112
113
114 /*
115 * Allocate and set safe values for a DilloWeb structure
116 */
117 DilloWeb* a_Web_new(const DilloUrl *url)
118 {
119 DilloWeb *web= g_new(DilloWeb, 1);
120
121 _MSG(" a_Web_new: ValidWebs ==> %d\n", g_slist_length(ValidWebs));
122 web->url = a_Url_dup(url);
123 web->bw = NULL;
124 web->flags = 0;
125 web->Image = NULL;
126 web->stream = NULL;
127 web->SavedBytes = 0;
128
129 ValidWebs = g_slist_append(ValidWebs, (gpointer)web);
130 return web;
131 }
132
133 /*
134 * Validate a DilloWeb pointer
135 */
136 gint a_Web_valid(DilloWeb *web)
137 {
138 return (g_slist_find(ValidWebs, web) != NULL);
139 }
140
141 /*
142 * Deallocate a DilloWeb structure
143 */
144 void a_Web_free(DilloWeb *web)
145 {
146 if (!web) return;
147 if (web->url)
148 a_Url_free(web->url);
149 if (web->Image)
150 a_Image_unref(web->Image);
151 ValidWebs = g_slist_remove(ValidWebs, (gpointer)web);
152 g_free(web);
153 }
154
0 /*
1 * File: web.cc
2 *
3 * Copyright 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include "msg.h"
12 #include "nav.h"
13
14 #include "uicmd.hh"
15
16 #include "IO/IO.h"
17 #include "IO/mime.h"
18
19 #include "dw/core.hh"
20 #include "styleengine.hh"
21 #include "web.hh"
22
23 // Platform independent part
24 using namespace dw::core;
25
26
27 /*
28 * Local data
29 */
30 static Dlist *ValidWebs; /* Active web structures list; it holds
31 * pointers to DilloWeb structures. */
32
33 /*
34 * Initialize local data
35 */
36 void a_Web_init(void)
37 {
38 ValidWebs = dList_new(32);
39 }
40
41 /*
42 * Given the MIME content type, and a fd to read it from,
43 * this function connects the proper MIME viewer to it.
44 * Return value:
45 * 1 on success (and Call and Data properly set).
46 * -1 for unhandled MIME types (and Call and Data untouched).
47 */
48 int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web,
49 CA_Callback_t *Call, void **Data)
50 {
51 Widget *dw = NULL;
52
53 _MSG("a_Web_dispatch_by_type\n");
54
55 dReturn_val_if_fail(Web->bw != NULL, -1);
56
57 // get the Layout object from the bw structure.
58 Layout *layout = (Layout*)Web->bw->render_layout;
59
60 if (Web->flags & WEB_RootUrl) {
61 /* We have RootUrl! */
62
63 style::Color *bgColor = style::Color::create (layout, prefs.bg_color);
64 Web->bgColor = bgColor->getColor ();
65 layout->setBgColor (bgColor);
66
67 /* Set a style for the widget */
68 StyleEngine styleEngine (layout);
69 styleEngine.startElement ("body");
70
71 dw = (Widget*) a_Mime_set_viewer(Type, Web, Call, Data);
72 if (dw == NULL)
73 return -1;
74
75 dw->setStyle (styleEngine.style ());
76
77 /* This method frees the old dw if any */
78 layout->setWidget(dw);
79
80 /* Set the page title with the bare filename (e.g. for images),
81 * HTML pages with a <TITLE> tag will overwrite it later */
82 const char *p = strrchr(URL_STR(Web->url), '/');
83 a_UIcmd_set_page_title(Web->bw, p ? p+1 : "");
84
85 a_UIcmd_set_location_text(Web->bw, URL_STR(Web->url));
86 /* Reset both progress bars */
87 a_UIcmd_set_page_prog(Web->bw, 0, 2);
88 a_UIcmd_set_img_prog(Web->bw, 0, 0, 2);
89 /* Reset the bug meter */
90 a_UIcmd_set_bug_prog(Web->bw, 0);
91
92 /* Let the Nav module know... */
93 a_Nav_expect_done(Web->bw);
94
95 } else {
96 /* A non-RootUrl. At this moment we only handle image-children */
97 if (!dStrncasecmp(Type, "image/", 6)) {
98 dw = (Widget*) a_Mime_set_viewer(Type, Web, Call, Data);
99 } else {
100 MSG_HTTP("'%s' cannot be displayed as image; has media type '%s'\n",
101 URL_STR(Web->url), Type);
102 }
103 }
104 return (dw ? 1 : -1);
105 }
106
107
108 /*
109 * Allocate and set safe values for a DilloWeb structure
110 */
111 DilloWeb* a_Web_new(const DilloUrl *url, const DilloUrl *requester)
112 {
113 DilloWeb *web= dNew(DilloWeb, 1);
114
115 _MSG(" a_Web_new: ValidWebs ==> %d\n", dList_length(ValidWebs));
116 web->url = a_Url_dup(url);
117 web->requester = a_Url_dup(requester);
118 web->bw = NULL;
119 web->flags = 0;
120 web->Image = NULL;
121 web->filename = NULL;
122 web->stream = NULL;
123 web->SavedBytes = 0;
124 web->bgColor = 0x000000; /* Dummy value will be overwritten
125 * in a_Web_dispatch_by_type. */
126 dList_append(ValidWebs, (void *)web);
127 return web;
128 }
129
130 /*
131 * Validate a DilloWeb pointer
132 */
133 int a_Web_valid(DilloWeb *web)
134 {
135 return (dList_find(ValidWebs, web) != NULL);
136 }
137
138 /*
139 * Deallocate a DilloWeb structure
140 */
141 void a_Web_free(DilloWeb *web)
142 {
143 if (!web) return;
144 a_Url_free(web->url);
145 a_Url_free(web->requester);
146 a_Image_unref(web->Image);
147 dFree(web->filename);
148 dList_remove(ValidWebs, (void *)web);
149 _MSG("a_Web_free: ValidWebs=%d\n", dList_length(ValidWebs));
150 dFree(web);
151 }
152
+0
-44
src/web.h less more
0 #ifndef __WEB_H__
1 #define __WEB_H__
2
3 #include <gtk/gtk.h>
4
5 #include "cache.h" /* for CA_Callback_t */
6 #include "html.h" /* for DilloHtmlLB */
7 #include "image.h" /* for DilloImage */
8
9 #ifdef __cplusplus
10 extern "C" {
11 #endif /* __cplusplus */
12
13 /*
14 * Flag defines
15 */
16 #define WEB_RootUrl 1
17 #define WEB_Image 2
18 #define WEB_Download 4 /* Half implemented... */
19
20
21 typedef struct _DilloWeb DilloWeb;
22
23 struct _DilloWeb {
24 DilloUrl *url; /* Requested URL */
25 BrowserWindow *bw; /* The requesting browser window [reference] */
26 gint flags; /* Additional info */
27
28 DilloImage *Image; /* For image urls [reference] */
29 FILE *stream; /* File stream for local saving */
30 gint SavedBytes; /* Also for local saving */
31 };
32
33
34 DilloWeb* a_Web_new (const DilloUrl* url);
35 gint a_Web_valid(DilloWeb *web);
36 void a_Web_free (DilloWeb*);
37 gint a_Web_dispatch_by_type (const char *Type, DilloWeb *web,
38 CA_Callback_t *Call, void **Data);
39
40 #ifdef __cplusplus
41 }
42 #endif /* __cplusplus */
43 #endif /* __WEB_H__ */
0 #ifndef __WEB_H__
1 #define __WEB_H__
2
3 #include <stdio.h> /* for FILE */
4 #include "bw.h" /* for BrowserWindow */
5 #include "cache.h" /* for CA_Callback_t */
6 #include "image.hh" /* for DilloImage */
7
8 #ifdef __cplusplus
9 extern "C" {
10 #endif /* __cplusplus */
11
12 /*
13 * Flag defines
14 */
15 #define WEB_RootUrl 1
16 #define WEB_Image 2
17 #define WEB_Download 4 /* Half implemented... */
18
19
20 typedef struct _DilloWeb DilloWeb;
21
22 struct _DilloWeb {
23 DilloUrl *url; /* Requested URL */
24 DilloUrl *requester; /* URL that caused this request, or
25 * NULL if user-initiated. */
26 BrowserWindow *bw; /* The requesting browser window [reference] */
27 int flags; /* Additional info */
28
29 DilloImage *Image; /* For image urls [reference] */
30
31 int32_t bgColor; /* for image backgrounds */
32 char *filename; /* Variables for Local saving */
33 FILE *stream;
34 int SavedBytes;
35 };
36
37 void a_Web_init(void);
38 DilloWeb* a_Web_new (const DilloUrl* url, const DilloUrl *requester);
39 int a_Web_valid(DilloWeb *web);
40 void a_Web_free (DilloWeb*);
41 int a_Web_dispatch_by_type (const char *Type, DilloWeb *web,
42 CA_Callback_t *Call, void **Data);
43
44 #ifdef __cplusplus
45 }
46 #endif /* __cplusplus */
47 #endif /* __WEB_H__ */
0 /*
1 * File: xembed.cc
2 *
3 * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 */
10
11 #include <string.h>
12 #include <ctype.h>
13
14 #define FL_INTERNALS
15 #include <FL/Fl_Window.H>
16 #include <FL/Fl.H>
17 #include <FL/x.H>
18
19 #include "xembed.hh"
20
21 #ifdef X_PROTOCOL
22
23 typedef enum {
24 XEMBED_EMBEDDED_NOTIFY = 0,
25 XEMBED_WINDOW_ACTIVATE = 1,
26 XEMBED_WINDOW_DEACTIVATE = 2,
27 XEMBED_REQUEST_FOCUS = 3,
28 XEMBED_FOCUS_IN = 4,
29 XEMBED_FOCUS_OUT = 5,
30 XEMBED_FOCUS_NEXT = 6,
31 XEMBED_FOCUS_PREV = 7,
32 XEMBED_GRAB_KEY = 8,
33 XEMBED_UNGRAB_KEY = 9,
34 XEMBED_MODALITY_ON = 10,
35 XEMBED_MODALITY_OFF = 11,
36 } XEmbedMessageType;
37
38 void
39 Xembed::setXembedInfo(unsigned long flags)
40 {
41 unsigned long buffer[2];
42
43 Atom xembed_info_atom = XInternAtom (fl_display, "_XEMBED_INFO", false);
44
45 buffer[0] = 1;
46 buffer[1] = flags;
47
48 XChangeProperty (fl_display,
49 xid,
50 xembed_info_atom, xembed_info_atom, 32,
51 PropModeReplace,
52 (unsigned char *)buffer, 2);
53 }
54
55 void
56 Xembed::sendXembedEvent(uint32_t message) {
57 XClientMessageEvent xclient;
58
59 memset (&xclient, 0, sizeof (xclient));
60 xclient.window = xid;
61 xclient.type = ClientMessage;
62 xclient.message_type = XInternAtom (fl_display, "_XEMBED", false);
63 xclient.format = 32;
64 xclient.data.l[0] = fl_event_time;
65 xclient.data.l[1] = message;
66
67 XSendEvent(fl_display, xid, False, NoEventMask, (XEvent *)&xclient);
68 XSync(fl_display, False);
69 }
70
71 int
72 Xembed::handle(int e) {
73 if (e == FL_PUSH)
74 sendXembedEvent(XEMBED_REQUEST_FOCUS);
75
76 return Fl_Window::handle(e);
77 }
78
79 static int event_handler(int e, Fl_Window *w) {
80 Atom xembed_atom = XInternAtom (fl_display, "_XEMBED", false);
81
82 if (fl_xevent->type == ClientMessage) {
83 if (fl_xevent->xclient.message_type == xembed_atom) {
84 long message = fl_xevent->xclient.data.l[1];
85
86 switch (message) {
87 case XEMBED_WINDOW_ACTIVATE:
88 // Force a ConfigureNotify message so fltk can get the new
89 // coordinates after a move of the embedder window.
90 if (w)
91 w->resize(0,0, w->w(), w->h());
92 break;
93 case XEMBED_WINDOW_DEACTIVATE:
94 break;
95 default:
96 break;
97 }
98 }
99 }
100
101 return Fl::handle_(e, w);
102 }
103
104 // TODO: Implement more XEMBED support;
105
106 void Xembed::show() {
107 createInternal(xid);
108 setXembedInfo(1);
109 Fl::event_dispatch(event_handler);
110 }
111
112 void Xembed::createInternal(uint32_t parent) {
113 Fl_Window *window = this;
114 Colormap colormap = fl_colormap;
115
116 XSetWindowAttributes attr;
117 attr.border_pixel = 0;
118 attr.colormap = colormap;
119 attr.bit_gravity = 0; // StaticGravity;
120 int mask = CWBorderPixel|CWColormap|CWEventMask|CWBitGravity;
121
122 int W = window->w();
123 if (W <= 0) W = 1; // X don't like zero...
124 int H = window->h();
125 if (H <= 0) H = 1; // X don't like zero...
126 int X = window->x();
127 int Y = window->y();
128
129 attr.event_mask =
130 ExposureMask | StructureNotifyMask
131 | KeyPressMask | KeyReleaseMask | KeymapStateMask | FocusChangeMask
132 | ButtonPressMask | ButtonReleaseMask
133 | EnterWindowMask | LeaveWindowMask
134 | PointerMotionMask;
135
136 Fl_X::set_xid(window,
137 XCreateWindow(fl_display,
138 parent,
139 X, Y, W, H,
140 0, // borderwidth
141 fl_visual->depth,
142 InputOutput,
143 fl_visual->visual,
144 mask, &attr));
145 }
146
147 #else // X_PROTOCOL
148
149 void
150 Xembed::setXembedInfo(unsigned long flags) {};
151
152 void
153 Xembed::sendXembedEvent(uint32_t message) {};
154
155 int
156 Xembed::handle(int e) {
157 return Fl_Window::handle(e);
158 }
159
160 void
161 Xembed::show() {
162 Fl_Window::show();
163 }
164
165 #endif
0 #ifndef __XEMBED_HH__
1 #define __XEMBED_HH__
2
3 #include <FL/Fl_Window.H>
4
5 #include "d_size.h"
6
7 class Xembed : public Fl_Window {
8 private:
9 uint32_t xid;
10 void createInternal(uint32_t parent);
11 void setXembedInfo(unsigned long flags);
12 void sendXembedEvent(uint32_t message);
13
14 public:
15 Xembed(uint32_t xid, int _w, int _h) : Fl_Window(_w, _h) {
16 this->xid = xid;
17 };
18 void show();
19 int handle(int event);
20 };
21
22 #endif
0 AM_CPPFLAGS = \
1 -I$(top_srcdir)
2 AM_CFLAGS = @LIBFLTK_CFLAGS@
3 AM_CXXFLAGS = @LIBFLTK_CXXFLAGS@
4
5 noinst_PROGRAMS = \
6 dw-anchors-test \
7 dw-example \
8 dw-find-test \
9 dw-links \
10 dw-links2 \
11 dw-images-simple \
12 dw-images-scaled \
13 dw-images-scaled2 \
14 dw-lists \
15 dw-table-aligned \
16 dw-table \
17 dw-border-test \
18 dw-imgbuf-mem-test \
19 dw-resource-test \
20 dw-ui-test \
21 fltk-browser \
22 shapes \
23 cookies
24
25 dw_anchors_test_SOURCES = dw_anchors_test.cc
26 dw_anchors_test_LDADD = \
27 $(top_builddir)/dw/libDw-widgets.a \
28 $(top_builddir)/dw/libDw-fltk.a \
29 $(top_builddir)/dw/libDw-core.a \
30 $(top_builddir)/lout/liblout.a \
31 @LIBFLTK_LIBS@
32
33 dw_example_SOURCES = dw_example.cc
34 dw_example_LDADD = \
35 $(top_builddir)/dw/libDw-widgets.a \
36 $(top_builddir)/dw/libDw-fltk.a \
37 $(top_builddir)/dw/libDw-core.a \
38 $(top_builddir)/lout/liblout.a \
39 @LIBFLTK_LIBS@
40
41 dw_find_test_SOURCES = dw_find_test.cc
42 dw_find_test_LDADD = \
43 $(top_builddir)/dw/libDw-widgets.a \
44 $(top_builddir)/dw/libDw-fltk.a \
45 $(top_builddir)/dw/libDw-core.a \
46 $(top_builddir)/lout/liblout.a \
47 @LIBFLTK_LIBS@
48
49 dw_links_SOURCES = dw_links.cc
50 dw_links_LDADD = \
51 $(top_builddir)/dw/libDw-widgets.a \
52 $(top_builddir)/dw/libDw-fltk.a \
53 $(top_builddir)/dw/libDw-core.a \
54 $(top_builddir)/lout/liblout.a \
55 @LIBFLTK_LIBS@
56
57 dw_links2_SOURCES = dw_links2.cc
58 dw_links2_LDADD = \
59 $(top_builddir)/dw/libDw-widgets.a \
60 $(top_builddir)/dw/libDw-fltk.a \
61 $(top_builddir)/dw/libDw-core.a \
62 $(top_builddir)/lout/liblout.a \
63 @LIBFLTK_LIBS@
64
65 dw_images_simple_SOURCES = dw_images_simple.cc
66 dw_images_simple_LDADD = \
67 $(top_builddir)/dw/libDw-widgets.a \
68 $(top_builddir)/dw/libDw-fltk.a \
69 $(top_builddir)/dw/libDw-core.a \
70 $(top_builddir)/lout/liblout.a \
71 @LIBFLTK_LIBS@
72
73 dw_images_scaled_SOURCES = dw_images_scaled.cc
74 dw_images_scaled_LDADD = \
75 $(top_builddir)/dw/libDw-widgets.a \
76 $(top_builddir)/dw/libDw-fltk.a \
77 $(top_builddir)/dw/libDw-core.a \
78 $(top_builddir)/lout/liblout.a \
79 @LIBFLTK_LIBS@
80
81 dw_images_scaled2_SOURCES = dw_images_scaled2.cc
82 dw_images_scaled2_LDADD = \
83 $(top_builddir)/dw/libDw-widgets.a \
84 $(top_builddir)/dw/libDw-fltk.a \
85 $(top_builddir)/dw/libDw-core.a \
86 $(top_builddir)/lout/liblout.a \
87 @LIBFLTK_LIBS@
88
89 dw_lists_SOURCES = dw_lists.cc
90 dw_lists_LDADD = \
91 $(top_builddir)/dw/libDw-widgets.a \
92 $(top_builddir)/dw/libDw-fltk.a \
93 $(top_builddir)/dw/libDw-core.a \
94 $(top_builddir)/lout/liblout.a \
95 @LIBFLTK_LIBS@
96
97 dw_table_aligned_SOURCES = dw_table_aligned.cc
98 dw_table_aligned_LDADD = \
99 $(top_builddir)/dw/libDw-widgets.a \
100 $(top_builddir)/dw/libDw-fltk.a \
101 $(top_builddir)/dw/libDw-core.a \
102 $(top_builddir)/lout/liblout.a \
103 @LIBFLTK_LIBS@
104
105 dw_table_SOURCES = dw_table.cc
106 dw_table_LDADD = \
107 $(top_builddir)/dw/libDw-widgets.a \
108 $(top_builddir)/dw/libDw-fltk.a \
109 $(top_builddir)/dw/libDw-core.a \
110 $(top_builddir)/lout/liblout.a \
111 @LIBFLTK_LIBS@
112
113 dw_border_test_SOURCES = dw_border_test.cc
114 dw_border_test_LDADD = \
115 $(top_builddir)/dw/libDw-widgets.a \
116 $(top_builddir)/dw/libDw-fltk.a \
117 $(top_builddir)/dw/libDw-core.a \
118 $(top_builddir)/lout/liblout.a \
119 @LIBFLTK_LIBS@
120
121
122 dw_imgbuf_mem_test_SOURCES = dw_imgbuf_mem_test.cc
123 dw_imgbuf_mem_test_LDADD = \
124 $(top_builddir)/dw/libDw-widgets.a \
125 $(top_builddir)/dw/libDw-fltk.a \
126 $(top_builddir)/dw/libDw-core.a \
127 $(top_builddir)/lout/liblout.a \
128 @LIBFLTK_LIBS@
129
130 dw_resource_test_SOURCES = dw_resource_test.cc
131 dw_resource_test_LDADD = \
132 $(top_builddir)/dw/libDw-widgets.a \
133 $(top_builddir)/dw/libDw-fltk.a \
134 $(top_builddir)/dw/libDw-core.a \
135 $(top_builddir)/lout/liblout.a \
136 @LIBFLTK_LIBS@
137
138 dw_ui_test_SOURCES = \
139 dw_ui_test.cc \
140 form.cc \
141 form.hh
142 dw_ui_test_LDADD = \
143 $(top_builddir)/dw/libDw-widgets.a \
144 $(top_builddir)/dw/libDw-fltk.a \
145 $(top_builddir)/dw/libDw-core.a \
146 $(top_builddir)/lout/liblout.a \
147 @LIBFLTK_LIBS@
148
149 fltk_browser_SOURCES = fltk_browser.cc
150 fltk_browser_LDADD = @LIBFLTK_LIBS@
151
152 shapes_SOURCES = shapes.cc
153 shapes_LDADD = \
154 $(top_builddir)/dw/libDw-core.a \
155 $(top_builddir)/lout/liblout.a
156
157 cookies_SOURCES = cookies.c
158 cookies_LDADD = \
159 $(top_builddir)/dpip/libDpip.a \
160 $(top_builddir)/dlib/libDlib.a
0 /*
1 * Dillo cookies test
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 /*
18 * This has a big blob of the current src/IO/dpi.c in it.
19 * I hope there's a better way.
20 */
21
22 #include <stdlib.h> /* malloc, etc. */
23 #include <unistd.h> /* read, etc. */
24 #include <stdio.h>
25 #include <stdarg.h> /* va_list */
26 #include <string.h> /* strchr */
27 #include <errno.h>
28 #include <ctype.h>
29 #include <time.h>
30 /* net */
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
34 #include <netinet/in.h>
35
36
37 #define _MSG(...)
38
39 #define MSG_INNARDS(prefix, ...) \
40 D_STMT_START { \
41 printf(prefix __VA_ARGS__); \
42 fflush (stdout); \
43 } D_STMT_END
44
45 #define MSG(...) MSG_INNARDS("", __VA_ARGS__)
46 #define MSG_ERR(...) MSG_INNARDS("** ERROR **: ", __VA_ARGS__)
47
48
49 #include "../dlib/dlib.h"
50 #include "../dpip/dpip.h"
51
52 static uint_t failed = 0;
53 static uint_t passed = 0;
54
55 static char SharedKey[32];
56
57 /*
58 * Read all the available data from a filedescriptor.
59 * This is intended for short answers, i.e. when we know the server
60 * will write it all before being preempted. For answers that may come
61 * as an stream with delays, non-blocking is better.
62 * Return value: read data, or NULL on error and no data.
63 */
64 static char *Dpi_blocking_read(int fd)
65 {
66 int st;
67 const int buf_sz = 8*1024;
68 char buf[buf_sz], *msg = NULL;
69 Dstr *dstr = dStr_sized_new(buf_sz);
70
71 do {
72 st = read(fd, buf, buf_sz);
73 if (st < 0) {
74 if (errno == EINTR) {
75 continue;
76 } else {
77 MSG_ERR("[Dpi_blocking_read] %s\n", dStrerror(errno));
78 break;
79 }
80 } else if (st > 0) {
81 dStr_append_l(dstr, buf, st);
82 }
83 } while (st == buf_sz);
84
85 msg = (dstr->len > 0) ? dstr->str : NULL;
86 dStr_free(dstr, (dstr->len > 0) ? FALSE : TRUE);
87 return msg;
88 }
89
90 static void Dpi_close_fd(int fd)
91 {
92 int st;
93
94 dReturn_if (fd < 0);
95 do
96 st = close(fd);
97 while (st < 0 && errno == EINTR);
98 }
99
100 static int Dpi_make_socket_fd()
101 {
102 int fd, ret = -1;
103
104 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
105 ret = fd;
106 }
107 return ret;
108 }
109
110 /*
111 * Read dpid's communication keys from its saved file.
112 * Return value: 1 on success, -1 on error.
113 */
114 static int Dpi_read_comm_keys(int *port)
115 {
116 FILE *In;
117 char *fname, *rcline = NULL, *tail;
118 int i, ret = -1;
119
120 fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
121 if ((In = fopen(fname, "r")) == NULL) {
122 MSG_ERR("[Dpi_read_comm_keys] %s\n", dStrerror(errno));
123 } else if ((rcline = dGetline(In)) == NULL) {
124 MSG_ERR("[Dpi_read_comm_keys] empty file: %s\n", fname);
125 } else {
126 *port = strtol(rcline, &tail, 10);
127 for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
128 SharedKey[i] = tail[i+1];
129 SharedKey[i] = 0;
130 ret = 1;
131 }
132 if (In)
133 fclose(In);
134 dFree(rcline);
135 dFree(fname);
136
137 return ret;
138 }
139
140 static int Dpi_check_dpid_ids()
141 {
142 struct sockaddr_in sin;
143 const socklen_t sin_sz = sizeof(sin);
144 int sock_fd, dpid_port, ret = -1;
145
146 /* socket connection test */
147 memset(&sin, 0, sizeof(sin));
148 sin.sin_family = AF_INET;
149 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
150
151 if (Dpi_read_comm_keys(&dpid_port) != -1) {
152 sin.sin_port = htons(dpid_port);
153 if ((sock_fd = Dpi_make_socket_fd()) == -1) {
154 MSG("Dpi_check_dpid_ids: sock_fd=%d %s\n", sock_fd, dStrerror(errno));
155 } else if (connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
156 MSG("Dpi_check_dpid_ids: %s\n", dStrerror(errno));
157 } else {
158 Dpi_close_fd(sock_fd);
159 ret = 1;
160 }
161 }
162 return ret;
163 }
164
165 static int Dpi_blocking_write(int fd, const char *msg, int msg_len)
166 {
167 int st, sent = 0;
168
169 while (sent < msg_len) {
170 st = write(fd, msg + sent, msg_len - sent);
171 if (st < 0) {
172 if (errno == EINTR) {
173 continue;
174 } else {
175 MSG_ERR("[Dpi_blocking_write] %s\n", dStrerror(errno));
176 break;
177 }
178 }
179 sent += st;
180 }
181
182 return (sent == msg_len) ? 1 : -1;
183 }
184
185 /*
186 * Start dpid.
187 * Return: 0 starting now, 1 Error.
188 */
189 static int Dpi_start_dpid(void)
190 {
191 pid_t pid;
192 int st_pipe[2], ret = 1;
193 char *answer;
194
195 /* create a pipe to track our child's status */
196 if (pipe(st_pipe))
197 return 1;
198
199 pid = fork();
200 if (pid == 0) {
201 /* This is the child process. Execute the command. */
202 char *path1 = dStrconcat(dGethomedir(), "/.dillo/dpid", NULL);
203 Dpi_close_fd(st_pipe[0]);
204 if (execl(path1, "dpid", (char*)NULL) == -1) {
205 dFree(path1);
206 if (execlp("dpid", "dpid", (char*)NULL) == -1) {
207 MSG("Dpi_start_dpid (child): %s\n", dStrerror(errno));
208 if (Dpi_blocking_write(st_pipe[1], "ERROR", 5) == -1) {
209 MSG("Dpi_start_dpid (child): can't write to pipe.\n");
210 }
211 Dpi_close_fd(st_pipe[1]);
212 _exit (EXIT_FAILURE);
213 }
214 }
215 } else if (pid < 0) {
216 /* The fork failed. Report failure. */
217 MSG("Dpi_start_dpid: %s\n", dStrerror(errno));
218 /* close the unused pipe */
219 Dpi_close_fd(st_pipe[0]);
220 Dpi_close_fd(st_pipe[1]);
221
222 } else {
223 /* This is the parent process, check our child status... */
224 Dpi_close_fd(st_pipe[1]);
225 if ((answer = Dpi_blocking_read(st_pipe[0])) != NULL) {
226 MSG("Dpi_start_dpid: can't start dpid\n");
227 dFree(answer);
228 } else {
229 ret = 0;
230 }
231 Dpi_close_fd(st_pipe[0]);
232 }
233
234 return ret;
235 }
236
237 /*
238 * Confirm that the dpid is running. If not, start it.
239 * Return: 0 running OK, 1 starting (EAGAIN), 2 Error.
240 */
241 static int Dpi_check_dpid(int num_tries)
242 {
243 static int starting = 0;
244 int check_st = 1, ret = 2;
245
246 check_st = Dpi_check_dpid_ids();
247 _MSG("Dpi_check_dpid: check_st=%d\n", check_st);
248
249 if (check_st == 1) {
250 /* connection test with dpi server passed */
251 starting = 0;
252 ret = 0;
253 } else {
254 if (!starting) {
255 /* start dpid */
256 if (Dpi_start_dpid() == 0) {
257 starting = 1;
258 ret = 1;
259 }
260 } else if (++starting < num_tries) {
261 /* starting */
262 ret = 1;
263 } else {
264 /* we waited too much, report an error... */
265 starting = 0;
266 }
267 }
268
269 _MSG("Dpi_check_dpid:: %s\n",
270 (ret == 0) ? "OK" : (ret == 1 ? "EAGAIN" : "ERROR"));
271 return ret;
272 }
273
274
275 static int Dpi_blocking_start_dpid(void)
276 {
277 int cst, try = 0,
278 n_tries = 12; /* 3 seconds */
279
280 /* test the dpid, and wait a bit for it to start if necessary */
281 while ((cst = Dpi_check_dpid(n_tries)) == 1) {
282 MSG("Dpi_blocking_start_dpid: try %d\n", ++try);
283 usleep(250000); /* 1/4 sec */
284 }
285 return cst;
286 }
287
288
289 /*
290 * Return the dpi server's port number, or -1 on error.
291 * (A query is sent to dpid and then its answer parsed)
292 * note: as the available servers and/or the dpi socket directory can
293 * change at any time, we'll ask each time. If someday we find
294 * that connecting each time significantly degrades performance,
295 * an optimized approach can be tried.
296 */
297 static int Dpi_get_server_port(const char *server_name)
298 {
299 int sock_fd = -1, dpi_port = -1;
300 int dpid_port, ok = 0;
301 struct sockaddr_in sin;
302 char *cmd, *request, *rply = NULL, *port_str;
303 socklen_t sin_sz;
304
305 dReturn_val_if_fail (server_name != NULL, dpi_port);
306 _MSG("Dpi_get_server_port:: server_name = [%s]\n", server_name);
307
308 /* Read dpid's port from saved file */
309 if (Dpi_read_comm_keys(&dpid_port) != -1) {
310 ok = 1;
311 }
312 if (ok) {
313 /* Connect a socket with dpid */
314 ok = 0;
315 sin_sz = sizeof(sin);
316 memset(&sin, 0, sizeof(sin));
317 sin.sin_family = AF_INET;
318 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
319 sin.sin_port = htons(dpid_port);
320 if ((sock_fd = Dpi_make_socket_fd()) == -1 ||
321 connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
322 MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
323 } else {
324 ok = 1;
325 }
326 }
327 if (ok) {
328 /* ask dpid to check the dpi and send its port number back */
329 ok = 0;
330 request = a_Dpip_build_cmd("cmd=%s msg=%s", "check_server", server_name);
331 _MSG("[%s]\n", request);
332
333 if (Dpi_blocking_write(sock_fd, request, strlen(request)) == -1) {
334 MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
335 } else {
336 ok = 1;
337 }
338 dFree(request);
339 }
340 if (ok) {
341 /* Get the reply */
342 ok = 0;
343 if ((rply = Dpi_blocking_read(sock_fd)) == NULL) {
344 MSG("Dpi_get_server_port: can't read server port from dpid.\n");
345 } else {
346 ok = 1;
347 }
348 }
349 if (ok) {
350 /* Parse reply */
351 ok = 0;
352 cmd = a_Dpip_get_attr(rply, "cmd");
353 if (strcmp(cmd, "send_data") == 0) {
354 port_str = a_Dpip_get_attr(rply, "msg");
355 _MSG("Dpi_get_server_port: rply=%s\n", rply);
356 _MSG("Dpi_get_server_port: port_str=%s\n", port_str);
357 dpi_port = strtol(port_str, NULL, 10);
358 dFree(port_str);
359 ok = 1;
360 }
361 dFree(cmd);
362 }
363 dFree(rply);
364 Dpi_close_fd(sock_fd);
365
366 return ok ? dpi_port : -1;
367 }
368
369
370 static int Dpi_connect_socket(const char *server_name, int retry)
371 {
372 struct sockaddr_in sin;
373 int sock_fd, err, dpi_port, ret=-1;
374 char *cmd = NULL;
375
376 /* Query dpid for the port number for this server */
377 if ((dpi_port = Dpi_get_server_port(server_name)) == -1) {
378 _MSG("Dpi_connect_socket:: can't get port number for %s\n", server_name);
379 return -1;
380 }
381 _MSG("Dpi_connect_socket: server=%s port=%d\n", server_name, dpi_port);
382
383 /* connect with this server's socket */
384 memset(&sin, 0, sizeof(sin));
385 sin.sin_family = AF_INET;
386 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
387 sin.sin_port = htons(dpi_port);
388
389 if ((sock_fd = Dpi_make_socket_fd()) == -1) {
390 perror("[dpi::socket]");
391 } else if (connect(sock_fd, (void*)&sin, sizeof(sin)) == -1) {
392 err = errno;
393 sock_fd = -1;
394 MSG("[dpi::connect] errno:%d %s\n", errno, dStrerror(errno));
395 if (retry) {
396 switch (err) {
397 case ECONNREFUSED: case EBADF: case ENOTSOCK: case EADDRNOTAVAIL:
398 sock_fd = Dpi_connect_socket(server_name, FALSE);
399 break;
400 }
401 }
402
403 /* send authentication Key (the server closes sock_fd on error) */
404 } else if (!(cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey))) {
405 MSG_ERR("[Dpi_connect_socket] Can't make auth message.\n");
406 } else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
407 MSG_ERR("[Dpi_connect_socket] Can't send auth message.\n");
408 } else {
409 ret = sock_fd;
410 }
411 dFree(cmd);
412
413 return ret;
414 }
415
416
417 char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd)
418 {
419 int cst, sock_fd;
420 char *ret = NULL;
421
422 /* test the dpid, and wait a bit for it to start if necessary */
423 if ((cst = Dpi_blocking_start_dpid()) != 0) {
424 return ret;
425 }
426
427 if ((sock_fd = Dpi_connect_socket(server_name, TRUE)) == -1) {
428 MSG_ERR("[a_Dpi_send_blocking_cmd] Can't connect to server.\n");
429 } else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
430 MSG_ERR("[a_Dpi_send_blocking_cmd] Can't send message.\n");
431 } if ((ret = Dpi_blocking_read(sock_fd)) == NULL) {
432 MSG_ERR("[a_Dpi_send_blocking_cmd] Can't read message.\n");
433 }
434 Dpi_close_fd(sock_fd);
435
436 return ret;
437 }
438
439
440
441 void a_Cookies_set(const char *cookie, const char *host, const char *path,
442 const char *date)
443 {
444 char *cmd, *dpip_tag;
445
446 if (date)
447 cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s date=%s",
448 "set_cookie", cookie,
449 host, path, date);
450 else
451 cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s",
452 "set_cookie", cookie,
453 host, path);
454
455 dpip_tag = a_Dpi_send_blocking_cmd("cookies", cmd);
456 _MSG("a_Cookies_set: dpip_tag = {%s}\n", dpip_tag);
457 dFree(dpip_tag);
458 dFree(cmd);
459 }
460
461
462 char *a_Cookies_get_query(const char *scheme, const char *host,
463 const char *path)
464 {
465 char *cmd, *dpip_tag, *query;
466
467 cmd = a_Dpip_build_cmd("cmd=%s scheme=%s host=%s path=%s",
468 "get_cookie", scheme,
469 host, path);
470
471 /* Get the answer from cookies.dpi */
472 _MSG("cookies.c: a_Dpi_send_blocking_cmd cmd = {%s}\n", cmd);
473 dpip_tag = a_Dpi_send_blocking_cmd("cookies", cmd);
474 _MSG("cookies.c: after a_Dpi_send_blocking_cmd resp={%s}\n", dpip_tag);
475 dFree(cmd);
476
477 if (dpip_tag != NULL) {
478 query = a_Dpip_get_attr(dpip_tag, "cookie");
479 dFree(dpip_tag);
480 } else {
481 query = dStrdup("");
482 }
483
484 return query;
485 }
486
487 static void expect(int lineno, const char *exp_reply,
488 const char *scheme, const char *host, const char *path)
489 {
490 char *reply = a_Cookies_get_query(scheme, host, path);
491
492 if (strcmp(reply, exp_reply)) {
493 MSG("line %d: EXPECTED: %s GOT: %s\n", lineno, exp_reply, reply);
494 failed++;
495 } else {
496 passed++;
497 }
498 }
499
500 static void toomany()
501 {
502 a_Cookies_set("1=1", "toomany.com", "/", NULL);
503 a_Cookies_set("2=1", "toomany.com", "/", NULL);
504 a_Cookies_set("3=1", "toomany.com", "/", NULL);
505 a_Cookies_set("4=1", "toomany.com", "/", NULL);
506 a_Cookies_set("5=1", "toomany.com", "/", NULL);
507 a_Cookies_set("6=1", "toomany.com", "/", NULL);
508 a_Cookies_set("7=1", "toomany.com", "/path/", NULL);
509 a_Cookies_set("8=1", "toomany.com", "/", NULL);
510 a_Cookies_set("9=1", "toomany.com", "/", NULL);
511 a_Cookies_set("10=1", "toomany.com", "/", NULL);
512 a_Cookies_set("11=1", "toomany.com", "/", NULL);
513 a_Cookies_set("12=1", "toomany.com", "/", NULL);
514 a_Cookies_set("13=1", "toomany.com", "/", NULL);
515 a_Cookies_set("14=1", "toomany.com", "/", NULL);
516 a_Cookies_set("15=1", "toomany.com", "/", NULL);
517 a_Cookies_set("16=1", "toomany.com", "/", NULL);
518 a_Cookies_set("17=1", "toomany.com", "/", NULL);
519 a_Cookies_set("18=1", "toomany.com", "/", NULL);
520 a_Cookies_set("19=1", "toomany.com", "/", NULL);
521 a_Cookies_set("20=1", "toomany.com", "/", NULL);
522 a_Cookies_set("21=1", "toomany.com", "/", NULL);
523 /* 1 was oldest and discarded */
524 expect(__LINE__, "Cookie: 7=1; 2=1; 3=1; 4=1; 5=1; 6=1; 8=1; 9=1; 10=1; "
525 "11=1; 12=1; 13=1; 14=1; 15=1; 16=1; 17=1; 18=1; 19=1; "
526 "20=1; 21=1\r\n", "http", "toomany.com", "/path/");
527 sleep(1);
528 /* touch all of them except #7 (path matching) */
529 expect(__LINE__, "Cookie: 2=1; 3=1; 4=1; 5=1; 6=1; 8=1; 9=1; 10=1; "
530 "11=1; 12=1; 13=1; 14=1; 15=1; 16=1; 17=1; 18=1; 19=1; "
531 "20=1; 21=1\r\n", "http", "toomany.com", "/");
532 a_Cookies_set("22=1", "toomany.com", "/", NULL);
533 /* 7 was oldest and discarded */
534 expect(__LINE__, "Cookie: 2=1; 3=1; 4=1; 5=1; 6=1; 8=1; 9=1; 10=1; "
535 "11=1; 12=1; 13=1; 14=1; 15=1; 16=1; 17=1; 18=1; 19=1; "
536 "20=1; 21=1; 22=1\r\n", "http", "toomany.com", "/path/");
537 }
538
539 static void maxage()
540 {
541 time_t t = time(NULL)+1000;
542 char *server_date = dStrdup(ctime(&t));
543
544 a_Cookies_set("name=val; max-age=0", "maxage0.com", "/", NULL);
545 expect(__LINE__, "", "http", "maxage0.com", "/");
546
547 a_Cookies_set("name=val; max-age=100", "maxage100.com", "/", NULL);
548 expect(__LINE__, "Cookie: name=val\r\n", "http", "maxage100.com", "/");
549
550 a_Cookies_set("name=val; max-age=-100", "maxage-100.com", "/", NULL);
551 expect(__LINE__, "", "http", "maxage-100.com", "/");
552
553 a_Cookies_set("name=val; max-age=2000000000", "maxage-huge.com", "/", NULL);
554 expect(__LINE__, "Cookie: name=val\r\n", "http", "maxage-huge.com", "/");
555 /* just having a server date shouldn't matter */
556
557 a_Cookies_set("name=val; max-age=0", "maxage0s.com", "/", server_date);
558 expect(__LINE__, "", "http", "maxage0s.com", "/");
559
560 a_Cookies_set("name=val; max-age=100", "maxage100s.com", "/", server_date);
561 expect(__LINE__, "Cookie: name=val\r\n", "http", "maxage100s.com", "/");
562
563 a_Cookies_set("name=val; max-age=-100", "maxage-100s.com", "/",server_date);
564 expect(__LINE__, "", "http", "maxage-100s.com", "/");
565
566 /* MAX-AGE and EXPIRES */
567 a_Cookies_set("name=val; max-age=90; expires=Wed Jan 20 01:26:32 2010",
568 "maxagelater.com", "/", NULL);
569 expect(__LINE__, "Cookie: name=val\r\n", "http", "maxagelater.com", "/");
570
571 a_Cookies_set("name=val; max-age=90; expires=Wed Jan 20 01:26:32 2010",
572 "maxagelaters.com", "/", server_date);
573 expect(__LINE__, "Cookie: name=val\r\n", "http", "maxagelaters.com", "/");
574
575 dFree(server_date);
576 }
577
578 static void expires_server_ahead()
579 {
580 char *string;
581 time_t t = time(NULL)+1000;
582 char *server_date = dStrdup(ctime(&t));
583 time_t expt = t + 1000;
584 char *exp_date = dStrdup(ctime(&expt));
585
586 string = dStrconcat("name=val; expires=", exp_date, NULL);
587 a_Cookies_set(string, "e2000s1000.com", "/", NULL);
588 expect(__LINE__, "Cookie: name=val\r\n", "http", "e2000s1000.com", "/");
589
590 a_Cookies_set(string, "e2000s1000s.com", "/", server_date);
591 expect(__LINE__, "Cookie: name=val\r\n", "http", "e2000s1000s.com", "/");
592
593 expt = t - 500; /* past for the server, future for us */
594 dFree(exp_date);
595 exp_date = dStrdup(ctime(&expt));
596
597 string = dStrconcat("name=val; expires=", exp_date, NULL);
598 a_Cookies_set(string, "e500s1000.com", "/", NULL);
599 expect(__LINE__, "Cookie: name=val\r\n", "http", "e500s1000.com", "/");
600
601 a_Cookies_set(string, "e500s1000s.com", "/", server_date);
602 expect(__LINE__, "", "http", "e500s1000s.com", "/");
603
604 expt = t; /* expire at future-for-us server date */
605 dFree(exp_date);
606 exp_date = dStrdup(ctime(&expt));
607
608 string = dStrconcat("name=val; expires=", exp_date, NULL);
609 a_Cookies_set(string, "e1000s1000.com", "/", NULL);
610 expect(__LINE__, "Cookie: name=val\r\n", "http", "e1000s1000.com", "/");
611
612 a_Cookies_set(string, "e1000s1000s.com", "/", server_date);
613 expect(__LINE__, "", "http", "e1000s1000s.com", "/");
614
615 expt = time(NULL); /* now */
616 dFree(exp_date);
617 exp_date = dStrdup(ctime(&expt));
618
619 string = dStrconcat("name=val; expires=", exp_date, NULL);
620 a_Cookies_set(string, "e0s1000.com", "/", NULL);
621 expect(__LINE__, "", "http", "e0s1000.com", "/");
622
623 a_Cookies_set(string, "e0s1000s.com", "/", server_date);
624 expect(__LINE__, "", "http", "e0s1000s.com", "/");
625
626 dFree(exp_date);
627 dFree(server_date);
628 }
629
630 static void expires_server_behind()
631 {
632 char *string;
633 time_t t = time(NULL)-1000;
634 char *server_date = dStrdup(ctime(&t));
635
636 time_t expt = t + 1000;
637 char *exp_date = dStrdup(ctime(&expt));
638
639 string = dStrconcat("name=val; expires=", exp_date, NULL);
640 a_Cookies_set(string, "e0s-1000.com", "/", NULL);
641 expect(__LINE__, "", "http", "e0s-1000.com", "/");
642
643 a_Cookies_set(string, "e0s-1000s.com", "/", server_date);
644 expect(__LINE__, "Cookie: name=val\r\n", "http", "e0s-1000s.com","/");
645
646 expt = t + 500; /* future for the server, past for us */
647 dFree(exp_date);
648 exp_date = dStrdup(ctime(&expt));
649
650 string = dStrconcat("name=val; expires=", exp_date, NULL);
651 a_Cookies_set(string, "e-500s-1000.com", "/", NULL);
652 expect(__LINE__, "", "http", "e-500s-1000.com", "/");
653
654 a_Cookies_set(string, "e-500s-1000s.com", "/", server_date);
655 expect(__LINE__, "Cookie: name=val\r\n", "http", "e-500s-1000s.com", "/");
656
657 expt = t; /* expire at past-for-us server date */
658 dFree(exp_date);
659 exp_date = dStrdup(ctime(&expt));
660
661 string = dStrconcat("name=val; expires=", exp_date, NULL);
662 a_Cookies_set(string, "e-1000s-1000.com", "/", NULL);
663 expect(__LINE__, "", "http", "e-1000s-1000.com", "/");
664
665 a_Cookies_set(string, "e-1000s-1000s.com", "/", server_date);
666 expect(__LINE__, "", "http", "e-1000s-1000s.com", "/");
667
668 dFree(server_date);
669 dFree(exp_date);
670 }
671
672 static void expires_extremes()
673 {
674 time_t t;
675 char *server_date;
676
677 a_Cookies_set("name=val; expires=Fri Dec 13 20:45:52 1901", "expmin.com",
678 "/", NULL);
679 expect(__LINE__, "", "http", "expmin.com", "/");
680
681 a_Cookies_set("name=val; expires=Wed Dec 31 23:59:59 1969", "expneg.com",
682 "/", NULL);
683 expect(__LINE__, "", "http", "expneg.com", "/");
684
685 a_Cookies_set("name=val; expires=Thu Jan 1 00:00:00 1970", "expepoch.com",
686 "/", NULL);
687 expect(__LINE__, "", "http", "expepoch.com", "/");
688
689 /* TODO: revisit these tests in a few decades */
690 a_Cookies_set("name=val; expires=Tue Jan 19 03:14:07 2038", "expmax.com",
691 "/", NULL);
692 expect(__LINE__, "Cookie: name=val\r\n", "http", "expmax.com", "/");
693
694 a_Cookies_set("name=val; expires=Sun Jan 1 00:00:00 2040", "pastmax.com",
695 "/", NULL);
696 expect(__LINE__, "Cookie: name=val\r\n", "http", "pastmax.com", "/");
697
698 t = time(NULL)+1000;
699 server_date = dStrdup(ctime(&t));
700
701 a_Cookies_set("name=val; expires=Fri Dec 13 20:45:52 1901", "expmina.com",
702 "/", server_date);
703 expect(__LINE__, "", "http", "expmina.com", "/");
704
705 a_Cookies_set("name=val; expires=Wed Dec 31 23:59:59 1969", "expnega.com",
706 "/", server_date);
707 expect(__LINE__, "", "http", "expnega.com", "/");
708
709 a_Cookies_set("name=val; expires=Thu Jan 1 00:00:00 1970", "expepocha.com",
710 "/", server_date);
711 expect(__LINE__, "", "http", "expepocha.com", "/");
712
713 a_Cookies_set("name=val; expires=Tue Jan 19 03:14:07 2038", "expmaxa.com",
714 "/", server_date);
715 expect(__LINE__, "Cookie: name=val\r\n", "http", "expmaxa.com", "/");
716
717 a_Cookies_set("name=val; expires=Sun Jan 1 00:00:00 2040", "pastmaxa.com",
718 "/", server_date);
719 expect(__LINE__, "Cookie: name=val\r\n", "http", "pastmaxa.com", "/");
720
721 t = time(NULL)-1000;
722 dFree(server_date);
723 server_date = dStrdup(ctime(&t));
724
725 a_Cookies_set("name=val; expires=Fri Dec 13 20:45:52 1901", "expminb.com",
726 "/", server_date);
727 expect(__LINE__, "", "http", "expminb.com", "/");
728
729 a_Cookies_set("name=val; expires=Wed Dec 31 23:59:59 1969", "expnegb.com",
730 "/", server_date);
731 expect(__LINE__, "", "http", "expnegb.com", "/");
732
733 a_Cookies_set("name=val; expires=Thu Jan 1 00:00:00 1970", "expepochb.com",
734 "/", server_date);
735 expect(__LINE__, "", "http", "expepochb.com", "/");
736
737 a_Cookies_set("name=val; expires=Tue Jan 19 03:14:07 2038", "expmaxb.com",
738 "/", server_date);
739 expect(__LINE__, "Cookie: name=val\r\n", "http", "expmaxb.com", "/");
740
741 a_Cookies_set("name=val; expires=Sun Jan 1 00:00:00 2040", "pastmaxb.com",
742 "/", server_date);
743 expect(__LINE__, "Cookie: name=val\r\n", "http", "pastmaxb.com", "/");
744
745 dFree(server_date);
746 }
747
748 static void path()
749 {
750 a_Cookies_set("name=val; path=/", "p1.com", "/", NULL);
751 expect(__LINE__, "Cookie: name=val\r\n", "http", "p1.com", "/");
752
753 a_Cookies_set("name=val; path=/dir1", "p2.com", "/dir2", NULL);
754 expect(__LINE__, "", "http", "p2.com", "/");
755 expect(__LINE__, "", "http", "p2.com", "/d");
756 expect(__LINE__, "Cookie: name=val\r\n", "http", "p2.com", "/dir1");
757 expect(__LINE__, "Cookie: name=val\r\n", "http", "p2.com", "/dir1/");
758 expect(__LINE__, "", "http", "p2.com", "/dir2");
759 expect(__LINE__, "", "http", "p2.com", "/dir11");
760
761 a_Cookies_set("name=val; path=dir1", "p3.com", "/dir2", NULL);
762 expect(__LINE__, "Cookie: name=val\r\n", "http", "p3.com", "/");
763 expect(__LINE__, "Cookie: name=val\r\n", "http", "p3.com", "/dir1");
764 expect(__LINE__, "Cookie: name=val\r\n", "http", "p3.com", "/dir2");
765
766 a_Cookies_set("name=val; path=/dir1/", "p4.com", "/dir2", NULL);
767 expect(__LINE__, "", "http", "p4.com", "/");
768 /* this next one strikes me as a bit odd, personally, but I suppose it's not
769 * a big deal */
770 expect(__LINE__, "", "http", "p4.com", "/dir1");
771 expect(__LINE__, "", "http", "p4.com", "/dir11");
772 expect(__LINE__, "Cookie: name=val\r\n", "http", "p4.com", "/dir1/");
773 expect(__LINE__, "Cookie: name=val\r\n", "http", "p4.com", "/dir1/sub");
774
775 a_Cookies_set("name=val", "p5.com", "/dir/subdir", NULL);
776 expect(__LINE__, "", "http", "p5.com", "/");
777 expect(__LINE__, "", "http", "p5.com", "/bir");
778 expect(__LINE__, "Cookie: name=val\r\n", "http", "p5.com", "/dir");
779 expect(__LINE__, "Cookie: name=val\r\n", "http", "p5.com", "/dir/");
780
781 a_Cookies_set("name=val", "p6.com", "/dir/subdir/", NULL);
782 expect(__LINE__, "", "http", "p6.com", "/dir/");
783 expect(__LINE__, "Cookie: name=val\r\n", "http", "p6.com", "/dir/subdir");
784 expect(__LINE__, "Cookie: name=val\r\n", "http", "p6.com", "/dir/subdir/s");
785 }
786
787 int main()
788 {
789 a_Cookies_set("name=val", "ordinary.com", "/", NULL);
790 expect(__LINE__, "Cookie: name=val\r\n", "http", "ordinary.com", "/");
791
792 toomany();
793 maxage();
794 expires_server_ahead();
795 expires_server_behind();
796 expires_extremes();
797
798 a_Cookies_set("name=val; expires=\"Sun Jan 10 00:00:00 2038\"",
799 "quoted-date.org", "/", NULL);
800 expect(__LINE__, "Cookie: name=val\r\n", "http", "quoted-date.org", "/");
801
802 a_Cookies_set("name=val; expires=\"Sun Jan 11 00:00:00 1970\"",
803 "quoted-pastdate.org", "/", NULL);
804 expect(__LINE__, "", "http", "quoted-pastdate.org", "/");
805
806 path();
807
808 /* LEADING/TRAILING DOTS AND A LITTLE PUBLIC SUFFIX */
809 a_Cookies_set("name=val; domain=co.uk", "www.co.uk", "/", NULL);
810 expect(__LINE__, "", "http", "www.co.uk", "/");
811
812 a_Cookies_set("name=val; domain=.co.uk", "www.co.uk", "/", NULL);
813 expect(__LINE__, "", "http", "www.co.uk", "/");
814
815 a_Cookies_set("name=val; domain=co.uk.", "www.co.uk.", "/", NULL);
816 expect(__LINE__, "", "http", "www.co.uk.", "/");
817
818 a_Cookies_set("name=val; domain=.co.uk.", "www.co.uk.", "/", NULL);
819 expect(__LINE__, "", "http", ".www.co.uk.", "/");
820
821 a_Cookies_set("name=val; domain=co.org", "www.co.org", "/", NULL);
822 expect(__LINE__, "Cookie: name=val\r\n", "http", "www.co.org", "/");
823
824 a_Cookies_set("name=val; domain=.cp.org", "www.cp.org", "/", NULL);
825 expect(__LINE__, "Cookie: name=val\r\n", "http", "www.cp.org", "/");
826
827
828 /* DOTDOMAIN */
829 a_Cookies_set("name=val; domain=.dotdomain.org", "dotdomain.org", "/",
830 NULL);
831 expect(__LINE__, "Cookie: name=val\r\n", "http", "dotdomain.org", "/");
832 expect(__LINE__, "Cookie: name=val\r\n", "http", "www.dotdomain.org", "/");
833
834 /* HOST_ONLY */
835 a_Cookies_set("name=val; domain=.hostonly.org", "hostonly.org", "/", NULL);
836 a_Cookies_set("name2=val2", "hostonly.org", "/", NULL);
837 a_Cookies_set("name3=val3; domain=hostonly.org", "hostonly.org", "/", NULL);
838 expect(__LINE__, "Cookie: name=val; name2=val2; name3=val3\r\n", "http",
839 "hostonly.org", "/");
840 a_Cookies_set("name=new; domain=.hostonly.org", "hostonly.org", "/", NULL);
841 expect(__LINE__, "Cookie: name=new; name2=val2; name3=val3\r\n", "http",
842 "hostonly.org", "/");
843 a_Cookies_set("name2=new2", "hostonly.org", "/", NULL);
844 expect(__LINE__, "Cookie: name=new; name2=new2; name3=val3\r\n", "http",
845 "hostonly.org", "/");
846 a_Cookies_set("name3=new3; domain=hostonly.org", "hostonly.org", "/", NULL);
847 expect(__LINE__, "Cookie: name=new; name2=new2; name3=new3\r\n", "http",
848 "hostonly.org", "/");
849
850 /* SUBDOMAIN */
851 a_Cookies_set("name=val; domain=www.subdomain.com", "subdomain.com", "/",
852 NULL);
853 a_Cookies_set("name=val; domain=.www.subdomain.com", "subdomain.com", "/",
854 NULL);
855 expect(__LINE__, "", "http", "subdomain.com", "/");
856 expect(__LINE__, "", "http", "www.subdomain.com", "/");
857
858 /* SUPERDOMAIN(?) */
859 a_Cookies_set("name=val; domain=.supdomain.com", "www.supdomain.com", "/",
860 NULL);
861 a_Cookies_set("name2=val2; domain=supdomain.com", "www.supdomain.com", "/",
862 NULL);
863 expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
864 "sub2.sub.supdomain.com", "/");
865 expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
866 "www.supdomain.com", "/");
867 expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
868 "supdomain.com", "/");
869
870 /* UNRELATED */
871 a_Cookies_set("name=val; domain=another.com", "unrelated.com", "/", NULL);
872 expect(__LINE__, "", "http", "another.com", "/");
873 a_Cookies_set("name=val; domain=another.com", "a.org", "/", NULL);
874 expect(__LINE__, "", "http", "another.com", "/");
875 a_Cookies_set("name=val; domain=another.com", "badguys.com", "/", NULL);
876 expect(__LINE__, "", "http", "another.com", "/");
877 a_Cookies_set("name=val; domain=another.com", "more.badguys.com", "/",
878 NULL);
879 expect(__LINE__, "", "http", "another.com", "/");
880 a_Cookies_set("name=val; domain=another.com", "verybadguys.com", "/", NULL);
881 expect(__LINE__, "", "http", "another.com", "/");
882
883 a_Cookies_set("name=val; domain=similar.com", "imilar.com", "/", NULL);
884 a_Cookies_set("name2=val2; domain=similar.com", "ssimilar.com", "/", NULL);
885 a_Cookies_set("name3=val3; domain=.similar.com", "imilar.com", "/", NULL);
886 a_Cookies_set("name4=val4; domain=.similar.com", "timilar.com", "/", NULL);
887 a_Cookies_set("name4=val4; domain=.similar.com", "tiimilar.com", "/", NULL);
888 expect(__LINE__, "", "http", "similar.com", "/");
889
890 /* SECURE */
891 a_Cookies_set("name=val; secure", "secure.com", "/", NULL);
892 expect(__LINE__, "", "http", "secure.com", "/");
893 expect(__LINE__, "Cookie: name=val\r\n", "https", "secure.com", "/");
894
895 /* HTTPONLY */
896 a_Cookies_set("name=val; HttpOnly", "httponly.net", "/", NULL);
897 expect(__LINE__, "Cookie: name=val\r\n", "http", "httponly.net", "/");
898
899 /* GIBBERISH ATTR IGNORED */
900 a_Cookies_set("name=val; ldkfals", "gibberish.net", "/", NULL);
901 expect(__LINE__, "Cookie: name=val\r\n", "http", "gibberish.net", "/");
902
903 /* WHITESPACE/DELIMITERS */
904 a_Cookies_set(" name=val ", "whitespace.net", "/", NULL);
905 a_Cookies_set("name2=val2;", "whitespace.net", "/", NULL);
906 expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
907 "whitespace.net", "/");
908
909 /* NAMELESS/VALUELESS */
910 a_Cookies_set("value", "nonameval.org", "/", NULL);
911 a_Cookies_set("name=", "nonameval.org", "/", NULL);
912 a_Cookies_set("name2= ", "nonameval.org", "/", NULL);
913 expect(__LINE__, "Cookie: name=; name2=\r\n", "http", "nonameval.org", "/");
914 a_Cookies_set("=val2", "nonameval.org", "/", NULL);
915 expect(__LINE__, "Cookie: name=; name2=\r\n", "http", "nonameval.org", "/");
916
917
918 /* SOME IP ADDRS */
919
920 a_Cookies_set("name=val", "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]",
921 "/", NULL);
922 expect(__LINE__, "Cookie: name=val\r\n", "http",
923 "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]", "/");
924
925 a_Cookies_set("name=val", "[::FFFF:129.144.52.38]", "/", NULL);
926 expect(__LINE__, "Cookie: name=val\r\n", "http", "[::FFFF:129.144.52.38]",
927 "/");
928
929 a_Cookies_set("name=val", "127.0.0.1", "/", NULL);
930 expect(__LINE__, "Cookie: name=val\r\n", "http", "127.0.0.1", "/");
931
932 a_Cookies_set("name=val; domain=128.0.0.1", "128.0.0.1", "/", NULL);
933 expect(__LINE__, "Cookie: name=val\r\n", "http", "128.0.0.1", "/");
934
935 a_Cookies_set("name=val; domain=130.0.0.1", "129.0.0.1", "/", NULL);
936 expect(__LINE__, "", "http", "129.0.0.1", "/");
937 expect(__LINE__, "", "http", "130.0.0.1", "/");
938
939 a_Cookies_set("name=val", "2.0.0.1", "/", NULL);
940 a_Cookies_set("name=bad; domain=22.0.0.1", "2.0.0.1", "/", NULL);
941 a_Cookies_set("name=bad; domain=.0.0.1", "2.0.0.1", "/", NULL);
942 a_Cookies_set("name=bad; domain=not-ip.org", "2.0.0.1", "/", NULL);
943 expect(__LINE__, "", "http", "22.0.0.1", "/");
944 expect(__LINE__, "", "http", "not-ip.org", "/");
945 expect(__LINE__, "Cookie: name=val\r\n", "http", "2.0.0.1", "/");
946
947 #if 0
948 HAD BEEN PLAYING AROUND WITH REAL PUBLIC SUFFIX
949 a_Cookies_set("name=val;domain=sub.sub.yokohama.jp", "sub.sub.yokohama.jp", "/", NULL);
950 MSG("sub sub yokohama should work: %s\n",
951 a_Cookies_get_query("http", "sub.sub.yokohama.jp", "/"));
952 a_Cookies_set("name=val; domain=sub.tokyo.jp", "sub.sub.tokyo.jp", "/", NULL);
953 MSG("sub tokyo jp should fail: %s\n",
954 a_Cookies_get_query("http", "sub.sub.tokyo.jp", "/"));
955 a_Cookies_set("name=val; domain=pref.chiba.jp", "sub.pref.chiba.jp", "/", NULL);
956 MSG("pref chiba jp should succeed: %s\n",
957 a_Cookies_get_query("http", "sub.pref.chiba.jp", "/"));
958 a_Cookies_set("name=val; domain=org", "www.dillo.org", "/", NULL);
959 a_Cookies_set("name=val; domain=org", "dillo.org", "/", NULL);
960 a_Cookies_set("name=val; domain=org", ".dillo.org", "/", NULL);
961 a_Cookies_set("name=val; domain=org.", ".dillo.org", "/", NULL);
962 a_Cookies_set("name=val; domain=org.", ".dillo.org.", "/", NULL);
963 MSG("org should fail: %s\n",
964 a_Cookies_get_query("http", "www.dillo.org", "/"));
965 #endif
966
967 MSG("TESTS: passed: %u failed: %u\n", passed, failed);
968 return 0;
969 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <ctype.h>
22 #include <FL/Fl_Window.H>
23 #include <FL/Fl.H>
24
25 #include "../dw/core.hh"
26 #include "../dw/fltkcore.hh"
27 #include "../dw/fltkviewport.hh"
28 #include "../dw/textblock.hh"
29
30 using namespace lout::container::typed;
31 using namespace dw;
32 using namespace dw::core;
33 using namespace dw::core::style;
34 using namespace dw::fltk;
35
36 static FltkPlatform *platform;
37 static Layout *layout;
38 static Fl_Window *window;
39 static FltkViewport *viewport;
40 static Style *topWidgetStyle, *widgetStyle, *wordStyle, *headingStyle;
41 static Textblock *topTextblock = NULL;
42 static int textblockNo = 0;
43
44 static const char *numbers[10] = {
45 "one", "two", "three", "four", "five",
46 "six", "seven", "eight", "nine", "ten"
47 };
48
49 static void anchorCallback (Fl_Widget *widget, void *data)
50 {
51 layout->setAnchor (numbers[(long)data]);
52 }
53
54 static void textTimeout (void *data)
55 {
56 Textblock *oldTop = topTextblock;
57 topTextblock = new Textblock (false);
58
59 if (oldTop) {
60 oldTop->addLinebreak (wordStyle);
61 oldTop->addWidget (topTextblock, widgetStyle);
62 } else {
63 topTextblock->setStyle (topWidgetStyle);
64 layout->setWidget (topTextblock);
65 }
66
67 topTextblock->addAnchor (numbers[textblockNo], headingStyle);
68
69 char buf[16];
70 strcpy (buf, numbers[textblockNo]);
71 buf[0] = toupper (buf[0]);
72 topTextblock->addText (buf, headingStyle);
73 topTextblock->addParbreak (5, headingStyle);
74
75 for (int i = 0; i < 30; i++) {
76 strcpy (buf, numbers[textblockNo]);
77 if (i == 0)
78 buf[0] = toupper (buf[0]);
79 strcat (buf, i == 29 ? "." : ",");
80
81 topTextblock->addText (buf, wordStyle);
82 topTextblock->addSpace (wordStyle);
83 }
84
85 topTextblock->flush ();
86
87 textblockNo++;
88 if (textblockNo < 10)
89 Fl::repeat_timeout (1, textTimeout, NULL);
90
91 }
92
93 int main(int argc, char **argv)
94 {
95 char *buttonLabel[10];
96
97 platform = new FltkPlatform ();
98 layout = new Layout (platform);
99
100 window = new Fl_Window(250, 200, "Dw Anchors Test");
101 window->box(FL_NO_BOX);
102 window->begin();
103
104 viewport = new FltkViewport (50, 0, 200, 200);
105 viewport->end();
106 layout->attachView (viewport);
107
108 for (int i = 0; i < 10; i++) {
109 char buf[16];
110 strcpy (buf, numbers[i]);
111 buf[0] = toupper (buf[0]);
112 buttonLabel[i] = strdup(buf);
113 Fl_Button *button = new Fl_Button(0, 20 * i, 50, 20, buttonLabel[i]);
114 button->callback (anchorCallback, (void*)(long)i);
115 button->when (FL_WHEN_RELEASE);
116 }
117
118 FontAttrs fontAttrs;
119 fontAttrs.name = "Bitstream Charter";
120 fontAttrs.size = 14;
121 fontAttrs.weight = 400;
122 fontAttrs.style = FONT_STYLE_NORMAL;
123 fontAttrs.letterSpacing = 0;
124 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
125
126 StyleAttrs styleAttrs;
127 styleAttrs.initValues ();
128 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
129 styleAttrs.margin.setVal (5);
130 styleAttrs.color = Color::create (layout, 0x000000);
131 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
132 topWidgetStyle = Style::create (layout, &styleAttrs);
133
134 styleAttrs.margin.left = 20;
135 styleAttrs.margin.right = 0;
136 styleAttrs.backgroundColor = NULL;
137 widgetStyle = Style::create (layout, &styleAttrs);
138
139 styleAttrs.margin.left = 0;
140 wordStyle = Style::create (layout, &styleAttrs);
141
142 fontAttrs.size = 28;
143 fontAttrs.weight = 700;
144 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
145 headingStyle = Style::create (layout, &styleAttrs);
146
147 Fl::add_timeout (0, textTimeout, NULL);
148
149 window->resizable(viewport);
150 window->show();
151
152 int errorCode = Fl::run();
153
154 topWidgetStyle->unref ();
155 widgetStyle->unref ();
156 wordStyle->unref ();
157 headingStyle->unref ();
158 for (int i = 0; i < 10; i++)
159 free(buttonLabel[i]);
160 delete layout;
161
162 return errorCode;
163 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl_Window.H>
22 #include <FL/Fl.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/textblock.hh"
28 #include "../dw/listitem.hh"
29
30 using namespace dw;
31 using namespace dw::core;
32 using namespace dw::core::style;
33 using namespace dw::fltk;
34
35 int main(int argc, char **argv)
36 {
37 FltkPlatform *platform = new FltkPlatform ();
38 Layout *layout = new Layout (platform);
39
40 Fl_Window *window = new Fl_Window(200, 300, "Dw Border Test");
41 window->box(FL_NO_BOX);
42 window->begin();
43
44 FltkViewport *viewport = new FltkViewport (0, 0, 200, 300);
45 layout->attachView (viewport);
46
47 StyleAttrs styleAttrs;
48 styleAttrs.initValues ();
49 styleAttrs.margin.setVal (5);
50 styleAttrs.borderWidth.setVal (2);
51 styleAttrs.setBorderColor (Color::create (layout, 0xffffff));
52 styleAttrs.setBorderStyle (BORDER_INSET);
53 styleAttrs.padding.setVal (5);
54
55 FontAttrs fontAttrs;
56 fontAttrs.name = "Bitstream Charter";
57 fontAttrs.size = 14;
58 fontAttrs.weight = 400;
59 fontAttrs.style = FONT_STYLE_NORMAL;
60 fontAttrs.letterSpacing = 0;
61 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
62 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
63
64 styleAttrs.color = Color::create (layout, 0x000000);
65 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
66
67 Style *widgetStyle1 = Style::create (layout, &styleAttrs);
68
69 styleAttrs.backgroundColor = Color::create (layout, 0xffff80);
70 styleAttrs.margin.setVal (0);
71 styleAttrs.borderWidth.setVal (1);
72 styleAttrs.setBorderColor (Color::create (layout, 0x4040ff));
73 styleAttrs.setBorderStyle (BORDER_SOLID);
74 styleAttrs.padding.setVal (1);
75
76 Style *widgetStyle2 = Style::create (layout, &styleAttrs);
77
78 Textblock *textblock1 = new Textblock (false);
79 textblock1->setStyle (widgetStyle1);
80 layout->setWidget (textblock1);
81
82 widgetStyle1->unref();
83
84 styleAttrs.borderWidth.setVal (0);
85 styleAttrs.padding.setVal (0);
86 styleAttrs.backgroundColor = NULL;
87 styleAttrs.cursor = CURSOR_TEXT;
88
89 Style *wordStyle = Style::create (layout, &styleAttrs);
90
91 const char *words1[] = { "Some", "random", "text.", NULL };
92 const char *words2[] = { "A", "nested", "paragraph.", NULL };
93
94 for(int i = 0; words1[i]; i++) {
95 if(i != 0)
96 textblock1->addSpace (wordStyle);
97 textblock1->addText (words1[i], wordStyle);
98 }
99
100 for(int i = 0; i < 1; i++) {
101 textblock1->addParbreak(0, wordStyle);
102
103 Textblock *textblock2 = new Textblock (false);
104 textblock1->addWidget (textblock2, widgetStyle2);
105
106 for(int j = 0; words2[j]; j++) {
107 if(j != 0)
108 textblock2->addSpace (wordStyle);
109 textblock2->addText (words2[j], wordStyle);
110 }
111
112 textblock2->flush ();
113 }
114
115 textblock1->flush ();
116
117 window->resizable(viewport);
118 window->show();
119 int errorCode = Fl::run();
120
121 widgetStyle2->unref();
122 wordStyle->unref();
123 delete layout;
124
125 return errorCode;
126 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl_Window.H>
22 #include <FL/Fl.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/textblock.hh"
28
29
30 int main(int argc, char **argv)
31 {
32 dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform ();
33 dw::core::Layout *layout = new dw::core::Layout (platform);
34
35 Fl_Window *window = new Fl_Window(200, 300, "Dw Example");
36 window->box(FL_NO_BOX);
37 window->begin();
38
39 dw::fltk::FltkViewport *viewport =
40 new dw::fltk::FltkViewport (0, 0, 200, 300);
41 layout->attachView (viewport);
42
43 dw::core::style::StyleAttrs styleAttrs;
44 styleAttrs.initValues ();
45 styleAttrs.margin.setVal (5);
46
47 dw::core::style::FontAttrs fontAttrs;
48 fontAttrs.name = "Bitstream Charter";
49 fontAttrs.size = 14;
50 fontAttrs.weight = 400;
51 fontAttrs.style = dw::core::style::FONT_STYLE_NORMAL;
52 fontAttrs.letterSpacing = 0;
53 fontAttrs.fontVariant = dw::core::style::FONT_VARIANT_NORMAL;
54 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
55
56 styleAttrs.color =
57 dw::core::style::Color::create (layout, 0x000000);
58 styleAttrs.backgroundColor =
59 dw::core::style::Color::create (layout, 0xffffff);
60
61 dw::core::style::Style *widgetStyle =
62 dw::core::style::Style::create (layout, &styleAttrs);
63
64 dw::Textblock *textblock = new dw::Textblock (false);
65 textblock->setStyle (widgetStyle);
66 layout->setWidget (textblock);
67
68 widgetStyle->unref();
69
70 styleAttrs.margin.setVal (0);
71 styleAttrs.backgroundColor = NULL;
72
73 dw::core::style::Style *wordStyle =
74 dw::core::style::Style::create (layout, &styleAttrs);
75
76 for(int i = 1; i <= 10; i++) {
77 char buf[4];
78 sprintf(buf, "%d.", i);
79
80 const char *words[] = { "This", "is", "the", buf, "paragraph.",
81 "Here", "comes", "some", "more", "text",
82 "to", "demonstrate", "word", "wrapping.",
83 NULL };
84
85 for(int j = 0; words[j]; j++) {
86 textblock->addText(words[j], wordStyle);
87 textblock->addSpace(wordStyle);
88 }
89
90 textblock->addParbreak(10, wordStyle);
91 }
92
93 wordStyle->unref();
94
95 textblock->flush ();
96
97 window->resizable(viewport);
98 window->show();
99 int errorCode = Fl::run();
100
101 delete layout;
102
103 return errorCode;
104 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23 #include <FL/Fl_Box.H>
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/textblock.hh"
28
29 using namespace lout::container::typed;
30 using namespace dw;
31 using namespace dw::core;
32 using namespace dw::core::style;
33 using namespace dw::fltk;
34
35 static FltkPlatform *platform;
36 static Layout *layout;
37 static Fl_Window *window;
38 static FltkViewport *viewport;
39 static Fl_Button *findButton, *resetButton;
40 static Fl_Widget *resultLabel;
41
42 static void findCallback (Fl_Widget *widget, void *data)
43 {
44 //switch(layout->search ("worm", true)) {
45 switch(layout->search ("WORM", false, false)) {
46 case FindtextState::SUCCESS:
47 resultLabel->label("SUCCESS");
48 break;
49
50 case FindtextState::RESTART:
51 resultLabel->label("RESTART");
52 break;
53
54 case FindtextState::NOT_FOUND:
55 resultLabel->label("NOT_FOUND");
56 break;
57 }
58
59 resultLabel->redraw ();
60 }
61
62 static void resetCallback (Fl_Widget *widget, void *data)
63 {
64 layout->resetSearch ();
65 resultLabel->label("---");
66 resultLabel->redraw ();
67 }
68
69 int main(int argc, char **argv)
70 {
71 platform = new FltkPlatform ();
72 layout = new Layout (platform);
73
74 window = new Fl_Window(200, 300, "Dw Find Test");
75 window->box(FL_NO_BOX);
76 window->begin();
77
78 viewport = new FltkViewport (0, 0, 200, 280);
79 viewport->end();
80 layout->attachView (viewport);
81
82 findButton = new Fl_Button(0, 280, 50, 20, "Find");
83 findButton->callback (findCallback, NULL);
84 findButton->when (FL_WHEN_RELEASE);
85 findButton->clear_visible_focus ();
86
87 resetButton = new Fl_Button(50, 280, 50, 20, "Reset");
88 resetButton->callback (resetCallback, NULL);
89 resetButton->when (FL_WHEN_RELEASE);
90 resetButton->clear_visible_focus ();
91
92 resultLabel = new Fl_Box(100, 280, 100, 20, "---");
93 resultLabel->box(FL_FLAT_BOX);
94
95 FontAttrs fontAttrs;
96 fontAttrs.name = "Bitstream Charter";
97 fontAttrs.size = 14;
98 fontAttrs.weight = 400;
99 fontAttrs.style = FONT_STYLE_NORMAL;
100 fontAttrs.letterSpacing = 0;
101 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
102
103 StyleAttrs styleAttrs;
104 styleAttrs.initValues ();
105 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
106 styleAttrs.margin.setVal (10);
107 styleAttrs.color = Color::create (layout, 0x000000);
108 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
109 Style *topWidgetStyle = Style::create (layout, &styleAttrs);
110
111 styleAttrs.margin.setVal (0);
112 styleAttrs.margin.left = 30;
113 styleAttrs.backgroundColor = NULL;
114 Style *widgetStyle = Style::create (layout, &styleAttrs);
115
116 styleAttrs.margin.left = 0;
117 Style *wordStyle = Style::create (layout, &styleAttrs);
118
119 Textblock *textblock = new Textblock (false);
120 textblock->setStyle (topWidgetStyle);
121 layout->setWidget (textblock);
122
123 Stack <Textblock> *stack = new Stack <Textblock> (false);
124 stack->push (textblock);
125
126 for(int i = 0; i < 10; i++)
127 for(int j = 0; j < 10; j++) {
128 Textblock *current;
129 if(j < 5) {
130 current = new Textblock (false);
131 stack->getTop()->addWidget (current, widgetStyle);
132 stack->push (current);
133 } else {
134 stack->getTop()->flush ();
135 stack->pop ();
136 current = stack->getTop ();
137 }
138
139 current->addText ((i == j ? "worm" : "apple"), wordStyle);
140 current->addLinebreak (wordStyle);
141 }
142
143 stack->getTop()->flush ();
144
145 topWidgetStyle->unref ();
146 widgetStyle->unref ();
147 wordStyle->unref ();
148
149 window->resizable(viewport);
150 window->show();
151 int errorCode = Fl::run();
152
153 delete layout;
154
155 return errorCode;
156 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/textblock.hh"
28 #include "../dw/image.hh"
29
30 using namespace dw;
31 using namespace dw::core;
32 using namespace dw::core::style;
33 using namespace dw::fltk;
34
35 static Layout *layout;
36 static Image *image;
37 static core::Imgbuf *imgbuf = NULL;
38 static int imgRow = 0;
39
40 static void imageInitTimeout (void *data)
41 {
42 //imgbuf = layout->createImgbuf (Imgbuf::RGBA, 400, 200);
43 imgbuf = layout->createImgbuf (Imgbuf::RGB, 400, 200);
44 image->setBuffer (imgbuf);
45 }
46
47 /*
48 static void imageDrawTimeout (void *data)
49 {
50 if (imgbuf) {
51 for (int i = 0; i < 1; i++) {
52 byte buf[4 * 400];
53 for(int x = 0; x < 400; x++) {
54 buf[4 * x + 0] = x * 255 / 399;
55 buf[4 * x + 1] = (399 - x) * 255 / 399;
56 buf[4 * x + 2] = imgRow * 255 / 199;
57 buf[4 * x + 3] = (199 - imgRow) * 255 / 199;
58 }
59
60 imgbuf->copyRow (imgRow, buf);
61 image->drawRow (imgRow);
62 imgRow++;
63 }
64 }
65
66 if(imgRow < 200)
67 ::fltk::repeat_timeout (0.5, imageDrawTimeout, NULL);
68 }
69 */
70
71 static void imageDrawTimeout (void *data)
72 {
73 if (imgbuf) {
74 for (int i = 0; i < 1; i++) {
75 byte buf[3 * 400];
76 for(int x = 0; x < 400; x++) {
77 buf[3 * x + 0] = x * 255 / 399;
78 buf[3 * x + 1] = (399 - x) * 255 / 399;
79 buf[3 * x + 2] = imgRow * 255 / 199;
80 }
81
82 imgbuf->copyRow (imgRow, buf);
83 image->drawRow (imgRow);
84 imgRow++;
85 }
86 }
87
88 if(imgRow < 200)
89 Fl::repeat_timeout (0.5, imageDrawTimeout, NULL);
90 }
91
92 int main(int argc, char **argv)
93 {
94 FltkPlatform *platform = new FltkPlatform ();
95 layout = new Layout (platform);
96
97 Fl_Window *window = new Fl_Window(410, 210, "Dw Scaled Image");
98 window->box(FL_NO_BOX);
99 window->begin();
100
101 FltkViewport *viewport = new FltkViewport (0, 0, 410, 210);
102 layout->attachView (viewport);
103
104 StyleAttrs styleAttrs;
105 styleAttrs.initValues ();
106 styleAttrs.margin.setVal (5);
107 styleAttrs.width = createPerLength (1.0);
108 styleAttrs.height = createPerLength (1.0);
109
110 FontAttrs fontAttrs;
111 fontAttrs.name = "Bitstream Charter";
112 fontAttrs.size = 14;
113 fontAttrs.weight = 400;
114 fontAttrs.style = FONT_STYLE_NORMAL;
115 fontAttrs.letterSpacing = 0;
116 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
117 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
118
119 styleAttrs.color = Color::create (layout, 0x000000);
120 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
121
122 Style *widgetStyle = Style::create (layout, &styleAttrs);
123
124 Textblock *textblock = new Textblock (false);
125 textblock->setStyle (widgetStyle);
126 layout->setWidget (textblock);
127
128 widgetStyle->unref();
129
130 styleAttrs.margin.setVal (0);
131 styleAttrs.backgroundColor = NULL;
132
133 Style *imageStyle = Style::create (layout, &styleAttrs);
134
135 image = new dw::Image ("");
136 textblock->addWidget (image, imageStyle);
137 textblock->addSpace (imageStyle);
138
139 imageStyle->unref();
140
141 textblock->flush ();
142
143 window->resizable(viewport);
144 window->show();
145
146 Fl::add_timeout (2.0, imageInitTimeout, NULL);
147 Fl::add_timeout (0.1, imageDrawTimeout, NULL);
148
149 int errorCode = Fl::run();
150
151 delete layout;
152
153 return errorCode;
154 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/textblock.hh"
28 #include "../dw/image.hh"
29
30 using namespace dw;
31 using namespace dw::core;
32 using namespace dw::core::style;
33 using namespace dw::fltk;
34
35 static Layout *layout;
36 static Image *image1, *image2;
37 static core::Imgbuf *imgbuf = NULL;
38 static int imgRow = 0;
39
40 static void imageInitTimeout (void *data)
41 {
42 imgbuf = layout->createImgbuf (Imgbuf::RGB, 400, 200);
43 image1->setBuffer (imgbuf);
44 image2->setBuffer (imgbuf);
45 }
46
47 static void imageDrawTimeout (void *data)
48 {
49 if (imgbuf) {
50 for (int i = 0; i < 1; i++) {
51 byte buf[3 * 400];
52 for(int x = 0; x < 400; x++) {
53 buf[3 * x + 0] = x * 255 / 399;
54 buf[3 * x + 1] = (399 - x) * 255 / 399;
55 buf[3 * x + 2] = imgRow * 255 / 199;
56 }
57
58 imgbuf->copyRow (imgRow, buf);
59 image1->drawRow (imgRow);
60 image2->drawRow (imgRow);
61 imgRow++;
62 }
63 }
64
65 if(imgRow < 200)
66 Fl::repeat_timeout (0.5, imageDrawTimeout, NULL);
67 }
68
69 int main(int argc, char **argv)
70 {
71 FltkPlatform *platform = new FltkPlatform ();
72 layout = new Layout (platform);
73
74 Fl_Window *window = new Fl_Window(410, 210, "Dw Scaled Image 2");
75 window->box(FL_NO_BOX);
76 window->begin();
77
78 FltkViewport *viewport = new FltkViewport (0, 0, 410, 210);
79 layout->attachView (viewport);
80
81 StyleAttrs styleAttrs;
82 styleAttrs.initValues ();
83 styleAttrs.margin.setVal (5);
84
85 FontAttrs fontAttrs;
86 fontAttrs.name = "Bitstream Charter";
87 fontAttrs.size = 14;
88 fontAttrs.weight = 400;
89 fontAttrs.style = FONT_STYLE_NORMAL;
90 fontAttrs.letterSpacing = 0;
91 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
92 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
93
94 styleAttrs.color = Color::create (layout, 0x000000);
95 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
96
97 Style *widgetStyle = Style::create (layout, &styleAttrs);
98
99 Textblock *textblock = new Textblock (false);
100 textblock->setStyle (widgetStyle);
101 layout->setWidget (textblock);
102
103 widgetStyle->unref();
104
105 styleAttrs.margin.setVal (0);
106 styleAttrs.borderWidth.setVal (0);
107 styleAttrs.padding.setVal (0);
108 styleAttrs.backgroundColor = NULL;
109
110 Style *wordStyle = Style::create (layout, &styleAttrs);
111
112 styleAttrs.borderWidth.setVal (1);
113 styleAttrs.setBorderColor (Color::create (layout, 0x000080));
114 styleAttrs.setBorderStyle (BORDER_SOLID);
115 styleAttrs.padding.setVal (1);
116 styleAttrs.backgroundColor = NULL;
117 styleAttrs.width = createPerLength (0.5);
118 styleAttrs.height = createPerLength (0.5);
119
120 Style *imageStyle1 = Style::create (layout, &styleAttrs);
121 image1 = new dw::Image ("A longer ALT Text to demonstrate clipping.");
122 textblock->addWidget (image1, imageStyle1);
123 imageStyle1->unref();
124
125 textblock->addParbreak (10, wordStyle);
126
127 styleAttrs.width = LENGTH_AUTO;
128 styleAttrs.height = LENGTH_AUTO;
129
130 Style *imageStyle2 = Style::create (layout, &styleAttrs);
131 image2 = new dw::Image ("A longer ALT Text to demonstrate clipping.");
132 textblock->addWidget (image2, imageStyle2);
133 imageStyle2->unref();
134
135 wordStyle->unref ();
136 textblock->flush ();
137
138 window->resizable(viewport);
139 window->show();
140
141 Fl::add_timeout (3.0, imageInitTimeout, NULL);
142 Fl::add_timeout (0.1, imageDrawTimeout, NULL);
143
144 int errorCode = Fl::run();
145
146 delete layout;
147
148 return errorCode;
149 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/textblock.hh"
28 #include "../dw/image.hh"
29
30 using namespace dw;
31 using namespace dw::core;
32 using namespace dw::core::style;
33 using namespace dw::fltk;
34
35 static Layout *layout;
36 static Image *image;
37 static core::Imgbuf *imgbuf = NULL;
38 static int imgRow = 0;
39
40 static void imageInitTimeout (void *data)
41 {
42 const bool resize = true;
43 //imgbuf = layout->createImgbuf (Imgbuf::RGBA, 400, 200);
44 imgbuf = layout->createImgbuf (Imgbuf::RGB, 400, 200);
45 image->setBuffer (imgbuf, resize);
46 }
47
48 /*
49 static void imageDrawTimeout (void *data)
50 {
51 if (imgbuf) {
52 for (int i = 0; i < 1; i++) {
53 byte buf[4 * 400];
54 for(int x = 0; x < 400; x++) {
55 buf[4 * x + 0] = x * 255 / 399;
56 buf[4 * x + 1] = (399 - x) * 255 / 399;
57 buf[4 * x + 2] = imgRow * 255 / 199;
58 buf[4 * x + 3] = (199 - imgRow) * 255 / 199;
59 }
60
61 imgbuf->copyRow (imgRow, buf);
62 image->drawRow (imgRow);
63 imgRow++;
64 }
65 }
66
67 if(imgRow < 200)
68 Fl::repeat_timeout (0.5, imageDrawTimeout, NULL);
69 }
70 */
71
72 static void imageDrawTimeout (void *data)
73 {
74 if (imgbuf) {
75 for (int i = 0; i < 1; i++) {
76 byte buf[3 * 400];
77 for(int x = 0; x < 400; x++) {
78 buf[3 * x + 0] = x * 255 / 399;
79 buf[3 * x + 1] = (399 - x) * 255 / 399;
80 buf[3 * x + 2] = imgRow * 255 / 199;
81 }
82
83 imgbuf->copyRow (imgRow, buf);
84 image->drawRow (imgRow);
85 imgRow++;
86 }
87 }
88
89 if(imgRow < 200)
90 Fl::repeat_timeout (0.5, imageDrawTimeout, NULL);
91 }
92
93 int main(int argc, char **argv)
94 {
95 FltkPlatform *platform = new FltkPlatform ();
96 layout = new Layout (platform);
97
98 Fl_Window *window = new Fl_Window(410, 210, "Dw Simple Image");
99 window->box(FL_NO_BOX);
100 window->begin();
101
102 FltkViewport *viewport = new FltkViewport (0, 0, 410, 210);
103 layout->attachView (viewport);
104
105 StyleAttrs styleAttrs;
106 styleAttrs.initValues ();
107 styleAttrs.margin.setVal (5);
108
109 FontAttrs fontAttrs;
110 fontAttrs.name = "Bitstream Charter";
111 fontAttrs.size = 14;
112 fontAttrs.weight = 400;
113 fontAttrs.style = FONT_STYLE_NORMAL;
114 fontAttrs.letterSpacing = 0;
115 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
116 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
117
118 styleAttrs.color = Color::create (layout, 0x000000);
119 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
120
121 Style *widgetStyle = Style::create (layout, &styleAttrs);
122
123 Textblock *textblock = new Textblock (false);
124 textblock->setStyle (widgetStyle);
125 layout->setWidget (textblock);
126
127 widgetStyle->unref();
128
129 styleAttrs.margin.setVal (0);
130 styleAttrs.backgroundColor = NULL;
131
132 Style *imageStyle = Style::create (layout, &styleAttrs);
133
134 image = new dw::Image ("");
135 textblock->addWidget (image, imageStyle);
136 textblock->addSpace (imageStyle);
137
138 imageStyle->unref();
139
140 textblock->flush ();
141
142 window->resizable(viewport);
143 window->show();
144
145 Fl::add_timeout (2.0, imageInitTimeout, NULL);
146 Fl::add_timeout (0.1, imageDrawTimeout, NULL);
147
148 int errorCode = Fl::run();
149
150 delete layout;
151
152 return errorCode;
153 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "../dw/core.hh"
20 #include "../dw/fltkcore.hh"
21
22 using namespace lout::signal;
23 using namespace dw::core;
24 using namespace dw::fltk;
25
26 void solution1 ()
27 {
28 FltkPlatform *platform = new FltkPlatform ();
29 Layout *layout = new Layout (platform);
30
31 Imgbuf *rootbuf = layout->createImgbuf (Imgbuf::RGB, 100, 100);
32 rootbuf->ref (); // Extra reference by the dicache.
33 printf ("=== Can be deleted? %s.\n",
34 rootbuf->lastReference () ? "Yes" : "No");
35 Imgbuf *scaledbuf = rootbuf->getScaledBuf (50, 50);
36 printf ("=== Can be deleted? %s.\n",
37 rootbuf->lastReference () ? "Yes" : "No");
38 rootbuf->unref ();
39 printf ("=== Can be deleted? %s.\n",
40 rootbuf->lastReference () ? "Yes" : "No");
41 scaledbuf->unref ();
42 printf ("=== Can be deleted? %s.\n",
43 rootbuf->lastReference () ? "Yes" : "No");
44 rootbuf->unref (); // Extra reference by the dicache.
45
46 delete layout;
47 }
48
49 void solution2 ()
50 {
51 FltkPlatform *platform = new FltkPlatform ();
52 Layout *layout = new Layout (platform);
53
54 Imgbuf *rootbuf = layout->createImgbuf (Imgbuf::RGB, 100, 100);
55 rootbuf->setDeleteOnUnref (false);
56 printf ("=== Can be deleted? %s.\n",
57 !rootbuf->isReferred () ? "Yes" : "No");
58 Imgbuf *scaledbuf = rootbuf->getScaledBuf (50, 50);
59 printf ("=== Can be deleted? %s.\n",
60 !rootbuf->isReferred () ? "Yes" : "No");
61 rootbuf->unref ();
62 printf ("=== Can be deleted? %s.\n",
63 !rootbuf->isReferred () ? "Yes" : "No");
64 scaledbuf->unref ();
65 printf ("=== Can be deleted? %s.\n",
66 !rootbuf->isReferred () ? "Yes" : "No");
67 delete rootbuf;
68
69 delete layout;
70 }
71
72 class RootbufDeletionReceiver: public ObservedObject::DeletionReceiver
73 {
74 void deleted (ObservedObject *object);
75 };
76
77 void RootbufDeletionReceiver::deleted (ObservedObject *object)
78 {
79 printf ("=== Is deleted now.\n");
80 delete this;
81 }
82
83 void solution3 ()
84 {
85 FltkPlatform *platform = new FltkPlatform ();
86 Layout *layout = new Layout (platform);
87
88 Imgbuf *rootbuf = layout->createImgbuf (Imgbuf::RGB, 100, 100);
89 rootbuf->connectDeletion (new RootbufDeletionReceiver ());
90 Imgbuf *scaledbuf = rootbuf->getScaledBuf (50, 50);
91 rootbuf->unref ();
92 scaledbuf->unref ();
93
94 delete layout;
95 }
96
97 int main (int argc, char **argv)
98 {
99 printf ("========== SOLUTION 1 ==========\n");
100 solution1 ();
101 printf ("========== SOLUTION 2 ==========\n");
102 solution2 ();
103 printf ("========== SOLUTION 3 ==========\n");
104 solution3 ();
105
106 return 0;
107 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/textblock.hh"
28
29 using namespace dw;
30 using namespace dw::core;
31 using namespace dw::core::style;
32 using namespace dw::fltk;
33
34 class LinkTestReceiver: public Layout::LinkReceiver
35 {
36 bool enter (Widget *widget, int link, int img, int x, int y);
37 bool press (Widget *widget, int link, int img, int x, int y,
38 EventButton *event);
39 bool release (Widget *widget, int link, int img, int x, int y,
40 EventButton *event);
41 bool click (Widget *widget, int link, int img,
42 int x, int y, EventButton *event);
43 };
44
45 bool LinkTestReceiver::enter (Widget *widget, int link, int img, int x, int y)
46 {
47 printf ("enter: %d\n", link);
48 return true;
49 }
50
51 bool LinkTestReceiver::press (Widget *widget, int link, int img, int x, int y,
52 EventButton *event)
53 {
54 printf ("press: %d\n", link);
55 return true;
56 }
57
58 bool LinkTestReceiver::release (Widget *widget, int link, int img, int x,int y,
59 EventButton *event)
60 {
61 printf ("release: %d\n", link);
62 return true;
63 }
64
65 bool LinkTestReceiver::click (Widget *widget, int link, int img, int x, int y,
66 EventButton *event)
67 {
68 printf ("click: %d\n", link);
69 return true;
70 }
71
72 int main(int argc, char **argv)
73 {
74 LinkTestReceiver linkTestReceiver;
75 FltkPlatform *platform = new FltkPlatform ();
76 Layout *layout = new Layout (platform);
77
78 Fl_Window *window = new Fl_Window(200, 300, "Dw Links");
79 window->box(FL_NO_BOX);
80 window->begin();
81
82 FltkViewport *viewport = new FltkViewport (0, 0, 200, 300);
83 layout->attachView (viewport);
84
85 StyleAttrs styleAttrs;
86 styleAttrs.initValues ();
87 styleAttrs.margin.setVal (5);
88
89 FontAttrs fontAttrs;
90 fontAttrs.name = "Bitstream Charter";
91 fontAttrs.size = 14;
92 fontAttrs.weight = 400;
93 fontAttrs.style = FONT_STYLE_NORMAL;
94 fontAttrs.letterSpacing = 0;
95 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
96 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
97
98 styleAttrs.color = Color::create (layout, 0x000000);
99 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
100
101 Style *widgetStyle = Style::create (layout, &styleAttrs);
102
103 Textblock *textblock = new Textblock (false);
104 textblock->setStyle (widgetStyle);
105 layout->setWidget (textblock);
106
107 layout->connectLink (&linkTestReceiver);
108
109 widgetStyle->unref();
110
111 styleAttrs.margin.setVal (0);
112 styleAttrs.backgroundColor = NULL;
113 styleAttrs.cursor = CURSOR_TEXT;
114
115 Style *wordStyle = Style::create (layout, &styleAttrs);
116
117 styleAttrs.color = Color::create (layout, 0x0000ff);
118 styleAttrs.textDecoration = TEXT_DECORATION_UNDERLINE;
119 styleAttrs.cursor = CURSOR_POINTER;
120
121 for(int i = 1; i <= 10; i++) {
122 char buf[4];
123 sprintf(buf, "%d.", i);
124
125 const char *words1[] = {
126 "This", "is", "the", buf, "paragraph.",
127 "Here", "comes", "some", "more", "text",
128 "to", "demonstrate", "word", "wrapping.",
129 NULL };
130 const char *words2[] = {
131 "Click", "here", "for", "more..", NULL };
132
133 for(int j = 0; words1[j]; j++) {
134 textblock->addText(words1[j], wordStyle);
135 textblock->addSpace(wordStyle);
136 }
137
138 styleAttrs.x_link = i;
139 Style *linkStyle = Style::create (layout, &styleAttrs);
140
141 for(int j = 0; words2[j]; j++) {
142 textblock->addText(words2[j], linkStyle);
143 textblock->addSpace(wordStyle);
144 }
145
146 linkStyle->unref ();
147
148 textblock->addParbreak(10, wordStyle);
149 }
150
151 wordStyle->unref();
152
153 textblock->flush ();
154
155 window->resizable(viewport);
156 window->show();
157 int errorCode = Fl::run();
158
159 delete layout;
160
161 return errorCode;
162 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23 #include <FL/Fl_Box.H>
24
25 #include "../dw/core.hh"
26 #include "../dw/fltkcore.hh"
27 #include "../dw/fltkviewport.hh"
28 #include "../dw/textblock.hh"
29
30 using namespace dw;
31 using namespace dw::core;
32 using namespace dw::core::style;
33 using namespace dw::fltk;
34
35 class LinkTestReceiver: public Layout::LinkReceiver
36 {
37 bool enter (Widget *widget, int link, int img, int x, int y);
38 bool press (Widget *widget, int link, int img, int x, int y,
39 EventButton *event);
40 bool release (Widget *widget, int link, int img, int x, int y,
41 EventButton *event);
42 bool click (Widget *widget, int link, int img, int x, int y,
43 EventButton *event);
44 };
45
46 bool LinkTestReceiver::enter (Widget *widget, int link, int img, int x, int y)
47 {
48 printf ("enter: %d\n", link);
49 return true;
50 }
51
52 bool LinkTestReceiver::press (Widget *widget, int link, int img, int x, int y,
53 EventButton *event)
54 {
55 printf ("press: %d\n", link);
56 return true;
57 }
58
59 bool LinkTestReceiver::release (Widget *widget, int link, int img, int x,int y,
60 EventButton *event)
61 {
62 printf ("release: %d\n", link);
63 return true;
64 }
65
66 bool LinkTestReceiver::click (Widget *widget, int link, int img, int x, int y,
67 EventButton *event)
68 {
69 printf ("click: %d\n", link);
70 return true;
71 }
72
73 int main(int argc, char **argv)
74 {
75 int MainIdx;
76 int ww = 200, wh = 300, lh = 24;
77
78 FltkPlatform *platform = new FltkPlatform ();
79 Layout *layout = new Layout (platform);
80
81 Fl_Window *window = new Fl_Window(200, 300, "Dw Links2");
82 window->box(FL_NO_BOX);
83 window->begin();
84 Fl_Widget *Panel = new Fl_Box(0, 0, ww, lh, "CONTROL PANEL");
85
86 Panel->color(FL_GRAY_RAMP + 3);
87 Panel->labelcolor(FL_WHITE);
88 Panel->box(FL_FLAT_BOX);
89 Fl_Widget *Main = new Fl_Box(0, lh, ww, wh - 2*lh, "MAIN RENDERING AREA");
90 Main->color(FL_GRAY_RAMP + 4);
91 Main->labelcolor(FL_WHITE);
92 MainIdx = window->find(Main);
93 /* status bar */
94 Fl_Widget *Bar = new Fl_Box(0, wh - lh, 200, lh, "STATUS BAR...");
95 Bar->color(FL_GRAY_RAMP + 3);
96 Bar->labelcolor(FL_WHITE);
97 Bar->box(FL_FLAT_BOX);
98
99 window->resizable(Main);
100 window->end();
101
102 //
103 // Create the main Dw and add some text there.
104 //
105 window->remove(MainIdx);
106 window->begin();
107
108 FltkViewport *viewport = new FltkViewport (0, lh, ww, wh - 2*lh);
109 layout->attachView (viewport);
110
111 window->end();
112
113
114 StyleAttrs styleAttrs;
115 styleAttrs.initValues ();
116 styleAttrs.margin.setVal (5);
117
118 FontAttrs fontAttrs;
119 fontAttrs.name = "Bitstream Charter";
120 fontAttrs.size = 14;
121 fontAttrs.weight = 400;
122 fontAttrs.style = FONT_STYLE_NORMAL;
123 fontAttrs.letterSpacing = 0;
124 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
125 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
126
127 styleAttrs.color = Color::create (layout, 0x000000);
128 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
129
130 Style *widgetStyle = Style::create (layout, &styleAttrs);
131
132 Textblock *textblock = new Textblock (false);
133 textblock->setStyle (widgetStyle);
134 layout->setWidget (textblock);
135
136 layout->connectLink (new LinkTestReceiver ());
137
138 widgetStyle->unref();
139
140 styleAttrs.margin.setVal (0);
141 styleAttrs.backgroundColor = NULL;
142 styleAttrs.cursor = CURSOR_TEXT;
143
144 Style *wordStyle = Style::create (layout, &styleAttrs);
145
146 styleAttrs.color = Color::create (layout, 0x0000ff);
147 styleAttrs.textDecoration = TEXT_DECORATION_UNDERLINE;
148 styleAttrs.cursor = CURSOR_POINTER;
149
150 for(int i = 1; i <= 30; i++) {
151 char buf[4];
152 sprintf(buf, "%d.", i);
153
154 const char *words1[] = {
155 "This", "is", "the", buf, "paragraph.",
156 "Here", "comes", "some", "more", "text",
157 "to", "demonstrate", "word", "wrapping.",
158 NULL };
159 const char *words2[] = {
160 "Click", "here", "for", "more..", NULL };
161
162 for(int j = 0; words1[j]; j++) {
163 textblock->addText (words1[j], wordStyle);
164 textblock->addSpace(wordStyle);
165 }
166
167 styleAttrs.x_link = i;
168 Style *linkStyle = Style::create (layout, &styleAttrs);
169
170 for(int j = 0; words2[j]; j++) {
171 textblock->addText (words2[j], linkStyle);
172 textblock->addSpace(wordStyle);
173 }
174
175 linkStyle->unref ();
176
177 textblock->addParbreak(10, wordStyle);
178 }
179
180 wordStyle->unref();
181
182 textblock->flush ();
183
184 window->resizable(viewport);
185 window->show();
186 int errorCode = Fl::run();
187
188 delete layout;
189
190 return errorCode;
191 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/textblock.hh"
28 #include "../dw/listitem.hh"
29
30 using namespace dw;
31 using namespace dw::core;
32 using namespace dw::core::style;
33 using namespace dw::fltk;
34
35 int main(int argc, char **argv)
36 {
37 FltkPlatform *platform = new FltkPlatform ();
38 Layout *layout = new Layout (platform);
39
40 Fl_Window *window = new Fl_Window(200, 300, "Dw Lists");
41 window->box(FL_NO_BOX);
42 window->begin();
43
44 FltkViewport *viewport = new FltkViewport (0, 0, 200, 300);
45 layout->attachView (viewport);
46
47 StyleAttrs styleAttrs;
48 styleAttrs.initValues ();
49 styleAttrs.margin.setVal (5);
50
51 FontAttrs fontAttrs;
52 fontAttrs.name = "Bitstream Charter";
53 fontAttrs.size = 14;
54 fontAttrs.weight = 400;
55 fontAttrs.style = FONT_STYLE_NORMAL;
56 fontAttrs.letterSpacing = 0;
57 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
58 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
59
60 styleAttrs.color = Color::create (layout, 0x000000);
61 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
62
63 Style *widgetStyle = Style::create (layout, &styleAttrs);
64
65 Textblock *textblock = new Textblock (false);
66 textblock->setStyle (widgetStyle);
67 layout->setWidget (textblock);
68
69 widgetStyle->unref();
70
71 styleAttrs.margin.setVal (0);
72 styleAttrs.backgroundColor = NULL;
73 styleAttrs.cursor = CURSOR_TEXT;
74
75 Style *wordStyle = Style::create (layout, &styleAttrs);
76
77 styleAttrs.margin.setVal (5);
78 styleAttrs.padding.setVal (5);
79 styleAttrs.backgroundColor = Color::create (layout, 0xffff40);
80 styleAttrs.setBorderColor (Color::create (layout, 0x000000));
81 styleAttrs.setBorderStyle (BORDER_SOLID);
82 styleAttrs.borderWidth.setVal (1);
83
84 Style *itemStyle = Style::create (layout, &styleAttrs);
85
86 const char *wordsPar[] = {
87 "This", "is", "a", "normal", "paragraph.", "And",
88 "some", "list", "items", "follow:", NULL };
89 const char *wordsItem[] = {
90 "This", "is", "a", "list", "item.", "Here",
91 "comes", "some", "more", "text", "to",
92 "demonstrate", "word", "wrapping.", NULL };
93
94
95 for(int i = 0; wordsPar[i]; i++) {
96 if(i != 0)
97 textblock->addSpace (wordStyle);
98 textblock->addText (wordsPar[i], wordStyle);
99 }
100 textblock->addParbreak (5, wordStyle);
101
102 ListItem *refItem = NULL;
103
104 for(int i = 1; i <= 100; i++) {
105 ListItem *listItem = new ListItem (refItem, false);
106 refItem = listItem;
107
108 textblock->addWidget (listItem, itemStyle);
109 textblock->addParbreak (2, wordStyle);
110
111 char buf[16];
112 sprintf (buf, "%d.", i);
113 listItem->initWithText (buf, wordStyle);
114
115 for(int j = 0; wordsItem[j]; j++) {
116 if(j != 0)
117 listItem->addSpace (wordStyle);
118 listItem->addText (wordsItem[j], wordStyle);
119 }
120
121 listItem->flush ();
122 }
123
124 wordStyle->unref();
125
126 textblock->flush ();
127
128 window->resizable(viewport);
129 window->show();
130 int errorCode = Fl::run();
131
132 delete layout;
133
134 return errorCode;
135 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/textblock.hh"
28 #include "../dw/ui.hh"
29
30 using namespace dw;
31 using namespace dw::core;
32 using namespace dw::core::style;
33 using namespace dw::core::ui;
34 using namespace dw::fltk;
35
36 int main(int argc, char **argv)
37 {
38 FltkPlatform *platform = new FltkPlatform ();
39 Layout *layout = new Layout (platform);
40
41 Fl_Window *window = new Fl_Window(410, 210, "Dw Resource test");
42 window->box(FL_NO_BOX);
43 window->begin();
44
45 FltkViewport *viewport = new FltkViewport (0, 0, 410, 210);
46 layout->attachView (viewport);
47
48 StyleAttrs styleAttrs;
49 styleAttrs.initValues ();
50 styleAttrs.margin.setVal (5);
51
52 FontAttrs fontAttrs;
53 fontAttrs.name = "Bitstream Charter";
54 fontAttrs.size = 14;
55 fontAttrs.weight = 400;
56 fontAttrs.style = FONT_STYLE_NORMAL;
57 fontAttrs.letterSpacing = 0;
58 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
59 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
60
61 styleAttrs.color = Color::create (layout, 0x000000);
62 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
63
64 Style *widgetStyle = Style::create (layout, &styleAttrs);
65
66 Textblock *textblock = new Textblock (false);
67 textblock->setStyle (widgetStyle);
68 layout->setWidget (textblock);
69
70 widgetStyle->unref();
71
72 styleAttrs.margin.setVal (0);
73 styleAttrs.backgroundColor = NULL;
74
75 widgetStyle = Style::create (layout, &styleAttrs);
76
77 SelectionResource *res = layout->getResourceFactory()->createListResource
78 (ListResource::SELECTION_AT_MOST_ONE, 4);
79 //SelectionResource *res =
80 // layout->getResourceFactory()->createOptionMenuResource ();
81
82 Embed *embed = new Embed (res);
83 textblock->addWidget (embed, widgetStyle);
84 textblock->addSpace (widgetStyle);
85
86 widgetStyle->unref();
87
88 for(int i = 0; i < 50; i++)
89 res->addItem ("Hello, world!", true, i == 0 ? true : false);
90
91 textblock->flush ();
92
93 window->resizable(viewport);
94 window->show();
95
96 int errorCode = Fl::run();
97
98 delete layout;
99
100 return errorCode;
101 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/table.hh"
28 #include "../dw/tablecell.hh"
29
30 using namespace dw;
31 using namespace dw::core;
32 using namespace dw::core::style;
33 using namespace dw::fltk;
34
35 int main(int argc, char **argv)
36 {
37 FltkPlatform *platform = new FltkPlatform ();
38 Layout *layout = new Layout (platform);
39
40 Fl_Window *window = new Fl_Window(300, 300, "Dw Table");
41 window->box(FL_NO_BOX);
42 window->begin();
43
44 FltkViewport *viewport = new FltkViewport (0, 0, 300, 300);
45 layout->attachView (viewport);
46
47 StyleAttrs styleAttrs;
48 styleAttrs.initValues ();
49 styleAttrs.margin.setVal (5);
50 styleAttrs.padding.setVal (0);
51 styleAttrs.borderWidth.setVal (1);
52 styleAttrs.setBorderStyle (BORDER_OUTSET);
53 styleAttrs.setBorderColor (Color::create (layout, 0xffffff));
54 styleAttrs.color = Color::create (layout, 0x000000);
55 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
56 styleAttrs.hBorderSpacing = 5;
57 styleAttrs.vBorderSpacing = 5;
58
59 FontAttrs fontAttrs;
60 fontAttrs.name = "Bitstream Charter";
61 fontAttrs.size = 14;
62 fontAttrs.weight = 400;
63 fontAttrs.style = FONT_STYLE_NORMAL;
64 fontAttrs.letterSpacing = 0;
65 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
66 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
67
68 Style *tableStyle = Style::create (layout, &styleAttrs);
69
70 Table *table = new Table (false);
71 table->setStyle (tableStyle);
72 layout->setWidget (table);
73
74 tableStyle->unref();
75
76 styleAttrs.setBorderStyle (BORDER_INSET);
77 styleAttrs.backgroundColor = NULL;
78 styleAttrs.margin.setVal (0);
79 styleAttrs.padding.setVal (5);
80
81 Style *cellStyle = Style::create (layout, &styleAttrs);
82
83 styleAttrs.borderWidth.setVal (0);
84 styleAttrs.margin.setVal (0);
85 styleAttrs.cursor = CURSOR_TEXT;
86 styleAttrs.textAlignChar = '.';
87
88 Style *wordStyle = Style::create (layout, &styleAttrs);
89
90 for (int i = 0; i < 4; i++) {
91 table->addRow (wordStyle);
92
93 for (int j = 0; j < 4; j++) {
94 Textblock *cell = new Textblock (false);
95 cell->setStyle (cellStyle);
96 table->addCell (cell, 1, 1);
97
98 char buf[10];
99 sprintf (buf, "cell %c", 'A' + 4 * i + j);
100
101 cell->addText (buf, wordStyle);
102 cell->flush ();
103 }
104 }
105
106 wordStyle->unref();
107 cellStyle->unref();
108
109 window->resizable(viewport);
110 window->show();
111 int errorCode = Fl::run();
112
113 delete layout;
114
115 return errorCode;
116 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/table.hh"
28 #include "../dw/tablecell.hh"
29
30 using namespace dw;
31 using namespace dw::core;
32 using namespace dw::core::style;
33 using namespace dw::fltk;
34
35 int main(int argc, char **argv)
36 {
37 FltkPlatform *platform = new FltkPlatform ();
38 Layout *layout = new Layout (platform);
39
40 Fl_Window *window = new Fl_Window(200, 300, "Dw Table Aligned");
41 window->box(FL_NO_BOX);
42 window->begin();
43
44 FltkViewport *viewport = new FltkViewport (0, 0, 200, 300);
45 layout->attachView (viewport);
46
47 StyleAttrs styleAttrs;
48 styleAttrs.initValues ();
49 styleAttrs.margin.setVal (5);
50 styleAttrs.borderWidth.setVal (1);
51 styleAttrs.setBorderStyle (BORDER_OUTSET);
52 styleAttrs.setBorderColor (Color::create (layout, 0x808080));
53
54 FontAttrs fontAttrs;
55 fontAttrs.name = "Bitstream Charter";
56 fontAttrs.size = 14;
57 fontAttrs.weight = 400;
58 fontAttrs.style = FONT_STYLE_NORMAL;
59 fontAttrs.letterSpacing = 0;
60 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
61 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
62
63 styleAttrs.color = Color::create (layout, 0x000000);
64 styleAttrs.backgroundColor = Color::create (layout, 0xa0a0a0);
65 styleAttrs.hBorderSpacing = 5;
66 styleAttrs.vBorderSpacing = 5;
67
68 Style *tableStyle = Style::create (layout, &styleAttrs);
69
70 Table *table = new Table (false);
71 table->setStyle (tableStyle);
72 layout->setWidget (table);
73
74 tableStyle->unref();
75
76 styleAttrs.borderWidth.setVal (1);
77 styleAttrs.setBorderStyle (BORDER_INSET);
78
79 Style *cellStyle = Style::create (layout, &styleAttrs);
80
81 styleAttrs.borderWidth.setVal (0);
82 styleAttrs.margin.setVal (0);
83 styleAttrs.backgroundColor = NULL;
84 styleAttrs.cursor = CURSOR_TEXT;
85 styleAttrs.textAlignChar = '.';
86
87 Style *wordStyle = Style::create (layout, &styleAttrs);
88
89 TableCell *ref = NULL;
90 for(int i = 0; i < 10; i++) {
91 //for(int i = 0; i < 1; i++) {
92 TableCell *cell = new TableCell (ref, false);
93 cell->setStyle (cellStyle);
94 ref = cell;
95 table->addRow (wordStyle);
96 table->addCell (cell, 1, 1);
97
98 char buf[16];
99 for(int j = 0; j < i; j++)
100 buf[j] = '0' + j;
101 buf[i] = '.';
102 for(int j = i + 1; j < 11; j++)
103 buf[j] = '0' + (j - 1);
104 buf[11] = 0;
105
106 cell->addText (buf, wordStyle);
107 cell->flush ();
108 }
109
110 wordStyle->unref();
111 cellStyle->unref();
112
113 window->resizable(viewport);
114 window->show();
115 int errorCode = Fl::run();
116
117 delete layout;
118
119 return errorCode;
120 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl.H>
22 #include <FL/Fl_Window.H>
23
24 #include "../dw/core.hh"
25 #include "../dw/fltkcore.hh"
26 #include "../dw/fltkviewport.hh"
27 #include "../dw/table.hh"
28 #include "../dw/textblock.hh"
29 #include "../dw/ui.hh"
30 #include "form.hh"
31
32 using namespace lout::object;
33 using namespace lout::container::typed;
34 using namespace dw;
35 using namespace dw::core;
36 using namespace dw::core::style;
37 using namespace dw::core::ui;
38 using namespace dw::fltk;
39
40 int main(int argc, char **argv)
41 {
42 FltkPlatform *platform = new FltkPlatform ();
43 Layout *layout = new Layout (platform);
44
45 Fl_Window *window = new Fl_Window(400, 400, "Dw UI Test");
46 window->box(FL_NO_BOX);
47 window->begin();
48
49 FltkViewport *viewport = new FltkViewport (0, 0, 400, 400);
50 layout->attachView (viewport);
51
52 StyleAttrs styleAttrs;
53 styleAttrs.initValues ();
54 styleAttrs.margin.setVal (5);
55 styleAttrs.color = Color::create (layout, 0x000000);
56 styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
57
58 FontAttrs fontAttrs;
59 fontAttrs.name = "Helvetica";
60 fontAttrs.size = 14;
61 fontAttrs.weight = 400;
62 fontAttrs.style = FONT_STYLE_NORMAL;
63 fontAttrs.letterSpacing = 0;
64 fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
65 styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
66
67 Style *tableStyle = Style::create (layout, &styleAttrs);
68
69 Table *table = new Table (false);
70 table->setStyle (tableStyle);
71 layout->setWidget (table);
72
73 tableStyle->unref();
74
75 styleAttrs.backgroundColor = NULL;
76 styleAttrs.margin.setVal (0);
77
78 Style *cellStyle = Style::create (layout, &styleAttrs);
79
80 // First of all, the resources. Later, they are embedded into the
81 // widget tree.
82 EntryResource *entryres1 =
83 layout->getResourceFactory()->createEntryResource (10, false, NULL);
84 entryres1->setText ("Hi!");
85 EntryResource *entryres2 =
86 layout->getResourceFactory()->createEntryResource (10, true, NULL);
87 MultiLineTextResource *textres =
88 layout->getResourceFactory()->createMultiLineTextResource (15,3);
89 RadioButtonResource *radiores1 =
90 layout->getResourceFactory()->createRadioButtonResource (NULL, false);
91 RadioButtonResource *radiores2 =
92 layout->getResourceFactory()->createRadioButtonResource (radiores1,
93 false);
94 CheckButtonResource *checkres =
95 layout->getResourceFactory()->createCheckButtonResource (true);
96 SelectionResource *selres[2];
97 selres[0] = layout->getResourceFactory()->createOptionMenuResource ();
98 selres[1] = layout->getResourceFactory()->createListResource
99 (ListResource::SELECTION_AT_MOST_ONE, 4);
100 LabelButtonResource *buttonres =
101 layout->getResourceFactory()->createLabelButtonResource ("Run!");
102
103 // Note on complex buttons: before any operations on the widget, which
104 // need a layout, the complex button resource should be created, since
105 // then, a layout and a platform are instantiated.
106 Textblock *cbuttontext = new Textblock(false);
107 ComplexButtonResource *cbuttonres =
108 layout->getResourceFactory()->createComplexButtonResource (cbuttontext,
109 true);
110 cbuttontext->setStyle (cellStyle);
111 cbuttontext->addText ("Run (complex)!", cellStyle);
112 cbuttontext->flush ();
113
114 // The entry resources are put into a special handler, which is
115 // also a receiver for the button resources.
116 form::Form *form = new form::Form();
117 form->addTextResource ("val1", entryres1);
118 form->addTextResource ("val2", entryres2);
119 form->addTextResource ("text", textres);
120 const char *radiovalues[] = { "radio1", "radio2", NULL };
121 form->addRadioButtonResource ("val3", radiores1, radiovalues);
122 form->addCheckButtonResource ("check", checkres);
123 const char *selvalues[] = { "i1", "g1", "i11", "i12", "i13", "i2", "g2",
124 "i21", "i22", "i23", "i3", NULL };
125 form->addSelectionResource ("val4", selres[0], selvalues);
126 form->addSelectionResource ("val5", selres[1], selvalues);
127 form->addButtonResource ("button", buttonres, "Run!");
128 form->addButtonResource ("cbutton", cbuttonres, "cbuttonval");
129
130 // Create the widgets.
131 table->addRow (cellStyle);
132
133 Textblock *label1 = new Textblock(false);
134 label1->setStyle (cellStyle);
135 table->addCell (label1, 1, 1);
136 label1->addText ("val1 = ", cellStyle);
137 label1->flush ();
138
139 Embed *input1 = new Embed (entryres1);
140 input1->setStyle (cellStyle);
141 table->addCell (input1, 1, 1);
142
143 table->addRow (cellStyle);
144
145 Textblock *label2 = new Textblock(false);
146 label2->setStyle (cellStyle);
147 table->addCell (label2, 1, 1);
148 label2->addText ("val2 = ", cellStyle);
149 label2->flush ();
150
151 Embed *input2 = new Embed (entryres2);
152 input2->setStyle (cellStyle);
153 table->addCell (input2, 1, 1);
154
155 table->addRow (cellStyle);
156
157 Textblock *label = new Textblock(false);
158 label->setStyle (cellStyle);
159 table->addCell (label, 1, 1);
160 label->addText ("text = ", cellStyle);
161 label->flush ();
162
163 Embed *text = new Embed (textres);
164 textres->setText("Hi textarea");
165 text->setStyle (cellStyle);
166 table->addCell (text, 1, 1);
167
168 table->addRow (cellStyle);
169
170 Textblock *radiolabel1 = new Textblock(false);
171 radiolabel1->setStyle (cellStyle);
172 table->addCell (radiolabel1, 2, 1);
173 Embed *radio1 = new Embed (radiores1);
174 radiolabel1->addWidget (radio1, cellStyle);
175 radiolabel1->addText (" radio1", cellStyle);
176 radiolabel1->flush ();
177
178 table->addRow (cellStyle);
179 Textblock *radiolabel2 = new Textblock(false);
180 radiolabel2->setStyle (cellStyle);
181 table->addCell (radiolabel2, 2, 1);
182 Embed *radio2 = new Embed (radiores2);
183 radiolabel2->addWidget (radio2, cellStyle);
184 radiolabel2->addText (" radio2", cellStyle);
185 radiolabel2->flush ();
186
187 table->addRow (cellStyle);
188 Textblock *checklabel = new Textblock(false);
189 checklabel->setStyle (cellStyle);
190 table->addCell (checklabel, 2, 1);
191 Embed *check = new Embed (checkres);
192 checklabel->addWidget (check, cellStyle);
193 checklabel->addText (" check", cellStyle);
194 checklabel->flush ();
195
196 for(int i = 0; i < 2; i++) {
197 table->addRow (cellStyle);
198
199 Embed *sel = new Embed (selres[i]);
200 sel->setStyle (cellStyle);
201 table->addCell (sel, 2, 1);
202
203 selres[i]->addItem("item 1", true, false);
204
205 selres[i]->pushGroup("group 1", true);
206 selres[i]->addItem("item 1/1", true, false);
207 selres[i]->addItem("item 1/2", true, true);
208 selres[i]->addItem("item 1/3", false, false);
209 selres[i]->popGroup();
210
211 selres[i]->addItem("item 2", false, i == 1);
212
213 selres[i]->pushGroup("group 2", true);
214 selres[i]->addItem("item 2/1", true, false);
215 selres[i]->addItem("item 2/2", true, false);
216 selres[i]->addItem("item 2/3", false, false);
217 selres[i]->popGroup();
218
219 selres[i]->addItem("item 3", false, false);
220 }
221
222 table->addRow (cellStyle);
223 Embed *button = new Embed (buttonres);
224 button->setStyle (cellStyle);
225 table->addCell (button, 2, 1);
226
227 table->addRow (cellStyle);
228 Embed *cbutton = new Embed (cbuttonres);
229 cbutton->setStyle (cellStyle);
230 table->addCell (cbutton, 2, 1);
231
232 cellStyle->unref();
233
234 window->resizable(viewport);
235 window->show();
236 int errorCode = Fl::run();
237
238 delete form;
239 delete layout;
240
241 return errorCode;
242 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include <FL/Fl_Window.H>
22 #include <FL/Fl_Multi_Browser.H>
23 #include <FL/Fl.H>
24
25 int main (int argc, char *argv[])
26 {
27 Fl_Window *window = new Fl_Window (300, 300, "FLTK Browser");
28 window->box(FL_NO_BOX);
29 window->begin ();
30 Fl_Multi_Browser *browser = new Fl_Multi_Browser (0, 0, 300, 300);
31 browser->begin ();
32
33 for (int i = 0; i < 10; i++) {
34 browser->add("first");
35 browser->add("second");
36 browser->add("third");
37 }
38
39 window->resizable(browser);
40 window->show();
41 return Fl::run();
42 }
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "form.hh"
22
23 namespace form {
24
25 using namespace dw::core::ui;
26
27 Form::ResourceDecorator::ResourceDecorator (const char *name)
28 {
29 this->name = strdup (name);
30 }
31
32 Form::ResourceDecorator::~ResourceDecorator ()
33 {
34 free((char *)name);
35 }
36
37 Form::TextResourceDecorator::TextResourceDecorator (const char *name,
38 TextResource *resource):
39 Form::ResourceDecorator (name)
40 {
41 this->resource = resource;
42 }
43
44 const char *Form::TextResourceDecorator::getValue ()
45 {
46 return resource->getText ();
47 }
48
49 Form::RadioButtonResourceDecorator::RadioButtonResourceDecorator
50 (const char *name, RadioButtonResource *resource, const char **values):
51 Form::ResourceDecorator (name)
52 {
53 this->resource = resource;
54
55 int n = 0;
56 while (values[n])
57 n++;
58 this->values = new const char*[n + 1];
59 for (int i = 0; i < n; i++)
60 this->values[i] = strdup (values[i]);
61 this->values[n] = 0;
62 }
63
64 Form::RadioButtonResourceDecorator::~RadioButtonResourceDecorator ()
65 {
66 for (int i = 0; values[i]; i++)
67 free((char *)values[i]);
68 delete[] values;
69 }
70
71 const char *Form::RadioButtonResourceDecorator::getValue ()
72 {
73 RadioButtonResource::GroupIterator *it;
74 int i;
75 for (it = resource->groupIterator (), i = 0; it->hasNext (); i++) {
76 RadioButtonResource *resource = it->getNext ();
77 if(resource->isActivated ()) {
78 it->unref ();
79 return values[i];
80 }
81 }
82
83 it->unref ();
84 return NULL;
85 }
86
87 Form::CheckButtonResourceDecorator::CheckButtonResourceDecorator
88 (const char *name, CheckButtonResource *resource):
89 Form::ResourceDecorator (name)
90 {
91 this->resource = resource;
92 }
93
94 const char *Form::CheckButtonResourceDecorator::getValue ()
95 {
96 return resource->isActivated () ? "true" : NULL;
97 }
98
99 Form::SelectionResourceDecorator::SelectionResourceDecorator
100 (const char *name, SelectionResource *resource, const char **values):
101 Form::ResourceDecorator (name)
102 {
103 this->resource = resource;
104
105 int n = 0;
106 while (values[n])
107 n++;
108 this->values = new const char*[n + 1];
109 for(int i = 0; i < n; i++)
110 this->values[i] = strdup (values[i]);
111 this->values[n] = 0;
112 }
113
114 Form::SelectionResourceDecorator::~SelectionResourceDecorator ()
115 {
116 for(int i = 0; values[i]; i++)
117 free((char *)values[i]);
118 delete[] values;
119 }
120
121 const char *Form::SelectionResourceDecorator::getValue ()
122 {
123 valueBuf.clear();
124 int n = resource->getNumberOfItems ();
125 bool first = true;
126 for (int i = 0; i < n; i++) {
127 if (resource->isSelected (i)) {
128 if (!first)
129 valueBuf.append (", ");
130 valueBuf.append (values[i]);
131 first = false;
132 }
133 }
134
135 return valueBuf.getChars ();
136 }
137
138 void Form::FormActivateReceiver::activate (Resource *resource)
139 {
140 form->send (NULL, NULL, -1, -1);
141 }
142
143 void Form::FormActivateReceiver::enter (Resource *resource)
144 {
145 }
146
147 void Form::FormActivateReceiver::leave (Resource *resource)
148 {
149 }
150
151 Form::FormClickedReceiver::FormClickedReceiver (Form *form, const char *name,
152 const char *value)
153 {
154 this->form = form;
155 this->name = strdup (name);
156 this->value = strdup (value);
157 }
158
159 Form::FormClickedReceiver::~FormClickedReceiver ()
160 {
161 free((char *)name);
162 free((char *)value);
163 }
164
165 void Form::FormClickedReceiver::clicked (Resource *resource,
166 dw::core::EventButton *event)
167 {
168 form->send (name, value, event->xCanvas, event->yCanvas);
169 }
170
171 Form::Form ()
172 {
173 resources = new lout::container::typed::List <ResourceDecorator> (true);
174 activateReceiver = new FormActivateReceiver (this);
175 clickedReceivers =
176 new lout::container::typed::List <FormClickedReceiver> (true);
177 }
178
179 Form::~Form ()
180 {
181 delete resources;
182 delete activateReceiver;
183 delete clickedReceivers;
184 }
185
186 /**
187 * \brief Adds an instance of dw::core::ui::TextResource.
188 */
189 void Form::addTextResource (const char *name,
190 dw::core::ui::TextResource *resource)
191 {
192 resources->append (new TextResourceDecorator (name, resource));
193 resource->connectActivate (activateReceiver);
194 }
195
196 /**
197 * \brief Adds an instance of dw::core::ui::RadioButtonResource.
198 *
199 * This method has to be called only once for a group of radio buttons.
200 */
201 void Form::addRadioButtonResource (const char *name,
202 dw::core::ui::RadioButtonResource *resource,
203 const char **values)
204 {
205 resources->append (new RadioButtonResourceDecorator (name, resource,
206 values));
207 resource->connectActivate (activateReceiver);
208 }
209
210 /**
211 * \brief Adds an instance of dw::core::ui::CheckButtonResource.
212 */
213 void Form::addCheckButtonResource (const char *name,
214 dw::core::ui::CheckButtonResource *resource)
215 {
216 resources->append (new CheckButtonResourceDecorator (name, resource));
217 resource->connectActivate (activateReceiver);
218 }
219
220 /**
221 * \brief Adds an instance of dw::core::ui::SelectionResource.
222 */
223 void Form::addSelectionResource (const char *name,
224 dw::core::ui::SelectionResource *resource,
225 const char **values)
226 {
227 resources->append (new SelectionResourceDecorator (name, resource, values));
228 resource->connectActivate (activateReceiver);
229 }
230
231 /**
232 * \todo Comment this;
233 */
234 void Form::addButtonResource (const char *name,
235 dw::core::ui::ButtonResource *resource,
236 const char *value)
237 {
238 FormClickedReceiver *receiver =
239 new FormClickedReceiver (this, name, value);
240 resource->connectClicked (receiver);
241 clickedReceivers->append (receiver);
242 }
243
244 /**
245 * \todo Comment this;
246 */
247 void Form::send (const char *buttonName, const char *buttonValue, int x, int y)
248 {
249 for (lout::container::typed::Iterator <ResourceDecorator> it =
250 resources->iterator ();
251 it.hasNext (); ) {
252 ResourceDecorator *resource = it.getNext ();
253 const char *value = resource->getValue ();
254 if (value)
255 printf ("%s = %s; x=%d y=%d\n", resource->getName (), value, x, y);
256 }
257
258 if(buttonName && buttonValue)
259 printf ("%s = %s\n", buttonName, buttonValue);
260 }
261
262 } // namespace form
0 #ifndef __TEST_FORM_HH__
1 #define __TEST_FORM_HH__
2
3 #include "../dw/core.hh"
4 #include "../dw/ui.hh"
5
6 namespace form {
7
8 /**
9 * \brief Handles HTML form data.
10 *
11 * Add resources by calling the respective add...Resource method. Furtermore,
12 * this class impelements dw::core::ui::ButtonResource::ClickedReceiver, the
13 * form data is printed to stdout, when the "clicked" signal is received.
14 *
15 * \todo wrong comment
16 */
17 class Form
18 {
19 private:
20 /**
21 * \brief Decorates instances of dw::core::ui::Resource.
22 *
23 * This is the abstract base class, sub classes have to be defined to
24 * decorate specific sub interfaces of dw::core::ui::Resource.
25 */
26 class ResourceDecorator: public lout::object::Object
27 {
28 private:
29 const char *name;
30
31 protected:
32 ResourceDecorator (const char *name);
33 ~ResourceDecorator ();
34
35 public:
36 inline const char *getName () { return name; }
37 virtual const char *getValue () = 0;
38 };
39
40 /**
41 * \brief Decorates instances of dw::core::ui::TextResource.
42 */
43 class TextResourceDecorator: public ResourceDecorator
44 {
45 private:
46 dw::core::ui::TextResource *resource;
47
48 public:
49 TextResourceDecorator (const char *name,
50 dw::core::ui::TextResource *resource);
51 const char *getValue ();
52 };
53
54 /**
55 * \brief Decorates instances of dw::core::ui::RadioButtonResource.
56 *
57 * This class has to be instantiated only once for a group of radio
58 * buttons.
59 */
60 class RadioButtonResourceDecorator: public ResourceDecorator
61 {
62 private:
63 dw::core::ui::RadioButtonResource *resource;
64 const char **values;
65
66 public:
67 RadioButtonResourceDecorator (const char *name,
68 dw::core::ui::RadioButtonResource
69 *resource,
70 const char **values);
71 ~RadioButtonResourceDecorator ();
72 const char *getValue ();
73 };
74
75 /**
76 * \brief Decorates instances of dw::core::ui::CheckButtonResource.
77 */
78 class CheckButtonResourceDecorator: public ResourceDecorator
79 {
80 private:
81 dw::core::ui::CheckButtonResource *resource;
82
83 public:
84 CheckButtonResourceDecorator (const char *name,
85 dw::core::ui::CheckButtonResource
86 *resource);
87 const char *getValue ();
88 };
89
90 /**
91 * \brief Decorates instances of dw::core::ui::SelectionResource.
92 */
93 class SelectionResourceDecorator: public ResourceDecorator
94 {
95 private:
96 dw::core::ui::SelectionResource *resource;
97 const char **values;
98 lout::misc::StringBuffer valueBuf;
99
100 public:
101 SelectionResourceDecorator (const char *name,
102 dw::core::ui::SelectionResource *resource,
103 const char **values);
104 ~SelectionResourceDecorator ();
105 const char *getValue ();
106 };
107
108 class FormActivateReceiver: public dw::core::ui::Resource::ActivateReceiver
109 {
110 private:
111 Form *form;
112
113 public:
114 inline FormActivateReceiver (Form *form) { this->form = form; }
115
116 void activate (dw::core::ui::Resource *resource);
117 void enter (dw::core::ui::Resource *resource);
118 void leave (dw::core::ui::Resource *resource);
119 };
120
121 class FormClickedReceiver:
122 public dw::core::ui::Resource::ClickedReceiver
123 {
124 private:
125 Form *form;
126 const char *name, *value;
127
128 public:
129 FormClickedReceiver (Form *form, const char *name, const char *value);
130 ~FormClickedReceiver ();
131
132 void clicked(dw::core::ui::Resource *resource,
133 dw::core::EventButton *event);
134 };
135
136 lout::container::typed::List <ResourceDecorator> *resources;
137 FormActivateReceiver *activateReceiver;
138 lout::container::typed::List <FormClickedReceiver> *clickedReceivers;
139
140 public:
141 Form ();
142 ~Form ();
143
144 void addTextResource (const char *name,
145 dw::core::ui::TextResource *resource);
146 void addRadioButtonResource (const char *name,
147 dw::core::ui::RadioButtonResource *resource,
148 const char **values);
149 void addCheckButtonResource (const char *name,
150 dw::core::ui::CheckButtonResource *resource);
151 void addSelectionResource (const char *name,
152 dw::core::ui::SelectionResource *resource,
153 const char **values);
154 void addButtonResource (const char *name,
155 dw::core::ui::ButtonResource *resource,
156 const char *value);
157
158 void send (const char *buttonName, const char *buttonValue, int x, int y);
159 };
160
161 } // namespace form
162
163 #endif // __TEST_FORM_HH__
0 /*
1 * Dillo Widget
2 *
3 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20
21 #include "../dw/core.hh"
22
23 using namespace dw::core;
24 using namespace lout::misc;
25
26 int main()
27 {
28 Polygon poly;
29 poly.addPoint (50, 10);
30 poly.addPoint (90, 90);
31 poly.addPoint (10, 90);
32
33 printf("first test\n");
34 assert (poly.isPointWithin (50, 50));
35 printf("second test\n");
36 assert (!poly.isPointWithin (10, 10));
37 printf("third test\n");
38 assert (!poly.isPointWithin (90, 50));
39 }